| version 1.1, 2005/11/16 03:28:42 | 
version 1.25, 2007/02/02 17:50:09 | 
 | 
 | 
|  #!/usr/bin/perl
 | 
 #!/usr/bin/perl | 
|  # $RedRiver$
 | 
 # $RedRiver: update_trango.pl,v 1.24 2007/02/02 16:29:58 mike Exp $ | 
|  ########################################################################
 | 
 ######################################################################## | 
|  # update_trango.pl *** Updates trango foxes with a new firmware
 | 
 # update_trango.pl *** Updates trango hosts with a new firmware | 
|  # 
 | 
 # | 
|  # 2005.11.15 #*#*# andrew fresh <andrew@mad-techies.org>
 | 
 # 2005.11.15 #*#*# andrew fresh <andrew@mad-techies.org> | 
|  ########################################################################
 | 
 ######################################################################## | 
|  use strict;
 | 
 use strict; | 
|  use warnings;
 | 
 use warnings; | 
|  
 | 
  | 
|  use Net::Telnet;
 | 
 use YAML qw/ LoadFile /; | 
|  
 | 
 use Net::TFTP; | 
|   | 
 use lib '.'; | 
|   | 
 use Net::Telnet::Trango; | 
|   | 
 use RedRiver::Wireless; | 
|   | 
  | 
|   | 
 my $config_file = shift || 'update_trango.yaml'; | 
|   | 
 my $max_tries = 3; | 
|   | 
  | 
|   | 
 my $l = Mylogger->new( { log_prefix => 'UT' } ); | 
|   | 
  | 
|   | 
 $l->sp("Reading config file '$config_file'"); | 
|   | 
 my $conf = LoadFile($config_file); | 
|   | 
  | 
|   | 
 $l->sp("Reading hosts"); | 
|   | 
 my $hosts = RedRiver::Wireless::Read_Hosts(); | 
|   | 
  | 
|   | 
  | 
|   | 
  | 
