---
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
```