Total Complexity | 48 |
Total Lines | 382 |
Duplicated Lines | 0 % |
Complex classes like glances.outputs.GlancesBottle often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | # -*- coding: utf-8 -*- |
||
37 | class GlancesBottle(object): |
||
38 | |||
39 | """This class manages the Bottle Web server.""" |
||
40 | |||
41 | def __init__(self, args=None): |
||
42 | # Init args |
||
43 | self.args = args |
||
44 | |||
45 | # Init stats |
||
46 | # Will be updated within Bottle route |
||
47 | self.stats = None |
||
48 | |||
49 | # Init Bottle |
||
50 | self._app = Bottle() |
||
51 | # Enable CORS (issue #479) |
||
52 | self._app.install(EnableCors()) |
||
53 | # Password |
||
54 | if args.password != '': |
||
55 | self._app.install(auth_basic(self.check_auth)) |
||
56 | # Define routes |
||
57 | self._route() |
||
58 | |||
59 | # Path where the statics files are stored |
||
60 | self.STATIC_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'static') |
||
61 | |||
62 | def check_auth(self, username, password): |
||
63 | """Check if a username/password combination is valid.""" |
||
64 | if username == self.args.username: |
||
65 | from glances.password import GlancesPassword |
||
66 | pwd = GlancesPassword() |
||
67 | return pwd.check_password(self.args.password, pwd.sha256_hash(password)) |
||
68 | else: |
||
69 | return False |
||
70 | |||
71 | def _route(self): |
||
72 | """Define route.""" |
||
73 | self._app.route('/', method="GET", callback=self._index) |
||
74 | self._app.route('/<refresh_time:int>', method=["GET"], callback=self._index) |
||
75 | |||
76 | self._app.route('/<filename:re:.*\.css>', method="GET", callback=self._css) |
||
77 | self._app.route('/<filename:re:.*\.js>', method="GET", callback=self._js) |
||
78 | self._app.route('/<filename:re:.*\.js.map>', method="GET", callback=self._js_map) |
||
79 | self._app.route('/<filename:re:.*\.html>', method="GET", callback=self._html) |
||
80 | |||
81 | self._app.route('/<filename:re:.*\.png>', method="GET", callback=self._images) |
||
82 | self._app.route('/favicon.ico', method="GET", callback=self._favicon) |
||
83 | |||
84 | # REST API |
||
85 | self._app.route('/api/2/args', method="GET", callback=self._api_args) |
||
86 | self._app.route('/api/2/args/:item', method="GET", callback=self._api_args_item) |
||
87 | self._app.route('/api/2/help', method="GET", callback=self._api_help) |
||
88 | self._app.route('/api/2/pluginslist', method="GET", callback=self._api_plugins) |
||
89 | self._app.route('/api/2/all', method="GET", callback=self._api_all) |
||
90 | self._app.route('/api/2/all/limits', method="GET", callback=self._api_all_limits) |
||
91 | self._app.route('/api/2/all/views', method="GET", callback=self._api_all_views) |
||
92 | self._app.route('/api/2/:plugin', method="GET", callback=self._api) |
||
93 | self._app.route('/api/2/:plugin/limits', method="GET", callback=self._api_limits) |
||
94 | self._app.route('/api/2/:plugin/views', method="GET", callback=self._api_views) |
||
95 | self._app.route('/api/2/:plugin/:item', method="GET", callback=self._api_item) |
||
96 | self._app.route('/api/2/:plugin/:item/:value', method="GET", callback=self._api_value) |
||
97 | |||
98 | def start(self, stats): |
||
99 | """Start the bottle.""" |
||
100 | # Init stats |
||
101 | self.stats = stats |
||
102 | |||
103 | # Init plugin list |
||
104 | self.plugins_list = self.stats.getAllPlugins() |
||
105 | |||
106 | # Bind the Bottle TCP address/port |
||
107 | bindmsg = 'Glances web server started on http://{0}:{1}/'.format(self.args.bind_address, self.args.port) |
||
108 | logger.info(bindmsg) |
||
109 | print(bindmsg) |
||
110 | self._app.run(host=self.args.bind_address, port=self.args.port, quiet=not self.args.debug) |
||
111 | |||
112 | def end(self): |
||
113 | """End the bottle.""" |
||
114 | pass |
||
115 | |||
116 | def _index(self, refresh_time=None): |
||
117 | """Bottle callback for index.html (/) file.""" |
||
118 | # Update the stat |
||
119 | self.stats.update() |
||
120 | |||
121 | # Display |
||
122 | return static_file("index.html", root=os.path.join(self.STATIC_PATH, 'html')) |
||
123 | |||
124 | def _html(self, filename): |
||
125 | """Bottle callback for *.html files.""" |
||
126 | # Return the static file |
||
127 | return static_file(filename, root=os.path.join(self.STATIC_PATH, 'html')) |
||
128 | |||
129 | def _css(self, filename): |
||
130 | """Bottle callback for *.css files.""" |
||
131 | # Return the static file |
||
132 | return static_file(filename, root=os.path.join(self.STATIC_PATH, 'css')) |
||
133 | |||
134 | def _js(self, filename): |
||
135 | """Bottle callback for *.js files.""" |
||
136 | # Return the static file |
||
137 | return static_file(filename, root=os.path.join(self.STATIC_PATH, 'js')) |
||
138 | |||
139 | def _js_map(self, filename): |
||
140 | """Bottle callback for *.js.map files.""" |
||
141 | # Return the static file |
||
142 | return static_file(filename, root=os.path.join(self.STATIC_PATH, 'js')) |
||
143 | |||
144 | def _images(self, filename): |
||
145 | """Bottle callback for *.png files.""" |
||
146 | # Return the static file |
||
147 | return static_file(filename, root=os.path.join(self.STATIC_PATH, 'images')) |
||
148 | |||
149 | def _favicon(self): |
||
150 | """Bottle callback for favicon.""" |
||
151 | # Return the static file |
||
152 | return static_file('favicon.ico', root=self.STATIC_PATH) |
||
153 | |||
154 | def _api_help(self): |
||
155 | """Glances API RESTFul implementation. |
||
156 | |||
157 | Return the help data or 404 error. |
||
158 | """ |
||
159 | response.content_type = 'application/json' |
||
160 | |||
161 | # Update the stat |
||
162 | view_data = self.stats.get_plugin("help").get_view_data() |
||
163 | try: |
||
164 | plist = json.dumps(view_data, sort_keys=True) |
||
165 | except Exception as e: |
||
166 | abort(404, "Cannot get help view data (%s)" % str(e)) |
||
167 | return plist |
||
168 | |||
169 | def _api_plugins(self): |
||
170 | """ |
||
171 | @api {get} /api/2/pluginslist Get plugins list |
||
172 | @apiVersion 2.0 |
||
173 | @apiName pluginslist |
||
174 | @apiGroup plugin |
||
175 | |||
176 | @apiSuccess {String[]} Plugins list. |
||
177 | |||
178 | @apiSuccessExample Success-Response: |
||
179 | HTTP/1.1 200 OK |
||
180 | [ |
||
181 | "load", |
||
182 | "help", |
||
183 | "ip", |
||
184 | "memswap", |
||
185 | "processlist", |
||
186 | ... |
||
187 | ] |
||
188 | |||
189 | @apiError Cannot get plugin list. |
||
190 | |||
191 | @apiErrorExample Error-Response: |
||
192 | HTTP/1.1 404 Not Found |
||
193 | """ |
||
194 | response.content_type = 'application/json' |
||
195 | |||
196 | # Update the stat |
||
197 | self.stats.update() |
||
198 | |||
199 | try: |
||
200 | plist = json.dumps(self.plugins_list) |
||
201 | except Exception as e: |
||
202 | abort(404, "Cannot get plugin list (%s)" % str(e)) |
||
203 | return plist |
||
204 | |||
205 | def _api_all(self): |
||
206 | """Glances API RESTFul implementation. |
||
207 | |||
208 | Return the JSON representation of all the plugins |
||
209 | HTTP/200 if OK |
||
210 | HTTP/400 if plugin is not found |
||
211 | HTTP/404 if others error |
||
212 | """ |
||
213 | response.content_type = 'application/json' |
||
214 | |||
215 | if self.args.debug: |
||
216 | fname = os.path.join(tempfile.gettempdir(), 'glances-debug.json') |
||
217 | try: |
||
218 | with open(fname) as f: |
||
219 | return f.read() |
||
220 | except IOError: |
||
221 | logger.debug("Debug file (%s) not found" % fname) |
||
222 | |||
223 | # Update the stat |
||
224 | self.stats.update() |
||
225 | |||
226 | try: |
||
227 | # Get the JSON value of the stat ID |
||
228 | statval = json.dumps(self.stats.getAllAsDict()) |
||
229 | except Exception as e: |
||
230 | abort(404, "Cannot get stats (%s)" % str(e)) |
||
231 | return statval |
||
232 | |||
233 | def _api_all_limits(self): |
||
234 | """Glances API RESTFul implementation. |
||
235 | |||
236 | Return the JSON representation of all the plugins limits |
||
237 | HTTP/200 if OK |
||
238 | HTTP/400 if plugin is not found |
||
239 | HTTP/404 if others error |
||
240 | """ |
||
241 | response.content_type = 'application/json' |
||
242 | |||
243 | try: |
||
244 | # Get the JSON value of the stat limits |
||
245 | limits = json.dumps(self.stats.getAllLimitsAsDict()) |
||
246 | except Exception as e: |
||
247 | abort(404, "Cannot get limits (%s)" % (str(e))) |
||
248 | return limits |
||
249 | |||
250 | def _api_all_views(self): |
||
251 | """Glances API RESTFul implementation. |
||
252 | |||
253 | Return the JSON representation of all the plugins views |
||
254 | HTTP/200 if OK |
||
255 | HTTP/400 if plugin is not found |
||
256 | HTTP/404 if others error |
||
257 | """ |
||
258 | response.content_type = 'application/json' |
||
259 | |||
260 | try: |
||
261 | # Get the JSON value of the stat view |
||
262 | limits = json.dumps(self.stats.getAllViewsAsDict()) |
||
263 | except Exception as e: |
||
264 | abort(404, "Cannot get views (%s)" % (str(e))) |
||
265 | return limits |
||
266 | |||
267 | def _api(self, plugin): |
||
268 | """Glances API RESTFul implementation. |
||
269 | |||
270 | Return the JSON representation of a given plugin |
||
271 | HTTP/200 if OK |
||
272 | HTTP/400 if plugin is not found |
||
273 | HTTP/404 if others error |
||
274 | """ |
||
275 | response.content_type = 'application/json' |
||
276 | |||
277 | if plugin not in self.plugins_list: |
||
278 | abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list)) |
||
279 | |||
280 | # Update the stat |
||
281 | self.stats.update() |
||
282 | |||
283 | try: |
||
284 | # Get the JSON value of the stat ID |
||
285 | statval = self.stats.get_plugin(plugin).get_stats() |
||
286 | except Exception as e: |
||
287 | abort(404, "Cannot get plugin %s (%s)" % (plugin, str(e))) |
||
288 | return statval |
||
289 | |||
290 | def _api_limits(self, plugin): |
||
291 | """Glances API RESTFul implementation. |
||
292 | |||
293 | Return the JSON limits of a given plugin |
||
294 | HTTP/200 if OK |
||
295 | HTTP/400 if plugin is not found |
||
296 | HTTP/404 if others error |
||
297 | """ |
||
298 | response.content_type = 'application/json' |
||
299 | |||
300 | if plugin not in self.plugins_list: |
||
301 | abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list)) |
||
302 | |||
303 | # Update the stat |
||
304 | # self.stats.update() |
||
305 | |||
306 | try: |
||
307 | # Get the JSON value of the stat limits |
||
308 | ret = self.stats.get_plugin(plugin).limits |
||
309 | except Exception as e: |
||
310 | abort(404, "Cannot get limits for plugin %s (%s)" % (plugin, str(e))) |
||
311 | return ret |
||
312 | |||
313 | def _api_views(self, plugin): |
||
314 | """Glances API RESTFul implementation. |
||
315 | |||
316 | Return the JSON views of a given plugin |
||
317 | HTTP/200 if OK |
||
318 | HTTP/400 if plugin is not found |
||
319 | HTTP/404 if others error |
||
320 | """ |
||
321 | response.content_type = 'application/json' |
||
322 | |||
323 | if plugin not in self.plugins_list: |
||
324 | abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list)) |
||
325 | |||
326 | # Update the stat |
||
327 | # self.stats.update() |
||
328 | |||
329 | try: |
||
330 | # Get the JSON value of the stat views |
||
331 | ret = self.stats.get_plugin(plugin).get_views() |
||
332 | except Exception as e: |
||
333 | abort(404, "Cannot get views for plugin %s (%s)" % (plugin, str(e))) |
||
334 | return ret |
||
335 | |||
336 | def _api_itemvalue(self, plugin, item, value=None): |
||
337 | """ Father method for _api_item and _api_value""" |
||
338 | response.content_type = 'application/json' |
||
339 | |||
340 | if plugin not in self.plugins_list: |
||
341 | abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list)) |
||
342 | |||
343 | # Update the stat |
||
344 | self.stats.update() |
||
345 | |||
346 | if value is None: |
||
347 | ret = self.stats.get_plugin(plugin).get_stats_item(item) |
||
348 | |||
349 | if ret is None: |
||
350 | abort(404, "Cannot get item %s in plugin %s" % (item, plugin)) |
||
351 | else: |
||
352 | ret = self.stats.get_plugin(plugin).get_stats_value(item, value) |
||
353 | |||
354 | if ret is None: |
||
355 | abort(404, "Cannot get item(%s)=value(%s) in plugin %s" % (item, value, plugin)) |
||
356 | |||
357 | return ret |
||
358 | |||
359 | def _api_item(self, plugin, item): |
||
360 | """Glances API RESTFul implementation. |
||
361 | |||
362 | Return the JSON represenation of the couple plugin/item |
||
363 | HTTP/200 if OK |
||
364 | HTTP/400 if plugin is not found |
||
365 | HTTP/404 if others error |
||
366 | |||
367 | """ |
||
368 | return self._api_itemvalue(plugin, item) |
||
369 | |||
370 | def _api_value(self, plugin, item, value): |
||
371 | """Glances API RESTFul implementation. |
||
372 | |||
373 | Return the process stats (dict) for the given item=value |
||
374 | HTTP/200 if OK |
||
375 | HTTP/400 if plugin is not found |
||
376 | HTTP/404 if others error |
||
377 | """ |
||
378 | return self._api_itemvalue(plugin, item, value) |
||
379 | |||
380 | def _api_args(self): |
||
381 | """Glances API RESTFul implementation. |
||
382 | |||
383 | Return the JSON representation of the Glances command line arguments |
||
384 | HTTP/200 if OK |
||
385 | HTTP/404 if others error |
||
386 | """ |
||
387 | response.content_type = 'application/json' |
||
388 | |||
389 | try: |
||
390 | # Get the JSON value of the args' dict |
||
391 | # Use vars to convert namespace to dict |
||
392 | # Source: https://docs.python.org/2/library/functions.html#vars |
||
393 | args_json = json.dumps(vars(self.args)) |
||
394 | except Exception as e: |
||
395 | abort(404, "Cannot get args (%s)" % str(e)) |
||
396 | return args_json |
||
397 | |||
398 | def _api_args_item(self, item): |
||
399 | """Glances API RESTFul implementation. |
||
400 | |||
401 | Return the JSON representation of the Glances command line arguments item |
||
402 | HTTP/200 if OK |
||
403 | HTTP/400 if item is not found |
||
404 | HTTP/404 if others error |
||
405 | """ |
||
406 | response.content_type = 'application/json' |
||
407 | |||
408 | if item not in self.args: |
||
409 | abort(400, "Unknown item %s" % item) |
||
410 | |||
411 | try: |
||
412 | # Get the JSON value of the args' dict |
||
413 | # Use vars to convert namespace to dict |
||
414 | # Source: https://docs.python.org/2/library/functions.html#vars |
||
415 | args_json = json.dumps(vars(self.args)[item]) |
||
416 | except Exception as e: |
||
417 | abort(404, "Cannot get args item (%s)" % str(e)) |
||
418 | return args_json |
||
419 | |||
437 |