Наша почта:
В Москве:
РФ (звонок бесплатный):
Фуллстек-сетевик на минималках: поднимаем мини веб-сервис для управления VLAN'ами
{ Сетевой инженер в облачной платформе MWS }
Роман Карауланов

Видео доклада

Презентация спикера

Фуллстек-сетевик на минималках
Скрипты пишут практически все сетевые инженеры: кто-то больше, кто-то меньше, а те, кто утверждает, что не пишет, обычно пишут больше остальных. Но можно ли сказать, что сетевая автоматизация — это только про написание кода?
Поначалу кажется, что да. Однако сети растут, скриптов становится больше, сервисов — тоже: свои скрипты, свои сервисы, сторонние решения вроде NetBox, AWX и так далее. И сетевой инженер плавно превращается в своего рода full-stack-сетевика, которому всё это хозяйство приходится ещё и администрировать. В небольшой компании редко бывает так, что работодатель выделит под это отдельного программиста, бэкендера, фронтендера и продакта в придачу. Поэтому приходится вгрызаться зубами в «чужеземные» технологии, чтобы адаптировать их у себя на сети и начать применять.

Обзор технологий

В проекте задействованы:
  • Flask
    легковесный фреймворк для создания приложений на Python;
  • Gunicorn
    WSGI-сервер для запуска этих приложений;
  • Jinja
    шаблонизатор для создания динамических текстовых файлов;
  • re
    модуль для работы с регулярными выражениями;
  • Netmiko и Scrapli
    библиотеки для подключения к сетевому оборудованию и выполнения на нём команд;
  • Docker и Git

Flask

Flask — это мини-фреймворк для Python, который позволяет написать приложение и тут же его запустить. Он простой и гибкий, хорошо подходит сетевым инженерам, которые только начинают работать с Python. У него есть встроенный веб-сервер для разработки и тестов: можно прямо здесь и сейчас что-то запустить, проверить, потыкать.
  • Минимальный пример
    импортируется Flask, определяется приложение app = Flask(__name__) — __name__ по сути соответствует имени файла (если файл называется app.py, приложение тоже будет называться app). Затем регистрируется view через декоратор route: при переходе по этому маршруту возвращается некоторое содержимое — например, при заходе на корень сайта выводится Hello, network engineer.
  • Более близкий сетевикам пример
    то же самое, только маршрут принимает ping/<ip>. IP в скобках превращается в переменную, которая используется для запуска команды через библиотеку subprocess (она позволяет выполнять команды в Linux-системе). Результат обрабатывается и выводится прямо в окно браузера.

Gunicorn

Приложение написано — его нужно запустить. Встроенный сервер Flask для этого не годится: сами разработчики Flask пишут, что использовать его в проде не нужно, он малопроизводителен и предназначен только для тестов и разработки.

Gunicorn — легковесный WSGI-сервер для приложений на Flask или Django. WSGI (Web Server Gateway Interface) — это прослойка между приложением на Python и веб-сервером. Чаще всего такую связку используют для API, но она отлично подходит и для полноценных мини-веб-приложений.

Gunicorn production-ready, простой и удобный. Он умеет запускать воркеры: если приложение упёрлось в лимит, достаточно увеличить их количество. Дальше Gunicorn сам следит за воркерами и перезапускает их, если те падают. Функционал достаточно богатый — поддерживается и синхронный, и асинхронный код.

Jinja

Jinja — шаблонизатор для создания динамических текстов на основании шаблонов и набора данных. Большинство сетевых инженеров любят Jinja за возможность рендерить конфигурацию для сетевого оборудования. Второй плюс — Python-like синтаксис: тем, кто работает с Python, овладеть им очень легко.

Синтаксисов два: один для логики (циклы, условия — примерно всё как в Python), другой для переменных. Доступны готовые фильтры вроде lower, если с переменной нужно дополнительно поработать.

Типичный пример

Есть список словарей с именем интерфейса, description, IP-адресом и маской подсети. Есть шаблон на Jinja, который в цикле проходится по списку интерфейсов и выводит для каждого interface, description, ip address. Данные передаются в шаблон — и на выходе получается готовый конфиг для устройства. Так можно очень удобно кастомизировать конфигурацию сетевого оборудования.

Модуль re

Длинные, сложные, «мозговыносящие» строчки из разных символов видели практически все. На самом деле регулярные выражения — очень полезный и мощный инструмент, позволяющий выудить из большого объёма текста именно те данные, которые нужны.
Регулярка описывается на специальном языке, использующем метасимволы: они позволяют сматчить число, букву, конкретный символ, любой символ, начало или конец строки.
Основные методы модуля:
  • search — ищет первое совпадение в переданном тексте;
  • match — делает примерно то же самое, но ищет только в начале строки;
  • split — похож на обычный split в Python, но дробит строку по регулярному выражению;
  • sub — похож на replace: заменяет одно на другое по определённому паттерну;
  • findall — находит все вхождения в тексте;
  • compile — пригодится, если текст огромный и регулярку нужно вызывать много раз: выражение компилируется один раз и затем используется многократно, что оптимально по затратам ресурсов.

