Как стать автором
Обновить

Разблокируем порты коммутатора Cisco с помощью Zabbix, Ansible и Napalm

Время на прочтение7 мин
Количество просмотров7.6K

День добрый. Это вторая часть цикла из двух статей. В первой части мы ловили Zabbix-ом трапы PortSecurity от коммутаторов, а здесь мы, можно сказать, решаем обратную задачу — снимаем блокировку порта коммутатора щелчком мыши в фронтенде Zabbix-а.


Так получилось, что эта задача решалась два раза, двумя разными инструментами и с разницей в несколько месяцев. Сначала использовался Ansible, который вполне успешно справлялся. Но в один прекрасный момент он сломался (опять) и та же самая задача была решена простым Python-ом с использованием широко известной в узких кругах сетевой библиотекой Napalm.


Начнем с того зачем это надо.


Часто блокировка порта коммутатора — это не чье-то злонамеренное действие, а вполне ожидаемое событие. Наиболее часто порты блокируются при переезде компьютеров и/или IP телефонов из одного помещения в другое. Но это как правило контролируемый процесс, который обеспечивается техподдержкой. И хорошо бы дать инструмент техподдержке по разблокировке портов, которые ими же и были заблокированы.


Воспользуемся встроенной в Zabbix функциональностью по запуску внешних скриптов. А именно — запуск скриптов по факту подтверждения проблемы (Acknowledgement). Таким образом, если сотрудник техподдержки заблокировал порт своими действиями, то он собственными силами может порт разблокировать просто подтвердив проблему. То есть достаточно просто нажать Ack и заполнить форму подтверждения (Форму можно не заполнять, но мы им об этом конечно не скажем).


Далее, по факту подтверждения, Zabbix вызовет наш скрипт, который для конкретного коммутатора на конкретном порту очищает все залипшие на нем мак-адреса, а так же, при наличии, вычищает заданный мак-адрес на всем коммутаторе.


Наличие мак-адреса определяется типом трапа, а точнее типом настроенного на интерфейсе коммутатора параметра port-security violation (подробней в первой части). Здесь просто стоит упомянуть, что если мак-адреса в трапе нет, то в сценарии переезда какого нибудь устройства с порта на порт в пределах одного коммутатора, наша автоматизация не поможет. Просто потому что в трапе не содержится достаточно информации. В итоге, пока на старом порту будет оставаться мак устройства, новый порт так и будет блокироваться до бесконечности. Короче, по этой причине рекомендую использовать режим restrict, а режим shutdown для port-security violation лучше не использовать.


Теперь про реализацию. Как я уже говорил реализации целых две штуки. Начнем с первой. с Ansible.


На самом деле все работает очень просто: Zabbix вызывает bash скрипт, скрипт формирует параметры вызова сценария ansible, ansible заходит на нужный коммутатор и выполняет команду


clear port-security sticky interface XXX

А при наличии мак-адреса дополнительно выполняют еще одну команду


clear port-security sticky address YYY

где XXX это наш заблокированный порт, и YYY — мак-адрес, из-за которого порт был заблокирован


Для выполнения команд на коммутаторе использован ansible модуль ios_command
Сценарий ansiblе предельно лаконичен так что приведу его здесь целиком:


---
- name: clear a port-security sticky mac for a given interface
  hosts: all
  connection: local
  gather_facts: no

  vars_files:
    - defaults/creds.yml

  tasks: 
    - name: run a command that clears an interface
      ios_command:
        host: "{{ ansible_host }}"
        username: "{{ user }}"
        password: "{{ pass }}" 
        commands: 
          - clear port-security sticky interface {{ interface }}

    - name: run a command that clears mac-address
      ios_command:
        host: "{{ ansible_host }}"
        username: "{{ user }}"
        password: "{{ pass }}" 
        commands: 
          - clear port-security sticky address {{ mac | hwaddr('cisco') }}
      when: mac is defined