|   | 
 foreach my $host (@{ $hosts }) { | 
|   | 
   next if $host->{group} !~ /^Trango/; | 
|   | 
   $l->sp(""); | 
|   | 
   $l->sp("Checking: $host->{name}"); | 
|   | 
   my $needs_reboot = 0; | 
|   | 
  | 
|   | 
   ## Connect and login. | 
|   | 
   my $t = new Net::Telnet::Trango ( | 
|   | 
     Timeout => 5, | 
|   | 
     Errmode => 'return', | 
|   | 
   ) or die "Couldn't make new connection: $!"; | 
|   | 
   $l->p("Connecting to $host->{name}"); | 
|   | 
   unless ( $t->open($host->{name}) ) { | 
|   | 
     $l->sp("Error connecting: $!"); | 
|   | 
     next; | 
|   | 
   } | 
|   | 
  | 
|   | 
   my $password = $host->{Telnet_Password} || $conf->{general}->{password}; | 
|   | 
  | 
|   | 
   if ($host->{group} !~ /Client$/) { | 
|   | 
     $l->p("Logging in"); | 
|   | 
     $t->login($password); | 
|   | 
     unless ($t->logged_in) { | 
|   | 
       $l->p('Failed!'); | 
|   | 
       $t->close; | 
|   | 
       next; | 
|   | 
     } | 
|   | 
  | 
|   | 
     $l->sp("Getting sudb"); | 
|   | 
     my $sudb = $t->sudb_view; | 
|   | 
     if ($sudb) { | 
|   | 
       foreach my $su (@{ $sudb }) { | 
|   | 
         $l->p("Getting su info $su->{suid}"); | 
|   | 
         my $su_info = $t->su_info( $su->{suid} ); | 
|   | 
         if ($su_info->{ip}) { | 
|   | 
           $l->sp("Adding host $su_info->{ip}"); | 
|   | 
           my $new_host = { | 
|   | 
             Telnet_Password => $host->{Telnet_Password}, | 
|   | 
             group   => $host->{group} . '-Client', | 
|   | 
             name    => $su_info->{ip}, | 
|   | 
             remarks => $su_info->{remarks}, | 
|   | 
           }; | 
|   | 
           push @{ $hosts }, $new_host; | 
|   | 
         } else { | 
|   | 
           $l->sp("Couldn't get su info for $su->{suid}"); | 
|   | 
           if ($su_info->{ERR}) { | 
|   | 
             $l->sp("ERR: $su_info->{ERR}"); | 
|   | 
           } | 
|   | 
           next; | 
|   | 
         } | 
|   | 
       } | 
|   | 
     } | 
|   | 
   } | 
|   | 
  | 
|   | 
   foreach my $firmware_type ('Firmware', 'FPGA') { | 
|   | 
  | 
|   | 
     if (! exists $conf->{$firmware_type}) { | 
|   | 
           $l->s("No configs for '$firmware_type'"); | 
|   | 
           $t->close; | 
|   | 
           next; | 
|   | 
         } | 
|   | 
  | 
|   | 
     my $host_type = $t->host_type; | 
|   | 
     if ($firmware_type eq 'FPGA') { | 
|   | 
       $host_type =~ s/\s.*$//; | 
|   | 
     } | 
|   | 
  | 
|   | 
     if (! exists $conf->{$firmware_type}->{$host_type}) { | 
|   | 
           $l->sp("No '$firmware_type' config for type $host_type"); | 
|   | 
           $t->close; | 
|   | 
           next; | 
|   | 
         } | 
|   | 
  | 
|   | 
     if (! $t->logged_in) { | 
|   | 
       $l->p("Logging in"); | 
|   | 
       $t->login($password); | 
|   | 
       unless ($t->logged_in) { | 
|   | 
         $l->p('Failed!'); | 
|   | 
         $t->close; | 
|   | 
         next; | 
|   | 
       } | 
|   | 
     } | 
|   | 
  | 
|   | 
         foreach my $k (keys %{ $conf->{general} }) { | 
|   | 
           $conf->{$firmware_type}->{$host_type}->{$k} ||= $conf->{general}->{$k}; | 
|   | 
     } | 
|   | 
         $conf->{$firmware_type}->{$host_type}->{firmware_type} ||= $firmware_type; | 
|   | 
         $conf->{$firmware_type}->{$host_type}->{type} = $host_type; | 
|   | 
  | 
|   | 
     $l->sp("$host_type $firmware_type"); | 
|   | 
     $l->p("Sending commands"); | 
|   | 
     ## Send commands | 
|   | 
     my $rc = upload($t, $conf->{$firmware_type}->{$host_type}); | 
|   | 
         if ($rc) { | 
|   | 
       $l->sp("Successfull!"); | 
|   | 
           $needs_reboot = 1; | 
|   | 
     } elsif (defined $rc) { | 
|   | 
           $l->sp("Already up to date"); | 
|   | 
     } else { | 
|   | 
           $l->sp("Failed"); | 
|   | 
           $t->bye; | 
|   | 
           next; | 
|   | 
     } | 
|   | 
  | 
|   | 
   } | 
|   | 
  | 
|   | 
   if ($needs_reboot) { | 
|   | 
     $l->sp("Rebooting $host->{name}"); | 
|   | 
     $t->reboot; | 
|   | 
   } else { | 
|   | 
     $l->sp("Bye $host->{name}"); | 
|   | 
     $t->bye(); | 
|   | 
   } | 
|   | 
 } | 
|   | 
  | 
|   | 
 sub upload | 
