IOT Discovery [En construcción]

Este documento describe la funcionalidad IOTdel discovery de PandoraFMS.



Introducción

La integración Pandora IOT tiene como finalidad supervisar mensajes que se reciben de un servidor broker de mqtt.

Para ello, la integración hace uso de un servidor broket MQTT, que abre la posibilidad de suscribirse a un topic para recibir mensajes y un plugin de Discovery, que permite configurar un filtrado de estos mensajes y visualizar el postprocesado de estos en la consola de PandoraFMS mediante agentes y módulos que se envían vía XML al servidor de pandora.

Elementos de la integración IOT

La integración IOT se compone principalmente de dos elementos clave, un servidor broker MQTT que se encargara de recibir los mensajes y almacenarlos en base de datos y un plugin de discovery que se encargara de filtrar estos mensajes previamente almacenados en base de datos y procesar estos para una visualización según sea requerida de estos que facilitara la automatización de alarmas o lo que sea necesario realizar con estos datos.

Este servicio se encarga de conectarse a un servidor MQTT, recibir mensajes de un tema específico y almacenarlos en una base de datos. También registra logs en archivos y en la consola, limpia periódicamente los datos antiguos y maneja correctamente el cierre del programa para evitar pérdidas de información.

Este script se encarga de conectarse a la base de datos en los que se han almacenado los mensajes previamente con el receptor, realizar un filtrado de mensajes en base a una configuración previamente realizada por el usuario y crea agentes y módulos en base a los mensajes que pasan los filtros.

Instalación manual

Puedes realizar una instalación manual del servidor MQTT de la siguiente manera:

el8

Debes instalar el fichero .rpm llamado  pandora_iot_server-1.0-1.el8.x86_64.rpm

sudo dnf install ./pandora_iot_server-1.0-1.el8.x86_64.rpm
el9

Debes instalar el fichero .rpm llamado pandora_iot_server-1.0-1.el9.x86_64.rpm

sudo dnf install ./pandora_iot_server-1.0-1.el9.x86_64.rpm
ubuntu

Debes instalar el fichero .deb llamado pandora_iot_server_1.0-1.deb

sudo dpkg -i pandora_iot_server_1.0-1.deb

Una vez ejecutado el comando, nos pedirá confirmación, para instalar el server.

image.png

Después de aceptar, se instalará el servidor y podremos empezar a configurar la escucha de mensajes.

image.png


Prerrequisitos

Parámetros servidor MQTT

Parámetros
-c,--conf_file Ruta al archivo de configuración
-v,--verbosity Activar modo debug
Archivo de configuración (--conf)
[DEFAULT]
;;topic
topic = < Topic al que se suscribirse para recibir mensajes. >

;; connection
host = < Host del servidor en el que se escucharan los mensajes >
port = < Puerto del servidor en el que se escucharan los mensajes >
;; protocol supports:  tcp, websockets, unix
; protocol = < Tipo de protocolo. Aceptados : tcp, websockets, unix
; user = < Usuario >
; password = < Contraseña >
; ssl = 0 < Activar cifrado ssl >
; trust_ssl = < Verificacion de certificado >

;; Database
; dbuser = < Usuario de la base de datos (opcional) >
; dbpass = < Contraseña de la base de datos (opcional) >
; dblocation = < Localización del fichero de base de datos >
; dbname = < Nombre del fichero de base de datos >
; data_cleaning_period = < Periodo de reestablecimiento de los datos >  

;; Logs
;; log_name = < Nombre del fichero de logs >  
; log_location = < Ruta del fichero de logs >
; log_level = < Nivel del log >          
; max_log_bytes = < Tamaño máximo de log >          
; log_rotation_count = < >
Ejemplo
[DEFAULT]
;;topic
topic = testtopic/#

;; connection
host = test.mosquitto.org
port = 1883
;; protocol supports:  tcp, websockets, unix
; protocol = websockets 
; user = ro
; password = readonly
; ssl = 0
; trust_ssl = 1

;; Database
; dbuser = 
; dbpass = 
; dblocation = /opt/pandora/iot/db
; dbname = pandora_iot.db
; data_cleaning_period = 86400  

;; Logs
;; log_name = pandora_iot44.log   
; log_location = /var/log/pandora/
; log_level = debug              
; max_log_bytes = 50_000_000         
; log_rotation_count = 3

Configurando el servidor MQTT

Este script permite suscribirse a un tópico MQTT, recibir mensajes y almacenarlos en una base de datos SQLite. A continuación se describen los pasos para configurarlo y ejecutarlo correctamente.

1.Configuración del archivo de configuración

