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) |
||
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) |
||
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): |
||
91 | """Load the export <section> configuration in the Glances configuration file. |
||
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): |
||
0 ignored issues
–
show
unused-code
introduced
by
Loading history...
|
|||
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 |