|   | 
 { | 
|   | 
   my $t    = shift; | 
|   | 
   my $conf = shift; | 
|   | 
  | 
|   | 
   my $file = $conf->{firmware_path} . '/' . $conf->{file_name}; | 
|   | 
  | 
|   | 
   my $fw_type = $conf->{firmware_type}; | 
|   | 
  | 
|   | 
   my $ver = $t->ver; | 
|   | 
  | 
|   | 
   if ( | 
|   | 
     $ver->{$fw_type . ' Version'}  eq $conf->{'ver'} && | 
|   | 
     $ver->{$fw_type . ' Checksum'} eq $conf->{'cksum'} | 
|   | 
   ) { | 
|   | 
     return 0; | 
|   | 
   } | 
|   | 
  | 
|   | 
   $l->sp("Config information:"); | 
|   | 
   $l->sp("  Hardware Type: $conf->{'type'}"); | 
|   | 
   $l->sp("  File Name:     $conf->{'file_name'}"); | 
|   | 
   $l->sp("  File Size:     $conf->{'file_size'}"); | 
|   | 
   $l->sp("  File Checksum: $conf->{'file_cksum'}"); | 
|   | 
   $l->sp("  Conf Version:  $conf->{'ver'}"); | 
|   | 
   $l->sp("  Cur  Version:  $ver->{$fw_type . ' Version'}"); | 
|   | 
   $l->sp("  Conf Checksum: $conf->{'cksum'}"); | 
|   | 
   $l->sp("  Cur  Checksum: $ver->{$fw_type . ' Checksum'}"); | 
|   | 
  | 
|   | 
   $l->sp("Updating"); | 
|   | 
   my $try = 0; | 
|   | 
   while (1) { | 
|   | 
     if ($try >= $max_tries) { | 
|   | 
       $l->sp("Couldn't update in $max_tries tries!"); | 
|   | 
       return; | 
|   | 
     } | 
|   | 
     $try++; | 
|   | 
  | 
|   | 
     #sysinfo($self->{'_host'}); | 
|   | 
  | 
|   | 
     $l->p("Enabling TFTPd"); | 
|   | 
     unless ($t->enable_tftpd) { | 
|   | 
       $l->sp("Couldn't enable tftpd"); | 
|   | 
       next; | 
|   | 
     } | 
|   | 
  | 
|   | 
     $l->p("Uploading file ($file)"); | 
|   | 
     # use tftp to push the file up | 
|   | 
     my $tftp = Net::TFTP->new($t->Host, Mode => 'octet'); | 
|   | 
  | 
|   | 
     unless ($tftp->put($file, $file)) { | 
|   | 
       $l->sp("Error uploading: " . $tftp->error); | 
|   | 
       next; | 
|   | 
     } | 
|   | 
  | 
|   | 
     $l->p("Checking upload ($conf->{'file_cksum'})"); | 
|   | 
     my $results = $t->tftpd; | 
|   | 
     # check the 'File Length' against ??? | 
|   | 
     if ( $results->{'File Checksum'} ne $conf->{'file_cksum'}) { | 
|   | 
       $l->sp( | 
|   | 
         "File checksum '" . $results->{'File Checksum'} . | 
|   | 
         " does not match config file '" . $conf->{'file_cksum'} . "'!" | 
|   | 
       ); | 
|   | 
       next; | 
|   | 
     } | 
|   | 
     $l->p("File checksum matches . . . "); | 
|   | 
  | 
|   | 
     if ($results->{'File Length'}   !~ /^$conf->{'file_size'} bytes/) { | 
|   | 
       $l->sp( | 
|   | 
         "File length '" . $results->{'File Length'} . | 
|   | 
         "does not match config file '" . $conf->{'file_size'} . " bytes'!" | 
|   | 
       ); | 
|   | 
       next; | 
|   | 
     } | 
|   | 
     $l->p("File length matches . . . "); | 
|   | 
  | 
|   | 
     if ( uc($results->{'File Name'}) ne uc($file) ) { | 
|   | 
       $l->sp( | 
|   | 
         "File name '" . $results->{'File Name'} . | 
|   | 
         "' does not match config file '" . $file . "'!" | 
|   | 
       ); | 
|   | 
       next; | 
|   | 
     } | 
|   | 
     $l->p("File name  matches . . . "); | 
|   | 
  | 
|   | 
     my $image_type = 'mainimage'; | 
|   | 
     if ($fw_type eq 'FPGA') { | 
|   | 
       $image_type = 'fpgaimage'; | 
|   | 
     } | 
|   | 
     $l->p("Updating $image_type (new checksum '$conf->{'cksum'}')"); | 
|   | 
     unless ($results = $t->updateflash( | 
|   | 
       args => $image_type . ' ' . $ver->{$fw_type . ' Checksum'} . | 
|   | 
               ' '          . $conf->{'cksum'}, | 
|   | 
       Timeout => 90, | 
|   | 
     ) ) { | 
|   | 
       $l->sp("Couldn't update flash: $!"); | 
|   | 
       next; | 
|   | 
     } | 
|   | 
  | 
|   | 
     unless ( | 
|   | 
       defined $results->{'Checksum'} && | 
|   | 
       $results->{'Checksum'} eq $conf->{'cksum'} | 
|   | 
     ) { | 
|   | 
       $l->sp("Saved checksum " . $results->{'Checksum'} . " does not match config file " .  $conf->{'cksum'} . "!"); | 
|   | 
       next; | 
|   | 
     } | 
|   | 
     $l->p("Uploaded checksum ($results->{'Checksum'}) " . | 
|   | 
           "matches ($conf->{'cksum'})"); | 
|   | 
  | 
|   | 
     $l->sp("Successfully updated!"); | 
|   | 
     return 1; | 
|   | 
   } | 
|   | 
 } | 
|   | 
  | 
|   | 
 package Mylogger; | 
|   | 
  | 
|   | 
 use Fcntl ':flock'; # import LOCK_* constants | 
|   | 
 #use YAML; | 
|   | 
 use constant LOG_PRINT => 128; | 
|   | 
 use constant LOG_SAVE  =>  64; | 
|   | 
  | 
|   | 
 DESTROY { | 
|   | 
   my $self = shift; | 
|   | 
   if ($self->{'MYLOG'}) { | 
|   | 
     $self->p("Closing log ($self->{'log_path'}/$self->{'log_file'})"); | 
|   | 
     close $self->{'MYLOG'}; | 
|   | 
   } | 
|   | 
 } | 
|   | 
  | 