Тут есть один очень важный момент: при стандартной установке Zabbix пользователь zabbix создается системным и без домашней директории. Но чтобы ansible смог отработать сценарий от имени пользователя zabbix, у этого пользователя обязана быть в системе HOME_DIR. Без домашней директории не работает connection plugin paramico, который в ansible обеспечивает соединение по ssh с коммутаторами.


Еще один момент: в целях безопасности параметры login/password для доступа к коммутаторам зашифрованы с помощью ansible-vault.
Делается это командой ansible-vault create


iddqd@zabbix:~$ ansible-vault create creds.yaml
New Vault password: 
Confirm New Vault password: 

Команда попросит придумать, ввести и подтвердить новый пароль и далее запустит текстовый редактор по умолчанию.


Там прописываем нужные значения, сохраняемся и выходим.


Теперь наш файл с паролями выглядит как то так:


$ANSIBLE_VAULT;1.1;AES256
62383331636562393761373465393661636565383632613537616533316230666363316231653337
6562613533313563346432313262316635613038386630340a613630626363303038633632653962
63636230613539646262663562396233333662613761303062373565623038316265613361346430
3664303538346364350a356139306432303330393835646436373738353931663463343532636432
30633362646262343639346666383563646461663032323332343238653239313735

Посмотреть содержимое файла можно командой ansible-vault view creds.yml


iddqd@zabbix:~$ ansible-vault view creds.yaml
Vault password: 
user: admin
pass: admin

Едем дальше. В нашем сценарии есть такие параметры как ansible_host, interface и mac, но если первый параметр просто соответствует макросу {HOST.HOST}, то последние два параметра Zabbix не различает. Они для него выглядят как единственный параметр {ITEM.LASTVALUE}. Ну т.е. в {ITEM.LASTVALUE} может содержаться либо catalyst100 либо 00:08:5D:51:DA catalyst100 в зависимости от типа трапа.


Для решения задачи передачи правильных параметров из Zabbix в сценарий я накропал простенький bash-скриптик. Вот такой:


#!/bin/bash
cd /etc/zabbix/alertscripts/zabbix-errdisable-recovery