Antes de ejecutar el script, se debe crear un archivo de configuración en formato .ini. Este archivo contiene los parámetros necesarios para la conexión al broker MQTT, la base de datos y la configuración de logs. Un ejemplo de archivo de configuración es el siguiente:

[DEFAULT]
;; MQTT Topic
topic = testtopic/#

;; Conexión MQTT
host = test.mosquitto.org
port = 1883
protocol = tcp
ssl = 0
trust_ssl = 1

;; Base de Datos
dbuser = usuario
dbpass = contraseña
dblocation = /ruta/del/directorio/db
dbname = pandora_iot.db
data_cleaning_period = 86400

;; Logs
log_name = pandora_iot.log
log_location = /ruta/del/directorio/logs
log_level = debug
max_log_bytes = 50_000_000
log_rotation_count = 3

Parámetros clave:

2. Arrancar el servicio.

Se podrá lanzar el servicio con el siguiente comando :

systemctl start pandora_iot_server

Para dejar de recibir y almacenar mensajes se puede pausar el servicio con :

systemctl stop pandora_iot_server




Parámetros del plugin de Discovery

Parámetros
--conf Ruta al archivo de configuración
--filter_conf Ruta al achivo de filtros para los mensajes
--verbosity Sirve para activar el modo debug
Archivo de configuración (--conf)
db_path = Ruta a la base de datos sql lite
threads = < Número máximo de threads utilizados >
timestamp_filter = < Timestamp en segundos desde el que se filtraran los mensajes >
agents_group_name = < Nombre de grupo para los agentes creados >
interval = < Intervalo en segundos para los agentes >
tentacle_ip = < IP de la máquina destino para los agentes creados >
tentacle_port = < Puerto de tentacle, por defecto : 41121 >
Ejemplo
db_path = /var/spool/pandora/data_in/discovery/pandorafms.iot/mqtt_messages.db
threads = 4
timestamp_filter = 1738000000
agents_group_name = Unknown
interval = 300
tentacle_ip = 127.0.0.1
tentacle_port = 41121 
Archivo para filtrar los mensajes (--filter_conf)
#=======================================================================
#Configuración de filtros
#=======================================================================
filter_begin: < Marca el inicio de una configuración de filtro. Todo lo que esté hasta filter_end pertenece al mismo filtro. Obligatorio. >
filter_module_name: < Nombre asignado al módulo que genera el filtro. Obligatorio. >
filter_module_type: < Tipo de módulo que genera el filtro. Obligatorio. >
filter_topic: < Expresión regular o texto exacto que debe coincidir con el tema del mensaje para que se aplique el filtro. Opcional. Por defecto, coincide con cualquier tema. >
filter_topic_exact_match: < Define si filter_topic se trata como una expresión regular (0) o como texto exacto (1). Opcional. Por defecto es 0. >
filter_message: < Expresión regular o texto exacto que debe coincidir con el contenido del mensaje para que se aplique el filtro. Opcional. Por defecto, coincide con cualquier mensaje. >
filter_message_exact_match: < Define si filter_message se trata como una expresión regular (0) o como texto exacto (1). Opcional. Por defecto es 0. >
filter_message_capture_regex: < Expresión regular con grupos de captura para extraer valores del mensaje. >
                               < Los valores capturados se almacenarán en macros '__regexN__', donde N es el índice del grupo de captura. >
filter_message_capture_json: < Lista de rutas JSON (separadas por '|') para extraer valores, utilizando el mismo formato que jq. >
                               < Los valores capturados se almacenarán en macros '__jsonN__', donde N es el índice de la ruta. >
filter_module_value: < Valor asignado al módulo que genera el filtro. Obligatorio. >
filter_end: < Marca el final de una configuración de filtro. Todo lo que esté desde filter_begin pertenece al mismo filtro. Obligatorio. >

#Macros disponibles para su uso en filter_module_name y filter_module_value:
__topic__: < Se reemplaza con el tema exacto del mensaje procesado. >
__regexN__: < Se reemplaza con el valor correspondiente capturado por filter_message_capture_regex. >
__jsonN__: < Se reemplaza con el valor correspondiente capturado por filter_message_capture_json. >
Ejemplo
filter_begin​
filter_module_name pressure
filter_module_type generic_data_string
filter_topic sensor/humidity
filter_topic_exact_match 1
filter_message Value:
filter_message_exact_match 1
filter_message_capture_regex (\b\w{6}\b).*?(\b\w{7}\b).*?(\d{3})\s(\w)-
filter_message_capture_json 
filter_module_value 1__regex1__ __regex2__ __topic__
filter_end

