summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornicolargo <nicolas@nicolargo.com>2023-11-26 10:02:30 +0100
committernicolargo <nicolas@nicolargo.com>2023-11-26 10:02:30 +0100
commitbcf4ce45fc0394ea64d35062c97a5e5dfdedd29b (patch)
treee22f75fe8f89b6b1d306ce81629b4d7ce87ae642
parent36ed96b05a6627aa8eeb4f8afbd7a268ce0aa572 (diff)
Rename any reference to Bottle in doc and dep file. Ready to refactor the main glances_restful_api.py file.
-rw-r--r--Makefile3
-rw-r--r--README.rst8
-rw-r--r--docs/cmds.rst2
-rw-r--r--docs/docker.rst4
-rw-r--r--docs/man/glances.12
-rw-r--r--glances/README.txt4
-rw-r--r--glances/main.py2
-rw-r--r--glances/outputs/glances_bottle.py668
-rw-r--r--glances/outputs/static/README.md2
-rw-r--r--glances/plugins/processlist/__init__.py2
-rw-r--r--glances/webserver.py10
-rw-r--r--optional-requirements.txt3
-rw-r--r--requirements.txt4
-rwxr-xr-xsetup.py7
-rw-r--r--snap/snapcraft.yaml22
-rw-r--r--tox.ini3
-rw-r--r--webui-requirements.txt3
17 files changed, 49 insertions, 700 deletions
diff --git a/Makefile b/Makefile
index 4ffc82b9..fd0cfae9 100644
--- a/Makefile
+++ b/Makefile
@@ -80,6 +80,9 @@ test-min: ## Run unit tests in minimal environment
test-min-with-upgrade: venv-min-upgrade ## Upgrade deps and run unit tests in minimal environment
./venv-min/bin/python ./unitest.py
+test-restful-api: ## Run unit tests of the RESTful API
+ ./venv-min/bin/python ./unitest-restful.py
+
# ===================================================================
# Linters and profilers
# ===================================================================
diff --git a/README.rst b/README.rst
index 3af03fe4..50c9c0f3 100644
--- a/README.rst
+++ b/README.rst
@@ -92,11 +92,11 @@ Optional dependencies:
- ``batinfo`` (for battery monitoring)
- ``bernhard`` (for the Riemann export module)
-- ``bottle`` (for Web server mode)
- ``cassandra-driver`` (for the Cassandra export module)
- ``chevron`` (for the action script feature)
- ``docker`` (for the Containers Docker monitoring support)
- ``elasticsearch`` (for the Elastic Search export module)
+- ``FastAPI`` and ``Uvicorn`` (for Web server mode)
- ``graphitesender`` (For the Graphite export module)
- ``hddtemp`` (for HDD temperature monitoring support) [Linux-only]
- ``influxdb`` (for the InfluxDB version 1 export module)
@@ -207,10 +207,10 @@ Get the Glances container:
The following tags are availables:
- *latest-full* for a full Alpine Glances image (latest release) with all dependencies
-- *latest* for a basic Alpine Glances (latest release) version with minimal dependencies (Bottle and Docker)
+- *latest* for a basic Alpine Glances (latest release) version with minimal dependencies (FastAPI and Docker)
- *dev* for a basic Alpine Glances image (based on development branch) with all dependencies (Warning: may be instable)
- *ubuntu-latest-full* for a full Ubuntu Glances image (latest release) with all dependencies
-- *ubuntu-latest* for a basic Ubuntu Glances (latest release) version with minimal dependencies (Bottle and Docker)
+- *ubuntu-latest* for a basic Ubuntu Glances (latest release) version with minimal dependencies (FastAPI and Docker)
- *ubuntu-dev* for a basic Ubuntu Glances image (based on development branch) with all dependencies (Warning: may be instable)
Run last version of Glances container in *console mode*:
@@ -319,7 +319,7 @@ Start Termux on your device and enter:
$ apt update
$ apt upgrade
$ apt install clang python
- $ pip install bottle
+ $ pip install fastapi uvicorn
$ pip install glances
And start Glances:
diff --git a/docs/cmds.rst b/docs/cmds.rst
index cffeb153..91a8ed53 100644
--- a/docs/cmds.rst
+++ b/docs/cmds.rst
@@ -172,7 +172,7 @@ Command-Line Options
.. option:: -w, --webserver
- run Glances in web server mode (bottle lib needed)
+ run Glances in web server mode (FastAPI lib needed)
.. option:: --cached-time CACHED_TIME
diff --git a/docs/docker.rst b/docs/docker.rst
index ad91fd3d..9f4aa63e 100644
--- a/docs/docker.rst
+++ b/docs/docker.rst
@@ -28,7 +28,7 @@ Available tags (all images are based on both Alpine and Ubuntu Operating System)
* - `latest`
- Alpine
- Latest Release
- - Minimal + (Bottle & Docker)
+ - Minimal + (FastAPI & Docker)
* - `dev`
- Alpine
- develop
@@ -40,7 +40,7 @@ Available tags (all images are based on both Alpine and Ubuntu Operating System)
* - `ubuntu-latest`
- Ubuntu
- Latest Release
- - Minimal + (Bottle & Docker)
+ - Minimal + (FastAPI & Docker)
* - `ubuntu-dev`
- Ubuntu
- develop
diff --git a/docs/man/glances.1 b/docs/man/glances.1
index 87e52bf9..b2888dd8 100644
--- a/docs/man/glances.1
+++ b/docs/man/glances.1
@@ -254,7 +254,7 @@ set refresh time in seconds [default: 3 sec]
.INDENT 0.0
.TP
.B \-w, \-\-webserver
-run Glances in web server mode (bottle lib needed)
+run Glances in web server mode (FastAPI lib needed)
.UNINDENT
.INDENT 0.0
.TP
diff --git a/glances/README.txt b/glances/README.txt
index c5fd13eb..3e6de7eb 100644
--- a/glances/README.txt
+++ b/glances/README.txt
@@ -12,7 +12,7 @@ globals.py Share variables upon modules
main.py Main script to rule them up...
client.py Glances client
server.py Glances server
-webserver.py Glances web server (Bottle-based)
+webserver.py Glances web server (Based on FastAPI)
autodiscover.py Glances autodiscover module (via zeroconf)
standalone.py Glances standalone (curses interface)
password.py Manage password for Glances client/server
@@ -27,7 +27,7 @@ plugins
outputs
=> Glances UI
glances_curses.py The curses interface
- glances_bottle.py The web interface
+ glances_restful-api.py The HTTP/API & Web based interface
...
exports
=> Glances exports
diff --git a/glances/main.py b/glances/main.py
index 03acfd7a..85db14aa 100644
--- a/glances/main.py
+++ b/glances/main.py
@@ -363,7 +363,7 @@ Examples of use:
action='store_true',
default=False,
dest='webserver',
- help='run Glances in web server mode (bottle needed)',
+ help='run Glances in web server mode (FastAPI and Uvicorn lib needed)',
)
parser.add_argument(
'--cached-time',
diff --git a/glances/outputs/glances_bottle.py b/glances/outputs/glances_bottle.py
deleted file mode 100644
index df6cfc71..00000000
--- a/glances/outputs/glances_bottle.py
+++ /dev/null
@@ -1,668 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of Glances.
-#
-# SPDX-FileCopyrightText: 2023 Nicolas Hennion <nicolas@nicolargo.com>
-#
-# SPDX-License-Identifier: LGPL-3.0-only
-#
-
-"""RestFull API interface class."""
-
-import os
-import sys
-import tempfile
-from io import open
-import webbrowser
-import zlib
-import socket
-from urllib.parse import urljoin
-
-from glances.globals import b, json_dumps
-from glances.timer import Timer
-from glances.logger import logger
-
-try:
- from bottle import Bottle, static_file, abort, response, request, auth_basic, template, TEMPLATE_PATH
-except ImportError:
- logger.critical('Bottle module not found. Glances cannot start in web server mode.')
- sys.exit(2)
-
-
-def compress(func):
- """Compress result with deflate algorithm if the client ask for it."""
-
- def wrapper(*args, **kwargs):
- """Wrapper that take one function and return the compressed result."""
- ret = func(*args, **kwargs)
- logger.debug(
- 'Receive {} {} request with header: {}'.format(
- request.method,
- request.url,
- ['{}: {}'.format(h, request.headers.get(h)) for h in request.headers.keys()],
- )
- )
- if 'deflate' in request.headers.get('Accept-Encoding', ''):
- response.headers['Content-Encoding'] = 'deflate'
- ret = deflate_compress(ret)
- else:
- response.headers['Content-Encoding'] = 'identity'
- return ret
-
- def deflate_compress(data, compress_level=6):
- """Compress given data using the DEFLATE algorithm"""
- # Init compression
- zobj = zlib.compressobj(
- compress_level, zlib.DEFLATED, zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, zlib.Z_DEFAULT_STRATEGY
- )
-
- # Return compressed object
- return zobj.compress(b(data)) + zobj.flush()
-
- return wrapper
-
-
-class GlancesBottle(object):
- """This class manages the Bottle Web server."""
-
- API_VERSION = '3'
-
- def __init__(self, config=None, args=None):
- # Init config
- self.config = config
-
- # Init args
- self.args = args
-
- # Init stats
- # Will be updated within Bottle route
- self.stats = None
-
- # cached_time is the minimum time interval between stats updates
- # i.e. HTTP/RESTful calls will not retrieve updated info until the time
- # since last update is passed (will retrieve old cached info instead)
- self.timer = Timer(0)
-
- # Load configuration file
- self.load_config(config)
-
- # Set the bind URL (only used for log information purpose)
- self.bind_url = urljoin('http://{}:{}/'.format(self.args.bind_address, self.args.port), self.url_prefix)
-
- # Init Bottle
- self._app = Bottle()
- # Enable CORS (issue #479)
- self._app.install(EnableCors())
- # Password
- if args.password != '':
- self._app.install(auth_basic(self.check_auth))
- # Define routes
- self._route()
-
- # Path where the statics files are stored
- self.STATIC_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'static/public')
-
- # Paths for templates
- TEMPLATE_PATH.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), 'static/templates'))
-
- def load_config(self, config):
- """Load the outputs section of the configuration file."""
- # Limit the number of processes to display in the WebUI
- self.url_prefix = '/'
- if config is not None and config.has_section('outputs'):
- n = config.get_value('outputs', 'max_processes_display', default=None)
- logger.debug('Number of processes to display in the WebUI: {}'.format(n))
- self.url_prefix = config.get_value('outputs', 'url_prefix', default='/')
- logger.debug('URL prefix: {}'.format(self.url_prefix))
-
- def __update__(self):
- # Never update more than 1 time per cached_time
- if self.timer.finished():
- self.stats.update()
- self.timer = Timer(self.args.cached_time)
-
- def app(self):
- return self._app()
-
- def check_auth(self, username, password):
- """Check if a username/password combination is valid."""
- if username == self.args.username:
- from glances.password import GlancesPassword
-
- pwd = GlancesPassword(username=username, config=self.config)
- return pwd.check_password(self.args.password, pwd.get_hash(password))
- else:
- return False
-
- def _route(self):
- """Define route."""
- # REST API
- self._app.route('/api/%s/status' % self.API_VERSION, method="GET", callback=self._api_status)
- self._app.route('/api/%s/config' % self.API_VERSION, method="GET", callback=self._api_config)
- self._app.route('/api/%s/config/<item>' % self.API_VERSION, method="GET", callback=self._api_config_item)
- self._app.route('/api/%s/args' % self.API_VERSION, method="GET", callback=self._api_args)
- self._app.route('/api/%s/args/<item>' % self.API_VERSION, method="GET", callback=self._api_args_item)
- self._app.route('/api/%s/help' % self.API_VERSION, method="GET", callback=self._api_help)
- self._app.route('/api/%s/pluginslist' % self.API_VERSION, method="GET", callback=self._api_plugins)
- self._app.route('/api/%s/all' % self.API_VERSION, method="GET", callback=self._api_all)
- self._app.route('/api/%s/all/limits' % self.API_VERSION, method="GET", callback=self._api_all_limits)
- self._app.route('/api/%s/all/views' % self.API_VERSION, method="GET", callback=self._api_all_views)
- self._app.route('/api/%s/<plugin>' % self.API_VERSION, method="GET", callback=self._api)
- self._app.route('/api/%s/<plugin>/history' % self.API_VERSION, method="GET", callback=self._api_history)
- self._app.route(
- '/api/%s/<plugin>/history/<nb:int>' % self.API_VERSION, method="GET", callback=self._api_history
- )
- self._app.route('/api/%s/<plugin>/top/<nb:int>' % self.API_VERSION, method="GET", callback=self._api_top)
- self._app.route('/api/%s/<plugin>/limits' % self.API_VERSION, method="GET", callback=self._api_limits)
- self._app.route('/api/%s/<plugin>/views' % self.API_VERSION, method="GET", callback=self._api_views)
- self._app.route('/api/%s/<plugin>/<item>' % self.API_VERSION, method="GET", callback=self._api_item)
- self._app.route(
- '/api/%s/<plugin>/<item>/history' % self.API_VERSION, method="GET", callback=self._api_item_history
- )
- self._app.route(
- '/api/%s/<plugin>/<item>/history/<nb:int>' % self.API_VERSION, method="GET", callback=self._api_item_history
- )
- self._app.route('/api/%s/<plugin>/<item>/<value>' % self.API_VERSION, method="GET", callback=self._api_value)
- self._app.route(
- '/api/%s/<plugin>/<item>/<value:path>' % self.API_VERSION, method="GET", callback=self._api_value
- )
- bindmsg = 'Glances RESTful API Server started on {}api/{}'.format(self.bind_url, self.API_VERSION)
- logger.info(bindmsg)
-
- # WEB UI
- if not self.args.disable_webui:
- self._app.route('/', method="GET", callback=self._index)
- self._app.route('/<refresh_time:int>', method=["GET"], callback=self._index)
- self._app.route('/<filepath:path>', method="GET", callback=self._resource)
- bindmsg = 'Glances Web User Interface started on {}'.format(self.bind_url)
- else:
- bindmsg = 'The WebUI is disable (--disable-webui)'
-
- logger.info(bindmsg)
- print(bindmsg)
-
- def start(self, stats):
- """Start the bottle."""
- # Init stats
- self.stats = stats
-
- # Init plugin list
- self.plugins_list = self.stats.getPluginsList()
-
- # Bind the Bottle TCP address/port
- if self.args.open_web_browser:
- # Implementation of the issue #946
- # Try to open the Glances Web UI in the default Web browser if:
- # 1) --open-web-browser option is used
- # 2) Glances standalone mode is running on Windows OS
- webbrowser.open(self.bind_url, new=2, autoraise=1)
-
- # Run the Web application
- if self.url_prefix != '/':
- # Create an outer Bottle class instance to manage url_prefix
- self.main_app = Bottle()
- self.main_app.mount(self.url_prefix, self._app)
- try:
- self.main_app.run(host=self.args.bind_address, port=self.args.port, quiet=not self.args.debug)
- except socket.error as e:
- logger.critical('Error: Can not ran Glances Web server ({})'.format(e))
- else:
- try:
- self._app.run(host=self.args.bind_address, port=self.args.port, quiet=not self.args.debug)
- except socket.error as e:
- logger.critical('Error: Can not ran Glances Web server ({})'.format(e))
-
- def end(self):
- """End the bottle."""
- logger.info("Close the Web server")
- self._app.close()
- if self.url_prefix != '/':
- self.main_app.close()
-
- def _index(self, refresh_time=None):
- """Bottle callback for index.html (/) file."""
-
- if refresh_time is None or refresh_time < 1:
- refresh_time = int(self.args.time)
-
- # Update the stat
- self.__update__()
-
- # Display
- return template("index.html", refresh_time=refresh_time)
-
- def _resource(self, filepath):
- """Bottle callback for resources files."""
- # Return the static file
- return static_file(filepath, root=self.STATIC_PATH)
-
- @compress
- def _api_status(self):
- """Glances API RESTful implementation.
-
- Return a 200 status code.
- This entry point should be used to check the API health.
-
- See related issue: Web server health check endpoint #1988
- """
- response.status = 200
-
- return "Active"
-
- @compress
- def _api_help(self):
- """Glances API RESTful implementation.
-
- Return the help data or 404 error.
- """
- response.content_type = 'application/json; charset=utf-8'
-
- # Update the stat
- view_data = self.stats.get_plugin("help").get_view_data()
- try:
- plist = json_dumps(view_data)
- except Exception as e:
- abort(404, "Cannot get help view data (%s)" % str(e))
- return plist
-
- @compress
- def _api_plugins(self):
- """Glances API RESTFul implementation.
-
- @api {get} /api/%s/pluginslist Get plugins list
- @apiVersion 2.0
- @apiName pluginslist
- @apiGroup plugin
-
- @apiSuccess {String[]} Plugins list.
-
- @apiSuccessExample Success-Response:
- HTTP/1.1 200 OK
- [
- "load",
- "help",
- "ip",
- "memswap",
- "processlist",
- ...
- ]
-
- @apiError Cannot get plugin list.
-
- @apiErrorExample Error-Response:
- HTTP/1.1 404 Not Found
- """
- response.content_type = 'application/json; charset=utf-8'
-
- # Update the stat
- self.__update__()
-
- try:
- plist = json_dumps(self.plugins_list)
- except Exception as e:
- abort(404, "Cannot get plugin list (%s)" % str(e))
- return plist
-
- @compress
- def _api_all(self):
- """Glances API RESTful implementation.
-
- Return the JSON representation of all the plugins
- HTTP/200 if OK
- HTTP/400 if plugin is not found
- HTTP/404 if others error
- """
- response.content_type = 'application/json; charset=utf-8'
-
- if self.args.debug:
- fname = os.path.join(tempfile.gettempdir(), 'glances-debug.json')
- try:
- with open(fname) as f:
- return f.read()
- except IOError:
- logger.debug("Debug file (%s) not found" % fname)
-
- # Update the stat
- self.__update__()
-
- try:
- # Get the JSON value of the stat ID
- statval = json_dumps(self.stats.getAllAsDict())
- except Exception as e:
- abort(404, "Cannot get stats (%s)" % str(e))
-
- return statval
-
- @compress
- def _api_all_limits(self):
- """Glances API RESTful implementation.
-
- Return the JSON representation of all the plugins limits
- HTTP/200 if OK
- HTTP/400 if plugin is not found
- HTTP/404 if others error
- """
- response.content_type = 'application/json; charset=utf-8'
-
- try:
- # Get the JSON value of the stat limits
- limits = json_dumps(self.stats.getAllLimitsAsDict())
- except Exception as e:
- abort(404, "Cannot get limits (%s)" % (str(e)))
- return limits
-
- @compress
- def _api_all_views(self):
- """Glances API RESTful implementation.
-
- Return the JSON representation of all the plugins views
- HTTP/200 if OK
- HTTP/400 if plugin is not found
- HTTP/404 if others error
- """
- response.content_type = 'application/json; charset=utf-8'
-
- try:
- # Get the JSON value of the stat view
- limits = json_dumps(self.stats.getAllViewsAsDict())
- except Exception as e:
- abort(404, "Cannot get views (%s)" % (str(e)))
- return limits
-
- @compress
- def _api(self, plugin):
- """Glances API RESTful implementation.
-
- Return the JSON representation of a given plugin
- HTTP/200 if OK
- HTTP/400 if plugin is not found
- HTTP/404 if others error
- """
- response.content_type = 'application/json; charset=utf-8'
-
- if plugin not in self.plugins_list:
- abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list))
-
- # Update the stat
- self.__update__()
-
- try:
- # Get the JSON value of the stat ID
- statval = self.stats.get_plugin(plugin).get_stats()
- except Exception as e:
- abort(404, "Cannot get plugin %s (%s)" % (plugin, str(e)))
-
- return statval
-
- @compress
- def _api_top(self, plugin, nb=0):
- """Glances API RESTful implementation.
-
- Return the JSON representation of a given plugin limited to the top nb items.
- It is used to reduce the payload of the HTTP response (example: processlist).
-
- HTTP/200 if OK
- HTTP/400 if plugin is not found
- HTTP/404 if others error
- """
- response.content_type = 'application/json; charset=utf-8'
-
- if plugin not in self.plugins_list:
- abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list))
-
- # Update the stat
- self.__update__()
-
- try:
- # Get the value of the stat ID
- statval = self.stats.get_plugin(plugin).get_export()
- except Exception as e:
- abort(404, "Cannot get plugin %s (%s)" % (plugin, str(e)))
-
- if isinstance(statval, list):
- return json_dumps(statval[:nb])
- else:
- return json_dumps(statval)
-
- @compress
- def _api_history(self, plugin, nb=0):
- """Glances API RESTful implementation.
-
- Return the JSON representation of a given plugin history
- Limit to the last nb items (all if nb=0)
- HTTP/200 if OK
- HTTP/400 if plugin is not found
- HTTP/404 if others error
- """
- response.content_type = 'application/json; charset=utf-8'
-
- if plugin not in self.plugins_list:
- abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list))
-
- # Update the stat
- self.__update__()
-
- try:
- # Get the JSON value of the stat ID
- statval = self.stats.get_plugin(plugin).get_stats_history(nb=int(nb))
- except Exception as e:
- abort(404, "Cannot get plugin history %s (%s)" % (plugin, str(e)))
- return statval
-
- @compress
- def _api_limits(self, plugin):
- """Glances API RESTful implementation.
-
- Return the JSON limits of a given plugin
- HTTP/200 if OK
- HTTP/400 if plugin is not found
- HTTP/404 if others error
- """
- response.content_type = 'application/json; charset=utf-8'
-
- if plugin not in self.plugins_list:
- abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list))
-
- # Update the stat
- # self.__update__()
-
- try:
- # Get the JSON value of the stat limits
- ret = self.stats.get_plugin(plugin).limits
- except Exception as e:
- abort(404, "Cannot get limits for plugin %s (%s)" % (plugin, str(e)))
- return ret
-
- @compress
- def _api_views(self, plugin):
- """Glances API RESTful implementation.
-
- Return the JSON views of a given plugin
- HTTP/200 if OK
- HTTP/400 if plugin is not found
- HTTP/404 if others error
- """
- response.content_type = 'application/json; charset=utf-8'
-
- if plugin not in self.plugins_list:
- abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list))
-
- # Update the stat
- # self.__update__()
-
- try:
- # Get the JSON value of the stat views
- ret = self.stats.get_plugin(plugin).get_views()
- except Exception as e:
- abort(404, "Cannot get views for plugin %s (%s)" % (plugin, str(e)))
- return ret
-
- # No compression see issue #1228
- # @compress
- def _api_itemvalue(self, plugin, item, value=None, history=False, nb=0):
- """Father method for _api_item and _api_value."""
- response.content_type = 'application/json; charset=utf-8'
-
- if plugin not in self.plugins_list:
- abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list))
-
- # Update the stat
- self.__update__()
-
- if value is None:
- if history:
- ret = self.stats.get_plugin(plugin).get_stats_history(item, nb=int(nb))
- else:
- ret = self.stats.get_plugin(plugin).get_stats_item(item)
-
- if ret is None:
- abort(404, "Cannot get item %s%s in plugin %s" % (item, 'history ' if history else '', plugin))
- else:
- if history:
- # Not available
- ret = None
- else:
- ret = self.stats.get_plugin(plugin).get_stats_value(item, value)
-
- if ret is None:
- abort(
- 404, "Cannot get item %s(%s=%s) in plugin %s" % ('history ' if history else '', item, value, plugin)
- )
-
- return ret
-
- @compress
- def _api_item(self, plugin, item):
- """Glances API RESTful implementation.
-
- Return the JSON representation of the couple plugin/item
- HTTP/200 if OK
- HTTP/400 if plugin is not found
- HTTP/404 if others error
-
- """
- return self._api_itemvalue(plugin, item)
-
- @compress
- def _api_item_history(self, plugin, item, nb=0):
- """Glances API RESTful implementation.
-
- Return the JSON representation of the couple plugin/history of item
- HTTP/200 if OK
- HTTP/400 if plugin is not found
- HTTP/404 if others error
-
- """
- return self._api_itemvalue(plugin, item, history=True, nb=int(nb))
-
- @compress
- def _api_value(self, plugin, item, value):
- """Glances API RESTful implementation.
-
- Return the process stats (dict) for the given item=value
- HTTP/200 if OK
- HTTP/400 if plugin is not found
- HTTP/404 if others error
- """
- return self._api_itemvalue(plugin, item, value)
-
- @compress
- def _api_config(self):
- """Glances API RESTful implementation.
-
- Return the JSON representation of the Glances configuration file
- HTTP/200 if OK
- HTTP/404 if others error
- """
- response.content_type = 'application/json; charset=utf-8'
-
- try:
- # Get the JSON value of the config' dict
- args_json = json_dumps(self.config.as_dict())
- except Exception as e:
- abort(404, "Cannot get config (%s)" % str(e))
- return args_json
-
- @compress
- def _api_config_item(self, item):
- """Glances API RESTful implementation.
-
- Return the JSON representation of the Glances configuration item
- HTTP/200 if OK
- HTTP/400 if item is not found
- HTTP/404 if others error
- """
- response.content_type = 'application/json; charset=utf-8'
-
- config_dict = self.config.as_dict()
- if item not in config_dict:
- abort(400, "Unknown configuration item %s" % item)
-
- try:
- # Get the JSON value of the config' dict
- args_json = json_dumps(config_dict[item])
- except Exception as e:
- abort(404, "Cannot get config item (%s)" % str(e))
- return args_json
-
- @compress
- def _api_args(self):
- """Glances API RESTful implementation.
-
- Return the JSON representation of the Glances command line arguments
- HTTP/200 if OK
- HTTP/404 if others error
- """
- response.content_type = 'application/json; charset=utf-8'
-
- try:
- # Get the JSON value of the args' dict
- # Use vars to convert namespace to dict
- # Source: https://docs.python.org/%s/library/functions.html#vars
- args_json = json_dumps(vars(self.args))
- except Exception as e:
- abort(404, "Cannot get args (%s)" % str(e))
- return args_json
-
- @compress
- def _api_args_item(self, item):
- """Glances API RESTful implementation.
-
- Return the JSON representation of the Glances command line arguments item
- HTTP/200 if OK
- HTTP/400 if item is not found
- HTTP/404 if others error
- """
- response.content_type = 'application/json; charset=utf-8'
-
- if item not in self.args:
- abort(400, "Unknown argument item %s" % item)
-
- try:
- # Get the JSON value of the args' dict
- # Use vars to convert namespace to dict
- # Source: https://docs.python.org/%s/library/functions.html#vars
- args_json = json_dumps(vars(self.args)[item])
- except Exception as e:
- abort(404, "Cannot get args item (%s)" % str(e))
- return args_json
-
-
-class EnableCors(object):
- name = 'enable_cors'
- api = 2
-
- def apply(self, fn, context):
- def _enable_cors(*args, **kwargs):
- # set CORS headers
- response.headers['Access-Control-Allow-Origin'] = '*'
- response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS'
- response.headers[
- 'Access-Control-Allow-Headers'
- ] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
-
- if request.method != 'OPTIONS':
- # actual request; reply with the actual response
- return fn(*args, **kwargs)
-
- return _enable_cors
diff --git a/glances/outputs/static/README.md b/glances/outputs/static/README.md
index bac2decb..5e8e7838 100644
--- a/glances/outputs/static/README.md
+++ b/glances/outputs/static/README.md
@@ -65,7 +65,7 @@ static
|
|--- public # path where builds are put
|
-|--- templates (bottle)
+|--- templates
```
## Data
diff --git a/glances/plugins/pr