Test Failed
Push — develop ( 66c9ff...e21229 )
by Nicolas
05:06
created

glances/exports/glances_export.py (4 issues)

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
38
    exportable_plugins = ['cpu',
39
                          'percpu',
40
                          'load',
41
                          'mem',
42
                          'memswap',
43
                          'network',
44
                          'diskio',
45
                          'fs',
46
                          'processcount',
47
                          'ip',
48
                          'system',
49
                          'uptime',
50
                          'sensors',
51
                          'docker',
52
                          'gpu']
53
54
    def __init__(self, config=None, args=None):
55
        """Init the export class."""
56
        # Export name (= module name without glances_)
57
        self.export_name = self.__class__.__module__[len('glances_'):]
58
        logger.debug("Init export module %s" % self.export_name)
0 ignored issues
show
Coding Style Best Practice introduced by
Specify string format arguments as logging function parameters
Loading history...
59
60
        # Init the config & args
61
        self.config = config
62
        self.args = args
63
64
        # By default export is disable
65
        # Had to be set to True in the __init__ class of child
66
        self.export_enable = False
67
68
        # Mandatory for (most of) the export module
69
        self.host = None
70
        self.port = None
71
72
        # Build the export list on startup to avoid change during execution
73
        self.export_list = self._plugins_to_export()
74
75
    def exit(self):
76
        """Close the export module."""
77
        logger.debug("Finalise export interface %s" % self.export_name)
0 ignored issues
show
Coding Style Best Practice introduced by
Specify string format arguments as logging function parameters
Loading history...
78
79
    def _plugins_to_export(self):
80
        """Return the list of plugins to export."""
81
        ret = self.exportable_plugins
82
        for p in ret:
83
            if getattr(self.args, 'disable_' + p):
84
                ret.remove(p)
85
        return ret
86
87
    def plugins_to_export(self):
88
        return self.export_list
89
90
    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...
91
        """Load the export <section> configuration in the Glances configuration file.
0 ignored issues
show
This line is too long as per the coding-style (85/80).

This check looks for lines that are too long. You can specify the maximum line length.

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