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
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
|
|||
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
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
|
|||
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 |