---
# System prepended metadata

title: Работа с новой тушенкой
tags: [NOC, ' guide']

---

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