diff --git a/NEWS b/NEWS index c6eca7db..28cf312f 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ Version 3.0 Enhancements and new features: + * A way to have only REST API available and disable WEB GUI access #1149 * Docker module doesn't export details about stopped containers #1152 Bugs corrected: diff --git a/glances/main.py b/glances/main.py index 69211d16..ce687c6e 100644 --- a/glances/main.py +++ b/glances/main.py @@ -55,20 +55,24 @@ Examples of use: Monitor local machine (standalone mode): $ glances - Monitor local machine with the Web interface (Web UI): + Monitor local machine with the Web interface and start Restful server: $ glances -w Glances web server started on http://0.0.0.0:61208/ + Only start Restful API (without the WebUI): + $ glances -w --disable-webui + Glances API available on http://0.0.0.0:61208/api/ + Monitor local machine and export stats to a CSV file (standalone mode): $ glances --export-csv /tmp/glances.csv Monitor local machine and export stats to a InfluxDB server with 5s refresh time (standalone mode): $ glances -t 5 --export-influxdb - Start a Glances server (server mode): + Start a Glances XML/RCP server (server mode): $ glances -s - Connect Glances to a Glances server (client mode): + Connect Glances to a Glances XML/RCP server (client mode): $ glances -c Connect Glances to a Glances server and export stats to a StatsD server (client mode): @@ -138,6 +142,8 @@ Examples of use: dest='disable_raid', help='disable RAID module') parser.add_argument('--disable-sensors', action='store_true', default=False, dest='disable_sensors', help='disable sensors module') + parser.add_argument('--disable-webui', action='store_true', default=False, + dest='disable_webui', help='disable the Web Interface') parser.add_argument('--disable-wifi', action='store_true', default=False, dest='disable_wifi', help='disable wifi module') parser.add_argument('-0', '--disable-irix', action='store_true', default=False, diff --git a/glances/outputs/glances_bottle.py b/glances/outputs/glances_bottle.py index 1cc4aa06..2746d0e4 100644 --- a/glances/outputs/glances_bottle.py +++ b/glances/outputs/glances_bottle.py @@ -40,6 +40,8 @@ class GlancesBottle(object): """This class manages the Bottle Web server.""" + API_VERSION = '2' + def __init__(self, config=None, args=None): # Init config self.config = config @@ -59,6 +61,10 @@ class GlancesBottle(object): # Load configuration file self.load_config(config) + # Set the bind URL + self.bind_url = 'http://{}:{}/'.format(self.args.bind_address, + self.args.port) + # Init Bottle self._app = Bottle() # Enable CORS (issue #479) @@ -103,30 +109,58 @@ class GlancesBottle(object): def _route(self): """Define route.""" - self._app.route('/', method="GET", callback=self._index) - self._app.route('/', method=["GET"], callback=self._index) - # REST API - self._app.route('/api/2/config', method="GET", callback=self._api_config) - self._app.route('/api/2/config/', method="GET", callback=self._api_config_item) - self._app.route('/api/2/args', method="GET", callback=self._api_args) - self._app.route('/api/2/args/', method="GET", callback=self._api_args_item) - self._app.route('/api/2/help', method="GET", callback=self._api_help) - self._app.route('/api/2/pluginslist', method="GET", callback=self._api_plugins) - self._app.route('/api/2/all', method="GET", callback=self._api_all) - self._app.route('/api/2/all/limits', method="GET", callback=self._api_all_limits) - self._app.route('/api/2/all/views', method="GET", callback=self._api_all_views) - self._app.route('/api/2/', method="GET", callback=self._api) - self._app.route('/api/2//history', method="GET", callback=self._api_history) - self._app.route('/api/2//history/', method="GET", callback=self._api_history) - self._app.route('/api/2//limits', method="GET", callback=self._api_limits) - self._app.route('/api/2//views', method="GET", callback=self._api_views) - self._app.route('/api/2//', method="GET", callback=self._api_item) - self._app.route('/api/2///history', method="GET", callback=self._api_item_history) - self._app.route('/api/2///history/', method="GET", callback=self._api_item_history) - self._app.route('/api/2///', method="GET", callback=self._api_value) + self._app.route('/api/%s/config' % self.API_VERSION, method="GET", + callback=self._api_config) + self._app.route('/api/%s/config/' % 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/' % 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/' % self.API_VERSION, method="GET", + callback=self._api) + self._app.route('/api/%s//history' % self.API_VERSION, method="GET", + callback=self._api_history) + self._app.route('/api/%s//history/' % self.API_VERSION, method="GET", + callback=self._api_history) + self._app.route('/api/%s//limits' % self.API_VERSION, method="GET", + callback=self._api_limits) + self._app.route('/api/%s//views' % self.API_VERSION, method="GET", + callback=self._api_views) + self._app.route('/api/%s//' % self.API_VERSION, method="GET", + callback=self._api_item) + self._app.route('/api/%s///history' % self.API_VERSION, method="GET", + callback=self._api_item_history) + self._app.route('/api/%s///history/' % self.API_VERSION, method="GET", + callback=self._api_item_history) + self._app.route('/api/%s///' % 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) - self._app.route('/', method="GET", callback=self._resource) + # WEB UI + if not self.args.disable_webui: + self._app.route('/', method="GET", callback=self._index) + self._app.route('/', method=["GET"], callback=self._index) + self._app.route('/', method="GET", callback=self._resource) + bindmsg = 'Glances Web User Interface started on {}'.format(self.bind_url) + logger.info(bindmsg) + else: + logger.info('The WebUI is disable (--disable-webui)') + + print(bindmsg) def start(self, stats): """Start the bottle.""" @@ -137,18 +171,18 @@ class GlancesBottle(object): self.plugins_list = self.stats.getAllPlugins() # Bind the Bottle TCP address/port - bindurl = 'http://{}:{}/'.format(self.args.bind_address, - self.args.port) - bindmsg = 'Glances web server started on {}'.format(bindurl) - logger.info(bindmsg) - print(bindmsg) 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(bindurl, new=2, autoraise=1) - self._app.run(host=self.args.bind_address, port=self.args.port, quiet=not self.args.debug) + webbrowser.open(self.bind_url, + new=2, + autoraise=1) + + self._app.run(host=self.args.bind_address, + port=self.args.port, + quiet=not self.args.debug) def end(self): """End the bottle.""" @@ -188,7 +222,7 @@ class GlancesBottle(object): def _api_plugins(self): """ - @api {get} /api/2/pluginslist Get plugins list + @api {get} /api/%s/pluginslist Get plugins list @apiVersion 2.0 @apiName pluginslist @apiGroup plugin @@ -488,7 +522,7 @@ class GlancesBottle(object): try: # Get the JSON value of the args' dict # Use vars to convert namespace to dict - # Source: https://docs.python.org/2/library/functions.html#vars + # 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)) @@ -510,7 +544,7 @@ class GlancesBottle(object): try: # Get the JSON value of the args' dict # Use vars to convert namespace to dict - # Source: https://docs.python.org/2/library/functions.html#vars + # 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))