Netmiko и Scrapli

Netmiko — замечательная библиотека, которая позволяет подключиться к сетевому оборудованию и выполнить на нём какие-то действия. Синтаксис достаточно прост: создаётся словарь, в котором определяются тип устройства (в данном случае cisco_ios), хост (например, cisco-sv — хост, добавленный в /etc/hosts и резолвящийся в IP-адрес), username и password.
Способов подключения два.
  • Первый:
    Инициализируется ConnectHandler, передаются данные, посылается команда — и самое главное, по завершении нужно отключиться от устройства. Чтобы не делать этого каждый раз, рекомендуется использовать менеджер контекста.
  • Второй:
    Менеджер контекста — специальный протокол в Python, реализующий какие-то действия до и после: он сам заботится о подключении к оборудованию, а по завершении работы автоматически отключается. Если есть возможность, стоит использовать именно его.
У Netmiko хорошая широкая документация и большое комьюнити. Но есть и Scrapli — более современная библиотека для работы с сетевым оборудованием, которая тоже поддерживает огромное количество вендоров. Основное отличие в том, что она умеет работать с асинхронным кодом. Иногда синхронный код очень сильно тормозит и нужно ускориться; можно, конечно, распараллелить работу в потоках или процессах, но асинхронный код в этом плане гораздо гибче. Кроме того, Scrapli позволяет использовать различный транспорт — Telnet, SSH и другие — и имеет встроенные функции по парсингу вывода

Docker

Резонный вопрос сетевого инженера: зачем Docker, это же DevOps и всё такое, а тут нужно просто выполнить команду?
Если пишется приложение, Docker решает сразу несколько проблем.
  • Первый:
    изоляция приложения: оно упаковывается в контейнер, который можно условно считать бинарником, и запускается где угодно — Linux, macOS, Windows. Об окружении заботиться не нужно, это готовое упакованное приложение.
  • Второй:
    готовые образы. Существуют так называемые registry с готовыми образами: большинство решений (NetBox, nginx и другие) выкладывают свои образы в Docker Hub либо в другие registry. Запустить, например, nginx можно буквально одной командой — и больше ничего делать не надо. Удобно, и работает на любой платформе, где поддерживается Docker.
Команд у Docker очень много;
На практике для старта достаточно небольшого набора самых популярных.

Git

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

Проект Access VLAN Deployer

Структура
  • templates/index.html
    HTML-страница, написанная с использованием Jinja-шаблона: она рендерится в зависимости от того, какие данные вернёт программа.
  • api.py
    основной файл, запускающий само WSGI-приложение.
  • common_api.py
    общие вспомогательные функции.
  • netmiko_switch.py и scrapli_switch.py
    две реализации работы с оборудованием. Может возникнуть вопрос, зачем две, ведь Scrapli лучше. Реализованы обе, поскольку кто-то использует Netmiko, а кто-то Scrapli, и так адаптировать проект под себя проще. Они подключаются путём комментирования одной строки в api.py, и можно реально посмотреть, как работать и с тем, и с другим модулем.
  • Dockerfile
    файл, при помощи которого Docker собирает образ.
  • requirements.txt
    набор модулей и библиотек, необходимых для работы приложения.
  • gitignore
    особый файл для Git, куда попадают файлы и каталоги, которых не хотелось бы видеть в репозитории: временные файлы, кэши и прочее.
  • examples/
    Python-скрипты, рассмотренные в презентации, чтобы их можно было запустить и потыкать.

api.py

В докстринге маршрута указан запрос вида localhost/cisco-sv, где cisco-sv — это IP-адрес либо FQDN. Он передаётся внутрь view и далее в функцию get_switch_info, задача которой — получить информацию от свитча и вернуть её в обработанном виде. Если данные есть, запускается render_template, который рендерит страницу исходя из полученных данных. Если нет — возвращается 404: получить данные с коммутатора не удалось, дальше нужно разбираться.

deploy_vlan — второй маршрут, срабатывающий при нажатии кнопки на форме. Пользователь натыкал нужные VLAN, нажал кнопку — в этот момент срабатывает POST-запрос на эту ручку. Из формы достаются свитч, интерфейсы и VLAN и передаются в функцию deploy_vlan, которая и выкатывает изменения на коммутатор. В конце вызывается redirect, возвращающий на предыдущую страницу, чтобы для пользователя всё было бесшовно. Завершает файл app.run — тот самый встроенный сервер Flask, на котором удобно тестироваться.

common_api.py

Здесь находятся общие функции.

get_auth позволяет получить значения из переменных окружения. Воспитанные сетевые инженеры не хранят креды в коде: логин и пароль ни в коем случае не должны быть указаны в исходниках, чтобы никуда не утечь. Поэтому используется load_dotenv — он ищет файл, в котором могут быть указаны переменные окружения (например, если вместо переменных оболочки подсовывается файл). Если файла нет, он просто игнорируется, и значение пытаются получить из переменных оболочки. Если и там пусто — выбрасывается исключение.

Парсеры