filter_begin​
filter_module_name humidity
filter_module_type generic_data_string
filter_topic sensor/pressure
filter_topic_exact_match 1
filter_message 
filter_message_exact_match 1
filter_message_capture_regex 
filter_message_capture_json $.extra_info[0].value|$.extra_info[1].value
filter_module_value 2 __json1__ __json2__
filter_end

Configurando el plugin de Discovery

Este plugin puede integrarse con el Discovery de Pandora FMS.

Para ello se debe cargar el paquete ".disco" que puede descargar desde la librería de Pandora FMS:

https://pandorafms.com/library/

image.png

Una vez cargado, se podrá recibir y supervisar mensajes de un broker, creando tareas de Discovery desde la sección Management > Discovery > App

Para cada tarea se solicitarán los siguientes datos mínimos:

image.png

También se podrá ajustar la configuración de los filtros para personalizar la monitorización deseada:

image.png

Las tareas completadas con éxito tendrán un resumen de ejecución con la siguiente información:

image.png

Configurando el filtrado de mensajes

El plugin hace uso de distintos filtros en los que apoyarse para la filtración de mensajes.

Filtrado de topic

Para el filtrado de topic, se hace uso de las etiquetas Filter_topic y Filter_topic_exact_match

Expresión regular o texto exacto que deba coincidir con el topic del mensaje para cumplirse el filtro. Es opcional. Por defecto haría match con cualquier topic.

Indica si lo indicado en filter_topic se tratará como una expresión regular (0) o como un texto exacto (1). Es opcional. Por defecto su valor es 0.

Ejemplos

Haciendo uso de los filtros Filter_topic y Filter_topic_exact_match podremos elegir el topic al que suscribirse.

Podemos indicar directamente el nombre del topic o buscar un patron de regex en este.

Supongamos que tienes un topic MQTT llamado:

sensors/temperature/livingroom

EJEMPLO 1 filter_topic_exact_match 1 (Texto exacto)

Podríamos configurar filter_topic especifando el nombre del topic.

filter_topic sensors/temperature/livingroom

y configurar filter_topic_exact_match a 1, para que busque solo ese topic.

filter_topic_exact_match 1

Si en vez de especificar el nombre del topic completo, especificamos solo una parte de este :

filter_topic sensors/temperature/

También coincidiría. 

image.png

EJEMPLO 2 filter_topic_exact_match 0 (Expresión regular)

Podríamos configurar filter_topic especifando una expresión regular que haga match con el nombre del topic al que se pretende suscribir. Por ejemplo, la siguiente, que miraría que empiecen por "sensors" y terminen en "livingroom"

filter_topic ^sensors/.*?/livingroom$

y configurar filter_topic_exact_match a 0, para que filtre por expresión regular, en vez de texto exacto.

filter_topic_exact_match 0

image.png

Filtrado de mensajes

Para el filtrado de topic, se hace uso de las etiquetas Filter_topic y Filter_topic_exact_match

Expresión regular o texto exacto que deba coincidir con el mensaje para cumplirse el filtro. Es opcional. Por defecto haría match con cualquier mensaje.

Indica si lo indicado en filter_message se tratará como una expresión regular (0) o como un texto exacto (1). Es opcional. Por defecto su valor es 0.

Ejemplos

Haciendo uso de los filtros Filter_messagey Filter_message_exact_match podremos filtrar los mensajes que se van a recibir.

Podemos indicar directamente contenido del mensaje o buscar un patron de regex en este.

Supongamos que tienes un mensaje como este:

{"message":"Move up right",
"status":"active",
"datetime":"2025-02-11T14:30:54.260Z",
"data":{"yaw_rate":0.11466979980468749,
"pitch_rate":0.6159210205078126,
"yaw_direction":"right",
"pitch_direction":"up"}}

EJEMPLO 1 filter_message_exact_match 1 (Texto exacto)

Podríamos configurar filter_message especifando el texto del mensaje.

filter_message Move up right

y configurar filter_topic_exact_match a 1, para que busque solo ese topic.

filter_message_exact_match 1

Si en vez de especificar el nombre del topic completo, especificamos solo una parte de este :

filter_message Move up 

También coincidiría.

image.png

EJEMPLO 2 filter_message_exact_match 0 (Expresión Regular)

Podríamos configurar filter_message especifando una expresión regular que haga match con los mensajes que se pretenden filtrar. Por ejemplo, la siguiente, buscaría un mensaje que contenga las palabras "move" y "right"

filter_message Move.*right|right.*Move

y configurar filter_message_exact_match a 0, para que filtre por expresión regular, en vez de texto exacto.

filter_message_exact_match 0

image.png

Generación y uso de macros

Ejemplos

Imagina que tenemos el siguiente mensaje:

