Tuesday, September 17, 2013

A Perl script to collect information from Cisco IOS routers/switches


This is a journal about creating a perl script that establishes SSH session to Cisco IOS routers/switches and collect a list of information.



I started with:

google: perl script to ssh to cisco router
found: http://forums.freebsd.org/showthread.php?t=31396 posted by 'Business_Woman'. 

As per the post, she needed advice to enter the privileged EXEC mode, after a successful SSH session login

I checked my perl setup:
Platform: Windows 7
Perl: ActivePerl by ActiveState
Version: v5.10.1 (command: perl -v)

My perl doesn't support 'Net::SSH2'. Then:

google: Net::SSH2 for windows
found: http://www.perlmonks.org/?node_id=949926

I chose to try 1st option (cpan install) and as my network behind a proxy, then:

google: perl cpan set http_proxy
found: http://abhijit.name/setting_cpan_for_proxy.html

Followed the instructions, but got:

error:  LWP failed with code[301] message[Moved Permanently]
Warning: no success downloading 'C:\Perl\cpan\sources\authors\01mailrc.txt.gz.tmp7232'. Giving up on it. at C:\Perl\lib/CPAN/Index.pm line 225

Next, I tried the 2nd option, which was using Strawberry Perl. I downloaded and installed Strawberry Perl, which is now v5.18.1 (http://strawberryperl.com/). Now that I've Net::SSH2 library on my perl, I prepared my script based on 'Business_Woman' post and saved it as cisco.pl as follow:

use Net::SSH2;
use warnings;
use strict;
my $host = "10.10.1.1";            ### 10.10.1.1 is my target device
my $user = "admin";                 ### my ssh login is admin/cisco
my $password = "cisco";
my $ssh = Net::SSH2->new();
if(!$ssh->connect($host)){
        print("Connection Failed\n");
        exit(1);
}
if(!$ssh->auth_password($user,$password)){
        print("Authentication Failed");
        exit(1);
}
my $channel = $ssh->channel();
my $command = $channel->exec("sh ver") or die $_;
my $output;
my $len = $channel->read($output,2048);

Running it by the command: perl cisco.pl
Bingo! At this point, I got the same condition with 'Business_Woman'. The next step is to find way to enter the privileged EXEC mode:

google: perl script show running-config cisco ==>results: not on target
google: perl script cisco privilege mode ==>results: not on target
google: net::SSH2 documentation ==>results: not on target
google: perl net::ssh2 examples
found: http://www.perlmonks.org/?node_id=569657

On this URL, I came to the part '#to run multiple commands use a shell', then I modified my script as follow:

use Net::SSH2;
use warnings;
use strict;
my $host = "10.10.1.1";            ### 10.10.1.1 is my target device
my $user = "admin";                 ### my ssh login is admin/cisco
my $password = "cisco";
my $ssh = Net::SSH2->new();
if(!$ssh->connect($host)){
        print("Connection Failed\n");
        exit(1);
}
if(!$ssh->auth_password($user,$password)){
        print("Authentication Failed");
        exit(1);
}
my $channel = $ssh->channel();
$channel->blocking(0);
$channel->shell();
print $channel "enable\n";
print $channel "cisco\n";     ### enable password is cisco
print $channel "term len 0\n";
print "LINE : $_" while <$channel>;
print $channel "show clock\n";
print "LINE : $_" while <$channel>;
print $channel "dir\n";
print "LINE : $_" while <$channel>;

After few trials and error, my script (last 9 lines) became:

my $channel = $ssh->channel();
$channel->blocking(0);
$channel->shell();
print $channel "enable\n";
print $channel "cisco\n";     ### enable password is cisco
print $channel "term len 0\n";
print $channel "show clock\n";
print $channel "dir\n";
print "$_" while <$channel>;

At this point, it had achieved my target. To further shape the script:
1) remove the passwords from the scripts
2) write the output to a file instead of screen

google: perl input argument
found: http://stackoverflow.com/questions/361752/how-can-i-pass-command-line-arguments-to-a-perl-program

google: perl write to file
found: http://perl.about.com/od/perltutorials/a/readwritefiles_2.htm

From the found URLs, I made my target host as input variable, as well as the SSH password and enable secret. With that my script has the flexibility to be used for any devices with any password (not hardcoded on the script). Secondly, I wrote the show information results onto a file and I kept it as a log file. My script became:

use Net::SSH2;
use warnings;
use strict;
my $host = $ARGV[0];            ### target host as input string1
my $user = "admin";
my $password = $ARGV[1];        ### ssh password as input string2
my $secret = $ARGV[2];        ### enable password as input string3
my $ssh = Net::SSH2->new();
if(!$ssh->connect($host)){
        print("Connection Failed\n");
        exit(1);
}
if(!$ssh->auth_password($user,$password)){
        print("Authentication Failed");
        exit(1);
}
my $channel = $ssh->channel();
$channel->blocking(0);
$channel->shell();
print $channel "enable\n";
print $channel "$secret\n";     ### enable password from input string3
print $channel "term len 0\n";
print $channel "show clock\n";
print $channel "dir\n";
open (my $OUTPUTFILE, ">$host.log") or die "Can't open $host.log: $!";   ### target host as filename
print $OUTPUTFILE "$_" while <$channel>;
close $OUTPUTFILE or die "$OUTPUTFILE: $!";

