[root@localhost vyos_cfg_v2]# python3 vyos_cfg_v2.py -i all_devices.yaml -d reboot.yaml
######################################### Starting "R1" ##########################################
# REBOOT PHASE ####################################################################################
['reboot']
...
ValueError: Operation "reboot" not supported
Есть у меня два личных инстанса VyOS которые я администрирую. В последнее время появляются и еще машинки. Есть задача рассылки конфигурации сразу на несколько устройств, которую я решал с помощью скрипта vyos_cfg. В целом он работал довольно прилично, хоть и использовал старый добрый expect. Плюс, код был не самый уж элегантный. Поигравшись с API в прошлом посте я решил переписать скрипт и уйти от expect. Сегодня заметка про это.
Встречайте! vyos_cfg_v2!
Думаю, как он написан не особо интересно, да и это я хочу немного затронуть в будущем посте. Сегодня про то как его использовать и что он умеет.
Какие цели ставились:
- Поддержка неограниченного количества устройств
- Возможность разбивать сам процесс на логические стадии (самый простой пример - проверки до, сами изменения и проверки после)
- Простота использования
[root@localhost ~]# git clone https://github.com/MelHiour/vyos_cfg_v2.git
Cloning into 'vyos_cfg_v2'...
remote: Enumerating objects: 275, done.
remote: Counting objects: 100% (275/275), done.
remote: Compressing objects: 100% (192/192), done.
remote: Total 275 (delta 136), reused 108 (delta 47), pack-reused 0
Receiving objects: 100% (275/275), 50.73 KiB | 665.00 KiB/s, done.
Resolving deltas: 100% (136/136), done.
[root@localhost ~]# cd vyos_cfg_v2/
[root@localhost vyos_cfg_v2]# cat requirements.txt
click
PyYAML
requests
requests-mock
pytest
mock
[root@localhost vyos_cfg_v2]# pip3 install -r requirements.txt
...
Successfully installed attrs-20.3.0 importlib-metadata-3.10.0 iniconfig-1.1.1 mock-4.0.3 packaging-20.9 pluggy-0.13.1 py-1.10.0 pyparsing-2.4.7 pytest-6.2.3 requests-mock-1.8.0 toml-0.10.2 typing-extensions-3.7.4.3 zipp-3.4.1
[root@localhost vyos_cfg_v2]# cat all_devices.yaml
R1:
address: 192.168.0.21
port: 443
key_name: default
R2:
address: 192.168.0.22
port: 443
key_name: default
F1:
address: 192.168.0.11
port: 443
key_name: default
F2:
address: 192.168.0.12
port: 443
key_name: default
[root@localhost vyos_cfg_v2]# cat key.py
# This file should be in .gitignore
default = "SECRET_ONE"
- show - для отображения части конфигурации. Например, show interfaces вернет все что касается интерфейсов в конфигурации
- get - получение вывода operation комманд. get interfaces выведет то, что вы получите на устройстве введя show interfaces. Звучит, путано, но все просто
- delete - для удаления значения или ветки в конфигурации
- comment - для комментирования какого-то значения.
[root@localhost vyos_cfg_v2]# cat show-vs-get.yaml
show and get:
- show high-availability vrrp
- get vrrp
[root@localhost vyos_cfg_v2]# python3 vyos_cfg_v2.py -i all_devices.yaml -d show-vs-get.yaml
####################################### DEPLOYMENT STARTED #######################################
...
######################################### Starting "R2" ##########################################
# SHOW AND GET PHASE ##############################################################################
['show high-availability vrrp', 'get vrrp']
# RESULTS #########################################################################################
# COMMAND: show high-availability vrrp
# SUCCESS: True
# ERROR: None
# RESULT:
{'group': {'VLAN11': {'hello-source-address': '10.0.11.2',
'interface': 'eth1',
'peer-address': '10.0.11.1',
'priority': '100',
'virtual-address': '10.0.11.254/24',
'vrid': '11'},
'VLAN12': {'hello-source-address': '10.0.12.2',
'interface': 'eth2',
'peer-address': '10.0.12.1',
'priority': '200',
'transition-script': {'backup': '/config/vrrp-goes-backup.sh',
'master': '/config/vrrp-goes-master.sh'},
'virtual-address': '10.0.12.254/24',
'vrid': '12'}}}
# COMMAND: get vrrp
# SUCCESS: True
# ERROR: None
# RESULT:
['Name Interface VRID State Priority Last Transition',
'------ ----------- ------ ------- ---------- -----------------',
'VLAN11 eth1 11 MASTER 100 15m58s',
'VLAN12 eth2 12 MASTER 200 15m58s']
[root@localhost vyos_cfg_v2]# cat ntp.yaml
GENERAL:
- get system processes summary
- get system memory
PRE CHECKS:
- show system ntp
- get ntp
NTP CONFIG:
- delete system ntp
- set system ntp server 192.168.0.254
POST checks:
- show system ntp
- get ntp
[root@localhost vyos_cfg_v2]# python3 vyos_cfg_v2.py -i all_devices.yaml -d ntp.yaml
####################################### DEPLOYMENT STARTED #######################################
######################################### Starting "R1" ##########################################
# GENERAL PHASE ###################################################################################
['get system processes summary', 'get system memory']
# RESULTS #########################################################################################
# COMMAND: get system processes summary
# SUCCESS: True
# ERROR: None
# RESULT:
[' 20:53:33 up 1 day, 1:09, 1 user, load average: 1.04, 1.04, 1.01'] <<< Выглядит неплохо
# COMMAND: get system memory
# SUCCESS: True
# ERROR: None
# RESULT:
['Total: 484', 'Free: 279', 'Used: 205'] <<< Памяти тоже хватает
# PRE CHECKS PHASE ################################################################################
['show system ntp', 'get ntp']
# RESULTS #########################################################################################
# COMMAND: show system ntp <<< Смотрим настройки
# SUCCESS: True
# ERROR: None
# RESULT:
{'server': {'0.pool.ntp.org': {}, '1.pool.ntp.org': {}, '2.pool.ntp.org': {}}} <<< Дефолтные сервера
# COMMAND: get ntp
# SUCCESS: True
# ERROR: None
# RESULT:
["No association ID's returned"] <<< Время они не отдают
# NTP CONFIG PHASE ################################################################################
['delete system ntp', 'set system ntp server 192.168.0.254']
Do you want to continue? (y/n): y <<< Перед изменениями скрипт спрашивает разрешение
# RESULTS #########################################################################################
# COMMAND: Batched push of commands above <<< Конфигурационные команды отправляются пачкой
# SUCCESS: True <<< Ошибок нет
# ERROR: None
# RESULT:
None. <<< API ничего не вернул
# POST CHECKS PHASE ###############################################################################
['show system ntp', 'get ntp']
# RESULTS #########################################################################################
# COMMAND: show system ntp
# SUCCESS: True
# ERROR: None
# RESULT:
{'server': {'192.168.0.254': {}}}. < Видим только новый сервер в конфиге
# COMMAND: get ntp
# SUCCESS: True
# ERROR: None
# RESULT:
[' remote refid st t when poll reach delay offset jitter',
'==============================================================================',
' 192.168.0.254 27.124.125.251 3 u - 64 1 0.653 -0.273 0.100'] <<< и ассоциацию
###################################### SAVING CONFIGURATION ######################################
# COMMAND: Save config <<< Сохраняемся
# SUCCESS: True
# ERROR: None
# RESULT:
["Saving configuration to '/config/config.boot'...", 'Done']
Вместо выводов
- Хочется иметь возможность поставить скрипт в cron и подкладывать ему файлики. Делать это можно и сейчас, но по хорошему нужно переписать/дописать логирование или вообще обличить все это в отдельный процесс.
- Хочется провести нагрузочное тестирование и сравнить с expect версией
- Было бы интересно подумать над реализацией более серьезного варианта. Скажем, разбить саму рассылку конфигов на параллельные задачи, которые будут выполняться отдельными процессами/машинами/контейнерами. Надзором будет заниматься некий оркестратор. В таком варианте можно масштабировать скрипт. Знаю, что это уже придумали, но свой велосипед всегда милей, пусть и с квадратными колесами.
- Нужно понимать, что API все еще в rolling release и есть некоторые вопросы. Например, как перезагрузить роутер через API?
- Не поддерживаются сложные команды с пайпами в operation режиме. Скажем get system processes | no-more не пройдет. Та же история с комментариями. set interface description "Long description" оставит неправильный дескрипшен. Нужно переписывать логику. Завел ишу...
Такая вот заметочка получилась.
Комментариев нет:
Отправить комментарий