{
  "message": "Move up right",
  "status": "active",
  "datetime": "2025-02-11T14:30:54.260Z",
  "data": {
    "yaw_rate": 0.11466979980468749,
    "pitch_rate": 0.6159210205078126,
    "yaw_direction": "right",
    "pitch_direction": "up"
  }
}

Y queremos capturar algunas partes del mensaje, por ejemplo :

Además, también queremos capturar el topic al que pertenece el mensaje en una macro para incluirlo en el valor.

Para ello deberíamos seguir los siguientes pasos, pudiendo hacerlo con Filter_message_capture_regex y FIlter_message_capture_json:

Con Filter_message_capture_regex se espera una expresión regular con grupos de captura para obtener valores del mensaje. Aquello que haga match con los grupos de captura se almacenará en macros “__regexN__”, siendo N la posición del grupo de captura de la regex. Los pasos para configurar las macros a capturar serían los siguientes:

1. Configurar filter_message_capture_regex para capturar las partes necesarias:

Usamos una expresión regular para capturar "yaw_direction", "pitch_direction" y el contenido de "message".

filter_message_capture_regex \"yaw_direction\":\\s?\"(\\w+)\".*\"pitch_direction\":\\s?\"(\\w+)\".*\"message\":\\s?\"([^\"]+)\"

2. Configurar filter_module_value para incluir las macros que contienen los valores capturados y el topic.

filter_module_value __topic__ | Yaw Direction: __regex1__ | Pitch Direction: __regex2__ | Message: __regex3__


Ejemplo de configuración completa

image.png

filter_begin​
filter_module_name temperature humidity
filter_module_type generic_data_string
filter_topic testtopic/rgb/test/action/
filter_topic_exact_match 1
filter_message Move up right
filter_message_exact_match 1
filter_message_capture_regex \"yaw_direction\":\\s?\"(\\w+)\".*\"pitch_direction\":\\s?\"(\\w+)\".*\"message\":\\s?\"([^\"]+)\"
filter_message_capture_json 
filter_module_value __topic__ | Yaw Direction: __regex1__ | Pitch Direction: __regex2__ | Message: __regex3__
filter_end



Entonces, con el mensaje y la configuración mostradas de ejemplos previamente, se crearía el siguiente agente:

image.png

Y el siguiente módulo de mensaje:

image.png

image.png

Con Filter_message_capture_json se espera una lista de rutas dentro de un JSON (separadas por”|”) de las cuales capturar sus valores. Usando el mismo formato que el comando jq. Aquello que se capture por cada path se almacenará en macros “__jsonN__”, siendo N la posición del path dentro de la lista. . Los pasos para configurar las macros a capturar serían los siguientes:

1. Configurar filter_message_capture_json para capturar las partes necesarias del JSON.

filter_message_capture_json $.data.yaw_direction|$.data.pitch_direction|$.message

Las rutas JSONPath deben comenzar con $ porque representa la raíz del documento JSON. 

2. Configurar filter_module_value para incluir las macros que contienen los valores capturados y el topic.

filter_module_value __topic__ | Yaw Direction: __json1__ | Pitch Direction: __json2__ | Message: __json3__

Ejemplo de configuración completa

image.png

filter_begin​
filter_module_name __topic__ get right
filter_module_type generic_data_string
filter_topic testtopic/rgb/test/action/
filter_topic_exact_match 1
filter_message Move up right
filter_message_exact_match 1
filter_message_capture_regex 
filter_message_capture_json $.data.yaw_direction|$.data.pitch_direction|$.message
filter_module_value __topic__ | Yaw Direction: __json1__ | Pitch Direction: __json2__ | Message: __json3__
filter_end



Entonces, con el mensaje y la configuración mostradas de ejemplos previamente, se crearía el siguiente agente:

image.png

Y el siguiente módulo de mensaje:

image.png

image.png

Ejecución manual

El formato de la ejecución del plugin es el siguiente:

./proccess_messages --conf < ruta al fichero de configuración > --filter_conf < ruta al fichero con los filtros para los mensajes >

Por ejemplo:

./proccess_messages --conf /var/spool/pandora/data_in/discovery/pandorafms.iot/file.conf --filter_conf /var/spool/pandora/data_in/discovery/pandorafms.iot/file_filters.conf

La ejecución devolverá una salida en formato JSON con información sobre la ejecución, y generará un fichero XML para cada agente (topic) y cada mensaje que haya pasado los filtros, que enviará al servidor de Pandora FMS por el método de transferencia indicado en la configuración.

Por ejemplo:

{"summary": {"Total agents": 2, "Total messages": 141}}

{"summary": {"Total agents": 2, "Total messages": 2}}

Agentes y módulos generados por el plugin

Recuerda que no puede haber mas de un modulo con el mismo nombre en el mismo agente.