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 | """Web interface class.""" |
||
21 | |||
22 | import json |
||
23 | import os |
||
24 | import sys |
||
25 | import tempfile |
||
0 ignored issues
–
show
introduced
by
Loading history...
|
|||
26 | from io import open |
||
27 | import webbrowser |
||
28 | import zlib |
||
29 | |||
30 | from glances.compat import b |
||
31 | from glances.timer import Timer |
||
32 | from glances.logger import logger |
||
33 | |||
34 | try: |
||
35 | from bottle import Bottle, static_file, abort, response, request, auth_basic, template, TEMPLATE_PATH |
||
36 | except ImportError: |
||
37 | logger.critical('Bottle module not found. Glances cannot start in web server mode.') |
||
38 | sys.exit(2) |
||
39 | |||
40 | |||
41 | def compress(func): |
||
42 | """Compress result with deflate algorithm if the client ask for it.""" |
||
43 | def wrapper(*args, **kwargs): |
||
44 | """Wrapper that take one function and return the compressed result.""" |
||
45 | ret = func(*args, **kwargs) |
||
46 | logger.debug('Receive {} {} request with header: {}'.format( |
||
47 | request.method, |
||
48 | request.url, |
||
49 | ['{}: {}'.format(h, request.headers.get(h)) for h in request.headers.keys()] |
||
50 | )) |
||
51 | if 'deflate' in request.headers.get('Accept-Encoding', ''): |
||
52 | response.headers['Content-Encoding'] = 'deflate' |
||
53 | ret = deflate_compress(ret) |
||
54 | else: |
||
55 | response.headers['Content-Encoding'] = 'identity' |
||
56 | return ret |
||
57 | |||
58 | def deflate_compress(data, compress_level=6): |
||
59 | """Compress given data using the DEFLATE algorithm""" |
||
60 | # Init compression |
||
61 | zobj = zlib.compressobj(compress_level, |
||
62 | zlib.DEFLATED, |
||
63 | zlib.MAX_WBITS, |
||
64 | zlib.DEF_MEM_LEVEL, |
||
65 | zlib.Z_DEFAULT_STRATEGY) |
||
66 | |||
67 | # Return compressed object |
||
68 | return zobj.compress(b(data)) + zobj.flush() |
||
69 | |||
70 | return wrapper |
||
71 | |||
72 | |||
73 | class GlancesBottle(object): |
||
74 | """This class manages the Bottle Web server.""" |
||
75 | |||
76 | API_VERSION = '3' |
||
77 | |||
78 | def __init__(self, config=None, args=None): |
||
79 | # Init config |
||
80 | self.config = config |
||
81 | |||
82 | # Init args |
||
83 | self.args = args |
||
84 | |||
85 | # Init stats |
||
86 | # Will be updated within Bottle route |
||
87 | self.stats = None |
||
88 | |||
89 | # cached_time is the minimum time interval between stats updates |
||
90 | # i.e. HTTP/RESTful calls will not retrieve updated info until the time |
||
91 | # since last update is passed (will retrieve old cached info instead) |
||
92 | self.timer = Timer(0) |
||
93 | |||
94 | # Load configuration file |
||
95 | self.load_config(config) |
||
96 | |||
97 | # Set the bind URL |
||
98 | self.bind_url = 'http://{}:{}/'.format(self.args.bind_address, |
||
99 | self.args.port) |
||
100 | |||
101 | # Init Bottle |
||
102 | self._app = Bottle() |
||
103 | # Enable CORS (issue #479) |
||
104 | self._app.install(EnableCors()) |
||
105 | # Password |
||
106 | if args.password != '': |
||
107 | self._app.install(auth_basic(self.check_auth)) |
||
108 | # Define routes |
||
109 | self._route() |
||
110 | |||
111 | # Path where the statics files are stored |
||
112 | self.STATIC_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'static/public') |
||
113 | |||
114 | # Paths for templates |
||
115 | TEMPLATE_PATH.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), 'static/templates')) |
||
116 | |||
117 | def load_config(self, config): |
||
118 | """Load the outputs section of the configuration file.""" |
||
119 | # Limit the number of processes to display in the WebUI |
||
120 | if config is not None and config.has_section('outputs'): |
||
121 | logger.debug('Read number of processes to display in the WebUI') |
||
122 | n = config.get_value('outputs', 'max_processes_display', default=None) |
||
123 | logger.debug('Number of processes to display in the WebUI: {}'.format(n)) |
||
124 | |||
125 | def __update__(self): |
||
126 | # Never update more than 1 time per cached_time |
||
127 | if self.timer.finished(): |
||
128 | self.stats.update() |
||
129 | self.timer = Timer(self.args.cached_time) |
||
130 | |||
131 | def app(self): |
||
132 | return self._app() |
||
133 | |||
134 | def check_auth(self, username, password): |
||
135 | """Check if a username/password combination is valid.""" |
||
136 | if username == self.args.username: |
||
137 | from glances.password import GlancesPassword |
||
138 | pwd = GlancesPassword() |
||
139 | return pwd.check_password(self.args.password, pwd.sha256_hash(password)) |
||
140 | else: |
||
141 | return False |
||
142 | |||
143 | def _route(self): |
||
144 | """Define route.""" |
||
145 | # REST API |
||
146 | self._app.route('/api/%s/config' % self.API_VERSION, method="GET", |
||
147 | callback=self._api_config) |
||
148 | self._app.route('/api/%s/config/<item>' % self.API_VERSION, method="GET", |
||
149 | callback=self._api_config_item) |
||
150 | self._app.route('/api/%s/args' % self.API_VERSION, method="GET", |
||
151 | callback=self._api_args) |
||
152 | self._app.route('/api/%s/args/<item>' % self.API_VERSION, method="GET", |
||
153 | callback=self._api_args_item) |
||
154 | self._app.route('/api/%s/help' % self.API_VERSION, method="GET", |
||
155 | callback=self._api_help) |
||
156 | self._app.route('/api/%s/pluginslist' % self.API_VERSION, method="GET", |
||
157 | callback=self._api_plugins) |
||
158 | self._app.route('/api/%s/all' % self.API_VERSION, method="GET", |
||
159 | callback=self._api_all) |
||
160 | self._app.route('/api/%s/all/limits' % self.API_VERSION, method="GET", |
||
161 | callback=self._api_all_limits) |
||
162 | self._app.route('/api/%s/all/views' % self.API_VERSION, method="GET", |
||
163 | callback=self._api_all_views) |
||
164 | self._app.route('/api/%s/<plugin>' % self.API_VERSION, method="GET", |
||
165 | callback=self._api) |
||
166 | self._app.route('/api/%s/<plugin>/history' % self.API_VERSION, method="GET", |
||
167 | callback=self._api_history) |
||
168 | self._app.route('/api/%s/<plugin>/history/<nb:int>' % self.API_VERSION, method="GET", |
||
169 | callback=self._api_history) |
||
170 | self._app.route('/api/%s/<plugin>/limits' % self.API_VERSION, method="GET", |
||
171 | callback=self._api_limits) |
||
172 | self._app.route('/api/%s/<plugin>/views' % self.API_VERSION, method="GET", |
||
173 | callback=self._api_views) |
||
174 | self._app.route('/api/%s/<plugin>/<item>' % self.API_VERSION, method="GET", |
||
175 | callback=self._api_item) |
||
176 | self._app.route('/api/%s/<plugin>/<item>/history' % self.API_VERSION, method="GET", |
||
177 | callback=self._api_item_history) |
||
178 | self._app.route('/api/%s/<plugin>/<item>/history/<nb:int>' % self.API_VERSION, method="GET", |
||
179 | callback=self._api_item_history) |
||
180 | self._app.route('/api/%s/<plugin>/<item>/<value>' % self.API_VERSION, method="GET", |
||
181 | callback=self._api_value) |
||
182 | bindmsg = 'Glances RESTful API Server started on {}api/{}/'.format(self.bind_url, |
||
183 | self.API_VERSION) |
||
184 | logger.info(bindmsg) |
||
185 | |||
186 | # WEB UI |
||
187 | if not self.args.disable_webui: |
||
188 | self._app.route('/', method="GET", callback=self._index) |
||
189 | self._app.route('/<refresh_time:int>', method=["GET"], callback=self._index) |
||
190 | self._app.route('/<filepath:path>', method="GET", callback=self._resource) |
||
191 | bindmsg = 'Glances Web User Interface started on {}'.format(self.bind_url) |
||
192 | logger.info(bindmsg) |
||
193 | else: |
||
194 | logger.info('The WebUI is disable (--disable-webui)') |
||
195 | |||
196 | print(bindmsg) |
||
197 | |||
198 | def start(self, stats): |
||
199 | """Start the bottle.""" |
||
200 | # Init stats |
||
201 | self.stats = stats |
||
202 | |||
203 | # Init plugin list |
||
204 | self.plugins_list = self.stats.getPluginsList() |
||
205 | |||
206 | # Bind the Bottle TCP address/port |
||
207 | if self.args.open_web_browser: |
||
208 | # Implementation of the issue #946 |
||
209 | # Try to open the Glances Web UI in the default Web browser if: |
||
210 | # 1) --open-web-browser option is used |
||
211 | # 2) Glances standalone mode is running on Windows OS |
||
212 | webbrowser.open(self.bind_url, |
||
213 | new=2, |
||
214 | autoraise=1) |
||
215 | |||
216 | self._app.run(host=self.args.bind_address, |
||
217 | port=self.args.port, |
||
218 | quiet=not self.args.debug) |
||
219 | |||
220 | def end(self): |
||
221 | """End the bottle.""" |
||
222 | pass |
||
223 | |||
224 | def _index(self, refresh_time=None): |
||
225 | """Bottle callback for index.html (/) file.""" |
||
226 | |||
227 | if refresh_time is None or refresh_time < 1: |
||
228 | refresh_time = self.args.time |
||
229 | |||
230 | # Update the stat |
||
231 | self.__update__() |
||
232 | |||
233 | # Display |
||
234 | return template("index.html", refresh_time=refresh_time) |
||
235 | |||
236 | def _resource(self, filepath): |
||
237 | """Bottle callback for resources files.""" |
||
238 | # Return the static file |
||
239 | return static_file(filepath, root=self.STATIC_PATH) |
||
240 | |||
241 | @compress |
||
242 | def _api_help(self): |
||
243 | """Glances API RESTful implementation. |
||
244 | |||
245 | Return the help data or 404 error. |
||
246 | """ |
||
247 | response.content_type = 'application/json; charset=utf-8' |
||
248 | |||
249 | # Update the stat |
||
250 | view_data = self.stats.get_plugin("help").get_view_data() |
||
251 | try: |
||
252 | plist = json.dumps(view_data, sort_keys=True) |
||
253 | except Exception as e: |
||
0 ignored issues
–
show
Catching very general exceptions such as
Exception is usually not recommended.
Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed. So, unless you specifically plan to handle any error, consider adding a more specific exception.
Loading history...
|
|||
254 | abort(404, "Cannot get help view data (%s)" % str(e)) |
||
255 | return plist |
||
256 | |||
257 | @compress |
||
258 | def _api_plugins(self): |
||
259 | """Glances API RESTFul implementation. |
||
260 | |||
261 | @api {get} /api/%s/pluginslist Get plugins list |
||
262 | @apiVersion 2.0 |
||
263 | @apiName pluginslist |
||
264 | @apiGroup plugin |
||
265 | |||
266 | @apiSuccess {String[]} Plugins list. |
||
267 | |||
268 | @apiSuccessExample Success-Response: |
||
269 | HTTP/1.1 200 OK |
||
270 | [ |
||
271 | "load", |
||
272 | "help", |
||
273 | "ip", |
||
274 | "memswap", |
||
275 | "processlist", |
||
276 | ... |
||
277 | ] |
||
278 | |||
279 | @apiError Cannot get plugin list. |
||
280 | |||
281 | @apiErrorExample Error-Response: |
||
282 | HTTP/1.1 404 Not Found |
||
283 | """ |
||
284 | response.content_type = 'application/json; charset=utf-8' |
||
285 | |||
286 | # Update the stat |
||
287 | self.__update__() |
||
288 | |||
289 | try: |
||
290 | plist = json.dumps(self.plugins_list) |
||
291 | except Exception as e: |
||
292 | abort(404, "Cannot get plugin list (%s)" % str(e)) |
||
293 | return plist |
||
294 | |||
295 | @compress |
||
296 | def _api_all(self): |
||
297 | """Glances API RESTful implementation. |
||
298 | |||
299 | Return the JSON representation of all the plugins |
||
300 | HTTP/200 if OK |
||
301 | HTTP/400 if plugin is not found |
||
302 | HTTP/404 if others error |
||
303 | """ |
||
304 | response.content_type = 'application/json; charset=utf-8' |
||
305 | |||
306 | if self.args.debug: |
||
307 | fname = os.path.join(tempfile.gettempdir(), 'glances-debug.json') |
||
308 | try: |
||
309 | with open(fname) as f: |
||
310 | return f.read() |
||
311 | except IOError: |
||
312 | logger.debug("Debug file (%s) not found" % fname) |
||
313 | |||
314 | # Update the stat |
||
315 | self.__update__() |
||
316 | |||
317 | try: |
||
318 | # Get the JSON value of the stat ID |
||
319 | statval = json.dumps(self.stats.getAllAsDict()) |
||
320 | except Exception as e: |
||
0 ignored issues
–
show
Catching very general exceptions such as
Exception is usually not recommended.
Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed. So, unless you specifically plan to handle any error, consider adding a more specific exception.
Loading history...
|
|||
321 | abort(404, "Cannot get stats (%s)" % str(e)) |
||
322 | |||
323 | return statval |
||
324 | |||
325 | @compress |
||
326 | def _api_all_limits(self): |
||
327 | """Glances API RESTful implementation. |
||
328 | |||
329 | Return the JSON representation of all the plugins limits |
||
330 | HTTP/200 if OK |
||
331 | HTTP/400 if plugin is not found |
||
332 | HTTP/404 if others error |
||
333 | """ |
||
334 | response.content_type = 'application/json; charset=utf-8' |
||
335 | |||
336 | try: |
||
337 | # Get the JSON value of the stat limits |
||
338 | limits = json.dumps(self.stats.getAllLimitsAsDict()) |
||
339 | except Exception as e: |
||
340 | abort(404, "Cannot get limits (%s)" % (str(e))) |
||
341 | return limits |
||
342 | |||
343 | @compress |
||
344 | def _api_all_views(self): |
||
345 | """Glances API RESTful implementation. |
||
346 | |||
347 | Return the JSON representation of all the plugins views |
||
348 | HTTP/200 if OK |
||
349 | HTTP/400 if plugin is not found |
||
350 | HTTP/404 if others error |
||
351 | """ |
||
352 | response.content_type = 'application/json; charset=utf-8' |
||
353 | |||
354 | try: |
||
355 | # Get the JSON value of the stat view |
||
356 | limits = json.dumps(self.stats.getAllViewsAsDict()) |
||
357 | except Exception as e: |
||
358 | abort(404, "Cannot get views (%s)" % (str(e))) |
||
359 | return limits |
||
360 | |||
361 | @compress |
||
362 | def _api(self, plugin): |
||
363 | """Glances API RESTful implementation. |
||
364 | |||
365 | Return the JSON representation of a given plugin |
||
366 | HTTP/200 if OK |
||
367 | HTTP/400 if plugin is not found |
||
368 | HTTP/404 if others error |
||
369 | """ |
||
370 | response.content_type = 'application/json; charset=utf-8' |
||
371 | |||
372 | if plugin not in self.plugins_list: |
||
373 | abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list)) |
||
374 | |||
375 | # Update the stat |
||
376 | self.__update__() |
||
377 | |||
378 | try: |
||
379 | # Get the JSON value of the stat ID |
||
380 | statval = self.stats.get_plugin(plugin).get_stats() |
||
381 | except Exception as e: |
||
382 | abort(404, "Cannot get plugin %s (%s)" % (plugin, str(e))) |
||
383 | return statval |
||
384 | |||
385 | @compress |
||
386 | def _api_history(self, plugin, nb=0): |
||
387 | """Glances API RESTful implementation. |
||
388 | |||
389 | Return the JSON representation of a given plugin history |
||
390 | Limit to the last nb items (all if nb=0) |
||
391 | HTTP/200 if OK |
||
392 | HTTP/400 if plugin is not found |
||
393 | HTTP/404 if others error |
||
394 | """ |
||
395 | response.content_type = 'application/json; charset=utf-8' |
||
396 | |||
397 | if plugin not in self.plugins_list: |
||
398 | abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list)) |
||
399 | |||
400 | # Update the stat |
||
401 | self.__update__() |
||
402 | |||
403 | try: |
||
404 | # Get the JSON value of the stat ID |
||
405 | statval = self.stats.get_plugin(plugin).get_stats_history(nb=int(nb)) |
||
406 | except Exception as e: |
||
407 | abort(404, "Cannot get plugin history %s (%s)" % (plugin, str(e))) |
||
408 | return statval |
||
409 | |||
410 | @compress |
||
411 | def _api_limits(self, plugin): |
||
412 | """Glances API RESTful implementation. |
||
413 | |||
414 | Return the JSON limits of a given plugin |
||
415 | HTTP/200 if OK |
||
416 | HTTP/400 if plugin is not found |
||
417 | HTTP/404 if others error |
||
418 | """ |
||
419 | response.content_type = 'application/json; charset=utf-8' |
||
420 | |||
421 | if plugin not in self.plugins_list: |
||
422 | abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list)) |
||
423 | |||
424 | # Update the stat |
||
425 | # self.__update__() |
||
426 | |||
427 | try: |
||
428 | # Get the JSON value of the stat limits |
||
429 | ret = self.stats.get_plugin(plugin).limits |
||
430 | except Exception as e: |
||
431 | abort(404, "Cannot get limits for plugin %s (%s)" % (plugin, str(e))) |
||
432 | return ret |
||
433 | |||
434 | @compress |
||
435 | def _api_views(self, plugin): |
||
436 | """Glances API RESTful implementation. |
||
437 | |||
438 | Return the JSON views of a given plugin |
||
439 | HTTP/200 if OK |
||
440 | HTTP/400 if plugin is not found |
||
441 | HTTP/404 if others error |
||
442 | """ |
||
443 | response.content_type = 'application/json; charset=utf-8' |
||
444 | |||
445 | if plugin not in self.plugins_list: |
||
446 | abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list)) |
||
447 | |||
448 | # Update the stat |
||
449 | # self.__update__() |
||
450 | |||
451 | try: |
||
452 | # Get the JSON value of the stat views |
||
453 | ret = self.stats.get_plugin(plugin).get_views() |
||
454 | except Exception as e: |
||
455 | abort(404, "Cannot get views for plugin %s (%s)" % (plugin, str(e))) |
||
456 | return ret |
||
457 | |||
458 | @compress |
||
459 | def _api_itemvalue(self, plugin, item, value=None, history=False, nb=0): |
||
460 | """Father method for _api_item and _api_value.""" |
||
461 | response.content_type = 'application/json; charset=utf-8' |
||
462 | |||
463 | if plugin not in self.plugins_list: |
||
464 | abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list)) |
||
465 | |||
466 | # Update the stat |
||
467 | self.__update__() |
||
468 | |||
469 | if value is None: |
||
470 | if history: |
||
471 | ret = self.stats.get_plugin(plugin).get_stats_history(item, nb=int(nb)) |
||
472 | else: |
||
473 | ret = self.stats.get_plugin(plugin).get_stats_item(item) |
||
474 | |||
475 | if ret is None: |
||
476 | abort(404, "Cannot get item %s%s in plugin %s" % (item, 'history ' if history else '', plugin)) |
||
477 | else: |
||
478 | if history: |
||
479 | # Not available |
||
480 | ret = None |
||
481 | else: |
||
482 | ret = self.stats.get_plugin(plugin).get_stats_value(item, value) |
||
483 | |||
484 | if ret is None: |
||
485 | abort(404, "Cannot get item %s(%s=%s) in plugin %s" % ('history ' if history else '', item, value, plugin)) |
||
486 | |||
487 | return ret |
||
488 | |||
489 | @compress |
||
490 | def _api_item(self, plugin, item): |
||
491 | """Glances API RESTful implementation. |
||
492 | |||
493 | Return the JSON representation of the couple plugin/item |
||
494 | HTTP/200 if OK |
||
495 | HTTP/400 if plugin is not found |
||
496 | HTTP/404 if others error |
||
497 | |||
498 | """ |
||
499 | return self._api_itemvalue(plugin, item) |
||
500 | |||
501 | @compress |
||
502 | def _api_item_history(self, plugin, item, nb=0): |
||
503 | """Glances API RESTful implementation. |
||
504 | |||
505 | Return the JSON representation of the couple plugin/history of item |
||
506 | HTTP/200 if OK |
||
507 | HTTP/400 if plugin is not found |
||
508 | HTTP/404 if others error |
||
509 | |||
510 | """ |
||
511 | return self._api_itemvalue(plugin, item, history=True, nb=int(nb)) |
||
512 | |||
513 | @compress |
||
514 | def _api_value(self, plugin, item, value): |
||
515 | """Glances API RESTful implementation. |
||
516 | |||
517 | Return the process stats (dict) for the given item=value |
||
518 | HTTP/200 if OK |
||
519 | HTTP/400 if plugin is not found |
||
520 | HTTP/404 if others error |
||
521 | """ |
||
522 | return self._api_itemvalue(plugin, item, value) |
||
523 | |||
524 | @compress |
||
525 | def _api_config(self): |
||
526 | """Glances API RESTful implementation. |
||
527 | |||
528 | Return the JSON representation of the Glances configuration file |
||
529 | HTTP/200 if OK |
||
530 | HTTP/404 if others error |
||
531 | """ |
||
532 | response.content_type = 'application/json; charset=utf-8' |
||
533 | |||
534 | try: |
||
535 | # Get the JSON value of the config' dict |
||
536 | args_json = json.dumps(self.config.as_dict()) |
||
537 | except Exception as e: |
||
538 | abort(404, "Cannot get config (%s)" % str(e)) |
||
539 | return args_json |
||
540 | |||
541 | @compress |
||
542 | def _api_config_item(self, item): |
||
543 | """Glances API RESTful implementation. |
||
544 | |||
545 | Return the JSON representation of the Glances configuration item |
||
546 | HTTP/200 if OK |
||
547 | HTTP/400 if item is not found |
||
548 | HTTP/404 if others error |
||
549 | """ |
||
550 | response.content_type = 'application/json; charset=utf-8' |
||
551 | |||
552 | config_dict = self.config.as_dict() |
||
553 | if item not in config_dict: |
||
554 | abort(400, "Unknown configuration item %s" % item) |
||
555 | |||
556 | try: |
||
557 | # Get the JSON value of the config' dict |
||
558 | args_json = json.dumps(config_dict[item]) |
||
559 | except Exception as e: |
||
560 | abort(404, "Cannot get config item (%s)" % str(e)) |
||
561 | return args_json |
||
562 | |||
563 | @compress |
||
564 | def _api_args(self): |
||
565 | """Glances API RESTful implementation. |
||
566 | |||
567 | Return the JSON representation of the Glances command line arguments |
||
568 | HTTP/200 if OK |
||
569 | HTTP/404 if others error |
||
570 | """ |
||
571 | response.content_type = 'application/json; charset=utf-8' |
||
572 | |||
573 | try: |
||
574 | # Get the JSON value of the args' dict |
||
575 | # Use vars to convert namespace to dict |
||
576 | # Source: https://docs.python.org/%s/library/functions.html#vars |
||
577 | args_json = json.dumps(vars(self.args)) |
||
578 | except Exception as e: |
||
579 | abort(404, "Cannot get args (%s)" % str(e)) |
||
580 | return args_json |
||
581 | |||
582 | @compress |
||
583 | def _api_args_item(self, item): |
||
584 | """Glances API RESTful implementation. |
||
585 | |||
586 | Return the JSON representation of the Glances command line arguments item |
||
587 | HTTP/200 if OK |
||
588 | HTTP/400 if item is not found |
||
589 | HTTP/404 if others error |
||
590 | """ |
||
591 | response.content_type = 'application/json; charset=utf-8' |
||
592 | |||
593 | if item not in self.args: |
||
594 | abort(400, "Unknown argument item %s" % item) |
||
595 | |||
596 | try: |
||
597 | # Get the JSON value of the args' dict |
||
598 | # Use vars to convert namespace to dict |
||
599 | # Source: https://docs.python.org/%s/library/functions.html#vars |
||
600 | args_json = json.dumps(vars(self.args)[item]) |
||
601 | except Exception as e: |
||
602 | abort(404, "Cannot get args item (%s)" % str(e)) |
||
603 | return args_json |
||
604 | |||
605 | |||
606 | class EnableCors(object): |
||
607 | name = 'enable_cors' |
||
608 | api = 2 |
||
609 | |||
610 | def apply(self, fn, context): |
||
611 | def _enable_cors(*args, **kwargs): |
||
612 | # set CORS headers |
||
613 | response.headers['Access-Control-Allow-Origin'] = '*' |
||
614 | response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS' |
||
615 | response.headers['Access-Control-Allow-Headers'] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token' |
||
616 | |||
617 | if request.method != 'OPTIONS': |
||
618 | # actual request; reply with the actual response |
||
619 | return fn(*args, **kwargs) |
||
620 | |||
621 | return _enable_cors |
||
622 |