Если кто-то не в курсе, Nautobot - форк Netbox, его продвигает широко известный в узких кругах провайдер сетевой автоматизации NTC (Network to code). Возможно, порывшись в памяти, вы вспомните не очень красивую историю начала прошлого года про затирание истории коммитов Netbox - дело как раз касалось Nautobot и NTC. Но речь пойдет не о правилах приличия опенсорсного сообщества, а о том, как легко и непринужденно написать свой плагин тому, кто выбрал в качестве источника истины и платформы автоматизации Nautobot, а не Netbox. Хотя я уверен, что реализация плагина, о котором пойдет речь дальше, так же легко может быть выполнена и для Netbox. В нашей не очень большой сети имеется ~50 филиалов, в каждом из которых установлены коммутаторы с общим количество портов от 300 до 500. На самом деле филиалов больше, но начнем мы именно с этого количества, потому что во всех из них установлено оборудование Cisco. Это важно, так как наш плагин будет пока одновендорным. По каждому порту каждого коммутатора нам необходимо знать:  когда последний раз было подключение к этому порту устройство с каким MAC/IP адресом и в каком VLAN подключено к этому порту “Постой-ка, автор” - скажете вы - “такой функционал я уже где-то видел”. Совершенно точно, это switchmap (гуглится по словосочетанию cisco switchmap). Что ж, никто не обещал, что в статье будет совершенно новая идея для плагина. Но эта идея очень хорошо ложится в концепцию использования Nautobot как платформы автоматизации и SoT. Сначала я хотел отдать дань уважения разработчикам switchmap и увековечить их творение в названии своего плагина. Но в итоге остановился на более объясняющем функционал названии - nautobot-porthistory-plugin (Sorry guys) Ну тут всё просто. Раз мы используем Nautobot, будем писать на Python и активно использовать модули Django. Никаких внешних скриптов, все внутри Nautobot. Забирать данные с сетевых устройств будем по SNMP. Коммутаторы и маршрутизаторы - Cisco, все заведены в Nautobot, у каждого устройства учитываются primary address и все интерфейсы, для каждого филиала учитывается список префиксов и VLAN. У Nautobot замечательная документация. Еще бы, ведь она почти полностью скопирована у Netbox. Но все изменения и отличия от Netbox документируются не менее хорошо, за это разработчикам отдельное спасибо. Раздел про разработку плагинов достаточно большой - https://nautobot.readthedocs.io/en/stable/plugins/development/  Но вот только первое прочтение официально туториала не дало ответы на многие вопросы (впрочем, как и последующие). Многие знания пришлось выдергивать из исходного кода самого Nautobot, а также плагинов, коих становится все больше и больше.  В соответствии поставленной задаче разделим разработку плагина на две части. В первой части сделаем что полегче - научим плагин определять время последнего использования портов коммутатора, хранить и выводить эту информацию пользователям. По окончании этого этапа ожидается, что наш плагин выполнит следующее: создаст в БД таблицу для хранения информации о неиспользуемых интерфейсах; создаст job, который будет с нужной нам периодичностью заполнять созданную таблицу; изменит страницу вывода информации о коммутаторе так, чтобы было видно, какие порты не используются больше заданного количества дней. Пока действуем строго по документации Создадим в каталоге nautobot-plugin-porthistory скелет плагина на сервере (работаем под пользователем nautobot). Nautobot includes a command to help create the plugin directory: nautobot-server startplugin [app_name]   Команда nautobot-server startplugin nautobot_porthistory_plugin создаст следующий набор папок и файлов:   Ну что, файлы есть, давайте наполним их работающим кодом. Файл пакета  __init__.py уже содержит большинство нужных параметров. Всё, что нам требуется, указать какие опции конфига обязательны для заполнения, а также настройки плагина по умолчанию. Параметры, которые будем передавать в плагин: switches_role_slug - Роль, по которой будем фильтровать коммутаторы min_idle_days - Если порт не используется меньше дней, он нас не интересует workers - количество параллельных асинхронных запросов к оборудованию Модель определим в models.py. В таблице UnusedPorts будем хранить время обновления, время последнего использования интерфейса и собственно ID самого интерфейса. Как видите, пока вообще ничего сложного. Аргумент auto_now=True указывает, что при каждом сохранении данных в таблицу поле updated автоматически обновляется до текущего времени Файлы navigation.py, urls.py и views.py пока нам не нужны, оставим их в первозданном виде. Эти три файла понадобятся на следующем этапе, когда мы сделаем отдельную ссылку на плагин в меню. Но, как обычно, есть нюанс. Импорты в views.py “из коробки” ссылаются на несуществующие модули django, поэтому чтобы не словить исключение при установке, закомментим строчку from django.views.generic import views Итак, у нас в БД есть таблица, но в ней нет данных. Создадим джоб (job в Nautobot - как custom script и report в Netbox), который соберет информацию с коммутаторов, обработает ее и сохранит в нашей таблице. Добавим файл jobs.py с классом (джобом) UnusedPortsUpdate Логика его работы проста: Сгенерировать список коммутаторов - используем фильтр по device_role.slug = switches_role_slug из конфига Асинхронно опросить все коммутаторы по SNMP - uptime устройства Асинхронно опросить все коммутаторы по SNMP - соответствие ifindex и названий портов Асинхронно опросить все коммутаторы по SNMP - Last output на портах Если Last output отрицательный смотрим, есть ли в таблице информация по данному порту. Если отсутствует - добавляем дату последнего использования равной дате загрузке (п.2). Если данные есть, просто обновляем дату проверки Если Last output, переведенный в дни,  меньше, чем min_idle_days из конфига, удаляем запись о порте из таблицы. Иначе создаем/обновляем запись. На всякий случай в необязательные входные параметры добавим Site, чтобы иметь возможность обновлять информацию по конкретному филиалу.  В дальнейшем можно будет запланировать регулярный запуск джоба, чтобы всегда иметь актуальный список неиспользуемых портов. Напомню, что в Nautobot для этого есть встроенный шедулер джобов. Всего лишь надо при запуске джоба указать периодичность. Теперь научим плагин выводить полученные данные на странице коммутатора. Для этого добавим в корень плагина файлы template_content.py и templates/unused_ports.html Названия говорят сами за себя - подготовить содержимое шаблона и отрендерить блок HTML. Отрендеренный блок разместим на странице Device, в правой части. Осталось смешать все ингредиенты и попробовать, что получилось: Создадим миграции - стандартный механизм Django для обеспечения версионности БД в обертке Nautobot. nautobot-server makemigrations (выполняется из папки плагина) добавим файлы setup.py и MANIFEST.in установим плагин простым pip3 install . из каталога плагина включим плагин в конфигурационном файле nautobot и там же укажем параметры плагинаPLUGINS = [    'nautobot_porthistory_plugin', ]PLUGINS_CONFIG = {    'nautobot_porthistory_plugin': {    'switches_role_slug': ['Access-switch'],    'min_idle_days': 14,    } } запустим пост-инсталляционный скрипт nautobot-server post_upgrade. Он обновит БД и скопирует HTML файлы в соответствующую папку. Перезапустим сервисы: sudo systemctl restart nautobot nautobot-worker Готово! Можно запустить джоб и проверить результаты его выполнения: Полный код плагина, созданный на этом этапе, можно подсмотреть по ссылке - https://github.com/iontzev/nautobot-porthistory-plugin/tree/part_1 Вторая часть реализации будет посложнее. Тем интереснее, ведь это позволит понять, что еще могут плагины в Nautobot. Сперва добавим еще одну модель в БД - таблицу, в которой будем хранить MAC и IP адреса с привязкой к интерфейсу. Способ уже знакомый - обновить models.py и создать новую миграцию nautobot-server makemigrations В jobs.py добавим еще один класс - он же джоб. Алгоритм его работы относительно несложный: асинхронно опросить по SNMP все устройства с ролью device_role.slug = switches_role_slug из конфига - получить mac address table и соответствия MAC именам портов. асинхронно опросить по SNMP все устройства с ролью device_role.slug = routers_role_slug из конфига - получить ARP таблицу. Не забудем, что конфиг тоже надо подправить - он находится в __init__.py попробовать получить hostname по IP если IP адрес не внесен в Nautobot, добавить обновить информацию о привязке MAC и IP к интерфейсам устройств Обновление построено таким образом, что если MAC на порту пропадет физически, мы сможем его найти в истории подключений, если на этом порту коммутатора больше ничего не появлялось.  Важно! Особенность получения по SNMP таблицы MAC с коммутаторов Cisco в том, что необходимо указывать в строке community номер VLAN, в котором живут MAC адреса.  Еще учтем, что не про все VLAN надо знать. Например, если в каком-то VLAN расположены беспроводные клиенты, в них бессмысленно отслеживать MAC. То же самое касается портов. Значит нам нужен механизм фильтрации VLAN и портов коммутаторов. Сюда отлично подойдут custom_fields, но использовать в скриптах названия полей, которые надо добавить пользователю, значит надеяться на маленькое чудо. Так давайте будем реалистами, добавим custom_fileds при установке плагина. Поможет нам в этом функционал Django signals, опять же в обертке Nautobot. Добавим вызов сигнала в __init__.py, а сам сигнал в signals.py. Теперь при установке плагина автоматически добавятся новые поля к моделям VLAN и Device.interface. Отметив на странице редактирования VLAN галочку Search MACs on ports in this VLAN мы тем самым разрешаем нашему джобу искать MAC адреса в этом VLAN. Итак, у нас есть джоб и настроенные VLAN, а значит можно заполнять БД нужными данными. Но как посмотреть эти данные? Первый путь нам уже знаком - используем шаблоны для страниц интерфейса и IP-адреса. Таким образом можно точечно извлекать информацию из БД и показывать пользователю: Второй путь - создать отдельную страницу плагина с возможностью вывода и поиска полученных данных. Этот путь более тернист, чем первый, но и результаты более интересны. В подготовке страницы плагина участвуют 6 составляющих. Число их можно сократить, если использовать HTML-шаблоны, но это немного другая история Итак, по порядку: navigation.py - добавляет строку в выпадающее меню Plugins urls.py - вешает на url (добавленной строки в п.1) обработчик views.py - содержит обработчики (View). Обработчик собирает в одно целое данные, параметры вывода (таблицы), форму и алгоритмы фильтрации tables.py - выводит таблицу из БД forms.py - формирует HTML форму для фильтрации данных в таблице filters.py - содержит алгоритмы фильтрации данных Вот такая получилась страница плагина. Можно фильтровать по сайту, по устройству, по VLAN. Можно искать по части MAC, причем формат ввода в поле поиска абсолютно не важен, хоть через нижние подчеркивания, хоть капсом через одну. Написать свой плагин не так и сложно, как мне казалось в самом начале, когда я только изучал тему Netbox и Nautobot. Nautobot/Netbox представляют собой отличную платформу для автоматизации. Прикладывая минимум усилий можно достичь отличных результатов в деле адаптации Nautobot/Netbox к реалиям своей организации. На этом всё, спасибо за внимание. Ну и для тех, кому эта тема действительно интересна и кто дочитал до конца, ссылка на код плагина - https://github.com/iontzev/nautobot-porthistory-plugin Network Team Lead