--- tags: NOC, guide --- # Работа с новой тушенкой ## Что такое тушенка? Тушенкой (на англ. `Сanned Иeef`) в НОКе называется записанный ответ железки на запрос команды (CLI) или SNMP OID'a. Исторически, тушенка (или `Canned Beef`), термин, обозначающий записанный вывод устройства (SNMP, CLI) для последующего его воспроизведения. Позволяет воспроизвести сеанс взаимодействия без доступа к железке. Это может быть полезно для разработки профиля, выправления проблем или тестирования профилей и/или опроса. В версии `develop`, тушенка собиралась на скрипт: сохранялся вывод железки и вывод скрипта. Это позволяло сопоставлять их и выдавать ошибку, в случае несовпадения. В новой реализации добавилась возможность сбора по списку команд и SNMP OID'ов. Это позволяет разово собрать вывод целиком и на основе его проводить проверку всех скриптов профиля. ### Сбор тушенки на основе скрипта Используется параметр `-o <save_path>` команды `script`: `./noc script -o /tmp/saved_beef.yml <scrip_name> <MONAME>`. На выходе получаем файл с выводом, который можно отредактировать для скрытия критичной информации. ### Сбор тушенки на основе списка команд Потребуется файл со списком команд следующего формата: ```xml ``` После его составления запускаем команду `./noc beef collect ` ## Спецификации профилей. Для этого были введены несколько новый сущностей. Пройдёмся по ним. ### Что такое опросник (Quiz)? Опросник или `Quiz` это список вопросов, ответы на которые достаточны для начала работы с оборудованием. Н-р: * Производитель оборудование * Поддержка стэкирования * Как посмотреть версию ПО? * Как получить список интерфейсов? Зачастую, оборудование стоит у одного человека, а профиль для него описывает другой. И подробный опросник позволит составить представление о железке. Структура опросника - это список вопросов: * Имя (Name) - уникальное имя вопроса. Используется для сопоставления вопроса и ответа * Тип (Type) - ожидаемый тип ответа * str - текстовая строка * int - число * bool - Да/Нет * cli - команда, для выполнения в режиме CLI * snmp-get - SNMP OID для выполнения get запроса * snmp-getnext - SNMP OID для выполнения get next запроса (обычно, это таблица) * Question - текст вопроса * When - условия (имя вопроса логического типа), при которых будет отображаеться вопрос (н-р, если устройство не поддерживает CDP, то спрашивать команда для получения соседей CDP смысла нет). Хотя, формат имени произволен, для удобства желательно поддерживаться некоторых соглашений: * составлять имя из ключевых слов, разделённых точкой: X.Y.Z * X - тип ответа: `has` - есть (логический тип), `get` - получить (н-р по cli, snmp), `max` - числовой тип (н-р кол-во коммутаторов в стэке) * Y - сокращённое наименование вопроса, пробелы заменяются нижники подчёркиваниями (н-р stack, если речь о стеке, ) * Z - указатель на разделение внутри вопроса (н-р snmp, cli) Примеры имён: * vendor - производитель * get.version.cli - команда получения версии по CLI * set.vlan.cli - команда создания влана по CLI * get.interfaces_name.snmp - получение имём интерфейсов по SNMP Опросники находятся в пункте меню `Development` -> `Quiz` Пример опросника: ```javascript= { "name": "Standart Switches Spec", "$collection": "dev.quiz", "uuid": "2ee9c4f9-35bd-4582-8108-25368bce8f1f", "description": "Some question on L2 access swith", "revision": 1, "disclaimer": null, "changes": [], "questions": [ { "name": "vendor", "question": "Vendor name", "type": "str", "when": "" }, { "name": "Model sysObjectId(s)", "question": "Device sysObjectId(s) string", "type": "str", "when": "" }, { "name": "has.stack", "question": "Device support union stack", "type": "bool", "when": "" }, { "name": "max.stack", "question": "Max number device in stack", "type": "int", "when": "has.stack" }, { "name": "has.cli", "question": "Device support management by CLI", "type": "bool", "when": "" }, ``` ### Что такое спецификация (Spec)? Спецификация - это перечень информации необходимой для работы с оборудованием. Например: * список команд CLI, SNMP OID'ов * Ограничения (н-р подерживается только SNMP, не поддерживается протоколо CDP) * Числовые характеристики (число слотов, максимальный размер памяти, ограничение на кол-во сессий) * Другое Содержит в себе следующие поля: * **Имя** (Name) - имя вопроса из опросника (или просто уникальное имя) на который даётся ответ. Если ответов больше одного (н-р информацию можно получить несколькими командами), то к имени добавляется номер ответа: `get.version.cli.1`, `get.version.cli.2` * **Тип** (Type) - повторяет поле из опросника. * **Значение** (Value) - информация (н-р ответ на вопрос из опросника) Есть несколько путей создания/получения спецификации: 1. Воспользоваться одной из существующих: в меню `Development` -> `Spec` (получаются из коллекции). 2. Создать самому на основе опросника: При этом в поле `Name` вписывается `Имя` вопроса из опросника, а в поле `Value` информация. 3. Создать самому в свободной форме. :::info Если спецификация создаётся в свободоной форме, то необходимо указать пустой (`Ad-Hoc`) опросник (всегда находится в системе). ::: 4. Сгенерить на основании профиля: в этом случае в спецификацию включаются команды, которые используются при работе профиля. Спецификации расположены в пункте меню `Development` -> `Spec` Пример заполненной спецификации: ```javascript= { "name": "Raisecom L2 Access Switches", "$collection": "dev.specs", "uuid": "94408614-79fb-4d75-989f-7ec2bf1c623e", "description": "Raisecom L2 Access Switches general version", "revision": 1, "quiz__name": "Standart Switches Spec", "author": "avs", "profile__name": "Raisecom.ROS", "answers": [ { "name": "vendor", "type": "str", "value": "Raisecom" }, { "name": "has.stack", "type": "bool", "value": "false" }, { "name": "has.cli", "type": "bool", "value": "true" }, { "name": "has.snmp", "type": "bool", "value": "true" }, { "name": "get.version.cli", "type": "cli", "value": "show version" }, { "name": "get.version.snmp", "type": "snmp-get", "value": "1.3.6.1.2.1.1.1.0" }, { "name": "has.stp", "type": "bool", "value": "true" }, { "name": "get.caps.stp.cli", "type": "cli", "value": "show spanning-tree" }, ``` На основании спецификации возможно создание тушенки (Canned Beef). #### Заполнение спецификации по скрипту Если спецификация отсутствует или необходимо собрать тушенку по конкретному скрипту - есть возможность создать **Ad-Hoc** (по месту) спецификацию включив туда команды, которые выполняет скрипт при работе с оборудованием: ```shell= ./noc script --update-spec SPECNAME get_version MONAME ``` В процессе работы выполнится скрипт `get_version` профиля устройства и используемые команды сохранятся в спецификации с указанным именем (*SPECNAME* в примере). Если спецификация уже была - она обновится. Если не было - создастся :::info В составе всех профилей присутствует скрипт *commands*, который выполняет список команд на оборудовании. Можно воспользоваться им, для создания спецификации: `./noc script --update-spec SPECNAME commands commands:='["show version", "show inventory", "show cpu"]'` ::: ### Тушенка (Canned Beef) Это `"законсервированный"` в специальном формате вывод оборудования (SNMP и/или CLI). Подобная подход позволяет `"воспроизвести"` взаимодействие НОКа с оборудованием, без наличия непосредственного доступа. Может применяться для: * разработки профилей оборудоания * тестирования профилей или дискавери * предоставления вывода оборудования, без выдачи прямого доступа к нему Тушенка собирается на основе информации из полей типа `cli`, `snmp-get`, `snmp-getnext` в спецификации путём их передачи на оборудование и записи ответов в файл. Содержит следующие поля: * Версия (version) - версия формата тушенки (всегда `1`) * UUID - уникальный идентификатор тушенки (генерится при создании файла) * Spec - UUID спецификации на основе которой собиралась тушенка * box - сохраняет в себе метаинформацию (заполняется при сборе, на основе вывода скрипта `get_version`): * Профиль, для которого собралась тушенка * Версия ПО, платфома оборудования * Производитель оборудования * changed - Время последнего обновления (используется при автоматическом наполнении тушенки) * cli - вывод оборудования в формате `команда: вывод` * mib - вывод SNMP оборудования в формате: `OID: значение` ### Хранилище (Storage) и путь (Path) Хранилище - корневой путь на сервере, от которого, путём прибавления `Path`, будет строится путь, сохранения файлов тушенки. Задаётся в меню `Main` -> `Setup` -> `Ext. Storage`. При создании необходимо указать тип `Beef`, после этого его название можно будет указывать при выполнении команд или в профилей объекта. Путь (Path) - путь к файлу с тушенкой. Уникален в пределах `Хранилища` и задаётся шаблоном пути: меню `Main` -> `Setup` -> `Template` (пишется в `body`). Связка хранилище - путь наряду с UUID однозначно идентифицирует файл тушенки. ## Как приготовить тушенку ? ### Настройки хранения Для работы с тушенкой понадобится настроить хранилище (`Storage`) и шаблон пути. создать в меню `Main` -> `Setup` -> `Ext. Storage`. Поддерживается как локальная папка, так и различные сетевый варианты (`ftp`, `sshfs`). Указываются они путём задания протокола в `URL` (`ftp://`, `ssh://`), без указания протокола будет использоваться локальная папка (proto `OSFS`) :::info По умолчанию есть поддержка только локального и FTP. Для других необходимо доставить пакет. Подробнее тут ::: Есть несколько типов хранилищ (задаётся в настройках): * Beef - сохраняются файлы тушенки * Beef Test - хранятся результаты работы скриптов, для сравнения * Beef Test Config - хранятся настройки тестирования. Для сбора достаточно создать первый тип (Beef). :::warning Необходимо помнить, что при задании **локального** хранилища (н-р `/var/lib/noc/beef`) файл будет писаться на том хосте, где работает дискавери или даётся команда на сбор! ::: По умолчанию, рекомендуется создать хранилище в папке `/var/lib/noc/beef` (не забудьте создать её на хосте). #### Задание пути Для записи файлов в хранилище, понадобится указание пути. Для этого применяется шаблон пути `Path template`, который создаётся в меню `Main -> Setup -> Template`. Строка шаблона указывается в поле поле `title`: н-р `{{ object.profile.name }}/{{ object.pool.name }}/{{ object.address }}.beef.json`, в этом случае тушенка для устройства с профилем `Huawei.VRP` в пуле `default` и адресом `192.168.1.2` будет складываться в файл `Huawei.VRP/default/192.168.1.2.beef.json`. Поле `body` не используется, можно указать произвольный текст н-р, поставить точку. ### Сбор тушенки После подготовки спецификации можно переходить к сбору тушенки. За сбор тушенки отвечают настройки в профиле устройства: `Service Activation` -> `Setup` -> `ManagedObjectProfile` -> `Config` -> `Beef`. В них можно выбрать режим сбора (в данный момент автоматический сбору не реализован), хранилище и путь для указания полного пути к файлу тушенки. Непосредственный сбор происходит командой: ```shell= ./noc beef collect --force --storage STORAGE_NAME --spec SPECNAME MONAME ``` где * `SPECNAME` - имя спецификации, созданной на предыдущем этапе (доступные можно посмотреть в `Development` -> `Spec`) * `MONAME` - имя объекта управления (`ManagedOjbect`) или селектора (через `@`) * `force` - флаг позволяет игнорировать настройки в `ManagedObjectProfile`. В этом случае будет использован *storage*, указанные в параметре `--storage` и шаблон пути по умолчанию (`ad-hoc/{0.profile.name}/{0.pool.name}/{0.address}.beef.json`) После отработки по указанному пути появится файлик с тушенкой. Дальнейшем его можно использовать для тестирования профиля или передать разработчику для воспроизведения ошибки. #### Просмотр собранной тушенки Вывыести список собранной тушенки можно командой: ```shell= ./noc beef list --storage STORAGE_NAME ``` В последней колонке будет указан путь (`Path`) по нему можно в дальнейшем обращаться к тушенке Просмотреть собранную тушенку можно: ```shell= ./noc beef view --storage STORAGE_NAME --path PATH ``` #### Запуск скрипта по тушенке Запустить скрипт по собранной тушенке: ```shell= ./noc beef run --storage STORAGE_NAME --path PATH --script SCRIPTNAME --access-prefrence AP ``` где, - SCRIPTNAME - название скрипта - access-prefrence - приоритет методов (CS, SC, C, S) ## Тестирование профилей Для тестирования вводится дополнительный термин: **Test-case** (тестовый случай), применительно к тестированию скриптов представляет собой набор из: * собранной тушенки * конфигурации тестирования * сохранённых выводов скриптов (эталона), полученных при работе с тушенкой По данному набору проводится тест, путём прогона скриптов по тушенку и сравнения вывода с эталоном. ```shell= ./noc test run -v --statistics --coverage-report=var/coverage tests/test_beef.py ``` `test-config.yml` ```yaml= version: "1" tests: - script: get_arp access_preference: SC - script: get_config access_preference: SC - script: get_dhcp_binding access_preference: SC - script: get_version access_preference: SC - script: get_version access_preference: CS - script: get_interfaces access_preference: SC - script: get_lldp_neighbors access_preference: SC ``` >> делаешь шаблонный yml с конфигом тестов, они копипастятся, но профили могут иметь особенности. Через create-test-case он и тушенка копируются на новое место. Правишь конфиг по месту и через build-test-case он дозаписывает результаты скриптов ./noc test run .... tests/test_beef.py прогоняет тесты. в settings.yml нужно что-то вроде tests: beef_paths: - var/ext/beef-test там URL для external storage, их можно несколько зацепить - каждая железка - отдельный каталог, а что там по пути сверху - пофиг, хочешь по профилям, хочешь еще как группируй ## Тестирование дискавери ```yaml discovery: capabilities: - name: DB | Interfaces value: 25 interfaces: .... ``` > [Dmitry Volodin] короче я сейчас так сделаю:в MOProfile - поля beef_storage и beef_path_template оно будет указывать, куда сохранять и по какому пути сбор тушенки будет происходить через ./noc beef collect --spec=XXXX спеку пока придется указывать руками это решит проблему со сбором. Cледующим шагом в MO добавим дополнительный протокол - BEEF, eсли его выставляешь - активатор переключается на тушенку и я начну отлаживать выгрузку и чтение из тушенки. > ```javascript= MT_SPEC = { "version": "1", "uuid": "8a4b6878-ae17-4b55-8e61-2624160a4983", "quiz": "795f6e84-9f2c-4968-81f3-41b6acf43602", "author": "dv", "profile": "MikroTik.RouterOS", "answers": [ { "name": "cli_get_version.0", "type": "cli", "value": "system resource print" }, { "name": "cli_get_version.1", "type": "cli", "value": "system routerboard print" } ] } from noc.sa.models.managedobject import ManagedObject mo = ManagedObject.get_by_id(56032) r = mo.scripts.get_beef(spec=MT_SPEC) import ujson print ujson.dumps(r, indent=2) python commands/beef.py collect --spec="MikroTik.RouterOS | Ad-Hoc" mt111 ```