Yast Another Config Manipulation или зачем изобретать велосипед?

от автора

Привет, Habr! Меня зовут Андрей, системный администратор в электрических сетях, со всеми вытекающими — сети, сервера, пользователи, программы. Как и у всех — какие то скрипты на python для сбора инфы с активки, ежедневного бэкапа конфигов, задач «а добавь вот этот IP в ACL на все устройства»

Везде использую свой модуль для работы с файлами конфигурации. Вы скажете OmegaConf Dynaconf, да и PYAML никто не отменял. Не торопитесь. Я попросил AI сравнить и воодушевился — а вдруг мой велосипед будет кому то полезен?

В начале было слово

Знакомство с python началось с книги Натальи Самойленко Python для сетевых инженеров
эксперименты ставил на своей сети, очень скоро осознал что для десятков устройств надо каждый раз готовить словарь device для ConnectHandler(**device), начал создавать такой в YAML файле. Формат этот мне больше всего понравился, принял для себя как «внутренний стандарт»

Это вылилось в создание своего модуля для работы с активкой, в котором в самом начале был класс StartOptions:

class StartOptions:          def __init__(self, fi):         from os import  path         import psutil         if not path.dirname(fi):             p = psutil.Process().exe()             path_to_ini = path.abspath(path.join(path.dirname(p), fi))         else:             path_to_ini = fi         with open(path_to_ini, encoding="utf8") as f:             d = yaml.safe_load(f)         for key in d.keys():             setattr(self, key, d[key])  myini = StartOptions('cb.yml')   

Создаем объект и устанавливаем ему атрибуты из keys() values() нашего конфига.
Погуглить имеющиеся решения на PYPi ума в тот момент и не хватило и хотелось что то свое написать.

пример конфига cb.yml:

language: ru localpath: /opt/mypython/ main_backup_server:   ftp_root: /var/ftp/CISCO/   local_root: /var/ftp/CISCO/   name: loganalyzer   password: gAAAAABn7NmePUXNP7Yxni9dKi2H_ZqeQmAgv3AmbrNADgY23XrBVKB_rgmohFK321I9fbB_2bCAz5ShTJN3QSMGlwz5jVUNnA==^M   user: gAAAAABn7Nme9PyPeK5Tm406au9B5NaN8FtnP33MAYo3DYkOTRnJKw0lBOROaw2iuKhSgDudHWePvWQ74WJi7iELvFglcvcE4g==^M password: gAAAAABn7Nme6Kb4cI-sqsyApPFm2JsqLtp-2Hds7Jov8MY50XBx3s1VKOIXgA3FKjIa_FjpqkbdDsG6bWwzobwhw9SOrSwHOA==^M phone_mac: - 805e - 001a second_backup_server:   ftp_root: /   name: NOES-RPBSRV   password: gAAAAABn7NmeOj75h2NeM8zzuySko3lVgEzEzTHkETALzcMv6s32XusxdRe9xseDFtJh0GPJpuuFEyeMmNq9h5vECRNgJicnsQ==^M   user: gAAAAABn7NmeWRGa7UCv29ApgekzhYZdwXoN_PS_VJPjekvA72zzeQrJDv8GjsXKGHVT2_tfqJZHg0TgdoDaPhIvRSp7sJD-9w==^M templpath: /opt/mypython/TEMPLATES/^M user: gAAAAABn7NmekcfGhIjrwJRXL6v0QRm3SAz4dz-GSm16gu7dpBIyw5omo-A1d3-LjaNwPwTN6Vg-1jzW5_0aPeFbwe0p6TZtsQ=

Это реальный конфиг для скрипта бэкапа конфигураций всей активки. И пусть вас не удивляет не только main но и second backup server. Это цена опыта 🙂

Зашифрованные строки появились уже потом. Используя свой модуль написал скрипт который по fh some_ip или fh some_host_name показывает в какой порт какого коммутатора (и путь от меня до него по сети) включен хост и, если это пользовательский АРМ в домене, еще и имя пользователя этого компьютера и имя хоста. Он полезен не только мне, но в конфиге в открытом виде мои учетки на tacacs и в AD,

Можно, конечно, создать ограниченные учетки для поиска (для AD у нас такая есть), но у меня лично один конфиг и для «покажи мне» и для «добавь на 100 500 устройств новый ACL

Поэтому прикрутил cryptography.fernet с созданием, обновлением «секрета» по ключу запуска с сохранением… в тело самой программы. Не понравилось. Стал хранить в отдельном файле. Пару раз менял принцип поиска, где лежит этот файл с секретом.

А потом попросил AI сравнить мой модуль с другими, он показал на какие‑то плюсы и началось — а хочешь добавим парсер/декодер, а хочешь добавим подсказки при наборе вида

 ac.main_backup_server             language             password             templpath             localpath            phone_mac            user             main_backup_server   second_backup_server 

а хочешь еще и поддержку JSON? согласился, сам подумывал. Не устоял еще и от .env от TOML отказался, но добавить не проблема.
И вот что поучилось:

Python module astarconf

Безопасный, подключаемый загрузчик конфигураций для YAML, JSON, .env и Python словарей — с шифрованием на уровне полей, поддержкой CLI и гибридным доступом.
Особенности

