Доброго времени суток, в общем, в компании, в которой я работаю, используется Ivideon-server версии 3.9.0 либо 3.12.0. И появилась огромная потребность в мониторинге камер: их доступности, а также, пишется ли архив с этих камер. Поэтому я начал разработку шаблона Zabbix, который бы опрашивал сервера с Ivideon-server по API. Начал я с того, что с помощью WireShark я разобрал работу Ivideon Client.

Готовые шаблоны закреплю в конце статьи. А в статье постараюсь разобрать как они работают.
Первый запрос, что я смог вытащить и считаю его основным: http://IP-Адрес сервера/streams/info?server=1&sessionId=пароль от сервера, при отправке которого мы получаем JSON ответ:

[{
    "achannels" : 0,
    "acodec" : "none",
    "afreq" : 0,
    "archive" : {
        "edge" : false,
        "internal_speed_play" : false,
        "key_frame_seek" : true,
        "smooth_speed_play" : 4,
        "speeds" : [ 1, 2, 4, 8, 16, 32, 64 ],
        "writable" : true
    },
    "custom_device_info" : {
        "fw_version" : "2.800.0000000.12.R 2021-04-30",
        "hardware_id" : "1.00",
        "ipv4" : "19.44.0.27",
        "model" : "DH-IPC-HDW1230T1P-0280B",
        "name" : "401 ближний кабинет",
        "serial_number" : "",
        "serial_number_format" : "onvif",
        "vendor" : "Dahua"
    },
    "features" : {
        "analytics_info" : { }
    },
    "height" : 960,
    "id" : 1245184,
    "instance_id" : "",
    "is_forced_as_active" : false,
    "is_turned_off" : false,
    "name" : "401 ближний кабинет",
    "online" : true,
    "preview" : {
        "0" : false,
        "1" : false,
        "2" : false
    },
    "record" : {
        "scheduled" : true,
        "type" : "continuous"
    },
    "sources" : {
        "0" : null,
        "1" : "rtsp://admin:viewer121@0.0.0.0/cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif",
        "2" : "rtsp://admin:viewer121@0.0.0.0/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif"
    },
    "status" : 1,
    "streams" : {
        "0" : false,
        "1" : true,
        "2" : true
    },
    "vcodec" : "h265",
    "width" : 1280
}]

Здесь уже есть большая часть необходимых нам данных, IP‑Адреса камер в элементе «ipv4» а также их статус «status» где 0 — не работает 1 — работает.

Далее в Zabbix в шаблоне создается элемент данных, который как раз и будет опрашивать предполагаемый узел:

Основной элемент данных в шаблоне Zabbix
Основной элемент данных в шаблоне Zabbix

После создания основного элемента данных создается правило обнаружения, где как раз и происходит парсинг JSON ответа.

Правило обнаружения
Правило обнаружения
Предобработка JSONPath
Предобработка JSONPath

Все полученные данные из JSON записываются в LLD Макросы.

LLD Макросы
LLD Макросы

Наименование макросов лаконичное, поэтому, думаю, не должно возникнуть проблем с пониманием, какой макрос за что отвечает.

Теперь у нас есть IP-адрес, имя и статус камеры, можно переходить в прототипы элементов данных этого правила обнаружения

Прототипы элементов данных
Прототипы элементов данных

Создано 2 прототипа элемента данных,

