#!\usr\bin\perl
# Written by Richard Graves
#
#
# ARGV[0] = Device
# ARGV[1] = Command list file
# ARGV[2] = Username
# ARGV[3] = Password
# ARGV[4] = Enable Password
# ARGV[5] = Output Filename
#
# Please remember that you MUST use the 'wr mem' and 'wr net' command
# from the command file.  These commands will not run automatically!!!
#--------------------------------------------------------------------------
use strict;
use Net::SSH::Perl; 
use Net::SSH::Perl::Constants qw( :msg ); 
use strict; 
no warnings 'uninitialized';

my ($session, $host, $file, $SSHUSER, $SSHPWD, $ENBPWD, $OutputFile);

$host=$ARGV[0];
$file=$ARGV[1];
$SSHUSER=$ARGV[2];
$SSHPWD=$ARGV[3];
$ENBPWD=$ARGV[4];
$OutputFile=$ARGV[5];

print ("\nDevice: $host");	#let me know what device I am working on

#--------------------------------------------------------------------------------------------
my (@CONFIGVAR, $CONFIGVAR);
my ($cntr, $line);

if (-e $file && -r $file)						#if file exists and is readable
{
	open(IN, "$file");							#open the file using the IN filehandle
}


while($line=<IN>)								#while data is coming in from file
{
		
	unless($line=~ m/^\!+/)						#unless the line starts with # do the following..
	{											
		$cntr++;
		if ($cntr > 20) 
		{
			die "Please limit number of commands to 20!";
		}
		else
		{
			push (@CONFIGVAR, $line);
		}
			
	}
	
	
}
close(IN);										#close the filehandle
												#close the filehandle
#--------------------------------------------------------------------------------------------


my ($Site, $Region, %devices);

open(OUTPUT, ">>$OutputFile");

use Net::Telnet::Cisco;						#use the telnet::cisco module

if ($host =~ m/\d+\.(\d+)\.(\d+)\.\d+/ )  #break out region and site subnets
	{
	 $Region = $1;
	 $Site = $2;
	}
 		
print (OUTPUT "\n$host");

#--------------------------------------------------------------------------------------------


$|++; 

  
use constant SKIP_PROMPT => 1; 												# pix prints login prompt twice, skip first 

my $time2login = 10; 
my $time2run = 20; 


																			# modify these in case of prompt (hostname) changes 
																			# assuming alphanumeric characters only: 
																			# [a-zA-Z0-9] is actually \w, but some hosts have '_' or '-' in their names 
