Travis C
Travis C

Reputation: 33

Telnet input buffer issues I think in perl

So for the past month I've been working on a script to do master clears on an HP procurve switch. I've talked with internal HP people and they're using a totally different system than I am so they were no help. I believe I have it all figured out. This is what my script i supposed to do:

  1. Establishes a telnet connection to switch
  2. Reboots switch via an SNMP trap to attached APC
  3. waits for input from switch to identify it's ready to access the extended boot rom
  4. accesses extended bootrom.
  5. changes directory to the compact flash card
  6. runs the list command on switch and dumps it to an array
  7. runs a foreach on each item in the array to identify what is firmware, a file and a folder A) ignores firmware B) Adds each file to a master deletion array C) runs a similar foreach on files in the folders and adds them to the master deletion array then the folder (for deletion purposes)
  8. runs a foreach to delete all items in the master deletion array to clear the switch
  9. reboots and sets up basic info on switch.

UPDATE

Ok so I couldn't get the $telnet->buffer_empty; to work 100% of the time, so what I decided to do is call a second list command and dump it to a garbage file. Below is my script and so far it's working on the 10 switches I've tested. My night shift is going to break the switches and test this reset script.

For anyone that's interested, this script is being used on a HP Procurve switch and from my research it will work for most of them, but not all.

#!/usr/bin/perl
$| = 1;


use Net::Telnet;
use Net::SNMP;
use strict;
use CGI;

#variabls for power, connections and systemname
my $power_host     = ('Power-APC');
my $power_port     = ('Power-APC-Port');
my $system_name    = ('system-name');
my $console_server = ('Console-Server');
my $console_mgmtA  = ('Console-ManagementA');
my $match;
my ($garbage, $trash);
my $master_value;
my $telnet;
my @array;
my @master_array;


checkForNull("Console-Server",$console_server,1);
checkForNull("Console-ManagementA",$console_mgmtA,1);
checkForNull("Power_Host",$power_host,1);
checkForNull("Power_Port",$power_port,1);
checkForNull("System_Name", $system_name,1);


#Starting reset script
print("Please wait while $system_name resets.\n");
print("Please do not interact with any consoles open to this device\n");
print("The reset will take approximitly 5 minutes\n");

#establishing Telnet connection
$telnet = new Net::Telnet (Timeout=>120, Errmode=>'return', Port => $console_mgmtA);

$telnet->open($console_server);
sleep(1);

#executing reboot via SNMP
print("Deleting configuration files.\n");

   my ($snmp, $error) = Net::SNMP->session(
      -hostname  => shift || "$power_host",
      -community => shift || 'private',
      -port      => shift || 161
   );
   if (!defined($snmp)) {
      printf("ERROR: %s.\n", $error);
      exit 1;
   }
   my $sysx = ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.$power_port";
   my $result = $snmp->set_request(
      -varbindlist => [$sysx, INTEGER, '3']
   );
   $snmp->close;




$telnet->waitfor(match=> '/Profiles/');
$telnet->cmd(string => '0', prompt => '/=>/');
#shift to compact flash card
$telnet->cmd (string=> 'cd cfa0', prompt => '/=>/');
my $garbage_output = $telnet -> cmd (string => 'ls', prompt => '/=>/');
$telnet->buffer_empty;


#propigates primary array with file list
undef @array;
@array = $telnet->cmd(string => 'ls', prompt => '/=>/');


#determines what should be deleted
foreach my $item (@array) {
    #cleans up the files for use in the if statement
   chomp ($item);
   #determines if it's a folder, passes folder name to sub
   if ($item=~ /\//) {
       subfolders ($item);
       #adds folder AFTER files in dir to ensure deletion
       push(@master_array, "rmdir $item");
   }
   #finds and ignores the primary and secondary firmware

   elsif ($item =~ /btm.swi|secondary.swi/) {
   }
   else {
    #adds all files to the master deletion list
    push(@master_array,"rm $item");
   }



#deletes all files listed in the master array
foreach $master_value (@master_array){
$telnet -> print ("$master_value");
}


#reboot switch
$telnet->print('boot');
$telnet->put(chr(13));
sleep(120);


#looking for ready screen
$telnet->waitfor(match=> '/continue/');
sleep (1);
$telnet->put(chr(13));
sleep (1);
$telnet->put(chr(13));
$telnet->waitfor(match=> '/continue/');
$telnet->put(chr(13));
$telnet->print('config');
$telnet->put(chr(13));
$telnet->print("hostname $system_name\n");
$telnet->print('lldp run');
$telnet->put(chr(13));
$telnet->print('wr mem');
$telnet->put(chr(13));
$telnet->print('boot');
$telnet->put(chr(13));
$telnet->print('y');


print("$system_name has been reset to factory settings.\n");
}


#add all files in folders to master deletion list
sub subfolders {
 my @subfolder_contents;
 my $subfolder = $_[0];
 my $file;

 @subfolder_contents = $telnet ->cmd(string => "ls $subfolder", prompt => '/=>/');
 foreach $file (@subfolder_contents){
  chomp ($file);
    push(@master_array, "rm $subfolder$file");
 }
};


sub checkForNull {
    my $name = $_[0];
    my $param = $_[1];
    my $required = $_[2];

    if($param eq "") {
        if ($required == 1) {
            print("\nSorry, no '$name' specified for this device, cannot continue.\n");
            exit 1;
        }
    }
# else {
 #       print("\n$name: $param\n");
  #  }
}

exit;

Upvotes: 3

Views: 2553

Answers (1)

Muir
Muir

Reputation: 442

There is a $telnet->buffer_empty which does what you ask for, but I don't think that is your problem (try it, though).

Are you sure your prompt isn't matching too soon? In other words, is there some command output that happens to also contain '=>'? The best way to debug is through $telnet->dump_log('/tmp/some_file'), which gives you hex dumps of everything that goes in and out of the connection (and more importantly, the exact sequence of reading and writing Net::Telnet does so you can tell if and where it is matching too early).

If you are matching too early with the '=>' prompt, just try making the regex a little stricter. Adding '\z' to check for the end of the string would be a good start, but be careful if there is a space after the '=>' (so something like '/=> ?\z/'). (But, of course, '\z' is not really "the end", but rather whatever Net::Telnet has collected so far. So if one unlucky packet in the middle of the output happens to end with '=> ' then it will break the stricter regex, too)

Also, your long sequence of "prints" looks suspect since print doesn't wait for the prompt before returning. It is better practice to always set the "Prompt" field of the Net::Telnet object, and then use ->cmd which will wait for the prompt before returning.

Upvotes: 2

Related Questions