CAMERA [{#NAME}]: Get record status

  • Получение состояние записи камеры, ведет ли она архивную запись

Ivideon Get DataCAMERA [{#NAME}]: Get Status

  • Получение статуса камеры

Ivideon Get Data: CAMERA [{#NAME}]: Get Status
Ivideon Get Data: CAMERA [{#NAME}]: Get Status

Это зависимый элемент данных, он ссылается на созданный ранее основной элемент данных, где происходит получение JSON ответа.

Предобработка элемента данных
Предобработка элемента данных

Теперь разберем, как шаблон получает состояние записи архива камеры с помощью прототипа элемента данных CAMERA [{#NAME}]: Get record status

CAMERA [{#NAME}]: Get record status
CAMERA [{#NAME}]: Get record status

Этот элемент данных имеет тип скрипт, мною сделан небольшой Java скрипт который отправляет запрос на сервер по камере.

var obj = JSON.parse(value);

var host_ip = obj.hostip;
var camera_id = obj.cameraid;
var ivideon_pass = obj.ivideon_password
var request = new HttpRequest();

var nowUnix = Math.floor(Date.now() / 1000);
var timestamp_offset = nowUnix - 10800

var url = "http://" + host_ip + ":8080/archive/list"
        + "?startTime=" + timestamp_offset
        + "&endTime=" + nowUnix
        + "&server=1&sessionId=" + ivideon_pass
        + "&camera=" + camera_id;

var data = JSON.parse(request.get(url));

if (Array.isArray(data) && data.length === 0) {
    return 0;
} else {
    return 1;
}

В параметрах прототипа данных указывается все те макросы, что мы вытащили ранее.

Теперь когда у нас есть данные можно делать прототипы триггеров.

Прототипы триггеров
Прототипы триггеров

Camera {#NAME} archive not recording — ведется ли архив

Camera {#NAME} is unavailable — статус камеры

Ведется ли архив
Ведется ли архив

Скрипт созданный ранее имеет 2 варианта ответа, 0 — архив не ведется 1 — архив пишется, соответственно, в прототипе триггера для его срабатывания он должен получить 0, тогда триггер сработает.

Статус камеры
Статус камеры

В прототипе Camera {#NAME} is unavailable применяется тот же принцип 0 — не работает 1 — работает.

На этом шаблон готов. Далее создаем узел сети с применением этого шаблона.

Создание узла сети
Создание узла сети

Тип интерфейса SNMP. Также необходимо указать в макросах узла сети следующий макрос: {$IVIDEON_PASSWORD}, в значение записать пароль используемый для подключение к Ivideon-server.

Макрос узла сети
Макрос узла сети

После создания узла сети в элементах данных можно увидеть созданные правилом обнаружения элементы, по каждой камере создается отдельный элемент данных.

Элементы данных узла сети
Элементы данных узла сети

Сразу после появления элементов данных правило обнаружения создает триггеры по каждому элементу данных.

Триггеры
Триггеры

На этом всё, все работы и тестирование проводились с версиями: Zabbix 7.4, ivideon‑server 3.9 и ivideon‑server 3.12.0.

Готовый шаблон для версии Ivideon — server 3.9.0.

zabbix_export:
  version: '7.4'
  template_groups:
    - uuid: d37f71c7e3f7469bab645852a69a2018
      name: 'Templates/Video surveillance'
  templates:
    - uuid: d8d1bbd1d20346f7bb5153d23f024150
      template: 'ivideon-server 3.9 HTTP'
      name: 'ivideon-server 3.9 HTTP'
      groups:
        - name: 'Templates/Video surveillance'
      items:
        - uuid: a8047c121f154cf2a37ff3a50d2cf52c
          name: 'Ivideon Get Data'
          type: HTTP_AGENT
          key: ivideon.attributes
          delay: 3m
          value_type: TEXT
          url: 'http:/{HOST.IP}:8080/streams/info'
          query_fields:
            - name: server
              value: '1'
            - name: sessionId
              value: '{$IVIDEON_PASSWORD}'
          output_format: JSON
          triggers:
            - uuid: 6aebb2b119844e7c8223e90a9afbeba9
              expression: 'nodata(/ivideon-server 3.9 HTTP/ivideon.attributes,3m)=1'
              name: 'Ivideon-server service is unavailable'
              priority: HIGH
      discovery_rules:
        - uuid: 9a759a9eb47049a2bbf8e9effea1456b
          name: 'Ivideon-server discovery cameras'
          type: DEPENDENT
          key: ivideon.cameras
          item_prototypes:
            - uuid: 9a900ef7424f4d00bd3200f7ecb161e3
              name: 'CAMERA [{#NAME}]: Get record status'
              type: SCRIPT
              key: 'ivideon.cameras.record.status[{#NAME}]'
              value_type: TEXT
              params: |
                var obj = JSON.parse(value);
                
                var host_ip = obj.hostip;
                var camera_id = obj.cameraid;
                var ivideon_pass = obj.ivideon_password
                var request = new HttpRequest();
                
                var nowUnix = Math.floor(Date.now() / 1000);
                var timestamp_offset = nowUnix - 10800
                
                var url = "http://" + host_ip + ":8080/archive/list"
                        + "?startTime=" + timestamp_offset
                        + "&endTime=" + nowUnix
                        + "&server=1&sessionId=" + ivideon_pass
                        + "&camera=" + camera_id;
                
                var data = JSON.parse(request.get(url));
                
                if (Array.isArray(data) && data.length === 0) {
                    return 0;
                } else {
                    return 1;
                }
              parameters:
                - name: cameraid
                  value: '{#CAMERA_ID}'
                - name: hostip
                  value: '{HOST.IP}'
                - name: ivideon_password
                  value: '{$IVIDEON_PASSWORD}'
              trigger_prototypes:
                - uuid: 2c4c9a05f32d43f38e6d0a58c6e1d63e
                  expression: 'last(/ivideon-server 3.9 HTTP/ivideon.cameras.record.status[{#NAME}])=0'
                  name: 'Camera {#NAME} archive not recording'
                  priority: WARNING
                  dependencies:
                    - name: 'Camera {#NAME} is unavailable'
                      expression: 'last(/ivideon-server 3.9 HTTP/ivideon.cameras.status[{#NAME}])=0'
                    - name: 'Ivideon-server service is unavailable'
                      expression: 'nodata(/ivideon-server 3.9 HTTP/ivideon.attributes,3m)=1'
            - uuid: f76677285bf74103acdc473a92aad686
              name: 'CAMERA [{#NAME}]: Get Status'
              type: DEPENDENT
              key: 'ivideon.cameras.status[{#NAME}]'
              trends: '0'
              preprocessing:
                - type: JSONPATH
                  parameters:
                    - '$.body[?(@.name=="{#NAME}")].status.first()'
              master_item:
                key: ivideon.attributes
              trigger_prototypes:
                - uuid: 4f0bd15b54b2442cba8805f361787d7f
                  expression: 'last(/ivideon-server 3.9 HTTP/ivideon.cameras.status[{#NAME}])=0'
                  name: 'Camera {#NAME} is unavailable'
                  priority: HIGH
                  dependencies:
                    - name: 'Ivideon-server service is unavailable'
                      expression: 'nodata(/ivideon-server 3.9 HTTP/ivideon.attributes,3m)=1'
          master_item:
            key: ivideon.attributes
          lld_macro_paths:
            - lld_macro: '{#CAMERA_ID}'
              path: $.id
            - lld_macro: '{#NAME}'
              path: $.name
            - lld_macro: '{#STATUS}'
              path: $.status
          preprocessing:
            - type: JSONPATH
              parameters:
                - '$.body[*]'

Готовый шаблон для версии Ivideon-Server 3.12.0

zabbix_export:
  version: '7.4'
  template_groups:
    - uuid: d37f71c7e3f7469bab645852a69a2018
      name: 'Templates/Video surveillance'
  templates:
    - uuid: 8799d6b58bf3483895babbd76026c758
      template: 'ivideon-server 3.12 HTTP'
      name: 'ivideon-server 3.12 HTTP'
      groups:
        - name: 'Templates/Video surveillance'
      items:
        - uuid: a724aaa44e6e4c0d8c5fbadcb3247fa6
          name: 'Ivideon Get Data'
          type: HTTP_AGENT
          key: ivideon.attributes
          delay: 3m
          value_type: TEXT
          url: 'http:/{HOST.IP}:8080/streams/info'
          query_fields:
            - name: server
              value: '1'
            - name: sessionId
              value: '{$IVIDEON_PASSWORD}'
          output_format: JSON
          triggers:
            - uuid: 13e35f3290e644f09bead7f9caf78ab1
              expression: 'nodata(/ivideon-server 3.12 HTTP/ivideon.attributes,3m)=1'
              name: 'Ivideon-server service is unavailable'
              priority: HIGH
      discovery_rules:
        - uuid: 66bdeec591ac4239806f6c86c0179afd
          name: 'Ivideon-server discovery cameras'
          type: DEPENDENT
          key: ivideon.cameras
          item_prototypes:
            - uuid: 685ba95a1f364e1b958106b4d1eb76a0
              name: 'CAMERA [{#NAME}]: Get record status'
              type: SCRIPT
              key: 'ivideon.cameras.record.status[{#NAME}]'
              value_type: TEXT
              params: |
                var obj = JSON.parse(value);
                
                var host_ip = obj.hostip;
                var camera_id = obj.cameraid;
                var ivideon_pass = obj.ivideon_password
                var request = new HttpRequest();
                
                var nowUnix = Math.floor(Date.now() / 1000);
                var timestamp_offset = nowUnix - 10800
                
                var url = "http://" + host_ip + ":8080/archive/list"
                        + "?startTime=" + timestamp_offset
                        + "&endTime=" + nowUnix
                        + "&server=1&sessionId=" + ivideon_pass
                        + "&camera=" + camera_id;
                
                var data = JSON.parse(request.get(url));
                
                if (Array.isArray(data) && data.length === 0) {
                    return 0;
                } else {
                    return 1;
                }
              parameters:
                - name: cameraid
                  value: '{#CAMERA_ID}'
                - name: hostip
                  value: '{HOST.IP}'
                - name: ivideon_password
                  value: '{$IVIDEON_PASSWORD}'
              trigger_prototypes:
                - uuid: 787126b8f33d4a75b8a038013a4e273e
                  expression: 'last(/ivideon-server 3.12 HTTP/ivideon.cameras.record.status[{#NAME}])=0'
                  name: 'Camera {#NAME} archive not recording'
                  priority: WARNING
                  dependencies:
                    - name: 'Camera {#NAME} is unavailable'
                      expression: 'last(/ivideon-server 3.12 HTTP/ivideon.cameras.status[{#NAME}])=0'
                    - name: 'Ivideon-server service is unavailable'
                      expression: 'nodata(/ivideon-server 3.12 HTTP/ivideon.attributes,3m)=1'
            - uuid: 4e5d1fd4cb4c4f73b6cae860743c3cf3
              name: 'CAMERA [{#NAME}]: Get Status'
              type: DEPENDENT
              key: 'ivideon.cameras.status[{#NAME}]'
              trends: '0'
              preprocessing:
                - type: JSONPATH
                  parameters:
                    - '$.body[?(@.custom_device_info.name=="{#NAME}")].status.first()'
              master_item:
                key: ivideon.attributes
              trigger_prototypes:
                - uuid: cf2488d8a103465b983c039ae415415c
                  expression: 'last(/ivideon-server 3.12 HTTP/ivideon.cameras.status[{#NAME}])=0'
                  name: 'Camera {#NAME} is unavailable'
                  priority: HIGH
                  dependencies:
                    - name: 'Ivideon-server service is unavailable'
                      expression: 'nodata(/ivideon-server 3.12 HTTP/ivideon.attributes,3m)=1'
          master_item:
            key: ivideon.attributes
          lld_macro_paths:
            - lld_macro: '{#CAMERA_ID}'
              path: $.id
            - lld_macro: '{#NAME}'
              path: $.custom_device_info.name
            - lld_macro: '{#STATUS}'
              path: $.custom_device_info.status
          preprocessing:
            - type: JSONPATH
              parameters:
                - '$.body[*]'

Чтобы использовать шаблон, вставьте содержимое в текстовый файл и сохраните с расширением yaml, после импортируйте в Zabbix.