Question

I currently have an implementation using the pexpect python module, which interacts with Juniper, Cisco routers. It spawns a child application and runs command like 'show version' and logs the output.

I am looking for a better way to carry out this process, since if something changes on the switch end (a space or a colon in the prompt after an OS upgrade), then the program will not work. I think Juniper has an API to carry out such operations, but I don't think Cisco has one. I also need to extend this to other switches like HP etc.

Is there a generalized way I can approach this?

I also don't mind writing different code for different devices if required, if a more standard approach than pexpect exists.

Thanks

Was it helpful?

Solution

Using some version of expect is the accepted way of doing this. There's already a mature piece of OSS scripts for doing terminal interaction with most networking devices form most vendors. The same problem exists, but a lot more eyeballs are looking at it and updating things when vendors change prompts.

Check out RANCID.

Here's a quick example of a script I wrote based on RANCID to harvest ACL hit counts on ASAs:

#!/usr/bin/perl

use strict;
use Getopt::Std;

my $usage = "Usage: gethitcnt.pl -c configfile -o outputdir\n\n";
my %opts;

getopts('hc:o:', \%opts);

my $login = sprintf "~%s\/bin\/clogin.in", $ENV{'HOME'};
my $loginrc = sprintf "~%s\/.cloginrc", $ENV{'HOME'};
my $cmdfile = sprintf "~%s\/cmd", $ENV('HOME');
my $date = getdate;
my ($config,$outdir);

unless (-e $login) {
 die "Cannot find $login\n\n";
}

unless (-e $loginrc) {
 die "Cannot find $loginrc\n\n";
}

if ($opts{h} or !$opts{c}) {
 die $usage;
}

if ($opts{o}) {
 $outdir = $opts{o};
} else {
 $outdir = $ENV{'PWD'};
}

if (-e $opts{c}) {
 $config = getconfig($opts{c});
} else {
 die "Cannot open config file $opts{c}\n\n";
}

foreach my $firewall (keys %$config) {
 foreach my $acl (@{$config->{$firewall}}) {
  open (CMD,>$cmdfile);
  print CMD "show access-list $acl\n";
  print CMD "clear access-list $acl counters\n";
  close (CMD);
  my $command = ($login,"-x",$cmdfile,$firewall)
  open (TMP,"$command |");
  my $outfile = sprintf "%s\/%s-%s-%s.txt", $outdir, $firewall, $acl, $date;
  open (OUTFILE,>$outfile);
  foreach my $line (<TMP>) {
   if ($line =~ /\s*(access-list.*\(hitcnt=\d+\))/) {
    print OUTFILE "$1\n";
   }
  }
  system ("rm",$cmdfile);
 }
}

sub getconfig {
 my $configfile = shift;
 open(CONFIG,$configfile);
 my $out;
 foreach (<CONFIG>) {
  chomp;
  my @$elements = split(/,/);
  my $host = shift(@$elements);
  $out->{$host} = $elements;
 }
 close(CONFIG);
 return($out);
}

sub getdate {
 my @time = localtime;
 my @abbr = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
 my $month = $abbr[$time[4]];
 my $out = sprintf "%s-%d", $month, $time[3];
 return($out);
}

OTHER TIPS

In an ideal world, NETCONF would be the solution. It's basically a (set of) wire protocol(s) and some XML encoding for get/set/exec requests against network devices.

See:

Modern versions of JunOS support this, in addition to their older, proprietary interface:

http://www.juniper.net/support/products/junoscript/

Some Cisco hardware/software combinations support it - for example, NETCONF is present in the 12.2(33)SXI and 12.2SY/15.0SY trains on the 6500 sup720/sup2T. I believe all versions of NX-OS (Cisco Nexus) support NETCONF.

I don't know about HP. I do know Extreme XOS supports NETCONF in later versions in addition to Extreme's own XML API.

However - NETCONF is problematic in a few ways:

  1. Although the protocol is standardised, the supported data models aren't. So, you end up with very different XML from the different devices; you'll still need device-specific code. But at least you won't have to screen-scrape and abuse expect, and you'll have much more reliable error detection

  2. Some devices - for example, Cisco IOS - are not really XML under the hood. The XML is generated using a set of rule-based parsers (screen scrapers) and, in my testing, these are often incomplete or buggy. For example, contrary to what Cisco claims, "show run | format" does not "generate XML natively". It turns the Cisco line-based config into XML, and it breaks on a number of config constructs we use (e.g. address-family sub-stanzas of BGP router configs). We ended up not using XML for data and sticking to line-based Cisco commands/config - you still have to parse this, but at least you benefit from the NETCONF command framing and error codes.

  3. Although you can run NETCONF over SSH, many devices still won't perform SSH key-based login (Cisco IOS) meaning you still have to fiddle with hard-coded passwords.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top