Running it by the command: perl cisco.pl 10.10.1.1 cisco cisco
(My target host is '10.10.1.1', ssh password is 'cisco', enable password is 'cisco'; generated output filename is '10.10.1.1.log')
At this point, the result written on the file contained many newlines between the messages, so:

google: perl trim empty lines  ==>results: not on target
google: perl remove newline from $_ ==>results: not on target
google: perl $_ condition
found: http://perlmaven.com/the-default-variable-of-perl

This URL shows the elegant way of using $_ (perl default variable) and from the example, it shows the usage of 'chomp' too. I modified my script (last 6 lines) as follow:

open (my $OUTPUTFILE, ">$host.log") or die "Can't open $host.log: $!";   ### target host as filename
while (<$channel>) {
chomp;
print $OUTPUTFILE "$_";
} close $OUTPUTFILE or die "$OUTPUTFILE: $!";

This is the end of the journal, my script is now looks like the following:
#
# usage: perl cisco.pl (target_host) (ssl_password) (enable_password)
#
use Net::SSH2;
use warnings;
use strict;
my $host = $ARGV[0];            ### target host as input string1
my $user = "admin";
my $password = $ARGV[1];        ### ssh password as input string2
my $secret = $ARGV[2];        ### enable password as input string3
my $ssh = Net::SSH2->new();
if(!$ssh->connect($host)){
        print("Connection Failed\n");
        exit(1);
}
if(!$ssh->auth_password($user,$password)){
        print("Authentication Failed");
        exit(1);
}
my $channel = $ssh->channel();
$channel->blocking(0);
$channel->shell();
print $channel "enable\n";
print $channel "$secret\n";     ### enable password from input string3
print $channel "term len 0\n";
print $channel "show clock\n";
print $channel "dir\n";
open (my $OUTPUTFILE, ">$host.log") or die "Can't open $host.log: $!";   ### target host as filename
while (<$channel>) {
chomp;
print $OUTPUTFILE "$_";
}
 
close $OUTPUTFILE or die "$OUTPUTFILE: $!";

10 comments:

Anonymous said...

Thank you :)

Finopat said...

Hi, thanks a lot for this script.

Unknown said...

Thank you for sharing, it has been very useful for me

Regards

Nurmawan said...

you are all most welcome. feel free to share with others

Olengpotpot said...

Hi Nurmawan,

this is an excellent stuff! However I did some modifications. The problem is the output will generate 2 command result. Meaning if I ask the program to perform a terminal length 0 and show version, the output will generate 2 result on the said command. Do you have any suggestion on how we can resolve this?

#!c:/perl/bin/perl.exe
use Net::SSH2;
use strict;
use warnings;

#Citynet Devices
my $XRXCITPHIT_2XD01="XX.XXX.XXX.XX";
my $XRXCITPHGOS_2XD01="XX.XXX.XXX.XX";

#TACACS Account Login
#my $CTNetAd="USERNAME";
my $CTNetpass1="PASSWD1";
my $CTNetenapass="PASSWD2";

my $host="xxxx";


my $ssh = Net::SSH2->new();
if(!$ssh->connect($XRXCITPHIT_2XD01)){
print("Connection Failed\n");
exit(1);
}
my $CTNetAd="30194159";
if(!$ssh->auth_password($CTNetAd,$CTNetpass1)){
print("Authentication Failed");
exit(1);
}
my $channel = $ssh->channel();
$channel->blocking(0);
$channel->shell();


open (my $OUTPUTFILE, ">$host.log") or die "Can't open $host.log: $!"; ### target host as filename
while (<$channel>) {
chomp;
print $channel "term leng 0\n";
print $channel "sh version\n";
print $OUTPUTFILE "$_" while <$channel>;
}
close $OUTPUTFILE or die "$OUTPUTFILE: $!";

Hope you can help me on this. TIA!

Nurmawan said...

my2cents: i guess it was due to the print statements within the 'while {}' command. the 'print $channel' should be placed prior to the 'while {}' command and before the writing to the output file. ('open (my $OUTPUTFILE...')

Anonymous said...

hello
the script work well for cisco IOS and IOS-XE, butnot for IOS-XR.
Any idea?

thanks

vivek said...

Hi Nurmawan,

I have tried your script. it works very well but i'm endup creating empty files.
Means, script is working and log files also created but its blank.
Can u suggest ??

Thanks,
Vivek

Nurmawan said...

@Vivek, my best guess is on the print command. Try printing it out to the screen to see if variable contains data. insert this line: print "$_" while <$channel>; before open (my $OUTPUTFILE, ">$host.log")

Nurmawan said...

@anonymous, sorry, i don't have IOS-XR to test the script