Issues (46)

glances/exports/glances_influxdb.py (3 issues)

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
"""InfluxDB (up to InfluxDB 1.7.x) interface class."""
11
12
import sys
13
from platform import node
14
15
from glances.logger import logger
16
from glances.exports.glances_export import GlancesExport
17
18
from influxdb import InfluxDBClient
19
from influxdb.client import InfluxDBClientError
20
21
22
class Export(GlancesExport):
23
    """This class manages the InfluxDB export module."""
24
25
    def __init__(self, config=None, args=None):
26
        """Init the InfluxDB export IF."""
27
        super(Export, self).__init__(config=config, args=args)
28
29
        # Mandatory configuration keys (additional to host and port)
30
        self.user = None
31
        self.password = None
32
        self.db = None
33
34
        # Optional configuration keys
35
        self.protocol = 'http'
36
        self.prefix = None
37
        self.tags = None
38
        self.hostname = None
39
40
        # Load the InfluxDB configuration file
41
        self.export_enable = self.load_conf(
42
            'influxdb', mandatories=['host', 'port', 'user', 'password', 'db'], options=['protocol', 'prefix', 'tags']
43
        )
44
        if not self.export_enable:
45
            exit('Missing INFLUXDB version 1 config')
46
47
        # The hostname is always add as a tag
48
        self.hostname = node().split('.')[0]
49
50
        # Init the InfluxDB client
51
        self.client = self.init()
52
53
    def init(self):
54
        """Init the connection to the InfluxDB server."""
55
        if not self.export_enable:
56
            return None
57
58
        # Correct issue #1530
59
        if self.protocol is not None and (self.protocol.lower() == 'https'):
60
            ssl = True
61
        else:
62
            ssl = False
63
64
        try:
65
            db = InfluxDBClient(
66
                host=self.host,
67
                port=self.port,
68
                ssl=ssl,
69
                verify_ssl=False,
70
                username=self.user,
71
                password=self.password,
72
                database=self.db,
73
            )
74
            get_all_db = [i['name'] for i in db.get_list_database()]
75
        except InfluxDBClientError as e:
76
            logger.critical("Cannot connect to InfluxDB database '%s' (%s)" % (self.db, e))
77
            sys.exit(2)
78
79
        if self.db in get_all_db:
0 ignored issues
show
The variable get_all_db does not seem to be defined for all execution paths.
Loading history...
80
            logger.info("Stats will be exported to InfluxDB server: {}".format(db._baseurl))
81
        else:
82
            logger.critical("InfluxDB database '%s' did not exist. Please create it" % self.db)
83
            sys.exit(2)
84
85
        return db
86
87 View Code Duplication
    def _normalize(self, name, columns, points):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
88
        """Normalize data for the InfluxDB's data model.
89
90
        :return: a list of measurements.
91
        """
92
        ret = []
93
94
        # Build initial dict by crossing columns and point
95
        data_dict = dict(zip(columns, points))
96
97
        # issue1871 - Check if a key exist. If a key exist, the value of
98
        # the key should be used as a tag to identify the measurement.
99
        keys_list = [k.split('.')[0] for k in columns if k.endswith('.key')]
100
        if len(keys_list) == 0:
101
            keys_list = [None]
102
103
        for measurement in keys_list:
104
            # Manage field
105
            if measurement is not None:
106
                fields = {
107
                    k.replace('{}.'.format(measurement), ''): data_dict[k]
108
                    for k in data_dict
109
                    if k.startswith('{}.'.format(measurement))
110
                }
111
            else:
112
                fields = data_dict
113
            # Transform to InfluxDB data model
114
            # https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_reference/
115
            for k in fields:
116
                #  Do not export empty (None) value
117
                if fields[k] is None:
118
                    continue
119
                # Convert numerical to float
120
                try:
121
                    fields[k] = float(fields[k])
122
                except (TypeError, ValueError):
123
                    # Convert others to string
124
                    try:
125
                        fields[k] = str(fields[k])
126
                    except (TypeError, ValueError):
127
                        pass
128
            # Manage tags
129
            tags = self.parse_tags(self.tags)
130
            if 'key' in fields and fields['key'] in fields:
131
                # Create a tag from the key
132
                # Tag should be an string (see InfluxDB data model)
133
                tags[fields['key']] = str(fields[fields['key']])
134
                # Remove it from the field list (can not be a field and a tag)
135
                fields.pop(fields['key'])
136
            # Add the hostname as a tag
137
            tags['hostname'] = self.hostname
138
            # Add the measurement to the list
139
            ret.append({'measurement': name, 'tags': tags, 'fields': fields})
140
        return ret
141
142 View Code Duplication
    def export(self, name, columns, points):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
143
        """Write the points to the InfluxDB server."""
144
        # Manage prefix
145
        if self.prefix is not None:
146
            name = self.prefix + '.' + name
147
        # Write input to the InfluxDB database
148
        if len(points) == 0:
149
            logger.debug("Cannot export empty {} stats to InfluxDB".format(name))
150
        else:
151
            try:
152
                self.client.write_points(self._normalize(name, columns, points), time_precision="s")
153
            except Exception as e:
154
                # Log level set to debug instead of error (see: issue #1561)
155
                logger.debug("Cannot export {} stats to InfluxDB ({})".format(name, e))
156
            else:
157
                logger.debug("Export {} stats to InfluxDB".format(name))
158