From af62415923436b9c288c4b48d87fd309e562d5c2 Mon Sep 17 00:00:00 2001 From: Nicolargo Date: Sun, 16 Nov 2014 19:15:46 +0100 Subject: [PATCH] Browser first public version ! --- glances/__init__.py | 2 +- glances/core/glances_autodiscover.py | 33 ++++++++----- glances/core/glances_client.py | 26 ++++------ glances/core/glances_client_browser.py | 56 +++++++++++++++++----- glances/outputs/glances_curses.py | 66 ++++++++++++++++++-------- 5 files changed, 124 insertions(+), 59 deletions(-) diff --git a/glances/__init__.py b/glances/__init__.py index 4c6d9234..d552abc1 100644 --- a/glances/__init__.py +++ b/glances/__init__.py @@ -151,7 +151,7 @@ def main(): client.serve_forever() # Shutdown the client - client.close() + client.end() elif core.is_server(): logger.info("Start server mode") diff --git a/glances/core/glances_autodiscover.py b/glances/core/glances_autodiscover.py index 830b1331..f21f8991 100644 --- a/glances/core/glances_autodiscover.py +++ b/glances/core/glances_autodiscover.py @@ -21,7 +21,6 @@ # Import system libs import socket -import time try: import netifaces netifaces_tag = True @@ -55,9 +54,14 @@ class AutoDiscovered(object): def add_server(self, name, ip, port): """Add a new server to the list""" - new_server = {'key': name, 'name': name.split(':')[0], 'ip': ip, 'port': port} + new_server = {'key': name, # Zeroconf name with both hostname and port + 'name': name.split(':')[0], # Short name + 'ip': ip, # IP address seen by the client + 'port': port, # TCP port + } self._server_list.append(new_server) - logger.debug("Servers list: %s" % self._server_list) + logger.debug("Updated servers list (%s servers): %s" % + (len(self._server_list), self._server_list)) def remove_server(self, name): """Remove a server from the dict""" @@ -66,9 +70,11 @@ class AutoDiscovered(object): try: self._server_list.remove(i) logger.debug("Remove server %s from the list" % name) - logger.debug("Updated servers list: %s" % self._server_list) + logger.debug("Updated servers list (%s servers): %s" % ( + len(self._server_list), self._server_list)) except ValueError: - logger.error("Can not remove server %s from the list" % name) + logger.error( + "Can not remove server %s from the list" % name) class GlancesAutoDiscoverListener(object): @@ -90,7 +96,8 @@ class GlancesAutoDiscoverListener(object): """ if srv_type != zeroconf_type: return False - logger.debug("Check new Zeroconf server: %s / %s" % (srv_type, srv_name)) + logger.debug("Check new Zeroconf server: %s / %s" % + (srv_type, srv_name)) info = zeroconf.getServiceInfo(srv_type, srv_name) if info: new_server_ip = socket.inet_ntoa(info.getAddress()) @@ -145,21 +152,23 @@ class GlancesAutoDiscoverClient(object): def __init__(self, hostname, args=None): if netifaces_tag: - # !!! TO BE REFACTOR try: - zeroconf_bind_address = netifaces.ifaddresses(netifaces.interfaces()[1])[netifaces.AF_INET][0]['addr'] + zeroconf_bind_address = netifaces.ifaddresses( + netifaces.interfaces()[1])[netifaces.AF_INET][0]['addr'] except: zeroconf_bind_address = args.bind_address - print("Announce the Glances server on the local area network (using %s IP address)" % zeroconf_bind_address) - # /!!! + print("Announce the Glances server on the local area network (using %s IP address)" % + zeroconf_bind_address) if zeroconf_tag: logger.info( "Announce the Glances server on the local area network (using %s IP address)" % zeroconf_bind_address) self.zeroconf = Zeroconf() self.info = ServiceInfo(zeroconf_type, - hostname+':'+str(args.port)+'.'+zeroconf_type, - address=socket.inet_aton(zeroconf_bind_address), + hostname + ':' + + str(args.port) + '.' + zeroconf_type, + address=socket.inet_aton( + zeroconf_bind_address), port=args.port, weight=0, priority=0, diff --git a/glances/core/glances_client.py b/glances/core/glances_client.py index 1f258933..fafdb4ab 100644 --- a/glances/core/glances_client.py +++ b/glances/core/glances_client.py @@ -78,20 +78,9 @@ class GlancesClient(object): try: self.client = ServerProxy(uri, transport=transport) except Exception as e: - logger.error("Couldn't create socket {0}: {1}".format(uri, e)) + logger.error("Client couldn't create socket {0}: {1}".format(uri, e)) sys.exit(2) - # Start the autodiscover mode (Zeroconf listener) - if self.args.autodiscover: - self.autodiscover_server = GlancesAutoDiscoverServer() - - def get_servers_list(self): - """Return the current server list (dict of dict)""" - if self.args.autodiscover: - return self.autodiscover_server.get_servers_list() - else: - return {} - def set_mode(self, mode='glances'): """Set the client mode. @@ -175,7 +164,7 @@ class GlancesClient(object): return self.update_snmp() else: self.end() - logger.critical("Unknown server mode: {0}").format(self.get_mode()) + logger.critical(_("Unknown server mode: {0}").format(self.get_mode())) sys.exit(2) def update_glances(self): @@ -217,14 +206,19 @@ class GlancesClient(object): # Grab success return "SNMP" - def serve_forever(self): + def serve_forever(self, return_to_browser=False): """Main client loop.""" - while True: + + exitkey = False + + while True and not exitkey: # Update the stats cs_status = self.update() # Update the screen - self.screen.update(self.stats, cs_status=cs_status) + exitkey = self.screen.update(self.stats, + cs_status=cs_status, + return_to_browser=return_to_browser) def end(self): """End of the client session.""" diff --git a/glances/core/glances_client_browser.py b/glances/core/glances_client_browser.py index 6896d208..add12059 100644 --- a/glances/core/glances_client_browser.py +++ b/glances/core/glances_client_browser.py @@ -32,7 +32,7 @@ except ImportError: from glances.core.glances_globals import logger from glances.outputs.glances_curses import GlancesCursesBrowser from glances.core.glances_autodiscover import GlancesAutoDiscoverServer -from glances.core.glances_client import GlancesClientTransport +from glances.core.glances_client import GlancesClientTransport, GlancesClient class GlancesClientBrowser(object): @@ -52,7 +52,7 @@ class GlancesClientBrowser(object): self.screen = GlancesCursesBrowser(args=self.args) def get_servers_list(self): - """Return the current server list (dict of dict)""" + """Return the current server list (list of dict)""" if self.args.autodiscover: return self.autodiscover_server.get_servers_list() else: @@ -78,26 +78,60 @@ class GlancesClientBrowser(object): try: s = ServerProxy(uri, t) except Exception as e: - logger.warning("Couldn't create socket {0}: {1}".format(uri, e)) + logger.warning( + "Client browser couldn't create socket {0}: {1}".format(uri, e)) else: try: # LOAD v['load_min5'] = json.loads(s.getLoad())['min5'] # CPU% - v['cpu_percent'] = 100 - json.loads(s.getCpu())['idle'] + v['cpu_percent'] = 100 - \ + json.loads(s.getCpu())['idle'] # MEM% - v['mem_percent'] = json.loads(s.getMem())['percent'] + 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.warning("Can not grab stats form {0}: {1}".format(uri, e)) + logger.warning( + "Can not grab 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)") + logger.debug( + "Server list dictionnary change inside the loop (wait next update)") - # Update the screen - self.screen.update(self.get_servers_list()) + # Update the screen (list or Glances client) + if self.screen.get_active() is None: + # Display the Glances browser + self.screen.update(self.get_servers_list()) + else: + # Display the Glance client on the selected server + logger.info("Connect Glances client to the %s server" % + self.get_servers_list()[self.screen.get_active()]['key']) + + # Init the client + args_server = self.args + + # Overwrite connection setting + args_server.client = self.get_servers_list( + )[self.screen.get_active()]['ip'] + args_server.port = self.get_servers_list()[self.screen.get_active()][ + 'port'] + client = GlancesClient(config=self.config, + args=args_server) + + # Test if client and server are in the same major version + if not client.login(): + logger.error( + "The server version is not compatible with the client") + + # Start the client loop + client.serve_forever(return_to_browser=True) + + logger.debug("Disconnect Glances client from the %s server" % + self.get_servers_list()[self.screen.get_active()]['key']) + self.screen.set_active(None) def end(self): - """End of the client session.""" - pass + """End of the client browser session.""" + self.screen.end() diff --git a/glances/outputs/glances_curses.py b/glances/outputs/glances_curses.py index ca496a8f..a7251c89 100644 --- a/glances/outputs/glances_curses.py +++ b/glances/outputs/glances_curses.py @@ -19,8 +19,6 @@ """Curses interface class.""" -# !!! TODO: split GlancesCurses for client and client_browser - # Import system lib import sys @@ -218,7 +216,7 @@ class _GlancesCurses(object): keycode[1] = window.getch() if keycode != [-1, -1]: - logger.debug("Keypressed ! Code: %s" % keycode) + logger.debug("Keypressed (code: %s)" % keycode) if keycode[0] == 27 and keycode[1] != -1: # Do not escape on specials keys @@ -226,16 +224,19 @@ class _GlancesCurses(object): else: return keycode[0] - def __catch_key(self): + def __catch_key(self, return_to_browser=False): # Catch the pressed key self.pressedkey = self.get_key(self.term_window) # Actions... if self.pressedkey == ord('\x1b') or self.pressedkey == ord('q'): # 'ESC'|'q' > Quit - self.end() - logger.info("Stop Glances") - sys.exit(0) + if return_to_browser: + logger.info("Stop Glances client and return to the browser") + else: + self.end() + logger.info("Stop Glances") + sys.exit(0) elif self.pressedkey == 10: # 'ENTER' > Edit the process filter self.edit_filter = not self.edit_filter @@ -736,30 +737,44 @@ class _GlancesCurses(object): self.erase() self.display(stats, cs_status=cs_status) - def update(self, stats, cs_status="None"): + def update(self, stats, cs_status="None", return_to_browser=False): """Update the screen. Wait for __refresh_time sec / catch key every 100 ms. + INPUT stats: Stats database to display cs_status: "None": standalone or server mode "Connected": Client is connected to the server "Disconnected": Client is disconnected from the server + return_to_browser: + True: Do not exist, return to the browser list + False: Exit and return to the shell + + OUPUT + True: Exit key has been pressed + False: Others cases... """ # Flush display self.flush(stats, cs_status=cs_status) # Wait + exitkey = False countdown = Timer(self.__refresh_time) - while not countdown.finished(): + while not countdown.finished() and not exitkey: # Getkey - if self.__catch_key() > -1: + pressedkey = self.__catch_key(return_to_browser=return_to_browser) + # Is it an exit key ? + exitkey = (pressedkey == ord('\x1b') or pressedkey == ord('q')) + if not exitkey and pressedkey > -1: # Redraw display self.flush(stats, cs_status=cs_status) # Wait 100ms... curses.napms(100) + return exitkey + def get_stats_display_width(self, curse_msg, without_option=False): """Return the width of the formatted curses message. @@ -813,12 +828,24 @@ class GlancesCursesBrowser(_GlancesCurses): # Init the cursor position for the client browser self.cursor_init() + # Active Glances server number + self.set_active() + + def set_active(self, index=None): + """Set the active server or None if no server selected""" + self.active_server = index + return self.active_server + + def get_active(self): + """Return the active server (the one display in front) or None if it is the browser list""" + return self.active_server + def cursor_init(self): """Init the cursor position to the top of the list""" return self.cursor_set(0) def cursor_set(self, pos): - """Set the cursor position andd return it""" + """Set the cursor position and return it""" self.cursor_position = pos return self.cursor_position @@ -851,7 +878,7 @@ class GlancesCursesBrowser(_GlancesCurses): elif self.pressedkey == 10: # 'ENTER' > Run Glances on the selected server logger.debug("Server number %s selected" % (self.cursor_get() + 1)) - self.run_client(servers_list[self.cursor_get()]) + self.set_active(self.cursor_get()) elif self.pressedkey == 259: # 'UP' > Up in the server list logger @@ -863,11 +890,6 @@ class GlancesCursesBrowser(_GlancesCurses): # Return the key code return self.pressedkey - def run_client(self, server): - """Run the Glances client to the given server""" - - logger.debug("Run Glances client on %s" % server) - def update(self, servers_list): """Update the servers' list screen. @@ -879,15 +901,21 @@ class GlancesCursesBrowser(_GlancesCurses): self.flush(servers_list) # Wait + exitkey = False countdown = Timer(self.__refresh_time) - while not countdown.finished(): + while not countdown.finished() and not exitkey: # Getkey - if self.__catch_key(servers_list) > -1: + pressedkey = self.__catch_key(servers_list) + # Is it an exit or select server key ? + exitkey = (pressedkey == ord('\x1b') or pressedkey == ord('q') or pressedkey == 10) + if not exitkey and pressedkey > -1: # Redraw display self.flush(servers_list) # Wait 100ms... curses.napms(100) + return self.get_active() + def flush(self, servers_list): """Update the servers' list screen. servers_list: List of dict with servers stats