Не так давно, в связи с расширением штата сетевых администраторов, роста числа оборудования в сети и круга задач, возлагаемых на это оборудование, возникло желание отслеживать изменение его конфигурации, и хранить историю этих изменений.
Очевидным решением стало хранение конфигурации в системе управления версиями. Выбор пал на Subversion, так же известную как SVN.
На установке и настройке репозитория останавливаться не буду, так как этому посвящено много страниц в интернете и ничего нестандартного в этом нету. Здесь опишу свой способ сбора конфигурации с оборудования и учет изменений.
Для начала создадим рабочую копию нашего репозитория, куда и будут скриптом на perl собираться конфиги.
Приступим к написанию скрипта. Скрипт писался с учетом нескольких особенностей.
Инициализация переменных:
Сделаем update и получим номер текущей ревизии. Он нам пригодится для сравнения версий.
Функции лока/анлока. Позволяют запускать только одну копию скрипта.
Далее читаем конфигурационный файл. Файл должен представлять из себя набор строчек. Каждая строчка содержит адрес железки и ее тип, разделенные двоеточием:
Здесь мы для каждой строчки конфига создаем файл с конфигурацией (на случай если его небыло) и вызываем набор функций в зависимости от типа устройства. Функции расмотрим позже, скажу лишь, что после их выполнения у нас будет новый собраный с устройства конфиг. Проверяем есть ли изменения в конфиге. Если есть — коммитим и устанавливаем флаг наличия изменений в 1. Если добавился новый хост, также добавляем его в репозиторий. При выставленном флаге – пишем письмо.
Основной скрипт закончен. Далее набор функций.
Для функции сбора конфига ввиду мультивендорности, было решено использовать более или менее универсальный механизм – коннект на свитч и команда просмотра конфигурации.
Методы подключения и передачи команд выполняются в контексте eval, чтобы скрипт не вылетал если будут проблеммы с подключением. На всякий случай дается пять попыток подключения. Далее либо записываем сообщение об ошибке,(флаг выставляем, для того чтобы ошибка ушла почтой) либо записываем полученый массив в файл. Проверка длины масива сделана потому, что некоторые устройства переодически возвращают сообщения вида: «невозможно выдать конфигурацию, попробуйте позже», когда они очень чем-то заняты, что бы такое сообщение не записывалось в файл, потому как явной ошибки подключения нет, но конфига тоже нет. Можно конечно парсить вывод на предмет ключевых слов, но данная проверка проще и универсальней.
Для нового типа устройства добавляется новая функция в которой можно описать собственную логику работы с данным типом. Например Juniper:
В данном примере вместо telnet конфиг скачивается по scp. На устройстве создана учетка для доступа в шелл с ограниченными правами.
В процессе тестов выяснилось, что иногда попадаются строчки в конфиге, которые постоянно меняются, но не несут важной смысловой нагрузки. Например ntp clock-period в Cisco, или Extreme любит добавлять звездочку в приглашение, если есть несохраненные изменения. Была добавлена функция вырезания таких строчек, что бы игнорировать их и учитывать только важные изменения:
После прохода всех устройств из нашего списка, если где-то был выставлен флаг необходимости почтового сообщения, то отправляем письмо в которое включаем список изменений, и переменную с ошибками. Список изменений получаем стандартным svn diff, указывая предыдущий номер ревизии.
В конце удаляем локфайл
Данный скрипт запускается по крону несколько раз в день. Все внесенные изменения приходят на почту и в любой момент можно любым SVN клиентом подключаться наблюдать за изменениями, сравнивать версии, откатываться и т.д.
Оговорюсь, что я не гуру перлового программирования, и все замечания и советы будут с благодарностью приняты.
Очевидным решением стало хранение конфигурации в системе управления версиями. Выбор пал на Subversion, так же известную как SVN.
На установке и настройке репозитория останавливаться не буду, так как этому посвящено много страниц в интернете и ничего нестандартного в этом нету. Здесь опишу свой способ сбора конфигурации с оборудования и учет изменений.
Для начала создадим рабочую копию нашего репозитория, куда и будут скриптом на perl собираться конфиги.
svn checkout svn://svn.reposerver.ru/configs /configs
Приступим к написанию скрипта. Скрипт писался с учетом нескольких особенностей.
- простое добавление/удаление устройств в списке проверяемых
- мультивендорная сеть
- уведомление об изменениях почтой
- уведомление об ошибках подключения почтой
Инициализация переменных:
#!/usr/bin/perl -w
use Net::Appliance::Session;
use Net::SCP::Expect;
use Switch;
use Fcntl ':flock';
exit(1) unless &lock("lockfile");
my $date = `/bin/date`;
my $flag=0; ### флаг наличия изменений
my $path="/configs"; ###путь к рабочей копии
my $cfg = "/configs/switches.cfg"; ### файл списка оборудования
my $error = "\n"; ### переменная сообщений об ошибке.
Сделаем update и получим номер текущей ревизии. Он нам пригодится для сравнения версий.
$_=`/usr/local/bin/svn update $path`;
s/At revision (\d+)./$1/;
$prev_rev = $_;
Функции лока/анлока. Позволяют запускать только одну копию скрипта.
sub lock($) {
open(LK,">$_[0]") or return 0;
flock(LK,LOCK_EX|LOCK_NB);
}
sub unlock($) {
unlink($_[0]);
flock(LK,LOCK_UN);
close(LK);
}
Далее читаем конфигурационный файл. Файл должен представлять из себя набор строчек. Каждая строчка содержит адрес железки и ее тип, разделенные двоеточием:
switch1:e
switch2:cat
router1:c
router2:j
open (CFG, "$cfg") || die "Cant open file $cfg: $!";
while (<CFG>) {
next if /^\#/; ###Пропускаем комментарии в конфиге
next if /^\s*$/; ###Пропускаем пустые строки в конфиге
chomp;
my @host = split /:/,$_;
switch($host[1]) {
case "cat" {
`/usr/bin/touch $path/$host[0]`;
&cat_telnet($host[0]);
&str_remove ($host[0]);
}
case "c" {
`/usr/bin/touch $path/$host[0]`;
&cat_telnet($host[0]);
&str_remove ($host[0]);
}
case "e" {
`/usr/bin/touch $path/$host[0]`;
&extreme_telnet($host[0]);
&str_remove ($host[0]);
}
case "j" {
&juni_scp($host[0]);
}
}
Здесь мы для каждой строчки конфига создаем файл с конфигурацией (на случай если его небыло) и вызываем набор функций в зависимости от типа устройства. Функции расмотрим позже, скажу лишь, что после их выполнения у нас будет новый собраный с устройства конфиг. Проверяем есть ли изменения в конфиге. Если есть — коммитим и устанавливаем флаг наличия изменений в 1. Если добавился новый хост, также добавляем его в репозиторий. При выставленном флаге – пишем письмо.
my $svnst = `/usr/local/bin/svn status $path/$host[0]`;
if ($svnst =~ /^M/) { ### Если файл был модифицирован
$flag=1;
`/usr/local/bin/svn commit -m "$date" $path/$host[0]`;
}
if ($svnst =~/^\?/) { ### Если добавился новый файл
`/usr/local/bin/svn add $path/$host[0]`;
`/usr/local/bin/svn commit -m "Add new host at $date" $path/$host[0]`;
}
}
close CFG;
if ($flag==1) {
&diff_mail();
}
Основной скрипт закончен. Далее набор функций.
Для функции сбора конфига ввиду мультивендорности, было решено использовать более или менее универсальный механизм – коннект на свитч и команда просмотра конфигурации.
sub cat_telnet {
(my $host1)=@_;
$try = 5;
$update='false';
while ($try>0) {
my $s = Net::Appliance::Session->new(
Host => $host1,
Transport => 'Telnet',
Timeout => 50,
Source => '/configs/nas-pb.yml',
Platform=> 'IOS'
);
eval {
$s->do_login();
$s->connect(
Name => 'username',
Password => 'pass',
);
my @temp = $s->cmd('show run');
};
if ($#temp>5) {
$s->close;
$update='true';
last;
}
$try=$try-1 ;
if ($try==0) {
$error = $error.»Some error occure while getting config from «. $host1. «\n»;
$flag=1;
}
$s->close;
}
if ($update eq 'true'){
open (CFG1, «>$path/$host1») || die «Cant open file $path/$host1: $!»;
print CFG1 @temp;
close CFG1;
}
}
Методы подключения и передачи команд выполняются в контексте eval, чтобы скрипт не вылетал если будут проблеммы с подключением. На всякий случай дается пять попыток подключения. Далее либо записываем сообщение об ошибке,(флаг выставляем, для того чтобы ошибка ушла почтой) либо записываем полученый массив в файл. Проверка длины масива сделана потому, что некоторые устройства переодически возвращают сообщения вида: «невозможно выдать конфигурацию, попробуйте позже», когда они очень чем-то заняты, что бы такое сообщение не записывалось в файл, потому как явной ошибки подключения нет, но конфига тоже нет. Можно конечно парсить вывод на предмет ключевых слов, но данная проверка проще и универсальней.
Для нового типа устройства добавляется новая функция в которой можно описать собственную логику работы с данным типом. Например Juniper:
sub juni_scp {
(my $host1)=@_;
$try = 5;
$update = 'false';
while ($try>0) {
my $s = Net::SCP::Expect->new(
user=>'username',
password=>'pass',
auto_yes=>1
);
eval {
my @temp= $s->scp("$host1:/config/juniper.conf.gz","$path/$host1.gz" );
};
if ($#temp eq "0") {
$update='true';
last;
}
$try=$try-1 ;
if ($try==0) {
$error = $error."Some error occure while getting config from ". $host1. "\n";
$flag=1;
}
}
if ($update eq 'true'){
`/usr/bin/gunzip -f $path/$host1.gz`;
}
}
В данном примере вместо telnet конфиг скачивается по scp. На устройстве создана учетка для доступа в шелл с ограниченными правами.
В процессе тестов выяснилось, что иногда попадаются строчки в конфиге, которые постоянно меняются, но не несут важной смысловой нагрузки. Например ntp clock-period в Cisco, или Extreme любит добавлять звездочку в приглашение, если есть несохраненные изменения. Была добавлена функция вырезания таких строчек, что бы игнорировать их и учитывать только важные изменения:
sub str_remove {
(my $host)=@_;
open (F,"$path/$host") ;
my @f = <F>;
close F;
open (F, ">$path/$host");
foreach $str (@f) {
print F $str if ( $str !~ /^ntp clock-period/ and $str !~ /^Current configuration/ and $str !~ /^Slot-/ and $str !~ /^\* Slot-/ and $str !~ /^\*/);
}
close F;
}
После прохода всех устройств из нашего списка, если где-то был выставлен флаг необходимости почтового сообщения, то отправляем письмо в которое включаем список изменений, и переменную с ошибками. Список изменений получаем стандартным svn diff, указывая предыдущий номер ревизии.
sub diff_mail {
@mail = `/usr/local/bin/svn diff $path -r $prev_rev`;
open (MAIL, "| /usr/sbin/sendmail user@mydomain.ru, user2@mydomain.ru ") || die " $!";
print MAIL <<EOF;
From: Config Checker
Subject: Config Update
@mail
$error
EOF
close MAIL;
}
В конце удаляем локфайл
&unlock("lockfile");
Данный скрипт запускается по крону несколько раз в день. Все внесенные изменения приходят на почту и в любой момент можно любым SVN клиентом подключаться наблюдать за изменениями, сравнивать версии, откатываться и т.д.
Оговорюсь, что я не гуру перлового программирования, и все замечания и советы будут с благодарностью приняты.