Лучший способ выполнить удаленную команду на сетевых устройствах, чем ожидаемый модуль - PullRequest
0 голосов
/ 01 сентября 2011

В настоящее время у меня есть реализация, использующая модуль Python pexpect , который взаимодействует с Juniper, маршрутизаторами Cisco.Он порождает дочернее приложение и запускает команду типа 'show version' и регистрирует вывод.

Я ищу лучший способ выполнить этот процесс, поскольку если что-то изменится на стороне коммутатора (пробел или двоеточие в приглашении после обновления ОС), то программа не будет работать.Я думаю, что у Juniper есть API для выполнения таких операций, но я не думаю, что у Cisco есть тот.Мне также нужно распространить это на другие коммутаторы, такие как HP и т. Д.

Есть ли обобщенный способ, которым я могу подойти к этому?

Я также не против написать другой код для разных устройств, если требуется, если существует более стандартный подход, чем pexpect.

Спасибо

Ответы [ 2 ]

2 голосов
/ 19 октября 2011

В идеальном мире NETCONF будет решением.Это в основном (набор) проводного протокола (ов) и некоторая кодировка XML для запросов get / set / exec к сетевым устройствам.

См .:

Современные версии JunOS поддерживают это, в дополнение к их старому, проприетарному интерфейсу:

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

Некоторые комбинации аппаратного и программного обеспечения Cisco поддерживают его - например, NETCONF присутствует в поездах 12.2 (33) SXI и 12.2SY / 15.0SY на 6500 sup720 / sup2T.Я считаю, что все версии NX-OS (Cisco Nexus) поддерживают NETCONF.

Я не знаю о HP.Я знаю, что Extreme XOS поддерживает NETCONF в более поздних версиях в дополнение к собственному XML API Extreme.

Однако - NETCONF проблематичен в нескольких отношениях:

  1. Хотя протоколстандартизированные, поддерживаемые модели данных нет.Таким образом, вы получаете очень разные XML-файлы от разных устройств;вам все равно понадобится код для конкретного устройства.Но, по крайней мере, вам не нужно будет анализировать и злоупотреблять, и у вас будет гораздо более надежное обнаружение ошибок

  2. Некоторые устройства, например, Cisco IOS, на самом деле не являютсяXML под капотом.XML генерируется с использованием набора анализаторов на основе правил (скребков экрана), и в моем тестировании они часто бывают неполными или содержат ошибки.Например, вопреки утверждениям Cisco «show run | format» не «генерирует XML изначально».Он превращает конфигурацию Cisco на основе строки в XML и разбивает на несколько используемых нами конфигурационных конструкций (например, подстроки семейства адресов конфигураций маршрутизатора BGP).В итоге мы не использовали XML для данных и придерживались линейных команд / конфигураций Cisco - вам все равно придется анализировать это, но, по крайней мере, вы получаете выгоду от формирования кадров и кодов ошибок NETCONF.

  3. Несмотря на то, что вы можете запускать NETCONF через SSH, многие устройства по-прежнему не будут выполнять вход в систему на основе ключей SSH (Cisco IOS), то есть вам все равно придется возиться с жестко заданными паролями.

2 голосов
/ 30 сентября 2011

Использование некоторой версии ожидаемого - приемлемый способ сделать это.Уже есть зрелая часть сценариев OSS для выполнения терминального взаимодействия с большинством сетевых устройств от большинства поставщиков.Та же проблема существует, но гораздо больше глаз смотрит на нее и обновляет вещи, когда поставщики меняют запросы.

Проверьте RANCID .

Вот краткий примерСценарий, который я написал на основе RANCID для сбора ACL, рассчитывает на ASA:

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