вторник, 31 марта 2015 г.

Перепрошивка коммутаторов с помощью Briseis

Briseis - мой проект для сбора метрик с сетевых устройств. Иногда перед чтением метрики требуется предварительно выполнить операцию записи. Такое может понадобиться, например, при диагностики состояния кабельной линии - сначала выполняется инициализация теста, а затем уже сам сбор результатов. Briseis умеет производить операции snmpset и этой возможностью мы воспользуемся для использования программы не совсем по прямому назначению, а именно - для массовой перепрошивки коммутаторов. :) При этом несколько усложним себе задачу и представим, что мы не знаем с какими именно моделями и ревизиями коммутаторов D-Link нам потребуется работать. Будем считать, что нам известен критерий выбора этих устройств из базы MySQL и их write-community.

1. Для начала напишем MySQL-запрос к нашей СУБД для выбора коммутаторов по некоторому критерию. В моей случае это IP-адрес. В вашем случае это может быть что-то другое, поэтому не вижу смысла приводить здесь мой запрос - важен лишь результат. Убедитесь, что имеете доступ к вашему MySQL-серверу с машины, на которой будете запускать Briseis и что сам запрос возвращает табличку вида:
+------+---------------+-------------+
| id   | ip            | wcomm       |
+------+---------------+-------------+
| 6248 | 10.90.90.91   | private     |
| 6249 | 10.90.90.92   | private     |
| 6250 | 10.90.90.93   | private     |
+------+---------------+-------------+


2. Идем далее. Если мы не знаем, какие модели и ревизии коммутаторов присутствуют в этом списке, то мы можем получить эту информацию из статистики Briseis. Создадим на сервере MySQL базу данных с таблицей для размещения статистики и гарантируем пользователю 'briseis' права для работы с этой таблицей.
CREATE DATABASE IF NOT EXISTS `blackhole` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `blackhole`;

