比期望模块更好的方法在网络设备上执行远程命令

时间:2011-09-01 14:30:46

标签: networking network-programming automation cisco pexpect

我目前有一个使用 pexpect python模块的实现,该模块与Juniper,Cisco路由器交互。它生成一个子应用程序并运行'show version'之类的命令并记录输出。

我正在寻找一种更好的方法来执行此过程,因为如果交换机端发生了某些变化(操作系统升级后提示符中的空格或冒号),则程序将无法运行。我认为瞻博网络有一个API来执行此类操作,但我认为思科没有。我还需要将其扩展到HP等其他开关。

有没有一种通用的方法可以解决这个问题?

如果需要,我也不介意为不同的设备编写不同的代码,如果存在比pexpect更标准的方法。

由于

2 个答案:

答案 0 :(得分:2)

使用某些版本的expect是可以接受的方式。已经有一个成熟的OSS脚本用于与大多数供应商的大多数网络设备进行终端交互。存在同样的问题,但是当供应商改变提示时,更多的眼球正在关注它并更新事物。

查看RANCID

以下是我基于RANCID编写的脚本的快速示例,用于获取ASA上的ACL命中数:

#!/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);
}

答案 1 :(得分:2)

在理想的世界中,NETCONF将成为解决方案。它基本上是一组(一组)有线协议和一些针对网络设备的get / set / exec请求的XML编码。

请参阅:

现代版的JunOS除了支持旧的专有界面外,还支持此功能:

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

一些思科硬件/软件组合支持它 - 例如,NETCONF存在于6500 sup720 / sup2T上的12.2(33)SXI和12.2SY / 15.0SY列车中。我相信所有版本的NX-OS(Cisco Nexus)都支持NETCONF。

我不知道惠普。我知道Extreme XOS除了支持Extreme自己的XML API之外,还支持更高版本的NETCONF。

然而 - NETCONF在某些方面存在问题:

  1. 虽然协议是标准化的,但支持的数据模型却不是。因此,您最终会得到与不同设备截然不同的XML;你仍然需要特定于设备的代码。但至少你不需要筛选和滥用预期,并且你将有更可靠的错误检测

  2. 某些设备(例如Cisco IOS)并不是真正的XML。 XML是使用一组基于规则的解析器(屏幕抓取器)生成的,在我的测试中,这些通常是不完整的或错误的。例如,与思科声称的相反,“show run | format”不会“本地生成XML”。它将基于Cisco线路的配置转换为XML,并打破了我们使用的许多配置结构(例如BGP路由器配置的地址族子节)。我们最终没有将XML用于数据并坚持使用基于行的Cisco命令/配置 - 您仍然需要解析它,但至少您可以从NETCONF命令框架和错误代码中受益。

  3. 虽然您可以通过SSH运行NETCONF,但许多设备仍然不会执行基于SSH密钥的登录(Cisco IOS),这意味着您仍然需要使用硬编码密码。