|   | 
 sub new { | 
|   | 
   my $package = shift; | 
|   | 
   my $self = shift || {}; | 
|   | 
  | 
|   | 
   $self->{'base_path'}  ||= '.'; | 
|   | 
   $self->{'log_path'}   ||= $self->{'base_path'}; | 
|   | 
   $self->{'log_prefix'} ||= 'LOG'; | 
|   | 
   $self->{'log_file'}   ||= GetLogName( | 
|   | 
     $self->{'log_prefix'}, | 
|   | 
     $self->{'log_path'} | 
|   | 
   ); | 
|   | 
   bless $self, $package; | 
|   | 
 } | 
|   | 
  | 
|   | 
 sub s | 
|   | 
 { | 
|   | 
   my $self = shift; | 
|   | 
   my $m = shift; | 
|   | 
   return $self->mylog($m, LOG_SAVE); | 
|   | 
 } | 
|   | 
  | 
|   | 
 sub p | 
|   | 
 { | 
|   | 
   my $self = shift; | 
|   | 
   my $m = shift; | 
|   | 
   return $self->mylog($m, LOG_PRINT); | 
|   | 
 } | 
|   | 
  | 
|   | 
 sub sp | 
|   | 
 { | 
|   | 
   my $self = shift; | 
|   | 
   my $m = shift; | 
|   | 
   return $self->mylog($m, LOG_SAVE | LOG_PRINT); | 
|   | 
 } | 
|   | 
  | 
|   | 
 sub mylog | 
|   | 
 { | 
|   | 
   my $self = shift; | 
|   | 
  | 
|   | 
   my $thing = shift; | 
|   | 
   chomp $thing; | 
|   | 
  | 
|   | 
   my $which = shift; | 
|   | 
  | 
|   | 
   my $MYLOG; | 
|   | 
   if ($which & LOG_PRINT) { | 
|   | 
     print $thing, "\n"; | 
|   | 
   } | 
|   | 
  | 
|   | 
   if ($which & LOG_SAVE) { | 
|   | 
     if ($self->{'MYLOG'}) { | 
|   | 
       $MYLOG = $self->{'MYLOG'}; | 
|   | 
     } else { | 
|   | 
       unless ($MYLOG) { | 
|   | 
                 open ($MYLOG, '>>', $self->{'log_path'} . '/' . $self->{'log_file'}) | 
|   | 
           or die "Couldn't open logfile!\n"; | 
|   | 
         my $ofh = select $MYLOG; | 
|   | 
         $|=1; | 
|   | 
         select $ofh; | 
|   | 
         $self->{'MYLOG'} = $MYLOG; | 
|   | 
  | 
|   | 
         $self->p("Opened log ($self->{'log_path'}/$self->{'log_file'})"); | 
|   | 
       } | 
|   | 
     } | 
|   | 
     flock($MYLOG, LOCK_EX); | 
|   | 
     print $MYLOG (scalar gmtime), "\t", $thing, "\n" | 
|   | 
       or die "Couldn't print to MYLOG: $!"; | 
|   | 
     flock($MYLOG, LOCK_UN); | 
|   | 
   } | 
|   | 
 } | 
|   | 
  | 
|   | 
 sub GetLogName | 
|   | 
 { | 
|   | 
   my $prefix  = shift || die "Invalid prefix passed for log"; | 
|   | 
  | 
|   | 
   my $logdate = GetLogDate(); | 
|   | 
   my $logver  = 0; | 
|   | 
   my $logname; | 
|   | 
  | 
|   | 
   do { | 
|   | 
     $logname = $prefix . $logdate . sprintf("%02d", $logver) . '.log'; | 
|   | 
     $logver++; | 
|   | 
   } until (not -e $logname); | 
|   | 
  | 
|   | 
   return $logname; | 
|   | 
 } | 
|   | 
  | 
|   | 
 sub GetLogDate | 
|   | 
 { | 
|   | 
   my ($sec,$min,$hour,$mday,$mon,$year,,,) = localtime(); | 
|   | 
  | 
|   | 
   $mon++; | 
|   | 
   $year += 1900; | 
|   | 
  | 
|   | 
   if ($min  < 10) { $min  = "0$min"  } | 
|   | 
   if ($sec  < 10) { $sec  = "0$sec"  } | 
|   | 
   if ($hour < 10) { $hour = "0$hour" } | 
|   | 
   if ($mday < 10) { $mday = "0$mday" } | 
|   | 
   if ($mon  < 10) { $mon  = "0$mon"  } | 
|   | 
  | 
|   | 
   my $time = $year . $mon . $mday; | 
|   | 
  | 
|   | 
   return $time; | 
|   | 
 } |