Issues (46)

glances/exports/glances_mqtt.py (1 issue)

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# SPDX-FileCopyrightText: 2022 Nicolas Hennion <[email protected]>
6
#
7
# SPDX-License-Identifier: LGPL-3.0-only
8
#
9
10
"""MQTT interface class."""
11
12
import socket
13
import string
14
import sys
15
16
from glances.logger import logger
17
from glances.exports.glances_export import GlancesExport
18
from glances.globals import json_dumps
19
20
# Import paho for MQTT
21
from requests import certs
22
import paho.mqtt.client as paho
23
24
25
class Export(GlancesExport):
26
27
    """This class manages the MQTT export module."""
28
29
    def __init__(self, config=None, args=None):
30
        """Init the MQTT export IF."""
31
        super(Export, self).__init__(config=config, args=args)
32
33
        # Mandatory configuration keys (additional to host and port)
34
        self.user = None
35
        self.password = None
36
        self.topic = None
37
        self.tls = 'true'
38
39
        # Load the MQTT configuration file
40
        self.export_enable = self.load_conf(
41
            'mqtt', mandatories=['host', 'password'], options=['port', 'user', 'topic', 'tls', 'topic_structure']
42
        )
43
        if not self.export_enable:
44
            exit('Missing MQTT config')
45
46
        # Get the current hostname
47
        self.hostname = socket.gethostname()
48
        self.port = int(self.port) or 8883
49
        self.topic = self.topic or 'glances'
50
        self.user = self.user or 'glances'
51
        self.tls = self.tls and self.tls.lower() == 'true'
52
53
        self.topic_structure = (self.topic_structure or 'per-metric').lower()
54
        if self.topic_structure not in ['per-metric', 'per-plugin']:
55
            logger.critical("topic_structure must be either 'per-metric' or 'per-plugin'.")
56
            sys.exit(2)
57
58
        # Init the MQTT client
59
        self.client = self.init()
60
        if not self.client:
61
            exit("MQTT client initialization failed")
62
63
    def init(self):
64
        """Init the connection to the MQTT server."""
65
        if not self.export_enable:
66
            return None
67
        try:
68
            client = paho.Client(client_id='glances_' + self.hostname, clean_session=False)
69
            client.username_pw_set(username=self.user, password=self.password)
70
            if self.tls:
71
                client.tls_set(certs.where())
72
            client.connect(host=self.host, port=self.port)
73
            client.loop_start()
74
            return client
75
        except Exception as e:
76
            logger.critical("Connection to MQTT server %s:%s failed with error: %s " % (self.host, self.port, e))
77
            return None
78
79
    def export(self, name, columns, points):
80
        """Write the points in MQTT."""
81
82
        WHITELIST = '_-' + string.ascii_letters + string.digits
83
        SUBSTITUTE = '_'
84
85
        def whitelisted(s, whitelist=WHITELIST, substitute=SUBSTITUTE):
86
            return ''.join(c if c in whitelist else substitute for c in s)
87
88
        if self.topic_structure == 'per-metric':
89
            for sensor, value in zip(columns, points):
90
                try:
91
                    sensor = [whitelisted(name) for name in sensor.split('.')]
92
                    to_export = [self.topic, self.hostname, name]
93
                    to_export.extend(sensor)
94
                    topic = '/'.join(to_export)
95
96
                    self.client.publish(topic, value)
97
                except Exception as e:
98
                    logger.error("Can not export stats to MQTT server (%s)" % e)
99
        elif self.topic_structure == 'per-plugin':
100
            try:
101
                topic = '/'.join([self.topic, self.hostname, name])
102
                sensor_values = dict(zip(columns, points))
103
104
                # Build the value to output
105
                output_value = dict()
106
                for key in sensor_values:
107
                    split_key = key.split('.')
108
109
                    # Add the parent keys if they don't exist
110
                    current_level = output_value
111
                    for depth in range(len(split_key) - 1):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable len does not seem to be defined.
Loading history...
112
                        if split_key[depth] not in current_level:
113
                            current_level[split_key[depth]] = dict()
114
                        current_level = current_level[split_key[depth]]
115
116
                    # Add the value
117
                    current_level[split_key[len(split_key) - 1]] = sensor_values[key]
118
119
                json_value = json_dumps(output_value)
120
                self.client.publish(topic, json_value)
121
            except Exception as e:
122
                logger.error("Can not export stats to MQTT server (%s)" % e)
123