Не так давно, в связи с расширением штата сетевых администраторов, роста числа оборудования в сети и круга задач, возлагаемых на это оборудование, возникло желание отслеживать изменение его конфигурации, и хранить историю этих изменений.
Очевидным решением стало хранение конфигурации в системе управления версиями. Выбор пал на 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 клиентом подключаться наблюдать за изменениями, сравнивать версии, откатываться и т.д.
Оговорюсь, что я не гуру перлового программирования, и все замечания и советы будут с благодарностью приняты.