Annotation of trango/Net-Telnet-Trango/scripts/update_trango.pl, Revision 1.34
1.16 mike 1: #!/usr/bin/perl
1.34 ! andrew 2: # $RedRiver: update_trango.pl,v 1.33 2007/02/07 22:07:35 andrew Exp $
1.16 mike 3: ########################################################################
1.25 andrew 4: # update_trango.pl *** Updates trango hosts with a new firmware
1.33 andrew 5: #
1.16 mike 6: # 2005.11.15 #*#*# andrew fresh <andrew@mad-techies.org>
1.32 andrew 7: ########################################################################
8: # Copyright (C) 2005, 2006, 2007 by Andrew Fresh
9: #
10: # This program is free software; you can redistribute it and/or modify
11: # it under the same terms as Perl itself.
1.16 mike 12: ########################################################################
13: use strict;
14: use warnings;
15:
1.23 andrew 16: use YAML qw/ LoadFile /;
1.16 mike 17: use Net::TFTP;
18: use Net::Telnet::Trango;
19:
1.22 andrew 20: my $config_file = shift || 'update_trango.yaml';
1.16 mike 21: my $max_tries = 3;
22:
23: my $l = Mylogger->new( { log_prefix => 'UT' } );
24:
25: $l->sp("Reading config file '$config_file'");
1.22 andrew 26: my $conf = LoadFile($config_file);
1.16 mike 27:
1.27 andrew 28: my $hosts;
29: if (@ARGV) {
1.33 andrew 30: @{$hosts} = map { { name => $_, group => 'Trango-Client' } } @ARGV;
31: }
32: else {
33: $hosts = parse_hosts( $conf->{hosts} );
1.27 andrew 34: }
35:
36: #@{ $hosts } = grep { $_->{name} eq '10.100.7.2' } @{ $hosts };
1.22 andrew 37:
1.27 andrew 38: my $global_tries = $max_tries * 2;
1.33 andrew 39: while ( $global_tries > 0 ) {
1.31 andrew 40: $global_tries--;
41: my $processed = 0;
1.25 andrew 42:
1.33 andrew 43: foreach my $host ( @{$hosts} ) {
1.25 andrew 44:
1.33 andrew 45: if ( !exists $host->{retry} ) {
1.31 andrew 46: $host->{tries} = 0;
47: $host->{retry} = 1;
48: }
49:
1.33 andrew 50: if ( $host->{tries} >= $max_tries ) {
1.31 andrew 51: $host->{retry} = 0;
52: }
53:
1.33 andrew 54: if ( $host->{retry} <= 0 ) {
1.31 andrew 55: next;
56: }
57:
58: $host->{tries}++;
59: $processed++;
60:
61: $l->sp("");
62: $l->sp("Checking: $host->{name} (try $host->{tries})");
63: my $needs_reboot = 0;
64:
65: ## Connect and login.
1.33 andrew 66: my $t = new Net::Telnet::Trango(
1.31 andrew 67: Timeout => 5,
68: Errmode => 'return',
69: ) or die "Couldn't make new connection: $!";
70: $l->p("Connecting to $host->{name}");
1.33 andrew 71: unless ( $t->open( $host->{name} ) ) {
1.31 andrew 72: $l->sp("Error connecting: $!");
73: next;
74: }
75:
76: my $password = $host->{Telnet_Password} || $conf->{general}->{password};
1.27 andrew 77:
1.31 andrew 78: $l->p("Logging in");
79: $t->login($password);
80: unless ($t->logged_in) {
81: $l->p('Failed!');
82: $t->close;
83: next;
84: }
85:
86: $l->sp("Getting sudb");
87: my $sudb = $t->sudb_view;
88: if ($sudb) {
89: foreach my $su (@{ $sudb }) {
90: $l->p("Getting su info $su->{suid}");
1.33 andrew 91: my $su_info = $t->su_info( $su->{suid} );
1.31 andrew 92: if ($su_info->{ip}) {
93: if (grep { $_->{name} eq $su_info->{'ip'} } @{ $hosts }) {
94: $l->p("Already have $su_info->{ip}");
95: next;
96: }
97: $l->sp("Adding host $su_info->{ip}");
98: my $new_host = {
99: password => $host->{password},
100: name => $su_info->{ip},
101: remarks => $su_info->{remarks},
102: };
103: push @{ $hosts }, $new_host;
104: } else {
105: $l->sp("Couldn't get su info for $su->{suid}");
106: $l->sp("ERR: " . $t->last_error);
107: }
108: }
109: }
110:
1.33 andrew 111: foreach my $firmware_type ( 'Firmware', 'FPGA' ) {
1.31 andrew 112:
1.33 andrew 113: if ( !exists $conf->{$firmware_type} ) {
1.31 andrew 114: $l->s("No configs for '$firmware_type'");
115: next;
116: }
1.25 andrew 117:
1.31 andrew 118: my $host_type = $t->host_type;
1.33 andrew 119: if ( $firmware_type eq 'FPGA' ) {
1.31 andrew 120: $host_type =~ s/\s.*$//;
121: }
1.21 mike 122:
1.33 andrew 123: if ( !exists $conf->{$firmware_type}->{$host_type} ) {
1.31 andrew 124: $l->sp("No '$firmware_type' config for type $host_type");
125: next;
126: }
1.22 andrew 127:
1.33 andrew 128: if ( $firmware_type eq 'Firmware'
129: && $t->firmware_version eq
130: $conf->{$firmware_type}->{$host_type}->{ver} )
131: {
1.31 andrew 132: $l->sp("Firmware already up to date");
133: next;
134: }
1.22 andrew 135:
1.33 andrew 136: if ( !$t->logged_in ) {
1.31 andrew 137: $l->p("Logging in");
138: $t->login($password);
139: unless ($t->logged_in) {
140: $l->p('Failed!');
141: $t->close;
142: last;
143: }
144: }
1.28 andrew 145:
1.33 andrew 146: foreach my $k ( keys %{ $conf->{general} } ) {
147: $conf->{$firmware_type}->{$host_type}->{$k} ||=
148: $conf->{general}->{$k};
1.31 andrew 149: }
1.33 andrew 150: $conf->{$firmware_type}->{$host_type}->{firmware_type} ||=
151: $firmware_type;
1.31 andrew 152: $conf->{$firmware_type}->{$host_type}->{type} = $host_type;
153:
154: $l->sp("$host_type $firmware_type");
155: ## Send commands
1.33 andrew 156: my $rc = upload( $t, $conf->{$firmware_type}->{$host_type} );
1.31 andrew 157: if ($rc) {
158: $l->sp("Successfull!");
159: $host->{retry}--;
160: $needs_reboot = 1;
1.33 andrew 161: }
162: elsif ( defined $rc ) {
1.31 andrew 163: $l->sp("Already up to date");
164: $host->{retry}--;
1.33 andrew 165: }
166: else {
1.34 ! andrew 167: $l->sp("Failed! - Bye $host->{name}");
! 168: $l->e("Error updating $firmware_type on $host->{name}" .
! 169: "(try $host->{tries})");
1.31 andrew 170: $t->bye;
1.34 ! andrew 171: # don't try any other firmware, don't want to reboot
! 172: last;
1.31 andrew 173: }
1.22 andrew 174:
1.31 andrew 175: }
1.22 andrew 176:
1.31 andrew 177: if ($needs_reboot) {
178: $l->sp("Rebooting $host->{name}");
179: $t->reboot;
1.33 andrew 180: }
181: else {
1.31 andrew 182: $l->sp("Bye $host->{name}");
183: $t->bye();
184: }
1.22 andrew 185: }
186:
1.33 andrew 187: if ( !$processed ) {
1.31 andrew 188: $l->sp("");
189: $l->sp("Finished. No more hosts.");
190: last;
1.22 andrew 191: }
1.27 andrew 192: }
193:
1.33 andrew 194: sub upload {
1.31 andrew 195: my $t = shift;
196: my $conf = shift;
1.16 mike 197:
1.31 andrew 198: my $file = $conf->{firmware_path} . '/' . $conf->{file_name};
1.22 andrew 199:
1.31 andrew 200: my $fw_type = $conf->{firmware_type};
1.19 andrew 201:
1.31 andrew 202: my $ver = $t->ver;
1.26 andrew 203:
1.33 andrew 204: if (
205: !(
206: $ver->{ $fw_type . ' Version' } && $ver->{ $fw_type . ' Checksum' }
207: )
208: )
209: {
1.31 andrew 210: $l->sp("Error getting current version numbers");
211: return;
1.16 mike 212: }
213:
1.33 andrew 214: if ( $ver->{ $fw_type . ' Version' } eq $conf->{'ver'}
215: && $ver->{ $fw_type . ' Checksum' } eq $conf->{'cksum'} )
216: {
1.31 andrew 217: return 0;
1.22 andrew 218: }
1.16 mike 219:
1.31 andrew 220: $l->sp("Updating $fw_type");
221: $l->sp("Config information:");
222: $l->sp(" Hardware Type: $conf->{'type'}");
223: $l->sp(" File Name: $conf->{'file_name'}");
224: $l->sp(" File Size: $conf->{'file_size'}");
225: $l->sp(" File Checksum: $conf->{'file_cksum'}");
226: $l->sp(" Conf Version: $conf->{'ver'}");
227: $l->sp(" Cur Version: $ver->{$fw_type . ' Version'}");
228: $l->sp(" Conf Checksum: $conf->{'cksum'}");
229: $l->sp(" Cur Checksum: $ver->{$fw_type . ' Checksum'}");
230:
231: my $try = 0;
232: while (1) {
1.33 andrew 233: if ( $try >= $max_tries ) {
1.31 andrew 234: $l->sp("Couldn't update in $max_tries tries!");
235: return;
236: }
237: $try++;
238:
239: $l->p("Enabling TFTPd");
1.33 andrew 240: unless ( $t->enable_tftpd ) {
1.31 andrew 241: $l->sp("Couldn't enable tftpd");
242: next;
243: }
244:
245: $l->p("Uploading file ($conf->{file_name})");
1.33 andrew 246:
1.31 andrew 247: # use tftp to push the file up
1.33 andrew 248: my $tftp = Net::TFTP->new( $t->Host, Mode => 'octet' );
1.31 andrew 249:
1.33 andrew 250: unless ( $tftp->put( $file, $file ) ) {
251: $l->sp( "Error uploading: " . $tftp->error );
1.31 andrew 252: next;
253: }
254:
255: $l->p("Checking upload ($conf->{'file_cksum'})");
256: my $results = $t->tftpd;
1.33 andrew 257:
1.31 andrew 258: # check the 'File Length' against ???
1.33 andrew 259: if (
260: !(
261: $results->{'File Checksum'}
262: && $results->{'File Length'}
263: && $results->{'File Name'}
264: )
265: )
266: {
1.31 andrew 267: $l->sp("Unable to get results of upload");
268: next;
269: }
1.33 andrew 270: if ( $results->{'File Checksum'} ne $conf->{'file_cksum'} ) {
271: $l->sp( "File checksum ("
272: . $results->{'File Checksum'}
273: . ") does not match config file ("
274: . $conf->{'file_cksum'}
275: . ")!" );
1.31 andrew 276: next;
1.33 andrew 277: }
1.31 andrew 278: $l->p("File checksum matches . . . ");
279:
1.33 andrew 280: if ( $results->{'File Length'} !~ /^$conf->{'file_size'} bytes/ ) {
281: $l->sp( "File length ("
282: . $results->{'File Length'}
283: . ") does not match config file ("
284: . $conf->{'file_size'}
285: . " bytes)!" );
1.31 andrew 286: next;
287: }
288: $l->p("File length matches . . . ");
289:
1.33 andrew 290: if ( uc( $results->{'File Name'} ) ne uc($file) ) {
291: $l->sp( "File name ("
292: . $results->{'File Name'}
293: . ") does not match config file ("
294: . $file
295: . ")!" );
1.31 andrew 296: next;
297: }
298: $l->p("File name matches . . . ");
1.16 mike 299:
1.31 andrew 300: my $image_type = 'mainimage';
1.33 andrew 301: if ( $fw_type eq 'FPGA' ) {
1.31 andrew 302: $image_type = 'fpgaimage';
303: }
304: $l->p("Updating $image_type (new checksum '$conf->{'cksum'}')");
1.33 andrew 305: unless (
306: $results = $t->updateflash(
307: args => $image_type . ' '
308: . $ver->{ $fw_type . ' Checksum' } . ' '
309: . $conf->{'cksum'},
1.31 andrew 310: Timeout => 90,
1.33 andrew 311: )
312: )
313: {
1.31 andrew 314: $l->sp("Couldn't update flash: $!");
315: next;
316: }
1.16 mike 317:
1.33 andrew 318: unless ( defined $results->{'Checksum'}
319: && $results->{'Checksum'} eq $conf->{'cksum'} )
320: {
321: $l->sp( "Saved checksum "
322: . $results->{'Checksum'}
323: . " does not match config file "
324: . $conf->{'cksum'}
325: . "!" );
1.31 andrew 326: next;
327: }
1.16 mike 328:
1.31 andrew 329: $l->p("$fw_type saved checksum matches . . . ");
1.16 mike 330:
1.31 andrew 331: return 1;
1.16 mike 332: }
1.30 andrew 333: }
334:
1.33 andrew 335: sub parse_hosts {
1.30 andrew 336: my $src = shift;
337:
338: my @hosts;
1.33 andrew 339: foreach my $h ( @{$src} ) {
340: if ( $h->{name} =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.)(\d{1,3})-(\d{1,3})/ )
341: {
342: for ( $2 .. $3 ) {
1.30 andrew 343: my %cur_host;
1.33 andrew 344: foreach my $k ( keys %{$h} ) {
1.30 andrew 345: $cur_host{$k} = $h->{$k};
346: }
347: $cur_host{name} = $1 . $_;
1.33 andrew 348: if ( !grep { $cur_host{name} eq $h->{name} } @hosts ) {
1.30 andrew 349: push @hosts, \%cur_host;
350: }
351: }
1.33 andrew 352: }
353: else {
1.30 andrew 354: push @hosts, $h;
355: }
356: }
357:
358: return \@hosts;
1.16 mike 359: }
360:
361: package Mylogger;
362:
1.33 andrew 363: use Fcntl ':flock'; # import LOCK_* constants
364:
1.16 mike 365: #use YAML;
366: use constant LOG_PRINT => 128;
1.33 andrew 367: use constant LOG_SAVE => 64;
1.34 ! andrew 368: use constant LOG_ERR => 1;
1.16 mike 369:
370: DESTROY {
1.31 andrew 371: my $self = shift;
1.33 andrew 372: if ( $self->{'MYLOG'} ) {
1.31 andrew 373: $self->p("Closing log ($self->{'log_path'}/$self->{'log_file'})");
374: close $self->{'MYLOG'};
375: }
1.16 mike 376: }
377:
378: sub new {
1.31 andrew 379: my $package = shift;
380: my $self = shift || {};
1.16 mike 381:
1.31 andrew 382: $self->{'base_path'} ||= '.';
383: $self->{'log_path'} ||= $self->{'base_path'};
384: $self->{'log_prefix'} ||= 'LOG';
1.33 andrew 385: $self->{'log_file'} ||=
386: GetLogName( $self->{'log_prefix'}, $self->{'log_path'} );
1.31 andrew 387: bless $self, $package;
1.16 mike 388: }
389:
1.33 andrew 390: sub s {
1.31 andrew 391: my $self = shift;
1.33 andrew 392: my $m = shift;
393: return $self->mylog( $m, LOG_SAVE );
1.16 mike 394: }
395:
1.33 andrew 396: sub p {
1.31 andrew 397: my $self = shift;
1.33 andrew 398: my $m = shift;
399: return $self->mylog( $m, LOG_PRINT );
1.16 mike 400: }
401:
1.33 andrew 402: sub sp {
1.31 andrew 403: my $self = shift;
1.33 andrew 404: my $m = shift;
405: return $self->mylog( $m, LOG_SAVE | LOG_PRINT );
1.16 mike 406: }
407:
1.34 ! andrew 408: sub e {
! 409: my $self = shift;
! 410: my $m = shift;
! 411: return $self->mylog( $m, LOG_ERR );
! 412: }
! 413:
1.33 andrew 414: sub mylog {
1.31 andrew 415: my $self = shift;
1.16 mike 416:
1.31 andrew 417: my $thing = shift;
418: chomp $thing;
1.16 mike 419:
1.31 andrew 420: my $which = shift;
1.16 mike 421:
1.31 andrew 422: my $MYLOG;
1.33 andrew 423: if ( $which & LOG_PRINT ) {
1.31 andrew 424: print $thing, "\n";
425: }
1.16 mike 426:
1.33 andrew 427: if ( $which & LOG_SAVE ) {
428: if ( $self->{'MYLOG'} ) {
1.31 andrew 429: $MYLOG = $self->{'MYLOG'};
1.33 andrew 430: }
431: else {
1.31 andrew 432: unless ($MYLOG) {
1.33 andrew 433: open( $MYLOG, '>>',
434: $self->{'log_path'} . '/' . $self->{'log_file'} )
435: or die "Couldn't open logfile!\n";
1.31 andrew 436: my $ofh = select $MYLOG;
1.33 andrew 437: $| = 1;
1.31 andrew 438: select $ofh;
439: $self->{'MYLOG'} = $MYLOG;
1.16 mike 440:
1.33 andrew 441: $self->p(
442: "Opened log ($self->{'log_path'}/$self->{'log_file'})");
1.31 andrew 443: }
444: }
1.33 andrew 445: flock( $MYLOG, LOCK_EX );
446: print $MYLOG ( scalar gmtime ), "\t", $thing, "\n"
447: or die "Couldn't print to MYLOG: $!";
448: flock( $MYLOG, LOCK_UN );
1.34 ! andrew 449: }
! 450:
! 451: if ( $which & LOG_ERR ) {
! 452: # XXX Could tie in here to handle some sort of notifications.
! 453: print STDERR $thing, "\n";
1.16 mike 454: }
455: }
456:
1.33 andrew 457: sub GetLogName {
458: my $prefix = shift || die "Invalid prefix passed for log";
1.16 mike 459:
1.31 andrew 460: my $logdate = GetLogDate();
461: my $logver = 0;
462: my $logname;
463:
464: do {
1.33 andrew 465: $logname = $prefix . $logdate . sprintf( "%02d", $logver ) . '.log';
1.31 andrew 466: $logver++;
1.33 andrew 467: } until ( not -e $logname );
1.16 mike 468:
1.31 andrew 469: return $logname;
1.16 mike 470: }
471:
1.33 andrew 472: sub GetLogDate {
473: my ( $sec, $min, $hour, $mday, $mon, $year,,, ) = localtime();
1.16 mike 474:
1.31 andrew 475: $mon++;
476: $year += 1900;
1.16 mike 477:
1.33 andrew 478: if ( $min < 10 ) { $min = "0$min" }
479: if ( $sec < 10 ) { $sec = "0$sec" }
480: if ( $hour < 10 ) { $hour = "0$hour" }
481: if ( $mday < 10 ) { $mday = "0$mday" }
482: if ( $mon < 10 ) { $mon = "0$mon" }
1.16 mike 483:
1.31 andrew 484: my $time = $year . $mon . $mday;
1.16 mike 485:
1.31 andrew 486: return $time;
1.16 mike 487: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>