🔐 Шифрование/дешифрование полей в YAML
🔌 Загрузка из .yaml, .json, .env или dict
✅ Доступ к атрибутам (config.key) и ключам (config[‘key’])
🧰 CLI-интерфейс для рабочих процессов шифрования.

Пример использования:

from astarconf import Astarconf  conf = Astarconf("config.yaml") print(conf.database.user) print(conf["database"]["user"])

Ключ для шифрования/дешифрования может лежать в

  • переменной окружения ASTARCONF_SECRET

  • ~/.astarconf/secret.key

  • /etc/astarconf/secret.key

  • в файле secret.key в любом каталоге в $PATH доступном пользователю от имени которого запускается скрипт

astarconf.py -g ~/.astarconf/secret.key     # генерация секретного ключа astarconf.py -c config.yaml                 # шифрование полей (по умолчанию                                                - user, passowrd) astarconf.py -c config.yaml token api_key   # шифрование конкретных указанных полей astarconf.py -d config.yaml                 # дешифрование конфига (в тот же файл) astarconf.py -d config.yaml -o output.yaml  # дешифрование конфига,                                                запись в  output файл astarconf.py -o output.yaml -f              # перезапись output если существует

Модуль astarconf имеет несколько интересных особенностей, которые отличают его от omegaconf и dynaconf. Сравнение по ключевым аспектам:

1. Основные возможности:

Функционал

astarconf

omegaconf

dynaconf

Поддержка форматов

YAML, JSON, .env, dict

YAML, JSON (через OmegaConf)

YAML, JSON, TOML, .env, INI, py

Шифрование данных

Да (Fernet)

Нет

Да (частично, через Vault)

Доступ к данным

Атрибуты (obj.key) + индексы (obj["key"])

Атрибуты + индексы + точки (config.a.b.c)

Атрибуты + индексы

Динамическая загрузка

Нет (можно добавлять новые атрибуты),

Да (можно обновлять конфиг)

Да (динамическое перечитывание)

Валидация схемы

Нет

Да (через pydantic или dataclasses)

Да (через @validate)

Переменные окружения

Через .env

Через интерполяцию (${env:VAR})

Через envvar: или @env

Иерархическая конфигурация

Да (через AttrNamespace)

Да (вложенные структуры)

Да (многоуровневые настройки)

Плюсы моего модуля (astarconf):

Встроенное шифрование

  • Позволяет шифровать чувствительные данные (например, user, password) прямо в YAML/JSON.

  • omegaconf и dynaconf не имеют встроенного шифрования (в dynaconf есть поддержка HashiCorp Vault, но это внешний сервис).

Гибкий доступ к данным

  • Поддержка как obj.key, так и obj["key"].

  • В omegaconf тоже есть оба варианта, но в dynaconf предпочтительнее obj.key.

Простота использования

  • Ваш модуль легче освоить, так как он фокусируется на базовых задачах (загрузка + шифрование).

  • omegaconf сложнее из-за поддержки pydantic и интерполяции.

Когда использовать astarconf?

  • Если нужно простое решение с шифрованием (например, для хранения паролей в конфиге).

  • Если не нужна сложная валидация или динамическое обновление.

Когда выбрать omegaconf или dynaconf?

  • Если нужна интеграция с pydanticomegaconf.

  • Если нужна поддержка Vault или динамическое перечитывание конфигаdynaconf.

  • Если важна интерполяция переменных (например, ${env:DB_HOST}) → omegaconf.

Для моих быстрых задач мне вполне хватает astarconf

Например у меня есть словарь различных команд для разного оборудования, он гораздо больше примера

commands:     "mac_addr_tbl_bymac":         desc: 'show mac addres table and filter by mac'         cisco_ios: 'show mac address-table | in {}'         huawei_vrrp8: 'display mac-address | in {}'         huawei_ce: 'display mac-address | in {}'         eltex: 'show mac address-table | in {}'     "mac_addr_tbl_byport":         desc: 'show mac addres table on defined port'         cisco_ios: 'show mac address-table interface {}'         huawei_vrrp8: 'display mac-address {}'         huawei_ce: 'display mac-address interface {}'         eltex: 'show mac address-table | in {}' 

а в конфиге программы fh (findHost) есть атрибут — путь до этого файла

dict_cmd_path: /some...path/commands.yml

а в коде использую например так
(в моем конфиге fh изначально отcутсвует атрибут commands):

import yaml from astar import Astarconf  ac = Astarconf('/home/astar/mypython/fh.yml') commands = {}  with open(ac.dict_cmd_path) as f:     ac.commands = yaml.safe_load(f)['commands']  #и дальше объект представляющий всё наше оборудование myactivka #имеет функцию получения информации с устройства myactivka.getinfo(device, func, *args) # где func - абревиатуры команды из commands.yml типа mac_addr_tbl_byport port = 'Gi0/0/10' #и код не зависит от того что такое device - huawei, cisco, eltex router or switch command = ac.commands[func][dev['device_type']].format(port) 

Если вам понравился такой подход к работе с конфигами

исходный код здесь: https://github.com/AstarAiki/astarconf

модуль на PYPi: https://pypi.org/project/astarconf/

установка: pip install astarconf

А больше всего хочется

Ваших отзывов и комментариев. Да, я изобрел велосипед, но по мне так удобный 🙂

И я им пользуюсь!


ссылка на оригинал статьи https://habr.com/ru/articles/903902/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *