Annotation of trango/Net-Telnet-Trango/scripts/update_trango.pl, Revision 1.33
1.16 mike 1: #!/usr/bin/perl
1.33 ! andrew 2: # $RedRiver: update_trango.pl,v 1.32 2007/02/07 19:25:05 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.31 andrew 167: $l->sp("Failed");
168: $t->bye;
169: next;
170: }
1.22 andrew 171:
1.31 andrew 172: }
1.22 andrew 173:
1.31 andrew 174: if ($needs_reboot) {
175: $l->sp("Rebooting $host->{name}");
176: $t->reboot;
1.33 ! andrew 177: }
! 178: else {
1.31 andrew 179: $l->sp("Bye $host->{name}");
180: $t->bye();
181: }
1.22 andrew 182: }
183:
1.33 ! andrew 184: if ( !$processed ) {
1.31 andrew 185: $l->sp("");
186: $l->sp("Finished. No more hosts.");
187: last;
1.22 andrew 188: }
1.27 andrew 189: }
190:
1.33 ! andrew 191: sub upload {
1.31 andrew 192: my $t = shift;
193: my $conf = shift;
1.16 mike 194:
1.31 andrew 195: my $file = $conf->{firmware_path} . '/' . $conf->{file_name};
1.22 andrew 196:
1.31 andrew 197: my $fw_type = $conf->{firmware_type};
1.19 andrew 198:
1.31 andrew 199: my $ver = $t->ver;
1.26 andrew 200:
1.33 ! andrew 201: if (
! 202: !(
! 203: $ver->{ $fw_type . ' Version' } && $ver->{ $fw_type . ' Checksum' }
! 204: )
! 205: )
! 206: {
1.31 andrew 207: $l->sp("Error getting current version numbers");
208: return;
1.16 mike 209: }
210:
1.33 ! andrew 211: if ( $ver->{ $fw_type . ' Version' } eq $conf->{'ver'}
! 212: && $ver->{ $fw_type . ' Checksum' } eq $conf->{'cksum'} )
! 213: {
1.31 andrew 214: return 0;
1.22 andrew 215: }
1.16 mike 216:
1.31 andrew 217: $l->sp("Updating $fw_type");
218: $l->sp("Config information:");
219: $l->sp(" Hardware Type: $conf->{'type'}");
220: $l->sp(" File Name: $conf->{'file_name'}");
221: $l->sp(" File Size: $conf->{'file_size'}");
222: $l->sp(" File Checksum: $conf->{'file_cksum'}");
223: $l->sp(" Conf Version: $conf->{'ver'}");
224: $l->sp(" Cur Version: $ver->{$fw_type . ' Version'}");
225: $l->sp(" Conf Checksum: $conf->{'cksum'}");
226: $l->sp(" Cur Checksum: $ver->{$fw_type . ' Checksum'}");
227:
228: my $try = 0;
229: while (1) {
1.33 ! andrew 230: if ( $try >= $max_tries ) {
1.31 andrew 231: $l->sp("Couldn't update in $max_tries tries!");
232: return;
233: }
234: $try++;
235:
236: $l->p("Enabling TFTPd");
1.33 ! andrew 237: unless ( $t->enable_tftpd ) {
1.31 andrew 238: $l->sp("Couldn't enable tftpd");
239: next;
240: }
241:
242: $l->p("Uploading file ($conf->{file_name})");
1.33 ! andrew 243:
1.31 andrew 244: # use tftp to push the file up
1.33 ! andrew 245: my $tftp = Net::TFTP->new( $t->Host, Mode => 'octet' );
1.31 andrew 246:
1.33 ! andrew 247: unless ( $tftp->put( $file, $file ) ) {
! 248: $l->sp( "Error uploading: " . $tftp->error );
1.31 andrew 249: next;
250: }
251:
252: $l->p("Checking upload ($conf->{'file_cksum'})");
253: my $results = $t->tftpd;
1.33 ! andrew 254:
1.31 andrew 255: # check the 'File Length' against ???
1.33 ! andrew 256: if (
! 257: !(
! 258: $results->{'File Checksum'}
! 259: && $results->{'File Length'}
! 260: && $results->{'File Name'}
! 261: )
! 262: )
! 263: {
1.31 andrew 264: $l->sp("Unable to get results of upload");
265: next;
266: }
1.33 ! andrew 267: if ( $results->{'File Checksum'} ne $conf->{'file_cksum'} ) {
! 268: $l->sp( "File checksum ("
! 269: . $results->{'File Checksum'}
! 270: . ") does not match config file ("
! 271: . $conf->{'file_cksum'}
! 272: . ")!" );
1.31 andrew 273: next;
1.33 ! andrew 274: }
1.31 andrew 275: $l->p("File checksum matches . . . ");
276:
1.33 ! andrew 277: if ( $results->{'File Length'} !~ /^$conf->{'file_size'} bytes/ ) {
! 278: $l->sp( "File length ("
! 279: . $results->{'File Length'}
! 280: . ") does not match config file ("
! 281: . $conf->{'file_size'}
! 282: . " bytes)!" );
1.31 andrew 283: next;
284: }
285: $l->p("File length matches . . . ");
286:
1.33 ! andrew 287: if ( uc( $results->{'File Name'} ) ne uc($file) ) {
! 288: $l->sp( "File name ("
! 289: . $results->{'File Name'}
! 290: . ") does not match config file ("
! 291: . $file
! 292: . ")!" );
1.31 andrew 293: next;
294: }
295: $l->p("File name matches . . . ");
1.16 mike 296:
1.31 andrew 297: my $image_type = 'mainimage';
1.33 ! andrew 298: if ( $fw_type eq 'FPGA' ) {
1.31 andrew 299: $image_type = 'fpgaimage';
300: }
301: $l->p("Updating $image_type (new checksum '$conf->{'cksum'}')");
1.33 ! andrew 302: unless (
! 303: $results = $t->updateflash(
! 304: args => $image_type . ' '
! 305: . $ver->{ $fw_type . ' Checksum' } . ' '
! 306: . $conf->{'cksum'},
1.31 andrew 307: Timeout => 90,
1.33 ! andrew 308: )
! 309: )
! 310: {
1.31 andrew 311: $l->sp("Couldn't update flash: $!");
312: next;
313: }
1.16 mike 314:
1.33 ! andrew 315: unless ( defined $results->{'Checksum'}
! 316: && $results->{'Checksum'} eq $conf->{'cksum'} )
! 317: {
! 318: $l->sp( "Saved checksum "
! 319: . $results->{'Checksum'}
! 320: . " does not match config file "
! 321: . $conf->{'cksum'}
! 322: . "!" );
1.31 andrew 323: next;
324: }
1.16 mike 325:
1.31 andrew 326: $l->p("$fw_type saved checksum matches . . . ");
1.16 mike 327:
1.31 andrew 328: return 1;
1.16 mike 329: }
1.30 andrew 330: }
331:
1.33 ! andrew 332: sub parse_hosts {
1.30 andrew 333: my $src = shift;
334:
335: my @hosts;
1.33 ! andrew 336: foreach my $h ( @{$src} ) {
! 337: if ( $h->{name} =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.)(\d{1,3})-(\d{1,3})/ )
! 338: {
! 339: for ( $2 .. $3 ) {
1.30 andrew 340: my %cur_host;
1.33 ! andrew 341: foreach my $k ( keys %{$h} ) {
1.30 andrew 342: $cur_host{$k} = $h->{$k};
343: }
344: $cur_host{name} = $1 . $_;
1.33 ! andrew 345: if ( !grep { $cur_host{name} eq $h->{name} } @hosts ) {
1.30 andrew 346: push @hosts, \%cur_host;
347: }
348: }
1.33 ! andrew 349: }
! 350: else {
1.30 andrew 351: push @hosts, $h;
352: }
353: }
354:
355: return \@hosts;
1.16 mike 356: }
357:
358: package Mylogger;
359:
1.33 ! andrew 360: use Fcntl ':flock'; # import LOCK_* constants
! 361:
1.16 mike 362: #use YAML;
363: use constant LOG_PRINT => 128;
1.33 ! andrew 364: use constant LOG_SAVE => 64;
1.16 mike 365:
366: DESTROY {
1.31 andrew 367: my $self = shift;
1.33 ! andrew 368: if ( $self->{'MYLOG'} ) {
1.31 andrew 369: $self->p("Closing log ($self->{'log_path'}/$self->{'log_file'})");
370: close $self->{'MYLOG'};
371: }
1.16 mike 372: }
373:
374: sub new {
1.31 andrew 375: my $package = shift;
376: my $self = shift || {};
1.16 mike 377:
1.31 andrew 378: $self->{'base_path'} ||= '.';
379: $self->{'log_path'} ||= $self->{'base_path'};
380: $self->{'log_prefix'} ||= 'LOG';
1.33 ! andrew 381: $self->{'log_file'} ||=
! 382: GetLogName( $self->{'log_prefix'}, $self->{'log_path'} );
1.31 andrew 383: bless $self, $package;
1.16 mike 384: }
385:
1.33 ! andrew 386: sub s {
1.31 andrew 387: my $self = shift;
1.33 ! andrew 388: my $m = shift;
! 389: return $self->mylog( $m, LOG_SAVE );
1.16 mike 390: }
391:
1.33 ! andrew 392: sub p {
1.31 andrew 393: my $self = shift;
1.33 ! andrew 394: my $m = shift;
! 395: return $self->mylog( $m, LOG_PRINT );
1.16 mike 396: }
397:
1.33 ! andrew 398: sub sp {
1.31 andrew 399: my $self = shift;
1.33 ! andrew 400: my $m = shift;
! 401: return $self->mylog( $m, LOG_SAVE | LOG_PRINT );
1.16 mike 402: }
403:
1.33 ! andrew 404: sub mylog {
1.31 andrew 405: my $self = shift;
1.16 mike 406:
1.31 andrew 407: my $thing = shift;
408: chomp $thing;
1.16 mike 409:
1.31 andrew 410: my $which = shift;
1.16 mike 411:
1.31 andrew 412: my $MYLOG;
1.33 ! andrew 413: if ( $which & LOG_PRINT ) {
1.31 andrew 414: print $thing, "\n";
415: }
1.16 mike 416:
1.33 ! andrew 417: if ( $which & LOG_SAVE ) {
! 418: if ( $self->{'MYLOG'} ) {
1.31 andrew 419: $MYLOG = $self->{'MYLOG'};
1.33 ! andrew 420: }
! 421: else {
1.31 andrew 422: unless ($MYLOG) {
1.33 ! andrew 423: open( $MYLOG, '>>',
! 424: $self->{'log_path'} . '/' . $self->{'log_file'} )
! 425: or die "Couldn't open logfile!\n";
1.31 andrew 426: my $ofh = select $MYLOG;
1.33 ! andrew 427: $| = 1;
1.31 andrew 428: select $ofh;
429: $self->{'MYLOG'} = $MYLOG;
1.16 mike 430:
1.33 ! andrew 431: $self->p(
! 432: "Opened log ($self->{'log_path'}/$self->{'log_file'})");
1.31 andrew 433: }
434: }
1.33 ! andrew 435: flock( $MYLOG, LOCK_EX );
! 436: print $MYLOG ( scalar gmtime ), "\t", $thing, "\n"
! 437: or die "Couldn't print to MYLOG: $!";
! 438: flock( $MYLOG, LOCK_UN );
1.16 mike 439: }
440: }
441:
1.33 ! andrew 442: sub GetLogName {
! 443: my $prefix = shift || die "Invalid prefix passed for log";
1.16 mike 444:
1.31 andrew 445: my $logdate = GetLogDate();
446: my $logver = 0;
447: my $logname;
448:
449: do {
1.33 ! andrew 450: $logname = $prefix . $logdate . sprintf( "%02d", $logver ) . '.log';
1.31 andrew 451: $logver++;
1.33 ! andrew 452: } until ( not -e $logname );
1.16 mike 453:
1.31 andrew 454: return $logname;
1.16 mike 455: }
456:
1.33 ! andrew 457: sub GetLogDate {
! 458: my ( $sec, $min, $hour, $mday, $mon, $year,,, ) = localtime();
1.16 mike 459:
1.31 andrew 460: $mon++;
461: $year += 1900;
1.16 mike 462:
1.33 ! andrew 463: if ( $min < 10 ) { $min = "0$min" }
! 464: if ( $sec < 10 ) { $sec = "0$sec" }
! 465: if ( $hour < 10 ) { $hour = "0$hour" }
! 466: if ( $mday < 10 ) { $mday = "0$mday" }
! 467: if ( $mon < 10 ) { $mon = "0$mon" }
1.16 mike 468:
1.31 andrew 469: my $time = $year . $mon . $mday;
1.16 mike 470:
1.31 andrew 471: return $time;
1.16 mike 472: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>