diff options
Diffstat (limited to 'glances/client.py')
-rw-r--r-- | glances/client.py | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/glances/client.py b/glances/client.py new file mode 100644 index 00000000..2dc33bed --- /dev/null +++ b/glances/client.py @@ -0,0 +1,235 @@ +# -*- coding: utf-8 -*- +# +# This file is part of Glances. +# +# Copyright (C) 2015 Nicolargo <nicolas@nicolargo.com> +# +# Glances is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Glances is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +"""Manage the Glances client.""" + +import json +import socket +import sys + +from glances.compat import Fault, ProtocolError, ServerProxy, Transport +from glances.globals import version +from glances.logger import logger +from glances.stats import GlancesStatsClient +from glances.outputs.glances_curses import GlancesCursesClient + + +class GlancesClientTransport(Transport): + + """This class overwrite the default XML-RPC transport and manage timeout.""" + + def set_timeout(self, timeout): + self.timeout = timeout + + +class GlancesClient(object): + + """This class creates and manages the TCP client.""" + + def __init__(self, config=None, args=None, timeout=7, return_to_browser=False): + # Store the arg/config + self.args = args + self.config = config + + # Default client mode + self._client_mode = 'glances' + + # Return to browser or exit + self.return_to_browser = return_to_browser + + # Build the URI + if args.password != "": + uri = 'http://{0}:{1}@{2}:{3}'.format(args.username, args.password, + args.client, args.port) + else: + uri = 'http://{0}:{1}'.format(args.client, args.port) + logger.debug("Try to connect to {0}".format(uri)) + + # Try to connect to the URI + transport = GlancesClientTransport() + # Configure the server timeout + transport.set_timeout(timeout) + try: + self.client = ServerProxy(uri, transport=transport) + except Exception as e: + self.log_and_exit("Client couldn't create socket {0}: {1}".format(uri, e)) + + def log_and_exit(self, msg=''): + """Log and exit.""" + if not self.return_to_browser: + logger.critical(msg) + sys.exit(2) + else: + logger.error(msg) + + @property + def client_mode(self): + """Get the client mode.""" + return self._client_mode + + @client_mode.setter + def client_mode(self, mode): + """Set the client mode. + + - 'glances' = Glances server (default) + - 'snmp' = SNMP (fallback) + """ + self._client_mode = mode + + def login(self): + """Logon to the server.""" + ret = True + + if not self.args.snmp_force: + # First of all, trying to connect to a Glances server + client_version = None + try: + client_version = self.client.init() + except socket.error as err: + # Fallback to SNMP + self.client_mode = 'snmp' + logger.error("Connection to Glances server failed ({0} {1})".format(err.errno, err.strerror)) + fallbackmsg = 'No Glances server found. Trying fallback to SNMP...' + if not self.return_to_browser: + print(fallbackmsg) + else: + logger.info(fallbackmsg) + except ProtocolError as err: + # Other errors + msg = "Connection to server failed" + if err.errcode == 401: + msg += " (Bad username/password)" + else: + msg += " ({0} {1})".format(err.errcode, err.errmsg) + self.log_and_exit(msg) + return False + + if self.client_mode == 'glances': + # Check that both client and server are in the same major version + if version.split('.')[0] == client_version.split('.')[0]: + # Init stats + self.stats = GlancesStatsClient(config=self.config, args=self.args) + self.stats.set_plugins(json.loads(self.client.getAllPlugins())) + logger.debug("Client version: {0} / Server version: {1}".format(version, client_version)) + else: + self.log_and_exit("Client and server not compatible: \ + Client version: {0} / Server version: {1}".format(version, client_version)) + return False + + else: + self.client_mode = 'snmp' + + # SNMP mode + if self.client_mode == 'snmp': + logger.info("Trying to grab stats by SNMP...") + + from glances.stats import GlancesStatsClientSNMP + + # Init stats + self.stats = GlancesStatsClientSNMP(config=self.config, args=self.args) + + if not self.stats.check_snmp(): + self.log_and_exit("Connection to SNMP server failed") + return False + + if ret: + # Load limits from the configuration file + # Each client can choose its owns limits + self.stats.load_limits(self.config) + + # Init screen + self.screen = GlancesCursesClient(args=self.args) + + # Return result + return ret + + def update(self): + """Update stats from Glances/SNMP server.""" + if self.client_mode == 'glances': + return self.update_glances() + elif self.client_mode == 'snmp': + return self.update_snmp() + else: + self.end() + logger.critical("Unknown server mode: {0}".format(self.client_mode)) + sys.exit(2) + + def update_glances(self): + """Get stats from Glances server. + + Return the client/server connection status: + - Connected: Connection OK + - Disconnected: Connection NOK + """ + # Update the stats + try: + server_stats = json.loads(self.client.getAll()) + server_stats['monitor'] = json.loads(self.client.getAllMonitored()) + except socket.error: + # Client cannot get server stats + return "Disconnected" + except Fault: + # Client cannot get server stats (issue #375) + return "Disconnected" + else: + # Put it in the internal dict + self.stats.update(server_stats) + return "Connected" + + def update_snmp(self): + """Get stats from SNMP server. + + Return the client/server connection status: + - SNMP: Connection with SNMP server OK + - Disconnected: Connection NOK + """ + # Update the stats + try: + self.stats.update() + except Exception: + # Client cannot get SNMP server stats + return "Disconnected" + else: + # Grab success + return "SNMP" + + def serve_forever(self): + """Main client loop.""" + exitkey = False + try: + while True and not exitkey: + # Update the stats + cs_status = self.update() + + # Update the screen + exitkey = self.screen.update(self.stats, + cs_status=cs_status, + return_to_browser=self.return_to_browser) + + # Export stats using export modules + self.stats.export(self.stats) + except Exception as e: + logger.critical(e) + self.end() + + return self.client_mode + + def end(self): + """End of the client session.""" + self.screen.end() |