Теоретические основы и архитектура
Автоматизация конфигурации парка оборудования оператора реализуется с помощью Ansible - безагентной системы управления конфигурациями. Модули для сетевых устройств выполняются на контроллере, а на оборудование уходят CLI-команды или RPC, поэтому подключение задаётся плагином connection (ansible.netcommon.network_cli, netconf или httpapi). Основные компоненты: 1. Инвентарь (Inventory): файл YAML или INI с группами устройств (pe_routers, p_routers, rr_servers), их адресами, платформой (ansible_network_os: cisco.ios.ios, junipernetworks.junos.junos, community.network.ce для Huawei CloudEngine, community.routeros.routeros) и переменными групп (group_vars) и хостов (host_vars). 2. Модули (cisco.ios.ios_config, junipernetworks.junos.junos_config и другие): идемпотентность *_config реализована сравнением текста конфигурации: если нужные строки уже есть, модуль возвращает ok. Строки пишут в нормализованном виде, иначе задача будет вечно changed. 3. Плейбуки: сценарии для сбора фактов, проверки соответствия и выкатки VRF и интерфейсов с режимом --check --diff, backup: true и поэтапным serial. Плейбук снижает число ручных ошибок, но не заменяет проверку данных до применения и подтверждение с откатом.
Что даёт Ansible в сети оператора и чего не даёт
Ansible работает без агента: контроллер подключается к устройству по SSH (или NETCONF, HTTP API) и выполняет модуль. Для сети модули не запускаются на устройстве: исполняется Python на контроллере, а на железо уходят CLI-команды или RPC. Поэтому подключение задаётся не ssh, а плагином connection: ansible.netcommon.network_cli (CLI поверх SSH), ansible.netcommon.netconf (NETCONF) или ansible.netcommon.httpapi. Платформа выбирается переменной ansible_network_os.
| Платформа | Коллекция и модули | ansible_network_os |
|---|---|---|
| Cisco IOS-XE | cisco.ios: ios_config, ios_vrf, ios_command | cisco.ios.ios |
| Huawei CloudEngine/VRP | community.network: ce_config, ce_command | community.network.ce |
| Juniper Junos | junipernetworks.junos: junos_config, junos_command | junipernetworks.junos.junos |
| MikroTik RouterOS | community.routeros: command, api, api_modify | community.routeros.routeros |
Версии коллекций фиксируются в requirements.yml, иначе обновление контроллера изменит поведение модулей. Для Huawei официальной коллекции вендора для маршрутизаторов нет; рабочая альтернатива - ansible.netcommon.netconf_config с XML-пейлоадом по YANG-модели устройства.
Inventory, group_vars, host_vars
Inventory описывает, какие устройства существуют и как к ним подключаться. Формат YAML позволяет вложенные группы: pe_routers содержит pe_cisco, pe_juniper и так далее. Переменные берутся по приоритету: group_vars/all ниже group_vars/<группа>, а host_vars/<хост> выше группы. Правило проектирования: в inventory только то, что нужно для подключения и принадлежности к группам, а данные услуги (VRF, RD, RT) лежат в group_vars и host_vars или во внешнем источнике правды. Для крупных сетей inventory строится динамически: netbox.netbox.nb_inventory читает устройства и теги из NetBox, и ручной список исчезает.
# inventory/prod/hosts.yml
all:
children:
pe_routers:
children:
pe_cisco:
hosts: {pe1: {ansible_host: 10.255.0.1, pe_id: 1}}
pe_juniper:
hosts: {pe2: {ansible_host: 10.255.0.2, pe_id: 2}}
# group_vars/all.yml
as_number: 65000
allowed_rt: {A: ["65000:100"], B: ["65000:200"]}Команда ansible-inventory -i inventory/prod --graph показывает итоговое дерево групп, а --host pe1 итоговые переменные хоста после слияния. Это первая проверка при любой неожиданности.
Идемпотентность: как она устроена на самом деле
Идемпотентность значит, что повторный запуск не создаёт изменений. Для модулей *_config она реализована сравнением текста: Ansible загружает running-config, сопоставляет строки lines под нужным parents и отправляет только отсутствующие. Отсюда типовые проблемы:
- Строка в плейбуке должна совпадать с тем, как устройство хранит команду.
route-target both 65000:100на IOS-XE в running-config превращается в две строки import и export, поэтому такая задача всегда будетchanged. Пишите строки в нормализованном виде. - Команды со значениями по умолчанию, которые устройство не показывает, также дают вечный
changed. ios_configпо умолчаниюmatch: line; для порядка важных строк используйтеmatch: exactилиstrict, а для замены целого блокаreplace: block.- Команды RouterOS
/ip/vrf/addне идемпотентны: повторное добавление возвращает ошибку, поэтому либо проверка перед добавлением, либо модульcommunity.routeros.api_modify.
Check mode и diff
ansible-playbook --check --diff выполняет модули в режиме "что бы изменилось". Для ios_config, junos_config и ce_config модуль строит дифф, но не применяет его; на Junos candidate загружается с check_commit. Ограничения: модули *_command в check mode не выполняют команды изменения, а зависимые задачи могут ошибиться, потому что предыдущие шаги не применились. Поэтому check mode в CI считается проверкой синтаксиса и диффа, но не доказательством работоспособности: его дополняют стендом.