Issues (48)

glances/exports/export.py (1 issue)

1
#
2
# This file is part of Glances.
3
#
4
# SPDX-FileCopyrightText: 2022 Nicolas Hennion <[email protected]>
5
#
6
# SPDX-License-Identifier: LGPL-3.0-only
7
#
8
9
"""
10
I am your father...
11
12
...for all Glances exports IF.
13
"""
14
15
from glances.globals import NoOptionError, NoSectionError, iteritems, iterkeys, json_dumps
16
from glances.logger import logger
17
from glances.timer import Counter
18
19
20
class GlancesExport:
21
    """Main class for Glances export IF."""
22
23
    # List of non exportable plugins
24
    # @TODO: remove this part and make all plugins exportable (see issue #1556)
25
    # @TODO: also make this list configurable by the user (see issue #1443)
26
    non_exportable_plugins = [
27
        'alert',
28
        'amps',
29
        'help',
30
        'now',
31
        'plugin',
32
        'psutilversion',
33
        'quicklook',
34
        'version',
35
    ]
36
37
    def __init__(self, config=None, args=None):
38
        """Init the export class."""
39
        # Export name
40
        self.export_name = self.__class__.__module__
41
        logger.debug(f"Init export module {self.export_name}")
42
43
        # Init the config & args
44
        self.config = config
45
        self.args = args
46
47
        # By default export is disabled
48
        # Needs to be set to True in the __init__ class of child
49
        self.export_enable = False
50
51
        # Mandatory for (most of) the export module
52
        self.host = None
53
        self.port = None
54
55
        # Save last export list
56
        self._last_exported_list = None
57
58 View Code Duplication
    def _log_result_decorator(fct):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
59
        """Log (DEBUG) the result of the function fct."""
60
61
        def wrapper(*args, **kw):
62
            counter = Counter()
63
            ret = fct(*args, **kw)
64
            duration = counter.get()
65
            class_name = args[0].__class__.__name__
66
            class_module = args[0].__class__.__module__
67
            logger.debug(f"{class_name} {class_module} {fct.__name__} return {ret} in {duration} seconds")
68
            return ret
69
70
        return wrapper
71
72
    def exit(self):
73
        """Close the export module."""
74
        logger.debug(f"Finalise export interface {self.export_name}")
75
76
    def load_conf(self, section, mandatories=['host', 'port'], options=None):
77
        """Load the export <section> configuration in the Glances configuration file.
78
79
        :param section: name of the export section to load
80
        :param mandatories: a list of mandatory parameters to load
81
        :param options: a list of optional parameters to load
82
83
        :returns: Boolean -- True if section is found
84
        """
85
        options = options or []
86
87
        if self.config is None:
88
            return False
89
90
        # By default read the mandatory host:port items
91
        try:
92
            for opt in mandatories:
93
                setattr(self, opt, self.config.get_value(section, opt))
94
        except NoSectionError:
95
            logger.error(f"No {section} configuration found")
96
            return False
97
        except NoOptionError as e:
98
            logger.error(f"Error in the {section} configuration ({e})")
99
            return False
100
101
        # Load options
102
        for opt in options:
103
            try:
104
                setattr(self, opt, self.config.get_value(section, opt))
105
            except NoOptionError:
106
                pass
107
108
        logger.debug(f"Load {section} from the Glances configuration file")
109
        logger.debug(f"{section} parameters: {({opt: getattr(self, opt) for opt in mandatories + options})}")
110
111
        return True
112
113
    def get_item_key(self, item):
114
        """Return the value of the item 'key'."""
115
        ret = None
116
        try:
117
            ret = item[item['key']]
118
        except KeyError:
119
            logger.error(f"No 'key' available in {item}")
120
        if isinstance(ret, list):
121
            return ret[0]
122
        return ret
123
124
    def parse_tags(self, tags):
125
        """Parse tags into a dict.
126
127
        :param tags: a comma-separated list of 'key:value' pairs. Example: foo:bar,spam:eggs
128
        :return: a dict of tags. Example: {'foo': 'bar', 'spam': 'eggs'}
129
        """
130
        d_tags = {}
131
        if tags:
132
            try:
133
                d_tags = dict([x.split(':') for x in tags.split(',')])
134
            except ValueError:
135
                # one of the 'key:value' pairs was missing
136
                logger.info('Invalid tags passed: %s', tags)
137
                d_tags = {}
138
139
        return d_tags
140
141
    def plugins_to_export(self, stats):
142
        """Return the list of plugins to export.
143
144
        :param stats: the stats object
145
        :return: a list of plugins to export
146
        """
147
        return [p for p in stats.getPluginsList() if p not in self.non_exportable_plugins]
148
149
    def last_exported_list(self):
150
        """Return the list of plugins last exported."""
151
        return self._last_exported_list
152
153
    def update(self, stats):
154
        """Update stats to a server.
155
156
        The method builds two lists: names and values and calls the export method to export the stats.
157
158
        Note: this class can be overwritten (for example in CSV and Graph).
159
        """
160
        if not self.export_enable:
161
            return False
162
163
        # Get all the stats & limits
164
        self._last_exported_list = self.plugins_to_export(stats)
165
        all_stats = stats.getAllExportsAsDict(plugin_list=self.last_exported_list())
166
        all_limits = stats.getAllLimitsAsDict(plugin_list=self.last_exported_list())
167
168
        # Loop over plugins to export
169
        for plugin in self.last_exported_list():
170
            if isinstance(all_stats[plugin], dict):
171
                all_stats[plugin].update(all_limits[plugin])
172
            elif isinstance(all_stats[plugin], list):
173
                # TypeError: string indices must be integers (Network plugin) #1054
174
                for i in all_stats[plugin]:
175
                    i.update(all_limits[plugin])
176
            else:
177
                continue
178
            export_names, export_values = self.build_export(all_stats[plugin])
179
            self.export(plugin, export_names, export_values)
180
181
        return True
182
183
    def build_export(self, stats):
184
        """Build the export lists."""
185
        export_names = []
186
        export_values = []
187
188
        if isinstance(stats, dict):
189
            # Stats is a dict
190
            # Is there a key ?
191
            if 'key' in iterkeys(stats) and stats['key'] in iterkeys(stats):
192
                pre_key = '{}.'.format(stats[stats['key']])
193
            else:
194
                pre_key = ''
195
            # Walk through the dict
196
            for key, value in sorted(iteritems(stats)):
197
                if isinstance(value, bool):
198
                    value = json_dumps(value)
199
200
                if isinstance(value, list):
201
                    value = ' '.join([str(v) for v in value])
202
203
                if isinstance(value, dict):
204
                    item_names, item_values = self.build_export(value)
205
                    item_names = [pre_key + key.lower() + str(i) for i in item_names]
206
                    export_names += item_names
207
                    export_values += item_values
208
                else:
209
                    export_names.append(pre_key + key.lower())
210
                    export_values.append(value)
211
        elif isinstance(stats, list):
212
            # Stats is a list (of dict)
213
            # Recursive loop through the list
214
            for item in stats:
215
                item_names, item_values = self.build_export(item)
216
                export_names += item_names
217
                export_values += item_values
218
        return export_names, export_values
219
220
    def export(self, name, columns, points):
221
        # This method should be implemented by each exporter
222
        pass
223