Дополнение B. Как применять Zabbix в качестве вашего контроллера в MQTT
Содержание
Перевод блогов Instructions how-to use Zabbix as your Controller with MQTT, part 1 и Instructions how-to use Zabbix as your Controller with MQTT, part 2 (Auto-Discovery of Sensors) Jonas Paulin (@jpaulin).
Замечание: с изменениями от 2017-01-22. Был изменён формат ключа в элементах Zabbix с тем, чтобы соответствовать Auto-Discovery и автоматическому созданию датчиков и узлов. Item-key = child-id. См. Часть 2. Автоматическое обнаружение датчиков
Я хотел испытать Zabbix в качестве Контроллера, установить его в своём Raspberry Pi и заставить его работать с MySensors. Вот история моего опыта.
Zabbix является мощной системой мониторинга с открытым исходным кодом, применяемой для отслеживания состояния ИТ, центров телекоммуникационных данных и данных сетевых сред. Он привносит множество разнообразных функций, является очень гибким, имеет мощный Форум и огромную документацию.Он может интегрироваться посредством zabbix.api, zabbix-sender или при помощи своего протокола обнаружения на нижнем уровне, Zabbix LLD (Low Level Discovery), помимо прочего наполнения. Существуют различные библиотеки для разнообразных языков программирования с целью интеграции во внешние системы и программное обеспечение. Взгляните на его веб страницы на www.zabbix.org и www.zabbix.com. {Прим. пер.: также см.наш перевод отдельных глав второго издания Полного руководства Zabbix Андреа Далле Ваккье.}
До недавнего времени Zabbix спокойно пребывал на моём Raspberry Pi 2 совместно с моей интеграцией MySensors.
У меня он работает с брокером MQTT, шлюзом MQTT и некоторыми датчиками. На самом деле он работает с контроллером Domoticz выполняя регистрацию с тех же самых датчиков, которые установлены в том же самом Raspberry.
Вот пара снимков экрана моей установки, показывающие в работе датчики температуры и потребления энергии.
Для начала моему Raspberry требуется иметь запущенным некий брокер {сервер} MQTT для захвата и распространения сообщений MQTT, в данном случае от клиента шлюза MQTT MySensors. Я полагаю, что Mosquitto является наилучшим выбором. Для установки брокера Mosquitto MQTT придерживайтесь следующей инструкции или же напрямую исполните следующие команды в своём Raspberry Pi:
sudo apt-get update
sudo apt-get install mosquitto
sudo apt-get install mosquitto-clients
Для Zabbix требуется запущенной некая база данных. В нашей установке в качестве базы данных выбрана MySQL. Имеется два необходимых со стороны Zabbix
пакета чтобы всё заработало, zabbix-server-mysql
и zabbix-frontend-php
.
Также рекомендуется установить zabbix-agent
на ваш Raspberry. Затем Zabbix может автоматически выполнять мониторинг
производительности Raspberry Pi через этого zabbix-agent
совместно с предварительно установленными шаблонами.
Если вы установите Zabbix напрямую через apt-get install
, получаемая вами версия будет достаточно древней
(zabbix 2.x) и у меня возникли проблемы в попытках заставить её работать. В моём случае приводимая далее веб страница позволила установить Zabbix v3.0.
Вот руководство Zabbix 3.0.
Если у вас ещё не установлен mysql сделайте это для своего Raspberry:
sudo apt-get install mysql-server mysql-client
Воспользуйтесь данной пошаговой инструкцией для установки Zabbix в вашем Raspberry Pi. Она установит Zabbix 3.0.
Теперь у вас должны иметься установленными в вашем Raspberry Pi:
-
zabbix-server-mysql
-
zabbix-frontend-php
-
zabbix-agent
Вы можете проверить это выполнив теперь доступ к Zabbix через веб- браузер с указанием IP адреса Raspberry Pi, вы должны обнаружить следующий экран:
Введите для User
Admin
, а для
Password
zabbix
. Позднее вы сможете заменить эти
регистрационные данные.
Дополнительно нам понадобится установить пакет zabbix-sender чтобы иметь возможность отправлять сообщения MQTT из сценария в сервер Zabbix. В своём Raspberry Pi наберите:
cd ~
cd zabbix3-rpi-master
sudo dpkg –i zabbix-sender_3.0.*-1+jessie_armhf.deb
Чтобы переправлять сообщения MQTT из брокера MQTT в Zabbix я написал небольшой сценарий Python. Напомню, что формат MQTT с MySensors
представлен в виде "topic/a/b/c/d/e"
, однако Zabbix не допускает символ /
(прямой косой черты) в виде некоего "ключа элемента" и следовательно его требуется преобразовать в
"topic.a.b.c.d.e"
.
В Zabbix я установил некую группу хостов с названием "MySensors"
. В эту группу хостов добавляются
узлы датчиков (хосты). Для тех хостов, которые добавляются в качестве дочерних датчиков (элементов) ниже приводится формат MQTT MySensors:
MY_MQTT_TOPIC_PREFIX/SENSOR-NODE-ID/SENSOR-CHILD-ID/CMD-TYPE/ACK-FLAG/SUB-TYPE
-
hostgroup
="MySensors"
-
host
=MY_MQTT_TOPIC_PREFIX + "." + MY_NODE_ID
т.е.
mygateway1-out.5
-
item
=CHILD_ID + "." + CMD-TYPE + "." + "ACK-FLAG" + "." + SUB-TYPE
т.е.
1.1.0.0
(Child Id = 1, Cmdtype = set, Ack = 0, Subtype = V_TEMP)
Элемены Zabbix следует установить в тип "zabbix trapper"
. Вы найдёте эти элементы по
-item-
, -host-
, -item-
.
Улавливатель (trapper) Zabbix выполняет ожидание сообщений, отправляемых от "zabbix-sender"
,
которые мы и собираемся применять здесь.
Имейте в виду, никакое автоматическое назначение узлов Узлам датчиков от Контроллера не будет работать в данной установке Zabbix. Следовательно,
ввам предварительно требуется установить Node_ID
в сценарии arduino MySensor, то есть:
#define MY_NODE_ID 1
-
Определите группу хостов и присвойте ей название:
-
Определите немного хостов (узлов датчиков). Название хоста должно иметь точное соответствие с названием темы вашего шлюза MQTT, а идентификаторы узла разделяются точкой (
MY_MQTT_TOPIC_PREFIX + "." + MY_NODE_ID
). В нашем примере я применяю"domoticz.in.MyMQTT.11"
в своём узле = 11.
-
Определите некоторые элементы (потомки) для своих узлов датчиков. Присвойте им название по своему выбору. Выберите
Type = Zabbix trapper
. Ключом для каждого элемента являетсяChild-Id
, взятый из сообщения MQTT MySensors. В данном случаеChild-Id = 1
. Замечание редакции 2017-01.22: Ключ для элементов был изменён на то, чтобы применять толькоChild-Id
для удовлетворения интеграции с Auto-Discovery. подробнее...
Имеется доступным для исполнения файл, zabbix_sender
, который вы теперь можете непосредственно проверить из
командной строки Raspberry. Попробуйте отправить некий элемент, который был предварительно настроен в Zabbix выполнив такую команду:
zabbix_sender -v -z localhost -s domoticz.in.MyMQTT.11 -k 1 -o 10.5
Отклик на неё должен выглядеть примерно так:
info from server: "processed: 1; failed: 0; total: 1; seconds spent: 0.000176"
sent: 1; skipped: 0; total: 1
Если получены failed = 0, sent = 1
, в нашей установке всё хорошо.
Теперь вы должны видеть поднятый Zabbix. Просмотрите Zabbix, в Monitoring выберите Latest Data. Вы должны обнаружить некое событие с температурой
10.5 °C, зарегистрированной для node = 11, Child = 1
, то есть
"domoticz.in.MyMQTT.11"
с элементом ключа = "1"
и
полученной полезной нагрузкой = 10.5. Замечание редакции 2017-01-22: формат ключа изменён на Child-Id
для
удовлетворения интеграции с Auto-Discovery. подробнее...
Наш сценарий Python применяет две библиотеки, которые требуется для начала выгрузить: библиотеку клиента Mosquito и paho-mqtt.
pip install paho-mqtt
Библиотеки, которые применяет zabbix-sender/ zabbix-api www.zabbix.org/wiki/Docs/api/libraries / github.com/blacked/py-zabbix
pip install py-zabbix
Подготовьте сценарий с названием mqtt_zabbix.py
в предпочитаемом вами редакторе, например, vi или nano.
nano mqtt_zabbix.py
Вставьте приводимый ниже текст сценария и сохраните его в этом фйле. Запустите в качестве примера этот сценарий в своей командной строке в фоновом
режиме в качестве демона. Данный сценарий будет сохранять выводы stdout и stderr в текстовом файле zabbix.log
:
python -u mqtt_zabbix.py > zabbix.log 2>&1 &
Ваш файл zabbix.log
запоминает отправленные события MQTT с временным штампом и получаемый от zabbix-sender,
которые полезны для отладки.
Сценарий Python
import paho.mqtt.client as mqtt
import time
from pyzabbix import ZabbixMetric, ZabbixSender
topic_sub = "domoticz/in/MyMQTT/"
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe(topic_sub+"+/+/+/+/+")
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
msg.topic = msg.topic.replace("/", ".")
myNode = msg.topic[:msg.topic.find(".", len(topic_sub)+1)]
myItem = msg.topic[len(myNode)+1:]
mySplit = myItem.split('.') # split topic to indexed list
if mySplit[1] == '1': # type = set
myItem = mySplit[0] # change item to child-id
metrics = [ZabbixMetric(myNode, myItem, msg.payload)]
result = ZabbixSender(use_config=True).send(metrics)
print(time.strftime("%c")+" "+str(result)+" : "+myNode+" "+myItem+" "+str(msg.payload))
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("localhost", 1883, 60)
В этом сценарии чтобы изменить формат MQTT с topic/a/b/c/d/e
на
topic.a.b.c.d.e
, требуемый со стороны Zabbix, применяется вот ракая строка Python:
msg.topic = msg.topic.replace("/", ".")
Эти две строки имеют дело с отправкой событий MQTT в Zabbix:
metrics = [ZabbixMetric(myNode, myItem, msg.payload)]
result = ZabbixSender(use_config=True).send(metrics)
В Zabbix можно добавить триггеры для генерации предупреждения из полученных событий датчика MQTT. Оповещения могут также отправляться, например, через SMS, электронную почту или сценарий (но это выходит за пределы данной статьи).Следующим шагом будет создание узлов датчиков (хостов) и его потомков (элементов), путём подготовки некоторых Шаблонов в Zabbix вместе с zabbix.api или zabbix LLD. А также ознакомьтесь, как отправлять сообщения от Zabbix к реле и рычагам MySensor, а также для автономных действий в потоке.
Наслаждайтесь и получайте удовольствие!
Здесь приводится дополнительный сценарий созданный в Python для реализации Auto-Discovery и автоматического создания датчиков и узлов в Zabbix. Он применяет Zabbix-API для автоматических проверки и создания группы хостов, хостов и элементов, в случае если они новые, и представляет в ваш Контроллер (после включения соответствующих узлов датчиков). Этот сценарий исполняется в моём RPI, в котором также установлены Mosquitto и Zabbix.
Описание сценария
Данный сценарий подключается к брокеру Mosquitto и осуществляет ожидание обмена MQTT из сетевой среды MySensors.
-
Вначале он проверяет наличие Zabbix-hostgroup с названием
"MySensors"
. Если её нет, она автоматически создаётся. -
Когда представляется через MQTT некий новый датчик, автоматически создаётся некий хост Zabbix, добавляется в имеющуюся группу хостов, а сам этот хост именуется с префиксом темы +
Node-ID
. -
Когда создаётся некий новый потомок (Child), представляемый как элемент Zabbix, добавляется соответствующий хост и задаётся название темы представления. Значение ключа в элементе Zabbix устанавливается в значение
Child-Id
. Данное название элемента позднее может быть изменено на что- то более предпочтительное для вас.
Запустите этот сценарий в фоновом режиме, как это было сделано в части 1. Наш первый сценарий передаёт данные в Zabbix. А также этот сценарий обрабатывает автоматическое создание необходимых датчиков.
Сценарий Python
import paho.mqtt.client as mqtt
import time
from pyzabbix import ZabbixMetric, ZabbixSender
from zabbix.api import ZabbixAPI
# Create ZabbixAPI class instance
zapi = ZabbixAPI(url='http://localhost/zabbix/', user='Admin', password='zabbix')
# Moquitto Topic Prefix
topic_sub = "domoticz/in/MyMQTT/"
# Name of Zabbix hostgroup
hostGroup = 'MySensors'
# Create hostgroup, if missing
result = zapi.do_request('hostgroup.get', {'filter': {'name': [hostGroup]}})
if [name['name'] for name in result['result']] == []:
zapi.hostgroup.create(name=hostGroup)
result = zapi.do_request('hostgroup.get', {'filter': {'name': [hostGroup]}})
print(time.strftime("%c")+" Hostgroup created: "+hostGroup+" : "+str(result)) # log hostgroup creation
# Get hostgroup-id
groupId = [name['groupid'] for name in result['result']]
# Mosquitto: the callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
# Mosquitto: subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe(topic_sub+"+/+/+/+/+")
# Mosquitto: the callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
msg.topic = msg.topic.replace("/", ".")
myNode = msg.topic[:msg.topic.find(".", len(topic_sub)+1)]
myItem = msg.topic[len(myNode)+1:]
mySplit = myItem.split('.')
# Check if host (node) exist, or create new host.
# 255 = child, 0 = presentation, 17 = S_ARDUINO_NODE
if myItem == '255.0.0.17':
result = zapi.do_request('host.get', {'filter':{'host':[myNode]}})
if [host['host'] for host in result['result']] == []: # new node sensor, create host
result = zapi.do_request('host.create', {'host': myNode, 'interfaces': [{'type': 1, 'main': 1, 'useip': 1, 'ip': '127.0.0.1', 'dns': '', 'port': '10050'}], 'groups': [{'groupid': groupId[0]}]})
print(time.strftime("%c")+" Host created: "+myNode+" : "+str(result)) # log host creation
# create item I_BATTERY_LEVEL
result = zapi.do_request('host.get', {'filter':{'host':[myNode]}})
hostId = [item['hostid'] for item in result['result']]
result = zapi.do_request('item.create', {'hostid': hostId[0], 'value_type': '0','type': '2', 'name': 'I_BATTERY_LEVEL', ' key_': '255.3.0.0'})
print(time.strftime("%c")+" Item created: I_BATTERY_LEVEL : "+str(result)) # log item creation
if mySplit[1] == '0': # command = presentation
if mySplit[0] != '255': # child = 255, don't create item
result = zapi.do_request('item.get', {'host': myNode, 'filter': {'key_':[mySplit[0]]}})
if [host['key_'] for host in result['result']] == []: # new child, create item
result = zapi.do_request('host.get', {'filter':{'host':[myNode]}})
hostId = [item['hostid'] for item in result['result']]
result = zapi.do_request('item.create', {'hostid': hostId[0], 'value_type': '0','type': '2', 'name': myItem, 'key_': mySplit[0]})
print(time.strftime("%c")+" Item created: "+myItem+" : "+str(result)) # log item creation
# Mosquitto: client start-up.
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("localhost", 1883, 60)
# Mosquitto: blocking call that processes network traffic, dispatches callbacks and
# handles reconnecting.
client.loop_forever()
Выполните этот сценарий в фоновом режиме в качестве демона и выводы отправляйте в файл zabbix_cnfg.log
python -u mqtt_zabbix_api.py > zabbix_cnfg.log 2>&1 &
Я достаточный новичок в написании сценариев на Python, поэтому возможно, что код не такой элегантный. Но он отлично работает для моих целей. И это замечательно!