CREATE TABLE IF NOT EXISTS `stats` (
  `device_id` int(4) unsigned NOT NULL,
  `host` char(16) NOT NULL,
  `mname` char(16) NOT NULL,
  `set_timestamp` int(4) unsigned NOT NULL,
  `walk_timestamp` int(4) unsigned NOT NULL,
  `avail` int(4) unsigned NOT NULL,
  `not_avail` int(4) unsigned NOT NULL,
  `errors` int(4) unsigned NOT NULL,
  `time` int(4) unsigned NOT NULL,
  PRIMARY KEY (`device_id`),
  KEY `host` (`host`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

GRANT USAGE ON *.* TO 'briseis'@'%' IDENTIFIED BY 'briseis_pass';
GRANT ALL PRIVILEGES ON `blackhole`.`stats` TO 'briseis'@'%' WITH GRANT OPTION;


Не помешает выполнить проверку и убедиться, что доступ к таблице получен. После этого переходим к настройке программы.

3. Скачиваем Briseis отсюда. При написании этой заметки я проделал этот несложный маневр при помощи простой команды:
wget https://github.com/xcme/briseis/archive/v1.0.2.tar.gz --no-check-certificate

Распаковываем архив и притупаем к правке bconfig.py. Правим настройки для MySQL на нужные нам, т.е. прописываем адреса серверов, логины, пароли и т.п. Исправляем 'useMySQLstat' на 'True' и не забываем вписать в 'mysql_query_p' запрос, который мы разработали на шаге №1. Исправляем 'logfile', указывая желаемый путь для файла журнала.

Меняем 'PassSetSet' и 'PassSetWalk' на словари с одним пустым списком:
PassSetSet = {1:[]}
PassSetWalk = {1:[]}


Задаем 'oids_set' как пустой словарь, а 'oids_walk' как пустой список:
oids_set={}
oids_walk=[]


Значения 'useGraphite' и 'useAttractor' выставляем в 'False'.

При такой настройки Briseis выполнит только проверку устройств на доступность, опросив их по ключу 'oid_ModelName', и запишет результаты в таблицу статистики.

Важно: При написании заметки обнаружил один нюанс. По умолчанию программа использует '/var/run/briseis.pid' в качестве pid-файла. Доступ к этому каталогу для обычного пользователя может быть закрыт. Если вы не хотите запускать программу от root или давать доступ к этому каталогу, исправьте путь в файле 'briseis.py' на нужный вам. В этом случае программа запустится от обычного пользователя.

4. Сохраним настройки и выполним ручной запуск программы:
./briseis.py start
При появлении в логе сообщения об завершении цикла или же просто через несколько секунд останавливаем программу:
./briseis.py stop
При этом в файле журнала мы увидим примерно следующее:
Connection to MySQL Server '*******' established
MySQL Read-Query '*Select devices*' OK. 17 rows found
(+++) Added 17 devices to database
Polling devices from the database. Attempt #1 of 2 completed for 0.6087 sec
Polling devices from the database. Attempt #2 of 2 completed for 0.0000 sec
Sending set-queries for []...
Completed all set-requests. Elapsed time 0.0470 sec. Going to sleep for 3 sec...
Sending get/walk-queries for []...
Completed all walk-requests. Elapsed time 0.0566 sec
Total number of metrics in memory: 0
Connection to MySQL statistics Server '*******' established
Sended 17 entries to statictics server. Elapsed time 0.0632 sec
------- Pass №1 completed for 4.3851 sec -------


Как видим, 17 устройств было добавлено в память программы из базы, все они оказались доступными (нет сообщений о недоступных устройствах) и 17 записей было отправлено в таблицу статистики.

5. Подключимся к нашему серверу с базой статистики и выполним запрос:
SELECT mname,count(mname) FROM blackhole.stats GROUP by mname;

В ответ получаем:
+----------------+--------------+
| mname          | count(mname) |
+----------------+--------------+
| DES-3028       |           13 |
| DES-3200-28    |            1 |
| DES-3200-28/C1 |            3 |
+----------------+--------------+
3 rows in set (0.01 sec)


Прекрасно! Теперь мы знаем с какими устройствами нам предстоит работать.

6. Теперь нам нужно научить Briseis заливать ПО на коммутаторы. Если модели устройств были бы известны нам заранее, этот пункт можно было выполнять сразу после шага №1.

В любом месте файла userdict.py определяем списки OID для формирования varbind:
fw_up3200 = [
    ['1.3.6.1.4.1.171.12.1.2.1.1.3','1','10.90.90.101','IPADDR'],
    ['1.3.6.1.4.1.171.12.1.2.1.1.4','1','2','INTEGER'],
    ['1.3.6.1.4.1.171.12.1.2.1.1.5','1','DES-3200R_1.85.B008.had','OCTETSTR'],
    ['1.3.6.1.4.1.171.12.1.2.1.1.7','1','3','INTEGER'],
    ['1.3.6.1.4.1.171.12.1.2.1.1.8','1','3','INTEGER'],
    ['1.3.6.1.4.1.171.12.1.2.1.1.9','1','2','INTEGER'],
    ['1.3.6.1.4.1.171.12.1.2.1.1.10','1','1','INTEGER'],
]

fw_up3028 = [
    ['1.3.6.1.4.1.171.12.1.2.1.1.3','1','10.90.90.101','IPADDR'],
    ['1.3.6.1.4.1.171.12.1.2.1.1.4','1','2','INTEGER'],
    ['1.3.6.1.4.1.171.12.1.2.1.1.5','1','DES_3028_52_V2.94-B07.had','OCTETSTR'],
    ['1.3.6.1.4.1.171.12.1.2.1.1.7','1','3','INTEGER'],
    ['1.3.6.1.4.1.171.12.1.2.1.1.8','1','3','INTEGER'],
    ['1.3.6.1.4.1.171.12.1.2.1.1.9','1','2','INTEGER'],
    ['1.3.6.1.4.1.171.12.1.2.1.1.10','1','1','INTEGER'],
]

fw_up3200_c1 = [
    ['1.3.6.1.4.1.171.12.1.2.18.1.1.3','1','10.90.90.101','IPADDR'],
    ['1.3.6.1.4.1.171.12.1.2.18.1.1.5','1','DES3200R_4.39.B008.had','OCTETSTR'],
    ['1.3.6.1.4.1.171.12.1.2.18.1.1.8','1','3','INTEGER'],
    ['1.3.6.1.4.1.171.12.1.2.18.1.1.12','1','3','INTEGER'],
]


TFTP-сервером в нашем примере будет '10.90.90.101'. Значения остальных параметров рекомендую посмотреть тут.

Важно: Пакет net-snmp в какой то из версий имеет баг, не позволяющий работать с типом IPADDR в python-модуле. В этом случае при работе Briseis пакет не будет сформирован и ничего не произойдет. Если вы уверены, что делаете все правильно, но при этом результата нет - обновите пакет net-snmp.

Исправляем 'PassSetSet':
PassSetSet  = {1:['FW_upd']}

Исправляем 'oids_set':
oids_set={
    'DES-3200-28':{
        'FW_upd':fw_up3200
    },
    'DES-3200-28/C1':{
        'FW_upd':fw_up3200_c1
    },
    'DES-3028':{
        'FW_upd':fw_up3028
    }
}


Правим 'snmp_Retries':
snmp_Retries  = 0

Это нужно чтобы запрос отправлялся только 1 раз. При получении первого запроса на обновление ПО коммутатор может быть занят выполнением команды и не успеет отправить подтверждение. Когда мы выставляем 0 дополнительных попыток, то спасаем коммутатор от обновления ПО несколько раз - по 1 на каждую попытку.

Запускаем программу:
./briseis.py start
И через 5 секунд останавливаем:
./briseis.py stop
В логе видим:
Sending set-queries for ['FW_upd']...
Completed all set-requests. Elapsed time 0.2973 sec. Going to sleep for 3 sec...


7. Через 3-5 минут проверяем коммутаторы. Видим, что прошивка успешно загрузилась. Теперь в 'oids_set' комментируем строки:
#    'DES-3200-28/C1':{
#        'FW_upd':fw_up3200_c1
#    },

В файле userdict.py в списках 'fw_up3200' и 'fw_up3028' меняем строку
['1.3.6.1.4.1.171.12.1.2.1.1.10','1','1','INTEGER'],
на
['1.3.6.1.4.1.171.12.1.2.1.1.10','1','2','INTEGER'],

После чего снова запускаем и останавливаем программу
./briseis.py start
./briseis.py stop


Этими действиями мы перепрошили второй образ для DES-3200-28 и DES-3028, исключив при этом из обработки модель DES-3200-28/C1.

Все прошивки обновлены, осталось только перезагрузить коммутаторы. Об этом, быть может, в другой раз.

Комментариев нет:

Отправка комментария