Dracon (Draft of the config) - TFTP-сервер для Linux/FreeBSD, предназначенный для генерации конфигурационных файлов по требованию. Файлы генерируются на основе простых текстовых шаблонов и выгружаются по TFTP-протоколу. Уникальные файлы, передаваемые сервером или отправляемые на сервер, сохраняются в базе MySQL.
Сервис Dracon изначально разрабатывался для генерации 'на лету' конфигурационных файлов для коммутаторов D-Link, таких как DES-3028, DES-3200 и т.д. При конфигурировании этих устройств очень часто нужно определять команды с учетом ролей портов. Так, например, при настройке функционала обнаружения петель, сегментации трафика и управления мультикаст-трафиком требуется заранее определить как будет использоваться тот или иной порт. Очевидно, что обнаружение петель имеет смысл включать только на тех портах, куда будет подключено клиентское оборудование, а при конфигурировании multicast-vlan в качестве источника трафика требуется явно задать uplink-порт.
Роли портов можно определять заранее. Например, для всех коммутаторов DES-3200-28 можно использовать порты 1-24 как абонентские, 25-28 как магистральные, а сам 25-й порт при этом считать аплинком. Но чем больше разрастается сеть, тем больше появляется вынужденных отклонений от схемы - иногда приходится использовать другой порт в качестве аплинка, подключать собственное оборудование в абонентские порты или же самих абонентов в магистральные. При этом конфигурация, которая была разработана для "стандартного" коммутатора, приведет к неправильной работе устройства. Загружая на устройство такую конфигурацию нужно быть готовым к непредвиденным последствиям. © :)
Для решения этой проблемы необходимо хранить в биллинге тип (роль) каждого порта и строить конфигурацию с учетом этих типов. Делать последнее вручную, разумеется, неудобно. Именно поэтому появился сервис Dracon, который создает конфигурацию автоматически, опираясь на данные биллинга.
- Генерация конфигурационного файла с учетом типов портов и пользовательских параметров (по одному для порта и одному параметру для самого устройства)
- Сохранение в базе MySQL файлов, передаваемых в обоих направлениях (кроме файлов ПО)
- Возможность выгрузки программного обеспечения (прошивки) вместо файла конфигурации
- Применение пользовательских функций для обработки пользовательских параметров
- Возможность выгрузки по запросу конкретной секции конфигурационного файла
- Работа одновременно с несколькими устройствами (TFTP-клиентами)
- Работа системным сервисом (daemon) под FreeBSD/Linux
- Гибкая система конфигурирования, возможность определять типы портов, устройств и доступных команд
- Отсутствие привязки к конкретному оборудованию или вендору
- Операционная система Linux или FreeBSD
- Python с модулем MySQLdb
- Доступ к вашему MySQL-серверу (базе биллинга) для получения списка устройств и портов
- Доступ к MySQL для сохранения файлов (опционально)
Dracon запускается системным сервисом и периодически забирает из биллинга информацию о коммутаторах и их портах. Первым выполняется запрос для получения информации об устройствах. Он должен вернуть таблицу вида:
ip | type | custom |
---|---|---|
10.90.90.95 | 24 | 192.168.0.0/24 |
Здесь type - это идентификатор типа устройства. В дальнейшем для удобства происходит сопоставление типов текстовым именам устройств. Параметр custom - произвольные пользовательские данные, к примеру адрес установки коммутатора.
Затем запрашивается информация о портах. Запрос должен вернуть таблицу вида:
ip | port | ptype | comment |
---|---|---|---|
10.90.90.95 | 1 | 1 | user12345 |
В этом запросе ptype определяет тип (роль) порта - абонентский, магистральный, неисправный и т.д. Параметр comment - произвольный комментарий порта, содержащий, к примеру, логин пользователя из биллинга.
После этого Dracon начинает прослушивать порт 69 (по умолчанию) для работы в качестве TFTP-сервера. При получении запроса от устройства (или обычного TFTP-клиента) сервис либо сгенерирует и отдаст клиенту конфигурационный файл, если это был запрос на чтение, либо примет от клиента файл, если это был запрос на запись. Выгруженные и загруженные файлы помещаются в базу данных MySQL.
Через 5 минут (по умолчанию) сервис перезапросит из биллинга информацию о коммутаторах и портах и актуализирует эти данные в своей памяти.
Параметр | Описание |
---|---|
interface_ip | IP-адрес интерфейса, на котором будет работать демон. |
port | UDP-порт для TFTP-сервера. |
cycle_int | Время в секундах, через которое надо обновлять список портов и устройств. |
sleep_def | Пауза по умолчанию при опросе UDP-сокета. |
sleep_int | Время простоя (данные не поступают) в секундах, через которое надо выставить паузу по умолчанию. |
log_file | Имя файла журнала. |
log_size | Размер файла журнала при достижении которого начинается ротация. |
log_backupcount | Количество архивных копий журнала. |
Здесь необходимо сделать пояснение относительно пауз. Dracon опрашивает сокет в бесконечном цикле, ожидая поступления данных. В подавляющем большинстве случаев данные не поступают, и чтобы не выполнять бесполезную работу слишком часто, этот опрос выполняется с задержкой sleep_def. После того, как был получен запрос от клиента, это значение делится на 1000, чтобы успеть передать файл за меньшее время. Когда передача данных завершилась и прошел интервал sleep_int, сервис переходит в обычный режим и снова начинает использовать значение sleep_def.
Параметр | Описание |
---|---|
mysql_addr | Адрес MySQL-сервера биллинга. |
mysql_user | Имя пользователя. |
mysql_pass | Пароль. |
mysql_base | Имя базы данных. |
Настройки для PostgreSQL-сервера, откуда будут забираться данные. Используется как альтернатива MySQL
Параметр | Описание |
---|---|
postgresql_addr | Адрес PostgreSQL-сервера биллинга. |
postgresql_user | Имя пользователя. |
postgresql_pass | Пароль. |
postgresql_base | Имя базы данных. |
use_postgresql | Параметр, определяющий, использовать ли MySQL либо же PostgreSQL (когда установлен в True). |
Параметр | Описание |
---|---|
devices_query | Запрос к базе данных для получения списка устройств. |
ports_query | Запрос к базе данных для получения сведений о портах коммутатора. |
Параметр | Описание |
---|---|
mysql_addr_w | Адрес MySQL-сервера для сохранения конфигурационных файлов. |
mysql_user_w | Имя пользователя. |
mysql_pass_w | Пароль. |
mysql_base_w | Имя базы данных. |
mysql_ctbl_w | Имя таблицы для сохранения конфиругационных файлов. |
mysql_ttbl_w | Имя таблицы для сохранения транзакций. |
Параметр | Описание |
---|---|
dev_types | Соответствие идентификаторов типов устройств их названиям. |
Параметр dev_types задается в виде словаря python. Пример:
dev_types = {
24 : 'DES-3200-28', # DES-3200-28/A1
218 : 'DES-3200-28', # DES-3200-28/B1
210 : 'DES-3200-28_C1', # DES-3200-28/C1
216 : 'DES-3200-18', # DES-3200-18/A1
217 : 'DES-3200-18', # DES-3200-18/B1
209 : 'DES-3200-18_C1', # DES-3200-18/C1
205 : 'DES-3028', # DES-3028
215 : 'DGS-3000-24TC', # DES-3000-24TC
252 : 'DGS-3000-26TC', # DES-3000-24TC
}
Параметр | Описание |
---|---|
ports_types | Словарь с кодами портов и их сокращенными обозначениями |
Параметр ports_types содержит соответствие цифровых и символьных кодов портов. Цифровые коды хранятся в биллинге, а символьные ипользуются в конфигурационных шаблонах. Для себя я определил 9 типов портов:
Код | Тип |
---|---|
1 | абонентский порт |
2 | магистральный |
3 | сломанный |
4 | VIP-клиент |
5 | вход |
6 | нестандартный |
7 | оборудование |
8 | вход (патчкорд) |
9 | магистраль (патчкорд) |
Такие обозначения сложились исторически и, в принципе, удовлетворяют почти всем потребностям по разграничению ролей портов.
Параметр ports_types задается в виде словаря python. Пример:
ports_types = {1:'ss', 2:'mg', 3:'br', 4:'vp', 5:'up', 6:'ns', 7:'eq', 8:'pu', 9:'pd'}
Параметр | Описание |
---|---|
mags_list | Список кодов магистральных портов. |
Параметр mags_list определяет произвольный набор портов как магистральные и задается в виде списка python. Пример:
mags_list = [2,5,8,9]
Параметр | Описание |
---|---|
cf_path | Путь к каталогу с файлами конфигураций. |
fw_path | Путь к каталогу с программным обеспечением. |
Имя файла конфигураций должно соответствовать названию устройства (см. dev_types). Например, для устройства с ID=210 по пути /usr/local/etc/dracon/config/ (значение cf_path) будет производиться поиск файла DES-3200-28_C1.
Параметр | Описание |
---|---|
fw_names | Соответствие названий устройств именам файлов с программным обеспечением |
Параметр fw_names содержит имя файла "прошивки" для конкретного коммутатора и задается в виде словаря python. Пример:
fw_names = {
'DES-3200-28' : 'DES-3200R_1.85.B008.had',
'DES-3200-28_C1' : 'DES3200R_4.39.B008.had',
'DES-3200-18' : 'DES-3200R_1.85.B008.had',
'DES-3200-18_C1' : 'DES3200R_4.39.B008.had',
'DES-3028' : 'DES_3028_52_V2.94-B07.had',
'DGS-3000-24TC' : 'DGS3000_Run_1_14_B008.had',
'DGS-3000-26TC' : 'DGS3000_Run_1_14_B008.had',
}
Параметр | Описание |
---|---|
commands | Доступные команды (имена запрашиваемых файлов) и соответствующие им шаблоны с наборами команд |
Параметр commands задается в виде словаря python, где значениями являются списки. Сами команды и соответствующие им шаблоны целиком определяются пользователем. Пример:
commands = {
'acl':['*header*', '*acl*'],
'cpu_acl':['*header*', '*cpu_acl*'],
'accounts':['*header*', '*accounts*'],
'stp_lbd':['*header*', '*stp_lbd*'],
'snmp':['*header*', '*snmp*'],
'sntp':['*header*', '*sntp*'],
'lldp':['*header*', '*lldp*'],
'filtering':['*header*', '*filtering*'],
'trusted_hosts':['*header*', '*trusted_hosts*'],
'ipm':['*header*', '*ipm*'],
'dhcp_relay':['*header*', '*dhcp_relay*'],
'igmp_snooping':['*header*', '*igmp_snooping*'],
'igmp_auth':['*header*', '*igmp_auth*'],
'aaa':['*header*', '*aaa*'],
'multi_filter':['*header*', '*multi_filter*'],
'cos':['*header*', '*cos*'],
'mon_log':['*header*', '*mon_log*'],
'pdesc':['*header*', '*p_desc*'],
'config':['*header*', '*acl*', '*cpu_acl*', '*accounts*', '*stp_lbd*', '*snmp*', '*sntp*', '*lldp*', '*filtering*', '*trusted_hosts*',
'*ipm*', '*dhcp_relay*', '*igmp_snooping*', '*igmp_auth*', '*aaa*', '*multi_filter*', '*cos*', '*mon_log*', '*p_desc*', '*bottom*']
}
Данная конструкция обозначает следующее: если запросить у сервиса Dracon файл с именем acl, то программа заглянет в commands, увидит, что этому имени соответствуют секции конфигурационного файла с именами '*header*' и '*acl*', найдет эти секции в шаблоне файла, обработает и выдаст их содержимое. Подробнее о секциях будет рассказано ниже.
Нетрудно догадаться, что файл с именем config содержит все доступные команды.
Параметр | Описание |
---|---|
helpinfo | Содержимое справки, получаемой по команде (имени файла) 'help' |
Параметр helpinfo представляет собой строку, в которой можно написать мини-справку, возвращаемую при выполнении команды help
Примечание: С точки зрения TFTP-клиента команда является именем файла. Чтобы выполнить команду config, нужно затребовать с TFTP-сервера файл config.
Можно выполнять команды применительно к конкретному устройству, например, команда:
tftp -i dracon.myhost get 10.90.90.100@config
выгрузит полную конфигурацию для устройства с IP-адресом 10.90.90.100.
Помимо настраиваемых команд и команды help имеются еще две зарезервированные:
Команда | Описание |
---|---|
firmware | Выгрузка программного обеспечения для заданного устройства (имя файла задается в файле конфигурации) |
backup | Выгрузка из базы загруженных файлов последней конфигурации для заданного устройства |
Dracon позволяет использовать пользовательские функции для обработки параметров comment и custom. Эти функции хранятся в файле dfunc.py и написаны на обычном python. Примеры функций:
def fn_2oct(src):
try:
return hex(int(src.split('.')[1]))[2:].zfill(2)
except:
return ""
def fn_3oct(src):
try:
return hex(int(src.split('.')[2]))[2:].zfill(2)
except:
return ""
def fn_xp(n):
try:
return hex(int(n))[2:].zfill(2)
except:
return ""
def fn_tr_cst1(src):
return Translit(src.split('|')[0])
О практическом применении пользовательских функций рассказано в следующем разделе.
Ну вот мы и подошли к самому интересному - к написанию шаблонов конфигурационных файлов. Предположим, мы хотим создать шаблон файла конфигурации для устройства DES-3200-28/C1. Согласно параметрам cf_path и dev_types файл шаблона должен находиться по адресу /usr/local/etc/dracon/config/DES-3200-28_C1.
Структура файла шаблона:
:::*Заголовок секции*:::
Тело
...
секции
<пустая строка>
:::*Заголовок другой секции*:::
Тело другой секции
<пустая строка>
Пример секции, содержащей "шапку" конфигурационного файла коммутатора:
:::*header*:::
#-------------------------------------------------------------------------------
# DES-3200-28 Fast Ethernet Switch
# Configuration
#
# Firmware: Build 4.39.B008
# Copyright(C) 2012 D-Link Corporation. All rights reserved.
#
Внутри тела секции мы можем использовать специальные конструкции, которые при выгрузке будут заменены на некоторые значения. Специальная конструкция [mags] будет заменена на диапазон портов коммутатора с типами, которые были описаны в переменной mags_list. А конструкция [all] будет заменена на диапазон всех портов коммутатора.
В параметре ports_types мы определяли буквенные обозначения диапазонов портов, которые можно использовать в шаблонах конфигурации. Так, конструкция [ss] будет заменена на диапазон всех портов с кодом 1, а конструкция [eq] - на диапазон всех портов с кодом 7.
Можно получить конкретный порт из диапазона. Конструкция [ss#5] будет заменена на 5 в случае, если порт №5 входит в диапазон портов [ss].
Пользовательская функция вызывается конструкцией вида {fn_2oct#custom}. В данном примере будет вызвана пользовательская функция fn_2oct, а в качестве параметра ей будет передано значение custom.
Можно получить комментарий для конкретного порта используя конструкцию вида {comment#7}. В этом случае при замене будет произведена замена шаблона на значение comment порта 7. Кстати, такую конструкцию можно применять и как аргумент для пользовательской функции, например: {fn_tr#{comment#28}}. В этом случае будет получен комментарий для порта №28, который потом будет передан в качестве параметра внешней функции fn_tr. В данном примере эта функция выполняет транслитерацию текста.
Внимание! Если условие для замены не выполнилось, строка будет закомментирована символом #, а заменяемая конструкция останется неизменной.
Пример: У нас есть порт 25 с кодом 5 (up) и нет портов с кодом 8 (pu). В этом случае строки
config igmp_snooping multicast_vlan mvr add source_port [up]
config igmp_snooping multicast_vlan mvr add source_port [pu]
будут возвращены как
config igmp_snooping multicast_vlan mvr add source_port 25
#config igmp_snooping multicast_vlan mvr add source_port [pu]
Вторая команда не будет выполнена коммутатором, т.к. начинается с символа #.
Для использования доступны и две переменные окружения:
Переменная | Описание |
---|---|
{$target} | IP-адрес устройства, для которого собирается конфигурация. |
{$datetime} | Текущие дата и время. |
config cpu_filter l3_control_pkt [ss] all state enable
config cpu_filter l3_control_pkt [mags] all state disable
config ports 1 description "{comment#1}"
config ports 2 description "{comment#2}"
Вызов пользовательских функций fn_2oct и fn_3oct с параметром custom и подстановка номеров портов 1 и 2 из диапазона абонентских портов.
config access_profile profile_name pcf add access_id 101 packet_content offset_chunk_1 0x0800 offset_chunk_2 0x0A{fn_2oct#custom}{fn_3oct#custom}08 port [ss#1] permit
config access_profile profile_name pcf add access_id 102 packet_content offset_chunk_1 0x0800 offset_chunk_2 0x0A{fn_2oct#custom}{fn_3oct#custom}10 port [ss#2] permit
- Выполните команду: git clone https://github.com/xcme/dracon.git
- Скопируйте файл 'dracon.service' из директории './linux/centos/' в '/etc/systemd/system/'.
- Запустите сервис командой systemctl start dracon.
- Добавьте автозапуск сервиса при загрузке системы командой systemctl enable dracon.
- Скопируйте файл dracon из директории 'freebsd' в /usr/local/etc/rc.d/, а остальные файлы в /usr/local/etc/dracon/.
- Добавьте строку dracon_enable="YES" в файл /etc/rc.conf.
- Запустите сервис командой service dracon start.
- Поддержка PostgreSQL в качестве источника данных
- Отдельная таблица для хранения конфигурационных файлов
- Ротация логов
- Вместо MongoDB теперь используется MySQL
- Теперь используется общая таблица для транзакций, а само направление транзакции указано в дополнительном поле
- Записи о транзакциях теперь попадают в базу данных во всех случаях, а не только при уникальной передаваемой конфигурации
- Таблица для хранения транзакций больше не содержит самого конфигурационного файла, который теперь хранится в отдельной таблице
- Максимальный размер загружаемого на сервер файла уменьшен до 64 КБ
- Для каждого вывода в лог теперь определена его важность (severity)
- Изменены комментарии в файле конфигурации и имена некоторых переменных
- Падение программы при недоступном на момент запуска сервере баз данных
- Косметические улучшения кода
- Исправлены некоторые опечатки в тексте
- Поддержка MongoDB полностью удалена
- Добавлена возможность использовать комментарий к порту как аргумент пользовательской функции
- Добавлена переменная окружения {$datetime} для получения текущей даты и времени
- Пользовательские функции теперь нужно обрамлять в фигурные скобки {}, а не в круглые ()
- Функция транслитерации теперь считается пользовательской и хранится в dfunc.py
- Удален набор портов по умолчанию
- Выгрузка последней конфигурации устройства при запросе файла backup
- Переменная окружения {$target} для получения IP-адрес устройства
- Новые возможности добавлены в описание
- Подсчет MD5-суммы при получении файла