Test Failed
Push — develop ( 7228a9...9eb483 )
by Nicolas
03:59 queued 03:04
created

glances/exports/glances_export.py (1 issue)

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 2019 Nicolargo <[email protected]>
6
#
7
# Glances is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU Lesser General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Glances is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Lesser General Public License for more details.
16
#
17
# You should have received a copy of the GNU Lesser General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20
"""
21
I am your father...
22
23
...for all Glances exports IF.
24
"""
25
26
import json
27
28
from glances.compat import NoOptionError, NoSectionError, iteritems, iterkeys
29
from glances.logger import logger
30
31
32
class GlancesExport(object):
33
34
    """Main class for Glances export IF."""
35
36
    # For the moment, only thoses plugins can be exported
37
    # @TODO: remove this part and make all plugins exportable (see issue #1556)
38
    # @TODO: also make this list configurable by the user (see issue #1443)
39
    exportable_plugins = ['cpu',
40
                          'percpu',
41
                          'load',
42
                          'mem',
43
                          'memswap',
44
                          'network',
45
                          'diskio',
46
                          'fs',
47
                          'processcount',
48
                          'ip',
49
                          'system',
50
                          'uptime',
51
                          'sensors',
52
                          'docker',
53
                          'gpu']
54
55
    def __init__(self, config=None, args=None):
56
        """Init the export class."""
57
        # Export name (= module name without glances_)
58
        self.export_name = self.__class__.__module__[len('glances_'):]
59
        logger.debug("Init export module %s" % self.export_name)
60
61
        # Init the config & args
62
        self.config = config
63
        self.args = args
64
65
        # By default export is disable
66
        # Had to be set to True in the __init__ class of child
67
        self.export_enable = False
68
69
        # Mandatory for (most of) the export module
70
        self.host = None
71
        self.port = None
72
73
        # Build the export list on startup to avoid change during execution
74
        self.export_list = self._plugins_to_export()
75
76
    def exit(self):
77
        """Close the export module."""
78
        logger.debug("Finalise export interface %s" % self.export_name)
79
80
    def _plugins_to_export(self):
81
        """Return the list of plugins to export."""
82
        ret = self.exportable_plugins
83
        for p in ret:
84
            if getattr(self.args, 'disable_' + p):
85
                ret.remove(p)
86
        return ret
87
88
    def plugins_to_export(self):
89
        return self.export_list
90
91
    def load_conf(self, section, mandatories=['host', 'port'], options=None):
0 ignored issues
show
Bug Best Practice introduced by
The default value [] might cause unintended side-effects.

Objects as default values are only created once in Python and not on each invocation of the function. If the default object is modified, this modification is carried over to the next invocation of the method.

# Bad:
# If array_param is modified inside the function, the next invocation will
# receive the modified object.
def some_function(array_param=[]):
    # ...

# Better: Create an array on each invocation
def some_function(array_param=None):
    array_param = array_param or []
    # ...
Loading history...
92
        """Load the export <section> configuration in the Glances configuration file.
93
94
        :param section: name of the export section to load
95
        :param mandatories: a list of mandatories parameters to load
96
        :param options: a list of optionnals parameters to load
97
98
        :returns: Boolean -- True if section is found
99
        """
100
        options = options or []
101
102
        if self.config is None:
103
            return False
104
105
        # By default read the mandatory host:port items
106
        try:
107
            for opt in mandatories:
108
                setattr(self, opt, self.config.get_value(section, opt))
109
        except NoSectionError:
110
            logger.error("No {} configuration found".format(section))
111
            return False
112
        except NoOptionError as e:
113
            logger.error("Error in the {} configuration ({})".format(section, e))
114
            return False
115
116
        # Load options
117
        for opt in options:
118
            try:
119
                setattr(self, opt, self.config.get_value(section, opt))
120
            except NoOptionError:
121
                pass
122
123
        logger.debug("Load {} from the Glances configuration file".format(section))
124
        logger.debug("{} parameters: {}".format(section, {opt: getattr(self, opt) for opt in mandatories + options}))
125
126
        return True
127
128
    def get_item_key(self, item):
129
        """Return the value of the item 'key'."""
130
        try:
131
            ret = item[item['key']]
132
        except KeyError:
133
            logger.error("No 'key' available in {}".format(item))
134
        if isinstance(ret, list):
135
            return ret[0]
136
        else:
137
            return ret
138
139
    def parse_tags(self, tags):
140
        """Parse tags into a dict.
141
142
        input tags: a comma separated list of 'key:value' pairs.
143
            Example: foo:bar,spam:eggs
144
        output dtags: a dict of tags.
145
            Example: {'foo': 'bar', 'spam': 'eggs'}
146
        """
147
        dtags = {}
148
        if tags:
149
            try:
150
                dtags = dict([x.split(':') for x in tags.split(',')])
151
            except ValueError:
152
                # one of the 'key:value' pairs was missing
153
                logger.info('Invalid tags passed: %s', tags)
154
                dtags = {}
155
156
        return dtags
157
158
    def update(self, stats):
159
        """Update stats to a server.
160
161
        The method builds two lists: names and values
162
        and calls the export method to export the stats.
163
164
        Note: this class can be overwrite (for example in CSV and Graph).
165
        """
166
        if not self.export_enable:
167
            return False
168
169
        # Get all the stats & limits
170
        all_stats = stats.getAllExportsAsDict(plugin_list=self.plugins_to_export())
171
        all_limits = stats.getAllLimitsAsDict(plugin_list=self.plugins_to_export())
172
173
        # Loop over plugins to export
174
        for plugin in self.plugins_to_export():
175
            if isinstance(all_stats[plugin], dict):
176
                all_stats[plugin].update(all_limits[plugin])
177
            elif isinstance(all_stats[plugin], list):
178
                # TypeError: string indices must be integers (Network plugin) #1054
179
                for i in all_stats[plugin]:
180
                    i.update(all_limits[plugin])
181
            else:
182
                continue
183
            export_names, export_values = self.__build_export(all_stats[plugin])
184
            self.export(plugin, export_names, export_values)
185
186
        return True
187
188
    def __build_export(self, stats):
189
        """Build the export lists."""
190
        export_names = []
191
        export_values = []
192
193
        if isinstance(stats, dict):
194
            # Stats is a dict
195
            # Is there a key ?
196
            if 'key' in iterkeys(stats) and stats['key'] in iterkeys(stats):
197
                pre_key = '{}.'.format(stats[stats['key']])
198
            else:
199
                pre_key = ''
200
            # Walk through the dict
201
            for key, value in iteritems(stats):
202
                if isinstance(value, bool):
203
                    value = json.dumps(value)
204
                if isinstance(value, list):
205
                    try:
206
                        value = value[0]
207
                    except IndexError:
208
                        value = ''
209
                if isinstance(value, dict):
210
                    item_names, item_values = self.__build_export(value)
211
                    item_names = [pre_key + key.lower() + str(i) for i in item_names]
212
                    export_names += item_names
213
                    export_values += item_values
214
                else:
215
                    export_names.append(pre_key + key.lower())
216
                    export_values.append(value)
217
        elif isinstance(stats, list):
218
            # Stats is a list (of dict)
219
            # Recursive loop through the list
220
            for item in stats:
221
                item_names, item_values = self.__build_export(item)
222
                export_names += item_names
223
                export_values += item_values
224
        return export_names, export_values
225
226
    def export(self, name, columns, points):
227
        # This method should be implemented by each exporter
228
        pass
229