Подробная документация доступна по ссылке: ConfDB Overview, мы же кратко остановимся на основных вещах.
Существует множество способов хранения данных. Они появляются для при решении задач, и позволяют сохранять и работать с данными удобнее/быстрее/проще. В НОКе давно существует средство работы с конфигурацией основанное на раскладке оной на факты. Например, есть факты: имя хоста, адреса `NTP` серверов, при проходе по тексту конфигурации они заполняются обнаруженными там значениями. После этого и передаются движку CLIPS, который по результатам проверок выдаст результат (прошли/не прошли). Механизм работал но, с недостатками:
- Нельзя определить факты только для какого-то профиля.
- Невозможны пользовательские расширения (требовалось править базовый код системы)
- Библиотека PyCLIPS перестала развиваться и не работала под 3 питон
- Ресурсоёмкость: все факты загружались в память и чем больше устройств необходимо обработать, тем больше памяти надо.
Для преодоления недостатков был реализован новый механимз - ConfDB. В его основу положен список правил (`синтаксис`) после прохождения которых получаем дерево: ConfDB Syntax. В интерфейсе это выглядит так: ...
Новый механиз позволяет:
- Расширять основной синтаксис новыми фактами без правок основного кода
- Использовать единый язык запросов
- Положить данные в БД, ускоряя массовую обработку
Синтаксис
Базовый синтаксис можно посмотреть командой ./noc confdb syntax:
..... interfaces *<interface> type <type> description <description> admin-status <admin_status> mtu <mtu> speed <speed> duplex <duplex> flow-control <flow_control> .....
Файлы с описанием расположены в папке noc/core/confdb/syntax. Сам формат описания выглядит пугающе (тут главное понимать DEF - узел дерева, если название узла без кавычек (например IF_NAME), то вместо него будет подставлено значение и будет новая ветка):
INTERFACES_SYNTAX = DEF( "interfaces", [ DEF( IF_NAME, [ INTERFACES_META_SYNTAX, DEF( "type", [ DEF( CHOICES( "physical", "SVI", "aggregated", "loopback", "management", "null", "tunnel", "other", "template", "dry", "unknown", ), required=True, name="type", gen="make_interface_type", ) ], ), DEF( "description", [DEF(ANY, required=True, name="description", gen="make_interface_description")], ), DEF( "admin-status", [ DEF( BOOL, required=True, name="admin_status", gen="make_interface_admin_status", ) ], ), DEF("mtu", [DEF(INTEGER, required=True, name="mtu", gen="make_interface_mtu")]), DEF( "speed", [DEF(INTEGER, required=True, name="speed", gen="make_interface_speed")] ), DEF( "duplex", [DEF(BOOL, required=True, name="duplex", gen="make_interface_duplex")] ),
Но позволяет строить дерево и легко расширяется дополнительными полями (путём присоединения к какому-либо узлу). Например, можно для Huawei можно добавить состояние `bpdu` на интерфейсе, примерно так:
SYNTAX = [ DEF( "interfaces", [ DEF( IF_NAME, [ DEF( "bpdu", [ DEF( BOOL, required=False, name="enabled", gen="make_interface_ethernet_bpdu", ) ], ) ], multi=True, name="interface", ) ], ) ]
И в выводе ./noc confdb syntax --profile Huawei.VRP появится:
interfaces
*<interface>
bpdu
<enabled>
Определяя свои расширения базового синтаксиса можно расширять confdb в зависимости от потребностей.
Запросы
Описание языка запросов доступно в документации: ConfDB Query. Чем-то он напоминает Prolog. Для примера запросим интерфейс c именем GigabitEthernet0/0/1 у которого есть description:
Match('interfaces', X, 'description', Y) and Filter(X=="GigabitEthernet0/0/1") and Del(Y)
сам запрос состоит из Цепочки предикатов, разделённых словами and
(в этом случае исполнение идёт друг за другом) и/или or
(исполняются вместе).
- Предикат - конструкция вида
<FUNC_NAME>(agrs)
, гдеFUNC_NAME
- название применяемой функции, `args` - список аргументов. В примере: Match, Filter, Del - Путь (confdb path) - содержимое предиката (указывается в скобках). Состоит из списка элементов дерева. Если оный указан в кавычках (
'interfaces'
,'description'
) - то ищется точное совпадение а если без (X
,Y
) обозначает переменную, которой присвоится элемент - Контекст - место хранения переменных. После выполнения всех предикатов в запросе становится результатом. Можно посмотреть предикатом Dump.
Разберём выполнение запроса шаг за шагом:
- В самом начале запроса *контекст* пуст. И если выполнить, получим пустоту в результате. Поэтому первым предикатом, обычно, идут:
- Match и NotMatch. Выполняется запрос к ConfDB. Если в пути есть переменные - они заполняются значениями.
- Set - устанавливает переменной указанное значение
./noc confdb query --object "200" --query "Match('interfaces', X, 'description', Y)" Result: {'Y': 'description to_11_2.1', 'X': 'GigabitEthernet0/0/1'} {'Y': 'description to_AGG1', 'X': 'GigabitEthernet0/0/2'}
В данном случае мы получили список интерфейсов у которых заполнено описание. Их имена в переменной X
, а описания в переменной Y
- Операции над контекстом:
- Filter - проверка контекста над определёнными условием
- Re - проверка на соответствие регулярному выражению
- HasVLAN - проверка попадания влана в фильтр
- Group - схлопывание контекстов
- Collapse - разворачивание контекста в несколько
# ./noc confdb query --object "200" --query "Match('interfaces', X, 'description', Y) and Filter(X=='GigabitEthernet0/0/1')" Result: {'Y': 'description to_11_2.1', 'X': 'GigabitEthernet0/0/1'} Мы оставили только интерфейс с именем `GigabitEthernet0/0/1`
- Вывод контекста
- Dump - распечатать контекст
- Fact - установить значение в базе
- Sprintf - распечатать переменную
# ./noc confdb query --object "200" --query "Match('interfaces', X, 'description', Y) and Dump("Stage2") and Filter(X=='GigabitEthernet0/0/1')" Stage2: {'Y': 'description to_11_2.1', 'X': 'GigabitEthernet0/0/1'} {'Y': 'description to_11_2.1', 'X': 'GigabitEthernet0/0/1'} Result: Stage2: {'Y': 'description to_AGG1', 'X': 'GigabitEthernet0/0/2'}
Dump удобно использовать для отладки запросов, впрочем нико не мешает просто выполнять их поэлементно.
Помимо CLI для запросов в графическом Веб интерфейсе в форме ManagedObject по кнопке ConfDB доступна возможность выполнения (кнопка query).
Возможности
- Валидация - проверка некоего выражения на истинность (не пустой контекст) или ложность (пустой контекст) !описана тут (валидация конфигурации)
- Фильтрация - получение списка устройств или интерфейсов, соответсвующих условию (запросу)
- Выборки - извлечение информации из ConfDB (н-р описания всех интерфейсов, или поддержка интерфейсами протоколов - `LLDP`)
Наполнение (как заполнить)
Для наполнения ConfDB собранная конфигурация оборудования проходит несколько этапов:
- токенизация - разбивает исходную конфигурацию на отдельные слова (токены) для последующей обработки Подробнее тут. Указывается в настройках профиля:
- нормализация - собирает дерево ConfDB по найденным в токенах значениям. Подробнее смотреть тут: ConfDB Normalizer
- примененени `апликаторов` позволяют производить манипуляции над построенным деревом и применяются для:
- Задания умолчаний
- Заполнения дополнительной информации (секция `meta`)
- Модификации в зависимости от настроек