if [ $# -eq 3 ]
then
# call ansible playbook with 3 args
/usr/local/bin/ansible-playbook clear-portsecurity.yml --vault-password-file=vault/vault_password -e interface=$1 -e mac=$2 -i $3,
elif [ $# -eq 2 ]
# call ansible playbook with 2 args
then
/usr/local/bin/ansible-playbook clear-portsecurity.yml --vault-password-file=vault/vault_password -e interface=$1 -i $2,
fi

Скрипт считает сколько аргументов (разделенных пробелом) было в него передано и в зависимости от их количества вызывает сценарий ansible с разным набором параметров.
Кстати, обратите внимание на запятую в конце -i $2,


Таким интересным образом в ansible можно передать единственный хост.


Вызов скрипта из Zabbix задается командой:
/etc/zabbix/alertscripts/zabbix-errdisable-recovery.sh {ITEM.LASTVALUE} {HOST.HOST}


Команда задается в Actions -> Acknowledgement operations -> Run remote commands on current host


Там еще несколько мутных параметров. поэтому лучше картинкой. В моем Zabbix настроено таким образом:



sudo, который есть на картинке, использовать не нужно. Просто картинка старая, а я ленивый.


На этом с настройкой все.


Теперь показываю как это должно работать:


Итак прилетает к нам в Zabbix вот такое событие PortSecutity



Тут же нам звонит техподдержка и говорит что мол они там что-то куда то воткнули, но оно почему то не работает. Мы спрашиваем мак-адрес, сравниваем и понимаем, что это событие вызвано легитимными действиями и теперь нам порт надо разблокировать. Для чего просто нажимаем на ACK и в открывшемся окошке пишем что угодно, но лучше записать причину события конечно.



Далее нажимаем Ok и ждем пока отработает ansible.


Когда скрипт отработает, при наведении мыши на поле с нашим событием в колонке Actions отбразится состояние Executed.



Затем, через время указанное в функции nodata() в условии тригера, тригер дожен позеленеть.


Детальная информация по событию в итоге будет выглядеть как то так



Кстати, на этой картинке удобно показано множество информации и в том числе то самое время nodata(150).


Это время должно быть чуть больше чем время errdisable autorecovery в настройках коммутаторов.




Часть 2: python + napalm


Итак задача была реализована и все прекрасно работало. Более того, даже эта статья уже была написана и готова к публикации. Но в один прекрасный момент я настроил мониторинг оракловых баз в Zabbix через ODBC и мой ansible сценарий выполняться перестал.
Дело в том, что ansible сильно зависим от переменных окружения. И так получилось, что когда я добавил в HOMEDIR пользователя zabbix файл .bashrc с переменными окружения ORACLE_HOME, процесс zabbix при запуске стал получать какие то другие переменные окружения и ansible-сценарий запущенный от его имени, а точнее network-module, потерял способность цепляться к коммутаторам по ssh.


При этом если запускать сценарий руками из под пользователя zabbix то скрипт выполняется, а если из фронтенда то падает с ошибкой от network-module.


В тот момент я собирался в отпуск, дел было много и я просто забил на скрипт этот. А вернувшись из отпуска увлекся чем то еще, потом заработался, опять в отпуск итп.
И вот долгими зимнимим вечерами маясь от безделья я про эту задачу вспомнил. Но ковыряться с ansible очень не хотелось. Особенно учитывая, что не так давно с одним из последних апдейтов Убунты ansible обновился на новую версию и поломался добрый десяток моих старых ansible-сценариев. В частности там sudo превратился в become. Ерунда конечно и в общем давно было известно, что sudo deprecated и когда то оно обязательно превратится в тыкву. Но все равно было неприятно.


Как то разочаровался я в этом инструменте. Починю я его сейчас, а через пару апдейтов опять все накроется?!


Да и изначально применения ansible для единственного хоста — это такое себе решение.
В то же время с питоном у меня сложились неплохие отношения. Да и просто с ним удобней. Куча инструментов в распоряжении. Да хоть тот же дебаггер.


Скрипт был написан за один присест. Он получился абсолютно линейным и поэтому предельно понятным. При этом сохранилась функциональность ansible-vault и плюс добавился вызов ZabbixAPI, который при успешном выполнении команд на коммутаторе гасит событие PortSecurity в фронтенд Zabbix. Тут надо отметить, что для закрытия событий в Zabbix необходимо в соответствующем триггере поставить галочку Allow manual close. Впрочем в шаблоне она уже проставлена.


В качестве драйвера взаимодействия с коммутаторами используется библиотека NAPALM.


Впрочем, все взаимодействие вылилось в две строчки так что рассказывать особо не о чем. Просто посмотрите в код.


Единственное о чем стоит рассказать: библиотека прекрасно встает на стандартный для Ubuntu 16.04 python версии 3.5. Но при этом внутри NAPALM используются конструкции fstrings от питона 3.6. И так все вроде работает, но в консоль валятся левые трейсы. Не очень аккуратно получается. Впрочем, при запуске скрипта Заббиксом консоль не видно, а на результат не влияет.


Заббикс настраивается почти точно так как вариант с ansible-сценарием, но вместо 2/3 параметров тут уже передается 4/5 параметров. Добавились параметры {EVENT.ID} и {EVENT.STATUS} для очистки событий в Zabbix.


Соответственно изменилась и строка вызова скрипта из Zabbix. Теперь она выглядит следующим образом:



Похоже на этом все. Спасибо за внимание.


Все вышеперечисленное безобразие лежит на github.


И первое решение на ansible вполне рабочее в стандартном ENVIRONMENT. Поэтому убирать я его конечно не буду. Ни сам сценарий ни информацию про него. Тем более там же про использование vault написано.

Теги:
Хабы:
Всего голосов 4: ↑4 и ↓0+4
Комментарии0

Публикации

Истории

Работа

Data Scientist
71 вакансия
DevOps инженер
33 вакансии

Ближайшие события