Недостаточно просто получить данные с коммутатора, нужны конкретные данные, и этим занимаются регулярные выражения. Из таблицы show interface description нужны только имя интерфейса и description — этим и занимается соответствующая регулярка. Важная деталь — флаг MULTILINE: он позволяет регулярке понимать, что работа идёт с многострочным текстом. Дальше при помощи findall находятся все вхождения, получается список кортежей, который превращается в словарь, где ключ — интерфейс, а значение — его description.
parse_vlan_brief устроен примерно так же: из вывода show vlan brief достаются access-VLAN, их номера, имена и статус.

Функция prepare_data

Prepare_data складывает полученные данные воедино, чтобы с ними было удобнее работать: она проходится по интерфейсам с их description, смотрит VLAN, и если VLAN есть — добавляет его в current_vlan. На выходе получается структура, где для каждого интерфейса есть description и current_vlan.

Подключение и раскатка

Подключение к коммутатору реализовано отдельной функцией, формирующей словарь параметров: тип устройства cisco_ios, хост, а также username и password, которые берутся через get_auth, чтобы не указывать их в явном виде.
Дальше get_switch_info подключается к устройству и выполняет команды show interface description и show vlan brief с исключением неподдерживаемых служебных VLAN (1002–1005 — те самые, для FDDI, Token Ring и прочего старья, которые не нужны). Полученные данные парсятся и передаются в prepare_data, которая приводит всё к единой структуре.
Для раскатки берутся свитч, интерфейсы и VLAN. Список VLAN обходится в цикле, и если current_vlan не равен новому VLAN, формируется список команд: interface <такой-то>, switchport access vlan <такой-то>. Перевыкатывать конфиг, который уже есть на устройстве, не нужно — выкатывается только то, что действительно изменилось. Затем происходит подключение к оборудованию, и при помощи метода send_config_set изменения применяются.
Файл scrapli_switch.py устроен практически так же: те же названия методов get_switch_info и deploy_vlan, то же описание, за исключением мелких особенностей.

index.html

С HTML-тегами многие знакомы ещё со школы или института — из них составляется каркас страницы. Самое важное здесь — использование Jinja-шаблона: в шаблон передаются интерфейсы, по ним идёт обход, и при помощи тегов <tr>/<td> генерируется таблица. В последней колонке рендерится select — выпадающий список, который наполняется информацией о VLAN, имеющихся на коммутаторе, чтобы можно было выбрать только нужный.
Внизу страницы находится
input type="submit" — кнопка раскатки. В атрибуте action формы указан маршрут deploy_vlan: при нажатии кнопки данные передаются POST-запросом на ту самую ручку, описанную в api.py, и происходит магия.

Демонстрация

Приложение запускается командой python api.py, страница рендерится. Текущий VLAN в таблице соответствует тому, что сейчас на оборудовании; в селекторе видны только те VLAN, которые действительно есть на коммутаторе, — ничего лишнего.
Для наглядности: на портах Tatooine и Coruscant в данный момент живёт Luke Skywalker. Попробуем отправить туда Darth Vader и сделать так, чтобы Империя отпраздновала победу. На Gi0/1 и Gi0/2 выбирается соответствующий VLAN, нажимается кнопка раскатки — пара секунд, и 50-й VLAN спокойно прописался на нужных портах. Империя может праздновать победу.
Всё работает, но смущает дизайн: как будто интернет снова молодой и впереди премьера очередного эпизода «Звёздных войн».
Наведение красоты выглядит так:
  • Шаг 1
    Скопировать имеющуюся форму
  • Шаг 2
    Вставить её в языковую модель вместе с промптом
  • Шаг 3
    Получившийся текст вставить обратно
  • На выходе
    Ультрасовременное приложение, которое сделано полностью самостоятельно

Упаковка в Docker

Приложение нужно упаковать, чтобы его проще было запускать, например на сервере. Для этого есть Dockerfile, описывающий структуру того, что Docker должен сделать при сборке. Образы собираются не с нуля — они наследуются от уже готовых образов, которые сами состоят из таких же команд.
Финал
Проект является учебным, его задача — показать основные концепции, которые могут пригодиться в работе.
Что можно улучшить? Во-первых, наблюдаемость: модуль logging позволяет залогировать происходящее в приложении, что полезно с точки зрения отладки.
Во-вторых, поддержка вендоров. Сейчас поддерживается только Cisco, но редко у кого используется только Cisco — обычно есть и другие вендоры. В проект сейчас передаются по сути только IP-адрес и hostname; можно передавать ещё и вендора, а внутри приложения указывать: если вендор такой-то, использовать такие-то команды для подключения и такую-то функцию для парсинга данных. При этом результат парсинга должен быть абсолютно таким же, как для Cisco.
Главный же вывод шире: сетевая автоматизация — это не только про написание скриптов. Это ещё и про веб-фреймворки, серверы приложений, шаблонизаторы, контейнеры и системы контроля версий, которые из «чужеземных» технологий постепенно превращаются в повседневный инструмент сетевого инженера.
В Москве:
РФ (звонок бесплатный):
Наша почта:
GetNet
Остались вопросы?
Группа компаний VoxLink © 2011-2026