def __init__(self, config=None, args=None, timeout=7, return_to_browser=False): # Store the arg/config self.args = args self.config = config # Quiet mode self._quiet = args.quiet # 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 != "": self.uri = 'http://{}:{}@{}:{}'.format(args.username, args.password, args.client, args.port) else: self.uri = 'http://{}:{}'.format(args.client, args.port) logger.debug("Try to connect to {}".format(self.uri)) # Try to connect to the URI transport = GlancesClientTransport() # Configure the server timeout transport.set_timeout(timeout) try: self.client = ServerProxy(self.uri, transport=transport) except Exception as e: self.log_and_exit("Client couldn't create socket {}: {}".format(self.uri, e))
def __update_stats(self, server): """ Update stats for the given server (picked from the server list) """ # Get the server URI uri = self.__get_uri(server) # Try to connect to the server t = GlancesClientTransport() t.set_timeout(3) # Get common stats try: s = ServerProxy(uri, transport=t) except Exception as e: logger.warning( "Client browser couldn't create socket {}: {}".format(uri, e)) else: # Mandatory stats try: # CPU% cpu_percent = 100 - json.loads(s.getCpu())['idle'] server['cpu_percent'] = '{:.1f}'.format(cpu_percent) # MEM% server['mem_percent'] = json.loads(s.getMem())['percent'] # OS (Human Readable name) server['hr_name'] = json.loads(s.getSystem())['hr_name'] except (socket.error, Fault, KeyError) as e: logger.debug( "Error while grabbing stats form {}: {}".format(uri, e)) server['status'] = 'OFFLINE' except ProtocolError as e: if e.errcode == 401: # Error 401 (Authentication failed) # Password is not the good one... server['password'] = None server['status'] = 'PROTECTED' else: server['status'] = 'OFFLINE' logger.debug("Cannot grab stats from {} ({} {})".format(uri, e.errcode, e.errmsg)) else: # Status server['status'] = 'ONLINE' # Optional stats (load is not available on Windows OS) try: # LOAD load_min5 = json.loads(s.getLoad())['min5'] server['load_min5'] = '{:.2f}'.format(load_min5) except Exception as e: logger.warning( "Error while grabbing stats form {}: {}".format(uri, e)) return server
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 != "": self.uri = 'http://{}:{}@{}:{}'.format(args.username, args.password, args.client, args.port) else: self.uri = 'http://{}:{}'.format(args.client, args.port) logger.debug("Try to connect to {}".format(self.uri)) # Try to connect to the URI transport = GlancesClientTransport() # Configure the server timeout transport.set_timeout(timeout) try: self.client = ServerProxy(self.uri, transport=transport) except Exception as e: self.log_and_exit("Client couldn't create socket {}: {}".format(self.uri, e))
def __update_stats(self, server): """ Update stats for the given server (picked from the server list) """ # Get the server URI uri = self.__get_uri(server) # Try to connect to the server t = GlancesClientTransport() t.set_timeout(3) # Get common stats try: s = ServerProxy(uri, transport=t) except Exception as e: logger.warning( "Client browser couldn't create socket {}: {}".format(uri, e)) else: # Mandatory stats try: # CPU% cpu_percent = 100 - json.loads(s.getCpu())['idle'] server['cpu_percent'] = '{:.1f}'.format(cpu_percent) # MEM% server['mem_percent'] = json.loads(s.getMem())['percent'] # OS (Human Readable name) server['hr_name'] = json.loads(s.getSystem())['hr_name'] except (socket.error, Fault, KeyError) as e: logger.debug("Error while grabbing stats form {}: {}".format( uri, e)) server['status'] = 'OFFLINE' except ProtocolError as e: if e.errcode == 401: # Error 401 (Authentication failed) # Password is not the good one... server['password'] = None server['status'] = 'PROTECTED' else: server['status'] = 'OFFLINE' logger.debug("Cannot grab stats from {} ({} {})".format( uri, e.errcode, e.errmsg)) else: # Status server['status'] = 'ONLINE' # Optional stats (load is not available on Windows OS) try: # LOAD load_min5 = json.loads(s.getLoad())['min5'] server['load_min5'] = '{:.2f}'.format(load_min5) except Exception as e: logger.warning( "Error while grabbing stats form {}: {}".format( uri, e)) return server
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 != "": self.uri = 'http://{}:{}@{}:{}'.format(args.username, args.password, args.client, args.port) else: self.uri = 'http://{}:{}'.format(args.client, args.port) logger.debug("Try to connect to {}".format(self.uri)) # Try to connect to the URI transport = GlancesClientTransport() # Configure the server timeout transport.set_timeout(timeout) try: self.client = ServerProxy(self.uri, transport=transport) except Exception as e: self.log_and_exit("Client couldn't create socket {}: {}".format(self.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_glances(self): """Login 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 ({} {})".format(err.errno, err.strerror)) fallbackmsg = 'No Glances server found on {}. Trying fallback to SNMP...'.format(self.uri) if not self.return_to_browser: print(fallbackmsg) else: logger.info(fallbackmsg) except ProtocolError as err: # Other errors msg = "Connection to server {} failed".format(self.uri) if err.errcode == 401: msg += " (Bad username/password)" else: msg += " ({} {})".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: {} / Server version: {}".format(__version__, client_version)) else: self.log_and_exit("Client and server not compatible: \ Client version: {} / Server version: {}".format(__version__, client_version)) return False return True def _login_snmp(self): """Login to a SNMP server""" logger.info("Trying to grab stats by SNMP...") from glances.stats_client_snmp 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 return True def login(self): """Logon to the server.""" if self.args.snmp_force: # Force SNMP instead of Glances server self.client_mode = 'snmp' else: # First of all, trying to connect to a Glances server if not self._login_glances(): return False # Try SNMP mode if self.client_mode == 'snmp': if not self._login_snmp(): return False # Load limits from the configuration file # Each client can choose its owns limits logger.debug("Load limits from the client configuration file") self.stats.load_limits(self.config) # Init screen self.screen = GlancesCursesClient(config=self.config, args=self.args) # Return True: OK return True 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: {}".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()) 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()
def __serve_forever(self): """Main client loop.""" while True: # No need to update the server list # It's done by the GlancesAutoDiscoverListener class (autodiscover.py) # Or define staticaly in the configuration file (module static_list.py) # For each server in the list, grab elementary stats (CPU, LOAD, MEM, OS...) # logger.debug(self.get_servers_list()) try: for v in self.get_servers_list(): # Do not retreive stats for statics server # Why ? Because for each offline servers, the timeout will be reached # So ? The curse interface freezes if v['type'] == 'STATIC' and v['status'] in [ 'UNKNOWN', 'SNMP', 'OFFLINE' ]: continue # Get the server URI uri = self.__get_uri(v) # Try to connect to the server t = GlancesClientTransport() t.set_timeout(3) # Get common stats try: s = ServerProxy(uri, transport=t) except Exception as e: logger.warning( "Client browser couldn't create socket {0}: {1}". format(uri, e)) else: # Mandatory stats try: # CPU% cpu_percent = 100 - json.loads(s.getCpu())['idle'] v['cpu_percent'] = '{0:.1f}'.format(cpu_percent) # MEM% v['mem_percent'] = json.loads( s.getMem())['percent'] # OS (Human Readable name) v['hr_name'] = json.loads(s.getSystem())['hr_name'] except (socket.error, Fault, KeyError) as e: logger.debug( "Error while grabbing stats form {0}: {1}". format(uri, e)) v['status'] = 'OFFLINE' except ProtocolError as e: if e.errcode == 401: # Error 401 (Authentication failed) # Password is not the good one... v['password'] = None v['status'] = 'PROTECTED' else: v['status'] = 'OFFLINE' logger.debug( "Cannot grab stats from {0} ({1} {2})".format( uri, e.errcode, e.errmsg)) else: # Status v['status'] = 'ONLINE' # Optional stats (load is not available on Windows OS) try: # LOAD load_min5 = json.loads(s.getLoad())['min5'] v['load_min5'] = '{0:.2f}'.format(load_min5) except Exception as e: logger.warning( "Error while grabbing stats form {0}: {1}". format(uri, e)) # List can change size during iteration... except RuntimeError: logger.debug( "Server list dictionnary change inside the loop (wait next update)" ) # Update the screen (list or Glances client) if self.screen.active_server is None: # Display the Glances browser self.screen.update(self.get_servers_list()) else: # Display the Glances client for the selected server logger.debug("Selected server: {0}".format( self.get_servers_list()[self.screen.active_server])) # Connection can take time # Display a popup self.screen.display_popup('Connect to {0}:{1}'.format( v['name'], v['port']), duration=1) # A password is needed to access to the server's stats if self.get_servers_list()[ self.screen.active_server]['password'] is None: # First of all, check if a password is available in the [passwords] section clear_password = self.password.get_password(v['name']) if (clear_password is None or self.get_servers_list() [self.screen.active_server]['status'] == 'PROTECTED'): # Else, the password should be enter by the user # Display a popup to enter password clear_password = self.screen.display_popup( 'Password needed for {0}: '.format(v['name']), is_input=True) # Store the password for the selected server if clear_password is not None: self.set_in_selected( 'password', self.password.sha256_hash(clear_password)) # Display the Glance client on the selected server logger.info("Connect Glances client to the {0} server".format( self.get_servers_list()[self.screen.active_server]['key'])) # Init the client args_server = self.args # Overwrite connection setting args_server.client = self.get_servers_list()[ self.screen.active_server]['ip'] args_server.port = self.get_servers_list()[ self.screen.active_server]['port'] args_server.username = self.get_servers_list()[ self.screen.active_server]['username'] args_server.password = self.get_servers_list()[ self.screen.active_server]['password'] client = GlancesClient(config=self.config, args=args_server, return_to_browser=True) # Test if client and server are in the same major version if not client.login(): self.screen.display_popup( "Sorry, cannot connect to '{0}'\n" "See 'glances.log' for more details".format(v['name'])) # Set the ONLINE status for the selected server self.set_in_selected('status', 'OFFLINE') else: # Start the client loop # Return connection type: 'glances' or 'snmp' connection_type = client.serve_forever() try: logger.debug( "Disconnect Glances client from the {0} server". format(self.get_servers_list()[ self.screen.active_server]['key'])) except IndexError: # Server did not exist anymore pass else: # Set the ONLINE status for the selected server if connection_type == 'snmp': self.set_in_selected('status', 'SNMP') else: self.set_in_selected('status', 'ONLINE') # Return to the browser (no server selected) self.screen.active_server = None
def __serve_forever(self): """Main client loop.""" while True: # No need to update the server list # It's done by the GlancesAutoDiscoverListener class (autodiscover.py) # Or define staticaly in the configuration file (module static_list.py) # For each server in the list, grab elementary stats (CPU, LOAD, MEM, OS...) # logger.debug(self.get_servers_list()) try: for v in self.get_servers_list(): # Do not retreive stats for statics server # Why ? Because for each offline servers, the timeout will be reached # So ? The curse interface freezes if v['type'] == 'STATIC' and v['status'] in ['UNKNOWN', 'SNMP', 'OFFLINE']: continue # Get the server URI uri = self.__get_uri(v) # Try to connect to the server t = GlancesClientTransport() t.set_timeout(3) # Get common stats try: s = ServerProxy(uri, transport=t) except Exception as e: logger.warning( "Client browser couldn't create socket {0}: {1}".format(uri, e)) else: # Mandatory stats try: # CPU% cpu_percent = 100 - json.loads(s.getCpu())['idle'] v['cpu_percent'] = '{0:.1f}'.format(cpu_percent) # MEM% v['mem_percent'] = json.loads(s.getMem())['percent'] # OS (Human Readable name) v['hr_name'] = json.loads(s.getSystem())['hr_name'] except (socket.error, Fault, KeyError) as e: logger.debug( "Error while grabbing stats form {0}: {1}".format(uri, e)) v['status'] = 'OFFLINE' except ProtocolError as e: if e.errcode == 401: # Error 401 (Authentication failed) # Password is not the good one... v['password'] = None v['status'] = 'PROTECTED' else: v['status'] = 'OFFLINE' logger.debug("Cannot grab stats from {0} ({1} {2})".format(uri, e.errcode, e.errmsg)) else: # Status v['status'] = 'ONLINE' # Optional stats (load is not available on Windows OS) try: # LOAD load_min5 = json.loads(s.getLoad())['min5'] v['load_min5'] = '{0:.2f}'.format(load_min5) except Exception as e: logger.warning( "Error while grabbing stats form {0}: {1}".format(uri, e)) # List can change size during iteration... except RuntimeError: logger.debug( "Server list dictionnary change inside the loop (wait next update)") # Update the screen (list or Glances client) if not self.screen.active_server: # Display the Glances browser self.screen.update(self.get_servers_list()) else: # Display the Glances client for the selected server logger.debug("Selected server: {0}".format(self.get_servers_list()[self.screen.active_server])) # Connection can take time # Display a popup self.screen.display_popup( 'Connect to {0}:{1}'.format(v['name'], v['port']), duration=1) # A password is needed to access to the server's stats if self.get_servers_list()[self.screen.active_server]['password'] is None: # First of all, check if a password is available in the [passwords] section clear_password = self.password.get_password(v['name']) if (clear_password is None or self.get_servers_list() [self.screen.active_server]['status'] == 'PROTECTED'): # Else, the password should be enter by the user # Display a popup to enter password clear_password = self.screen.display_popup( 'Password needed for {0}: '.format(v['name']), is_input=True) # Store the password for the selected server if clear_password is not None: self.set_in_selected('password', self.password.sha256_hash(clear_password)) # Display the Glance client on the selected server logger.info("Connect Glances client to the {0} server".format( self.get_servers_list()[self.screen.active_server]['key'])) # Init the client args_server = self.args # Overwrite connection setting args_server.client = self.get_servers_list()[self.screen.active_server]['ip'] args_server.port = self.get_servers_list()[self.screen.active_server]['port'] args_server.username = self.get_servers_list()[self.screen.active_server]['username'] args_server.password = self.get_servers_list()[self.screen.active_server]['password'] client = GlancesClient(config=self.config, args=args_server, return_to_browser=True) # Test if client and server are in the same major version if not client.login(): self.screen.display_popup( "Sorry, cannot connect to '{0}'\n" "See 'glances.log' for more details".format(v['name'])) # Set the ONLINE status for the selected server self.set_in_selected('status', 'OFFLINE') else: # Start the client loop # Return connection type: 'glances' or 'snmp' connection_type = client.serve_forever() try: logger.debug("Disconnect Glances client from the {0} server".format( self.get_servers_list()[self.screen.active_server]['key'])) except IndexError: # Server did not exist anymore pass else: # Set the ONLINE status for the selected server if connection_type == 'snmp': self.set_in_selected('status', 'SNMP') else: self.set_in_selected('status', 'ONLINE') # Return to the browser (no server selected) self.screen.active_server = None
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 # Quiet mode self._quiet = args.quiet # 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 != "": self.uri = 'http://{}:{}@{}:{}'.format(args.username, args.password, args.client, args.port) else: self.uri = 'http://{}:{}'.format(args.client, args.port) logger.debug("Try to connect to {}".format(self.uri)) # Try to connect to the URI transport = GlancesClientTransport() # Configure the server timeout transport.set_timeout(timeout) try: self.client = ServerProxy(self.uri, transport=transport) except Exception as e: self.log_and_exit("Client couldn't create socket {}: {}".format(self.uri, e)) @property def quiet(self): return self._quiet 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_glances(self): """Login 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 ({} {})".format(err.errno, err.strerror)) fallbackmsg = 'No Glances server found on {}. Trying fallback to SNMP...'.format(self.uri) if not self.return_to_browser: print(fallbackmsg) else: logger.info(fallbackmsg) except ProtocolError as err: # Other errors msg = "Connection to server {} failed".format(self.uri) if err.errcode == 401: msg += " (Bad username/password)" else: msg += " ({} {})".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: {} / Server version: {}".format(__version__, client_version)) else: self.log_and_exit(('Client and server not compatible: ' 'Client version: {} / Server version: {}'.format(__version__, client_version))) return False return True def _login_snmp(self): """Login to a SNMP server""" logger.info("Trying to grab stats by SNMP...") from glances.stats_client_snmp 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 return True def login(self): """Logon to the server.""" if self.args.snmp_force: # Force SNMP instead of Glances server self.client_mode = 'snmp' else: # First of all, trying to connect to a Glances server if not self._login_glances(): return False # Try SNMP mode if self.client_mode == 'snmp': if not self._login_snmp(): return False # Load limits from the configuration file # Each client can choose its owns limits logger.debug("Load limits from the client configuration file") self.stats.load_limits(self.config) # Init screen if self.quiet: # In quiet mode, nothing is displayed logger.info("Quiet mode is ON: Nothing will be displayed") else: self.screen = GlancesCursesClient(config=self.config, args=self.args) # Return True: OK return True 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: {}".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()) 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.""" # Test if client and server are in the same major version if not self.login(): logger.critical("The server version is not compatible with the client") self.end() return self.client_mode exitkey = False try: while True and not exitkey: # Update the stats cs_status = self.update() # Update the screen if not self.quiet: 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.""" if not self.quiet: self.screen.end()
import json import shlex import subprocess import time import unittest from glances import __version__ from glances.compat import ServerProxy SERVER_PORT = 61234 URL = "http://localhost:%s" % SERVER_PORT pid = None # Init the XML-RPC client client = ServerProxy(URL) # Unitest class # ============== print('XML-RPC API unitary tests for Glances %s' % __version__) class TestGlances(unittest.TestCase): """Test Glances class.""" def setUp(self): """The function is called *every time* before test_*.""" print('\n' + '=' * 78) def test_000_start_server(self): """Start the Glances Web Server.""" global pid