my $cfg_prompt = qr/(?:[a-zA-Z0-9]+\(config\)#)\s*/; 						# alphanumeric followed by '(config)#'
my $enb_prompt = qr/(?:[a-zA-Z0-9]+#)\s*/; 									# alphanumeric followed by '#' 
my $reg_prompt = qr/(?:[a-zA-Z0-9]+>)\s*/; 									# alphanumeric followed by '>' 
my $pass_prompt = qr/Password:\s*/; 


my ($prompt_cnt,$save,$done) = (0,0,0); 
my ($ssh, @config); 


																			# login on the device 
eval { 
   local $SIG{'ALRM'} = sub { die 'TimedouT' }; 
   alarm $time2login; 
   $ssh = Net::SSH::Perl->new($host, protocol=>1, cipher=>'DES', port=>22); 
   $ssh->login($SSHUSER,$SSHPWD); 
   alarm 0; 
}; 


($@)? ( die '[',scalar localtime,'] ', ($@ =~ /TimedouT/)? 
                                       "Takes too long to login on $host.\n" : 
                                       "Unexpected eval err: $@.\n" 
      ) : undef; 



# set up handler and intercept everything that goes to STDOUT 


$ssh->register_handler(SSH_SMSG_STDOUT_DATA, sub { 
   my($ssh, $packet) = @_; 
   my $str = $packet->get_str; 


   if ( $save ) { # reading config 


      if ( $str =~ /$enb_prompt$/ ) { # last line of the config + prompt 
         my $packet = $ssh->packet_start(SSH_CMSG_STDIN_DATA); 
         $packet->put_str('exit ' . "\n"); 
         $packet->send; 
         $done++; 
      } 


      $str =~ s/\cM//g; 
      chomp $str; 
        
      # skip echo of the command and logout sequence 
      push @config, $str unless ( $done || $str =~ /^(\w|\s)$/ || 
                                  $str =~ /^:/ || $str eq '' ); 
    } 
    else { # login part 
       if ($str =~ /$reg_prompt$/) { # go to enable mode 
          $prompt_cnt++; # pix prints login prompt twice, remember 
          return unless $prompt_cnt > SKIP_PROMPT; 
          my $packet = $ssh->packet_start(SSH_CMSG_STDIN_DATA); 
          $packet->put_str('enable' . "\n"); 
          $packet->send; 
          $prompt_cnt = 0; # will resuse it in enable mode 
       } 
       elsif ( $str =~ /$pass_prompt$/ ) { 
          # going into enable mode.... 
          my $packet = $ssh->packet_start(SSH_CMSG_STDIN_DATA); 
          $packet->put_str("$ENBPWD\n"); 
          $packet->send; 
       } 
       elsif ( $str =~ /$enb_prompt$/ && !$prompt_cnt ) { 
          # exec first command in enable mode 
          my $packet = $ssh->packet_start(SSH_CMSG_STDIN_DATA); 
          $packet->put_str('' . "\n"); 
          $packet->send; 
          $prompt_cnt++; 
       } 
       elsif ( $str =~ /$enb_prompt$/ && $prompt_cnt == 1 ) { 
          # go into config mode 
          my $packet = $ssh->packet_start(SSH_CMSG_STDIN_DATA); 
          $packet->put_str('conf t' . "\n"); 
          $packet->send; 
          $prompt_cnt++;
       } 
       elsif ( $str =~ /$cfg_prompt$/ && $prompt_cnt == 2) { 
          # issue desired command in config mode 
          my $packet = $ssh->packet_start(SSH_CMSG_STDIN_DATA); 
          $packet->put_str(join ("", @CONFIGVAR) . "\n"); 
          $packet->send; 
          $prompt_cnt++;
       }
       elsif ( $str =~ /$cfg_prompt$/ && $prompt_cnt == 3 ) { 
          # exit config mode 
          my $packet = $ssh->packet_start(SSH_CMSG_STDIN_DATA); 
          $packet->put_str('exit' . "\n"); 
          $packet->send; 
          $prompt_cnt++;
          $save++; 
       }
       else { 
          # Uncomment this for debug purposes 
          # print "Useless data: $str\n"; 
       } 
   } 


}); 


eval { 
   local $SIG{'ALRM'} = sub { die 'TimedouT' }; 
   alarm $time2run; 
   $ssh->cmd(''); # thaaaat's right, nothing at all 
   alarm 0; 
}; 


($@)? ( die '[',scalar localtime,'] ', ($@ =~ /TimedouT/)? 
                                       "Timed out while talking to $host.\n" : 
                                       "Unexpected eval err: $@.\n" 
      ) : undef; 

print (OUTPUT "\tSuccessful");			#print to file
print ("\tSuccessful\n");					#print to screen

close(OUTPUT);





=head1 NAME

Combined Cisco Script

=head1 DESCRIPTION

This script is used to issue config commands to a cisco device using either telnet or SSHv1.

This is the SSH script, the other two scripts CCS_sub_telnet.pl and CCS_main.pl are also required

=head1 README


=head1 PREREQUISITES

This script requires:

Perl 5.8 or higher
Net::SSH::Perl 
Net::SSH::Perl::Constants
Net::Telnet
Net::Telnet::Cisco

=head1 COREQUISITES

CCS_sub_telnet.pl
CCS_main.pl


=pod OSNAMES

any

=pod SCRIPT CATEGORIES

Networking

=cut