Иногда, в нашей работе, возникает потребность учёта всего сетевого оборудования: типы, модели, адреса, прошивки, схемы соединений и так далее. И вот, просмотрев несколько вариантов ПО, выбор пал на открытый проект GLPI. Функционал у системы богатый, конкретно в нашем случае, использовалась для учёта «железа» и, главное, для учёта схем соединения (какой порт какого коммутатора куда подключен). В рамках осуществления основной деятельности организации (предоставление доступка к интернету), возникла (конечно не вдруг, а сугубо по плану) потребность резервного копирования конфигурации коммутаторов, зачем и почему данная операция необходима — это должно быть всем известно. А о том, как это реализовать, в связке с GLPI и будет данная статья.
Для осуществления задуманного, необходим список адресов оборудования и список производителей, все эти данные есть в БД GLPI, конечно если Вы их туда предварительно занесли. IP-адреса нужны для соединения по сети, а для чего нам знать производителя? Дело в том, что каждый изготовитель сетевого оборудования, в силу разных причин, привносит туда свои «оригинальные» идеи и посему команды для управления железками различаются. В нашем конкретном случае производителей всего два, но ничто не мешает список расширить.
Операцию копирования конфига будем выполнять через telnet-соединение, тоже самое можно делать и через SNMP, но в данном случае на всём железе SNMP на запись (выполнение команд) закрыт.
Языком реализации выбран был PERL. Отчего так? Ну, в то время мной пилилось, на нём-же, ещё кое-какое ПО (сборщик и разборщик RDR, вэб морда к нему) и, по сему — перл. В качестве ОС — GNU/Linux.
Приступим к коду:
Подключаем необходимые модули
#!/usr/bin/perl
use Net::Telnet ();
use IO::File;
use Getopt::Long;
use DBI;
Функция удаления пробелов, реализована самостоятельно, отчего и почему, уже не помню но пусть будет:
# функция удаления пробельных символов
sub trim {
my($string)=@_;
for ($string) {
s/^s+//;
s/s+$//;
}
return $string;
}
Дабы два раза не вставать, в скрипте параллельно реализовано архивирование, копирование и удаление старых файлов. По возможности используются системные команды:
# Архивируем конфиги
$date = `/bin/date "+%Y%m%d"`;
$date = trim($date);
$arch = "tar -czvf /var/srv/backup/conf/conf-".$date.".tgz /var/lib/tftpboot/";
my $result = `$arch`;
# удаляем архивы давностью в неделю
$del = '/bin/find /var/srv/backup/conf/ -ctime +7 -name conf-*.tgz -exec rm {} ; -print > /var/log/switch-conf_backup.log';
my $delResult = `$del`;
Подключаемся к БД:
# конектимся к GLPI базе
my $dsn = 'DBI:mysql:glpi:192.168.123.222';
my $db_user_name = 'user';
my $db_password = 'password';
my ($id, $password);
my $dbh = DBI->connect($dsn, $db_user_name, $db_password);
Выбираем необходимые адреса коммутаторов нужного производителя и, соответсвенно, «дёргаем» потребные нам процедуры (которые будут описаны ниже):
my $sth = $dbh->prepare(qq{SELECT ip FROM glpi_networkequipments WHERE manufacturers_id=1 AND states_id=1});
$sth->execute();
while (my ($ip) =
$sth->fetchrow_array())
{
# print "$ipn";
if ($ip eq "" || $ip =~ /^#+/) {
# не делать ничего. Тут криво, согласен :)
} else {
# Запускаем соответствующую процедуру
connectSwitchEdgeCore("$ip");
}
}
$sth->finish();
# выбираем все D-Link со статусом "Включен"
my $sth = $dbh->prepare(qq{SELECT ip FROM glpi_networkequipments WHERE manufacturers_id=3 AND states_id=1});
$sth->execute();
while (my ($ip) =
$sth->fetchrow_array())
{
if ($ip eq "" || $ip =~ /^#+/) {
} else {
connectSwitchDlink("$ip");
}
}
$sth->finish();
$dbh->disconnect();
Стоит отметить, что значения manufacturers_id и states_id (первое — код производителя, второе — статус оборудования («введён в эксплуатацию», «в проекте» и т.д.)) могут, да и скорее всего будут, различаться, т.к. создаются они автоматически при добавлении позиций в БД. В данном случае производитель «1» — это EdgeCore («3» — D-Link) и статус «1» — «Включен» т.е. введён в эксплуатацию. Отслеживание статуса необходимо, дабы исключить попытки соединения с неактивными, по разным причинам, железяками.
Далее две, почти одинаковые, процедуры, реализующие диалог между скриптом и коммутатором на тему «а скопируй-ка братец свой текущий конфиг на tftp-сервер»:
# telnet-ом цепляемся к коммутаторам и сливаем конфиг на tftp сервер
# слив конфигурации с коммутатора EdgeCore
sub connectSwitchEdgeCore
{
$hostname = "$_[0]";
$username = "user";
$passwd = "password";
$date = `/bin/date "+%Y%m%d"`;
$str_fn=$hostname;
print $str_fn;
$tftpserver="20.20.20.20";
$conn = new Net::Telnet ( Timeout=>3, Errmode=>'return', Prompt => '/#.?$/i');
$conn->open(Host => $hostname);
$conn->waitfor('/ame[: ]*$/');
$conn->print($username);
$conn->waitfor('/ord[: ]*$/');
$conn->print($passwd);
$conn->print("copy running-config tftp");
$conn->waitfor('/ess[: ]*$/');
$conn->print($tftpserver);
$conn->waitfor('/ame[: ]*$/');
$conn->print($str_fn);
$conn->print("exit");
$conn->print("logout");
}
# слив конфигурации с коммутатора D-Link
sub connectSwitchDlink
{
$hostname = "$_[0]";
$username = "user";
$passwd = "password";
$date = `/bin/date "+%Y%m%d"`;
$str_fn=$hostname;
print "$str_fnn";
$tftpserver="20.20.20.20";
$conn = new Net::Telnet ( Timeout=>3, Errmode=>'return', Prompt => '/#.?$/i');
$conn->open(Host => $hostname);
$conn->waitfor('/ame[: ]*$/');
$conn->print($username);
$conn->waitfor('/ord[: ]*$/');
$conn->print($passwd);
$conn->print("upload cfg_toTFTP $tftpserver dest_file $hostname");
$conn->waitfor('/Success/');
$conn->print("logout");
}
Адрес сервера и параметров соединения с БД, логичней было вынести в отдельный файлик, но в силу расположения небесных тел в тот момент, было оставлено так. Команды print оставлены для вывода в лог. Запуск скрипта производится по времени cron-ом.
Вот так просто и непринуждённо была автоматизирована рутинная операция. Традиционно, исходники доступны тут.
P.s. Думал в какой поток разместить статью, метался между «Администрированием» и «Разработкой», в итоге остановился на первом варианте.
Автор: svk28