#!/usr/local/bin/perl # # $Header: /home/metal1.nss/usra/monitor/airtalk/RCS/airtalk_get_readings,v 1.8 2010/01/21 15:33:18 doke Exp $ # # Telnet an airtalk uM260 monitoring device, # suck back the readings, # and store them in a file. # ####################### use vars qw( $airtalk_dir $logfile $device $passwd_file $timeout $email_addr $port ); $airtalk_dir = "/home/metal1.nss/usra/monitor/airtalk"; $logfile = "$airtalk_dir/airtalk_get_readings.log"; #$device = 'airtalk-nc31-18a-1.nss.udel.edu'; $device = '10.8.25.239'; $port = 10001; $passwd_file = "$airtalk_dir/airtalk_read.pw"; $timeout = 120; # seconds $email_addr = 'doke@udel.edu'; ##################### use strict; use warnings; use Expect; use Getopt::Long; $ENV{PATH} = "/usr/local/bin:/usr/sfw/bin:/opt/sfw/bin:/usr/bin"; use vars qw( $verbose $help $readings ); $verbose = 0; $help = 0; sub usage { my( $rc ) = @_; print "Usage: $0 [-vh] [-t n] -t n timeout after n seconds [$timeout] -v verbose -h help "; exit $rc; } Getopt::Long::Configure ("bundling"); GetOptions( 't=i' => \$timeout, 'v+' => \$verbose, 'h' => \$help, ); &usage( 0 ) if ( $help ); if ( ! $verbose ) { open( lH, ">>$logfile" ) || die "can't open log file: $!\n"; select lH; } $| = 1; &get_readings( $device ); exit 0; ############################## sub get_readings { my( $device ) = @_; my( $i, $msg, $file ); chdir $airtalk_dir; foreach $i ( 1 .. 5 ) { # try 5 times $msg = &get_readings_once( $device ); last if ( ! $msg ); # it worked sleep 5; } if ( $msg ) { $msg = "retrieval failed for $device: $msg"; &log( $msg ); #sendmail( $email_addr, $msg, $msg ); return; } $file = "$device.readings"; $verbose && print "writing buffer to $device file\n"; if ( ! open( fH, ">$file.new" ) ) { $msg = "can't open $device: $!\n"; &log( $msg ); return; } print fH $readings; close fH; chmod 0664, "$file.new"; $msg = &checkout( $file ); if ( $msg ) { $msg = "checkout failed for $device: $msg"; &log( $msg ); #sendmail( $email_addr, $msg, $msg ); return; } rename "$file.new", $file; $msg = &checkin( $file ); if ( $msg ) { $msg = "checkin failed for $file: $msg"; &log( $msg ); #sendmail( $email_addr, $msg, $msg ); return; } chmod 0444, $file; } # retrieve readings # return 0 if ok, message on error sub get_readings_once { my( $device ) = @_; my( $passwd, $cmd, $exp, $saw_prompt, $model, $version, $l ); if ( ! $verbose ) { # supposedly you can do $exp->log_stdout(0), but it doesn't work. $Expect::Log_Stdout = 0; } $passwd = get_passwd( $passwd_file ); if ( ! $passwd ) { return "couldn't get the password\n"; } $verbose && print "opening telnet connection to $device port $port\n"; #$cmd = "/usr/bin/telnet $device $port 2>&1"; $cmd = "/usr/bin/telnet $device $port"; $verbose && print "cmd $cmd\n"; $exp = new Expect; $exp->raw_pty(1); unless ( $exp->spawn( $cmd ) ) { return "$device: Cannot spawn $cmd: $!\n"; } if ( $verbose > 1 ) { $exp->exp_internal(1); $exp->debug(1); } $verbose && print "looking for password prompt\n"; $saw_prompt = 0; $exp->expect( $timeout, [ qr/Tring.*\n>/i, sub { exp_continue } ], [ qr/Connected to .*\n>/i, sub { exp_continue } ], [ qr/Escape character is.*\n>/i, sub { exp_continue } ], [ qr/Password.*>/i, sub { $saw_prompt = 1; } ], [ qr/Connection refused/i, sub { return } ], [ qr/node name or service name not known/i, sub { return } ], ); if ( ! $saw_prompt ) { $exp->hard_close(); return "** failed to get ${device}'s password prompt\n"; } $verbose && print "sending password\n"; $exp->send( $passwd . "\r\n" ); # have no idea why, but it seems to need the \n # Main menu #***************************************************************** # Monitor: uM260 LAN Version: 1.08 # # Name: MORRIS.nss.udel.edu Elevation: 110 # # # MAIN MENU # 1 Readings # 2 System settings # 3 Device settings # # Q Quit # # Choice> $model = ''; $version = ''; $saw_prompt = 0; $verbose && print "looking for menu\n"; $exp->expect( $timeout, [ qr/Monitor: (\w+)\s/i, sub { $model = ($exp->matchlist)[0]; $verbose && print "model $model\n"; exp_continue } ], [ qr/Version:\s+([\d\.]+)[\s\r\n]/i, sub { $version = ($exp->matchlist)[0]; $verbose && print "version $version\n"; exp_continue } ], [ qr/MAIN MENU/i, sub { exp_continue } ], [ qr/1 Readings/i, sub { exp_continue } ], [ qr/2 System settings/i, sub { exp_continue } ], [ qr/3 Device settings/i, sub { exp_continue } ], [ qr/Q Quit/i, sub { exp_continue } ], [ qr/Choice>/i, sub { $saw_prompt = 1; } ], ); if ( $model ne 'uM260' ) { return "** unknown model number: '$model'\n"; } if ( $version ne '1.08' ) { return "** unknown version number: '$version'\n"; } if ( ! $saw_prompt ) { $exp->hard_close(); return "** failed to get ${device}'s main menu prompt\n"; } $verbose && print "sending 1\n"; $exp->send( "1\n" ); # The readings data looks like this. Most lines have a space at the end. # Lines are terminated with carriage-return line-feed, except the last one # which just hangs the output after "mySwitch_50". # *** Acquiring data *** # #Device Location Type Thresh. Reading Last #-------------------------------------------------------------- # 1-1 MORRIS POWER OFF CPAMS TD CLOSED OK OK # 1-2 MORRIS EXCESS RUN CPAMS TD CLOSED OK OK # 1-3 HULLIHEN POWER OFF CPAMS TD CLOSED OK OK # 1-4 HULLIHEN EXCESS RUN CPAMS TD CLOSED OK OK # 2-1 MORRIS DRYER 018A CPA/30 9.0 10.7 11.2 # 2-2 HULLIHEN DRYER 007 CPA/30 9.0 10.2 10.5 # 2-3 MANIFOLD FLOW CF/47.5 20.0 49.0 * 61.0 # 2-4 MANIFOLD PRESSURE CPA/30 9.0 10.0 10.2 # 2-5 WOLF BASEMENT CPA/30 4.0 5.8 5.8 # 2-6 DUPONT COMM ROOM CPA/30 4.0 5.9 5.9 # 2-7 SHARP LAB BASEMENT CPA/30 7.0 9.1 9.0 # 2-8 EVANS BASEMENT CPA/30 4.0 5.1 5.1 # 2-9 BROWN MH-NC28MH CPA/30 4.0 4.9 4.8 # 2-10 MITCHELL MH-NC18MH CPA/30 8.0 9.2 9.3 # 2-11 ALISON BASEMENT CPA/30 8.0 8.8 8.8 # 2-12 CANNON MH-NC40MH CPA/30 7.0 7.4 7.3 # 2-13 ALISON MH-NC33MH CPA/30 8.0 8.9 8.9 # 2-14 MORRIS MH-NC32MH CPA/30 7.5 OPEN 8.5 # 2-15 ------- ------ ------ ------ # 2-16 ------- ------ ------ ------ # 3-1 mySwitch_50 $verbose && print "looking for readings\n"; $readings = ''; $exp->expect( $timeout, [ qr/Acquiring data/i, sub { exp_continue } ], [ qr/Device +Location.*\r\n/i, sub { $l = $exp->match(); #$verbose && print ">$l"; $readings .= $l; exp_continue } ], [ qr/---------.*\r\n/i, sub { $l = $exp->match(); #$verbose && print ">$l"; $readings .= $l; exp_continue } ], [ qr/\d-\d+ .*\r\n/i, sub { $l = $exp->match(); #$verbose && print ">$l"; $readings .= $l; exp_continue } ], [ qr/\d-\d+ *mySwitch_50/i, sub { # FIXME: is this the right way to terminate this? # it always hangs after emitting this $l = $exp->match(); #$verbose && print ">$l"; $readings .= $l . "\n"; } ], ); $verbose && print "sending q\n"; $exp->send( "q\n" ); $exp->hard_close(); $l = length $readings; if ( ! $l ) { return "** ${device} did not return any readings\n"; } $verbose && print "got $l bytes\n"; # strip out the ctrl-Ms $readings =~ s/\r//g; return 0; } sub checkout { my( $file ) = @_; my( $cmd, $rc ); if ( ! -d "RCS" ) { mkdir "RCS"; } if ( ! -f "RCS/${file},v" ) { # that's ok, we'll make one when we check in $verbose && print "$file doesn't have an RCS history file yet\n"; return 0; } if ( -w $file ) { # hmm already checked out? $verbose && print "$file is already writable\n"; return 0; } $cmd = "co -l -q -f $file"; $verbose && print "\$cmd = $cmd\n"; $rc = system( $cmd ); if ( ( $rc >> 8 ) > 0 ) { return "can't checkout $file\n"; } return 0; } sub checkin { my( $file ) = @_; my( $cmd, $rc ); $cmd = "ci -u -q $file < /dev/null"; $verbose && print "\$cmd = $cmd\n"; $rc = system( $cmd ); if ( $rc >> 8 > 0 ) { return "can't checkin $file\n"; } return 0; } sub get_passwd { my( $file ) = @_; my( $password, $msg ); $verbose && print "reading password from $file\n"; if ( ! open( pwfH, $file ) ) { $msg = "can't open $file: $!\n"; warn( $msg ); &log( $msg ); return undef; } $password = ; chomp( $password ); close pwfH; return $password } sub sendmail { my( $email_addr, $sub, $msg ) = @_; my( $cmd, $from ); $cmd = "/usr/lib/sendmail -- $email_addr"; if ( ! open( pH, "|$cmd" ) ) { $msg = "can't send email to $email_addr: $!\n"; warn( $msg ); &log( $msg ); return; } $from = getpwuid( $> ) . '@' . `hostname`; chomp( $from ); print pH qq{From: "gather_router_configs" <$from>\n}; print pH "To: $email_addr\n"; print pH "Subject: $sub\n"; print pH "\n"; print pH "$msg\n"; print pH ".\n"; close pH; } sub log { my( $msg ) = @_; chomp $msg; print lH scalar( localtime( time ) ), " ", $msg, "\n"; }