def end(): """Stop Glances.""" mode.end() logger.info("Glances stopped with CTRL-C") # The end... sys.exit(0)
def run(self, stat_name, criticity, commands, mustache_dict=None): """Run the commands (in background). - stats_name: plugin_name (+ header) - criticity: criticity of the trigger - commands: a list of command line with optional {{mustache}} - mustache_dict: Plugin stats (can be use within {{mustache}}) Return True if the commands have been ran. """ if self.get(stat_name) == criticity or not self.start_timer.finished(): # Action already executed => Exit return False logger.debug("Run action {} for {} ({}) with stats {}".format( commands, stat_name, criticity, mustache_dict)) # Run all actions in background for cmd in commands: # Replace {{arg}} by the dict one (Thk to {Mustache}) if pystache_tag: cmd_full = pystache.render(cmd, mustache_dict) else: cmd_full = cmd # Execute the action logger.info("Action triggered for {} ({}): {}".format(stat_name, criticity, cmd_full)) logger.debug("Stats value for the trigger: {}".format(mustache_dict)) try: Popen(cmd_full, shell=True) except OSError as e: logger.error("Can't execute the action ({})".format(e)) self.set(stat_name, criticity) 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 check_snmp(self): """Chek if SNMP is available on the server.""" # Import the SNMP client class from glances.snmp import GlancesSNMPClient # Create an instance of the SNMP client clientsnmp = GlancesSNMPClient(host=self.args.client, port=self.args.snmp_port, version=self.args.snmp_version, community=self.args.snmp_community, user=self.args.snmp_user, auth=self.args.snmp_auth) # If we cannot grab the hostname, then exit... ret = clientsnmp.get_by_oid("1.3.6.1.2.1.1.5.0") != {} if ret: # Get the OS name (need to grab the good OID...) oid_os_name = clientsnmp.get_by_oid("1.3.6.1.2.1.1.1.0") try: self.system_name = self.get_system_name(oid_os_name['1.3.6.1.2.1.1.1.0']) logger.info("SNMP system name detected: {}".format(self.system_name)) except KeyError: self.system_name = None logger.warning("Cannot detect SNMP system name") return ret
def init(self): """Init the connection to the InfluxDB server.""" if not self.export_enable: return None # Cluster try: cluster = Cluster([self.host], port=int(self.port), protocol_version=int(self.protocol_version)) session = cluster.connect() except Exception as e: logger.critical("Cannot connect to Cassandra cluster '%s:%s' (%s)" % (self.host, self.port, e)) sys.exit(2) # Keyspace try: session.set_keyspace(self.keyspace) except InvalidRequest as e: logger.info("Create keyspace {} on the Cassandra cluster".format(self.keyspace)) c = "CREATE KEYSPACE %s WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': '%s' }" % (self.keyspace, self.replication_factor) session.execute(c) session.set_keyspace(self.keyspace) logger.info( "Stats will be exported to Cassandra cluster {} ({}) in keyspace {}".format( cluster.metadata.cluster_name, cluster.metadata.all_hosts(), self.keyspace)) # Table try: session.execute("CREATE TABLE %s (plugin text, time timeuuid, stat map<text,float>, PRIMARY KEY (plugin, time)) WITH CLUSTERING ORDER BY (time DESC)" % self.table) except Exception: logger.debug("Cassandra table %s already exist" % self.table) return cluster, session
def start(config, args): """Start Glances""" # Load mode global mode if core.is_standalone(): from glances.standalone import GlancesStandalone as GlancesMode elif core.is_client(): if core.is_client_browser(): from glances.client_browser import GlancesClientBrowser as GlancesMode else: from glances.client import GlancesClient as GlancesMode elif core.is_server(): from glances.server import GlancesServer as GlancesMode elif core.is_webserver(): from glances.webserver import GlancesWebServer as GlancesMode # Init the mode logger.info("Start {} mode".format(GlancesMode.__name__)) mode = GlancesMode(config=config, args=args) # Start the main loop mode.serve_forever() # Shutdown mode.end()
def main(): """Main entry point for Glances. Select the mode (standalone, client or server) Run it... """ # Log Glances and PSutil version logger.info('Start Glances {}'.format(__version__)) logger.info('{} {} and PSutil {} detected'.format( platform.python_implementation(), platform.python_version(), psutil_version)) # Share global var global core # Create the Glances main instance core = GlancesMain() config = core.get_config() args = core.get_args() # Catch the CTRL-C signal signal.signal(signal.SIGINT, __signal_handler) # Glances can be ran in standalone, client or server mode start(config=config, args=args)
def __init__(self, requestHandler=GlancesXMLRPCHandler, cached_time=1, config=None, args=None): # Args self.args = args # Init the XML RPC server try: self.server = GlancesXMLRPCServer(args.bind_address, args.port, requestHandler) except Exception as e: logger.critical("Cannot start Glances server: {}".format(e)) sys.exit(2) # The users dict # username / password couple # By default, no auth is needed self.server.user_dict = {} self.server.isAuth = False # Register functions self.server.register_introspection_functions() self.server.register_instance(GlancesInstance(cached_time, config)) if not self.args.disable_autodiscover: # Note: The Zeroconf service name will be based on the hostname # Correct issue: Zeroconf problem with zeroconf service name #889 self.autodiscover_client = GlancesAutoDiscoverClient(socket.gethostname().split('.', 1)[0], args) else: logger.info("Glances autodiscover announce is disabled")
def init(self): """Init the connection to the InfluxDB server.""" if not self.export_enable: return None try: db = InfluxDBClient(host=self.host, port=self.port, username=self.user, password=self.password, database=self.db) get_all_db = [i['name'] for i in db.get_list_database()] self.version = INFLUXDB_09PLUS except InfluxDBClientError: # https://github.com/influxdb/influxdb-python/issues/138 logger.info("Trying fallback to InfluxDB v0.8") db = InfluxDBClient08(host=self.host, port=self.port, username=self.user, password=self.password, database=self.db) get_all_db = [i['name'] for i in db.get_list_database()] self.version = INFLUXDB_08 except InfluxDBClientError08 as e: logger.critical("Cannot connect to InfluxDB database '%s' (%s)" % (self.db, e)) sys.exit(2) if self.db in get_all_db: logger.info( "Stats will be exported to InfluxDB server: {}".format(db._baseurl)) else: logger.critical("InfluxDB database '%s' did not exist. Please create it" % self.db) sys.exit(2) return db
def filter(self, value): """Set the filter (as a sting) and compute the regular expression A filter could be one of the following: - python > Process name of cmd start with python - .*python.* > Process name of cmd contain python - username:nicolargo > Process of nicolargo user """ self._filter_input = value if value is None: self._filter = None self._filter_key = None else: new_filter = value.split(':') if len(new_filter) == 1: self._filter = new_filter[0] self._filter_key = None else: self._filter = new_filter[1] self._filter_key = new_filter[0] self._filter_re = None if self.filter is not None: logger.info("Set filter to {} on key {}".format(self.filter, self.filter_key)) # Compute the regular expression try: self._filter_re = re.compile(self.filter) logger.debug("Filter regex compilation OK: {}".format(self.filter)) except Exception as e: logger.error("Cannot compile filter regex: {} ({})".format(self.filter, e)) self._filter = None self._filter_re = None self._filter_key = None
def init(self): """Init the connection to the CouchDB server.""" if not self.export_enable: return None if self.user is None: server_uri = 'http://{}:{}/'.format(self.host, self.port) else: server_uri = 'http://{}:{}@{}:{}/'.format(self.user, self.password, self.host, self.port) try: s = couchdb.Server(server_uri) except Exception as e: logger.critical("Cannot connect to CouchDB server %s (%s)" % (server_uri, e)) sys.exit(2) else: logger.info("Connected to the CouchDB server %s" % server_uri) try: s[self.db] except Exception as e: # Database did not exist # Create it... s.create(self.db) else: logger.info("There is already a %s database" % self.db) return s
def __catch_key(self, stats): # Catch the browser pressed key self.pressedkey = self.get_key(self.term_window) if self.pressedkey != -1: logger.debug("Key pressed. Code=%s" % self.pressedkey) # Actions... if self.pressedkey == ord('\x1b') or self.pressedkey == ord('q'): # 'ESC'|'q' > Quit self.end() logger.info("Stop Glances client browser") sys.exit(0) elif self.pressedkey == 10: # 'ENTER' > Run Glances on the selected server self.active_server = self.cursor logger.debug("Server {}/{} selected".format(self.cursor + 1, len(stats))) elif self.pressedkey == curses.KEY_UP or self.pressedkey == 65: # 'UP' > Up in the server list self.cursor_up(stats) logger.debug("Server {}/{} selected".format(self.cursor + 1, len(stats))) elif self.pressedkey == curses.KEY_DOWN or self.pressedkey == 66: # 'DOWN' > Down in the server list self.cursor_down(stats) logger.debug("Server {}/{} selected".format(self.cursor + 1, len(stats))) # Return the key code return self.pressedkey
def init(self): """Init the Prometheus Exporter""" try: start_http_server(port=int(self.port), addr=self.host) except Exception as e: logger.critical("Can not start Prometheus exporter on {}:{} ({})".format(self.host, self.port, e)) sys.exit(2) else: logger.info("Start Prometheus exporter on {}:{}".format(self.host, self.port))
def init(self): """Init the connection to the Statsd server.""" if not self.export_enable: return None logger.info( "Stats will be exported to StatsD server: {}:{}".format(self.host, self.port)) return StatsClient(self.host, int(self.port), prefix=self.prefix)
def __init__(self, config=None, args=None): self.config = config self.args = args # Quiet mode self._quiet = args.quiet self.refresh_time = args.time # Init stats self.stats = GlancesStats(config=config, args=args) # Modules (plugins and exporters) are loaded at this point # Glances can display the list if asked... if args.modules_list: self.display_modules_list() sys.exit(0) # If process extended stats is disabled by user if not args.enable_process_extended: logger.debug("Extended stats for top process are disabled") glances_processes.disable_extended() else: logger.debug("Extended stats for top process are enabled") glances_processes.enable_extended() # Manage optionnal process filter if args.process_filter is not None: glances_processes.process_filter = args.process_filter if (not WINDOWS) and args.no_kernel_threads: # Ignore kernel threads in process list glances_processes.disable_kernel_threads() # Initial system informations update self.stats.update() if self.quiet: logger.info("Quiet mode is ON, nothing will be displayed") # In quiet mode, nothing is displayed glances_processes.max_processes = 0 elif args.stdout: logger.info("Stdout mode is ON, following stats will be displayed: {}".format(args.stdout)) # Init screen self.screen = GlancesStdout(config=config, args=args) else: # Default number of processes to displayed is set to 50 glances_processes.max_processes = 50 # Init screen self.screen = GlancesCursesStandalone(config=config, args=args) # Check the latest Glances version self.outdated = Outdated(config=config, args=args)
def init(self): """Init the connection to the restful server.""" if not self.export_enable: return None # Build the Restful URL where the stats will be posted url = '{}://{}:{}{}'.format(self.protocol, self.host, self.port, self.path) logger.info( "Stats will be exported to the restful endpoint {}".format(url)) return url
def _init_history(self): """Init the history option.""" self.reset_history_tag = False self.graph_tag = False if self.args.export_graph: logger.info('Export graphs function enabled with output path %s' % self.args.path_graph) from glances.exports.graph import GlancesGraph self.glances_graph = GlancesGraph(self.args.path_graph) if not self.glances_graph.graph_enabled(): self.args.export_graph = False logger.error('Export graphs disabled')
def end(): """Stop Glances.""" try: mode.end() except NameError: # NameError: name 'mode' is not defined in case of interrupt shortly... # ...after starting the server mode (issue #1175) pass logger.info("Glances stopped (keypressed: CTRL-C)") # The end... sys.exit(0)
def start(self, stats): """Start the bottle.""" # Init stats self.stats = stats # Init plugin list self.plugins_list = self.stats.getAllPlugins() # Bind the Bottle TCP address/port bindmsg = 'Glances web server started on http://{0}:{1}/'.format(self.args.bind_address, self.args.port) logger.info(bindmsg) print(bindmsg) self._app.run(host=self.args.bind_address, port=self.args.port, quiet=not self.args.debug)
def process_filter(self, value): """Set the process filter.""" logger.info("Set process filter to {0}".format(value)) self._process_filter = value if value is not None: try: self._process_filter_re = re.compile(value) logger.debug("Process filter regex compilation OK: {0}".format(self.process_filter)) except Exception: logger.error("Cannot compile process filter regex: {0}".format(value)) self._process_filter_re = None else: self._process_filter_re = None
def _init_history(self): '''Init the history option''' self.reset_history_tag = False self.graph_tag = False if self.args.export_graph: logger.info('Export graphs function enabled with output path %s' % self.args.path_graph) from glances.exports.graph import GlancesGraph self.glances_graph = GlancesGraph(self.args.path_graph) if not self.glances_graph.graph_enabled(): self.args.export_graph = False logger.error('Export graphs disabled')
def __init__(self, config=None, args=None): # Quiet mode self._quiet = args.quiet self.refresh_time = args.time # Init stats self.stats = GlancesStats(config=config, args=args) # If process extended stats is disabled by user if not args.enable_process_extended: logger.debug("Extended stats for top process are disabled") glances_processes.disable_extended() else: logger.debug("Extended stats for top process are enabled") glances_processes.enable_extended() # Manage optionnal process filter if args.process_filter is not None: glances_processes.process_filter = args.process_filter if (not WINDOWS) and args.no_kernel_threads: # Ignore kernel threads in process list glances_processes.disable_kernel_threads() try: if args.process_tree: # Enable process tree view glances_processes.enable_tree() except AttributeError: pass # Initial system informations update self.stats.update() if self.quiet: logger.info("Quiet mode is ON: Nothing will be displayed") # In quiet mode, nothing is displayed glances_processes.max_processes = 0 else: # Default number of processes to displayed is set to 50 glances_processes.max_processes = 50 # Init screen self.screen = GlancesCursesStandalone(config=config, args=args) # Check the latest Glances version self.outdated = Outdated(config=config, args=args) # Create the schedule instance self.schedule = sched.scheduler(timefunc=time.time, delayfunc=time.sleep)
def __init__(self, config=None, args=None): # Quiet mode self._quiet = args.quiet self.refresh_time = args.time # Init stats self.stats = GlancesStats(config=config, args=args) # If process extended stats is disabled by user if not args.enable_process_extended: logger.debug("Extended stats for top process are disabled") glances_processes.disable_extended() else: logger.debug("Extended stats for top process are enabled") glances_processes.enable_extended() # Manage optionnal process filter if args.process_filter is not None: glances_processes.process_filter = args.process_filter if (not WINDOWS) and args.no_kernel_threads: # Ignore kernel threads in process list glances_processes.disable_kernel_threads() try: if args.process_tree: # Enable process tree view glances_processes.enable_tree() except AttributeError: pass # Initial system informations update self.stats.update() if self.quiet: logger.info("Quiet mode is ON: Nothing will be displayed") # In quiet mode, nothing is displayed glances_processes.max_processes = 0 else: # Default number of processes to displayed is set to 50 glances_processes.max_processes = 50 # Init screen self.screen = GlancesCursesStandalone(config=config, args=args) # Check the latest Glances version self.outdated = Outdated(config=config, args=args) # Create the schedule instance self.schedule = sched.scheduler( timefunc=time.time, delayfunc=time.sleep)
def _init_history(self): '''Init the history option''' self.reset_history_tag = False self.history_tag = False if self.args.enable_history: logger.info('Stats history enabled with output path %s' % self.args.path_history) from glances.exports.glances_history import GlancesHistory self.glances_history = GlancesHistory(self.args.path_history) if not self.glances_history.graph_enabled(): self.args.enable_history = False logger.error( 'Stats history disabled because MatPlotLib is not installed')
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 process_filter(self, value): """Set the process filter.""" logger.info("Set process filter to {0}".format(value)) self._process_filter = value if value is not None: try: self._process_filter_re = re.compile(value) logger.debug("Process filter regex compilation OK: {0}".format( self.process_filter)) except Exception: logger.error( "Cannot compile process filter regex: {0}".format(value)) self._process_filter_re = None else: self._process_filter_re = None
def start_webserver(config, args): """Start the Web server mode""" logger.info("Start web server mode") # Share global var global webserver # Import the Glances web server module from glances.webserver import GlancesWebServer # Init the web server mode webserver = GlancesWebServer(config=config, args=args) # Start the web server loop webserver.serve_forever()
def _init_history(self): '''Init the history option''' self.reset_history_tag = False self.history_tag = False if self.args.enable_history: logger.info('Stats history enabled with output path %s' % self.args.path_history) from glances.exports.glances_history import GlancesHistory self.glances_history = GlancesHistory(self.args.path_history) if not self.glances_history.graph_enabled(): self.args.enable_history = False logger.error( 'Stats history disabled because MatPlotLib is not installed' )
def start_standalone(config, args): """Start the standalone mode""" logger.info("Start standalone mode") # Share global var global standalone # Import the Glances standalone module from glances.standalone import GlancesStandalone # Init the standalone mode standalone = GlancesStandalone(config=config, args=args) # Start the standalone (CLI) loop standalone.serve_forever()
def __init__(self, args=None): if zeroconf_tag: logger.info("Init autodiscover mode (Zeroconf protocol)") try: self.zeroconf = Zeroconf() except socket.error as e: logger.error("Cannot start Zeroconf (%s)" % e) self.zeroconf_enable_tag = False else: self.listener = GlancesAutoDiscoverListener() self.browser = ServiceBrowser( self.zeroconf, zeroconf_type, self.listener) self.zeroconf_enable_tag = True else: logger.error("Cannot start autodiscover mode (Zeroconf lib is not installed)") self.zeroconf_enable_tag = False
def start(self, stats): """Start the bottle.""" # Init stats self.stats = stats # Init plugin list self.plugins_list = self.stats.getAllPlugins() # Bind the Bottle TCP address/port bindmsg = 'Glances web server started on http://{0}:{1}/'.format( self.args.bind_address, self.args.port) logger.info(bindmsg) print(bindmsg) self._app.run(host=self.args.bind_address, port=self.args.port, quiet=not self.args.debug)
def init(self): """Init the connection to the Cassandra server.""" if not self.export_enable: return None # if username and/or password are not set the connection will try to connect with no auth auth_provider = PlainTextAuthProvider(username=self.username, password=self.password) # Cluster try: cluster = Cluster([self.host], port=int(self.port), protocol_version=int(self.protocol_version), auth_provider=auth_provider) session = cluster.connect() except Exception as e: logger.critical( "Cannot connect to Cassandra cluster '%s:%s' (%s)" % (self.host, self.port, e)) sys.exit(2) # Keyspace try: session.set_keyspace(self.keyspace) except InvalidRequest as e: logger.info("Create keyspace {} on the Cassandra cluster".format( self.keyspace)) c = "CREATE KEYSPACE %s WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': '%s' }" % ( self.keyspace, self.replication_factor) session.execute(c) session.set_keyspace(self.keyspace) logger.info( "Stats will be exported to Cassandra cluster {} ({}) in keyspace {}" .format(cluster.metadata.cluster_name, cluster.metadata.all_hosts(), self.keyspace)) # Table try: session.execute( "CREATE TABLE %s (plugin text, time timeuuid, stat map<text,float>, PRIMARY KEY (plugin, time)) WITH CLUSTERING ORDER BY (time DESC)" % self.table) except Exception: logger.debug("Cassandra table %s already exist" % self.table) return cluster, session
def get_password(self, description='', confirm=False, clear=False): """Get the password from a Glances client or server. For Glances server, get the password (confirm=True, clear=False): 1) from the password file (if it exists) 2) from the CLI Optionally: save the password to a file (hashed with salt + SHA-256) For Glances client, get the password (confirm=False, clear=True): 1) from the CLI 2) the password is hashed with SHA-256 (only SHA string transit through the network) """ if os.path.exists(self.password_file) and not clear: # If the password file exist then use it logger.info("Read password from file {}".format( self.password_file)) password = self.load_password() else: # password_sha256 is the plain SHA-256 password # password_hashed is the salt + SHA-256 password password_sha256 = self.sha256_hash(getpass.getpass(description)) password_hashed = self.hash_password(password_sha256) if confirm: # password_confirm is the clear password (only used to compare) password_confirm = self.sha256_hash( getpass.getpass('Password (confirm): ')) if not self.check_password(password_hashed, password_confirm): logger.critical("Sorry, passwords do not match. Exit.") sys.exit(1) # Return the plain SHA-256 or the salted password if clear: password = password_sha256 else: password = password_hashed # Save the hashed password to the password file if not clear: save_input = input( 'Do you want to save the password? [Yes/No]: ') if len(save_input) > 0 and save_input[0].upper() == 'Y': self.save_password(password_hashed) return password
def init(self): """Init the connection to the ES server.""" if not self.export_enable: return None try: es = Elasticsearch(hosts=['{}:{}'.format(self.host, self.port)]) except Exception as e: logger.critical( "Cannot connect to ElasticSearch server %s:%s (%s)" % (self.host, self.port, e)) sys.exit(2) else: logger.info("Connected to the ElasticSearch server %s:%s" % (self.host, self.port)) return es
def load(self, config): """Load the server list from the configuration file.""" server_list = [] if config is None: logger.debug("No configuration file available. Cannot load server list.") elif not config.has_section(self._section): logger.warning("No [%s] section in the configuration file. Cannot load server list." % self._section) else: logger.info("Start reading the [%s] section in the configuration file" % self._section) for i in range(1, 256): new_server = {} postfix = 'server_%s_' % str(i) # Read the server name (mandatory) for s in ['name', 'port', 'alias']: new_server[s] = config.get_value(self._section, '%s%s' % (postfix, s)) if new_server['name'] is not None: # Manage optionnal information if new_server['port'] is None: new_server['port'] = '61209' new_server['username'] = '******' # By default, try empty (aka no) password new_server['password'] = '' try: new_server['ip'] = gethostbyname(new_server['name']) except gaierror as e: logger.error("Cannot get IP address for server %s (%s)" % (new_server['name'], e)) continue new_server['key'] = new_server['name'] + ':' + new_server['port'] # Default status is 'UNKNOWN' new_server['status'] = 'UNKNOWN' # Server type is 'STATIC' new_server['type'] = 'STATIC' # Add the server to the list logger.debug("Add server %s to the static list" % new_server['name']) server_list.append(new_server) # Server list loaded logger.info("%s server(s) loaded from the configuration file" % len(server_list)) logger.debug("Static server list: %s" % server_list) return server_list
def load(self, config): """Load the server list from the configuration file.""" server_list = [] if config is None: logger.warning("No configuration file available. Cannot load server list.") elif not config.has_section(self._section): logger.warning("No [%s] section in the configuration file. Cannot load server list." % self._section) else: logger.info("Start reading the [%s] section in the configuration file" % self._section) for i in range(1, 256): new_server = {} postfix = "server_%s_" % str(i) # Read the server name (mandatory) for s in ["name", "port", "alias"]: new_server[s] = config.get_value(self._section, "%s%s" % (postfix, s)) if new_server["name"] is not None: # Manage optionnal information if new_server["port"] is None: new_server["port"] = "61209" new_server["username"] = "******" # By default, try empty (aka no) password new_server["password"] = "" try: new_server["ip"] = gethostbyname(new_server["name"]) except gaierror as e: logger.error("Cannot get IP address for server %s (%s)" % (new_server["name"], e)) continue new_server["key"] = new_server["name"] + ":" + new_server["port"] # Default status is 'UNKNOWN' new_server["status"] = "UNKNOWN" # Server type is 'STATIC' new_server["type"] = "STATIC" # Add the server to the list logger.debug("Add server %s to the static list" % new_server["name"]) server_list.append(new_server) # Server list loaded logger.info("%s server(s) loaded from the configuration file" % len(server_list)) logger.debug("Static server list: %s" % server_list) return server_list
def load(self, config): """Load the server list from the configuration file.""" server_list = [] if config is None: logger.warning("No configuration file available. Cannot load server list.") elif not config.has_section(self._section): logger.warning("No [%s] section in the configuration file. Cannot load server list." % self._section) else: logger.info("Start reading the [%s] section in the configuration file" % self._section) for i in range(1, 256): new_server = {} postfix = 'server_%s_' % str(i) # Read the server name (mandatory) for s in ['name', 'port', 'alias']: new_server[s] = config.get_value(self._section, '%s%s' % (postfix, s)) if new_server['name'] is not None: # Manage optionnal information if new_server['port'] is None: new_server['port'] = '61209' new_server['username'] = '******' # By default, try empty (aka no) password new_server['password'] = '' try: new_server['ip'] = gethostbyname(new_server['name']) except gaierror as e: logger.error("Cannot get IP address for server %s (%s)" % (new_server['name'], e)) continue new_server['key'] = new_server['name'] + ':' + new_server['port'] # Default status is 'UNKNOWN' new_server['status'] = 'UNKNOWN' # Server type is 'STATIC' new_server['type'] = 'STATIC' # Add the server to the list logger.debug("Add server %s to the static list" % new_server['name']) server_list.append(new_server) # Server list loaded logger.info("%s server(s) loaded from the configuration file" % len(server_list)) logger.debug("Static server list: %s" % server_list) return server_list
def update(self, stats): """Generate Graph file in the output folder.""" if self.generate_every != 0 and self._timer.finished(): self.args.generate_graph = True self._timer.reset() if not self.args.generate_graph: return plugins = stats.getPluginsList() for plugin_name in plugins: plugin = stats._plugins[plugin_name] if plugin_name in self.plugins_to_export(): self.export(plugin_name, plugin.get_export_history()) logger.info("Graphs created in the folder {}".format(self.path)) self.args.generate_graph = False
def start_clientbrowser(config, args): """Start the browser client mode""" logger.info("Start client mode (browser)") # Share global var global client # Import the Glances client browser module from glances.client_browser import GlancesClientBrowser # Init the client client = GlancesClientBrowser(config=config, args=args) # Start the client loop client.serve_forever() # Shutdown the client client.end()
def init(self): """Init the connection to the CouchDB server.""" if not self.export_enable: return None server_uri = 'tcp://{}:{}'.format(self.host, self.port) try: self.context = zmq.Context() publisher = self.context.socket(zmq.PUB) publisher.bind(server_uri) except Exception as e: logger.critical("Cannot connect to ZeroMQ server %s (%s)" % (server_uri, e)) sys.exit(2) else: logger.info("Connected to the ZeroMQ server %s" % server_uri) return publisher
def parse_tags(self, tags): """Parse tags into a dict. tags: a comma separated list of 'key:value' pairs. Example: foo:bar,spam:eggs dtags: a dict of tags. Example: {'foo': 'bar', 'spam': 'eggs'} """ dtags = {} if tags: try: dtags = dict([x.split(':') for x in tags.split(',')]) except ValueError: # one of the 'key:value' pairs was missing logger.info('Invalid tags passed: %s', tags) dtags = {} return dtags
def load(self, config): """Load the password from the configuration file.""" password_dict = {} if config is None: logger.warning("No configuration file available. Cannot load password list.") elif not config.has_section(self._section): logger.warning("No [%s] section in the configuration file. Cannot load password list." % self._section) else: logger.info("Start reading the [%s] section in the configuration file" % self._section) password_dict = dict(config.items(self._section)) # Password list loaded logger.info("%s password(s) loaded from the configuration file" % len(password_dict)) logger.debug("Password dictionary: %s" % password_dict) return password_dict
def init(self): """Init the connection to the mongodb server.""" if not self.export_enable: return None try: client = MongoClient(host=self.host, port=self.port) db = client[self.db] if not self.username and self.password: db.authenticate(self.username, self.password, source='source_database') collection = db[self.collection] except Exception as e: logger.critical("Cannot connect to Mongodb server %s:%s (%s)" % (self.host, self.port, e)) sys.exit(2) else: logger.info("Connected to the Mongodb server %s:%s" % (self.host, self.port)) return collection
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 run(self, stat_name, criticity, commands, repeat, mustache_dict=None): """Run the commands (in background). - stats_name: plugin_name (+ header) - criticity: criticity of the trigger - commands: a list of command line with optional {{mustache}} - If True, then repeat the action - mustache_dict: Plugin stats (can be use within {{mustache}}) Return True if the commands have been ran. """ if (self.get(stat_name) == criticity and not repeat) or \ not self.start_timer.finished(): # Action already executed => Exit return False logger.debug("{} action {} for {} ({}) with stats {}".format( "Repeat" if repeat else "Run", commands, stat_name, criticity, mustache_dict)) # Run all actions in background for cmd in commands: # Replace {{arg}} by the dict one (Thk to {Mustache}) if chevron_tag: cmd_full = chevron.render(cmd, mustache_dict) else: cmd_full = cmd # Execute the action logger.info("Action triggered for {} ({}): {}".format( stat_name, criticity, cmd_full)) try: ret = secure_popen(cmd_full) except OSError as e: logger.error("Action error for {} ({}): {}".format( stat_name, criticity, e)) else: logger.debug("Action result for {} ({}): {}".format( stat_name, criticity, ret)) self.set(stat_name, criticity) return True
def __catch_key(self, stats): # Catch the browser pressed key self.pressedkey = self.get_key(self.term_window) if self.pressedkey != -1: logger.debug("Key pressed. Code=%s" % self.pressedkey) # Actions... if self.pressedkey == ord('\x1b') or self.pressedkey == ord('q'): # 'ESC'|'q' > Quit self.end() logger.info("Stop Glances client browser") # sys.exit(0) self.is_end = True elif self.pressedkey == 10: # 'ENTER' > Run Glances on the selected server self.active_server = self._current_page * self._page_max_lines + self.cursor_position logger.debug("Server {}/{} selected".format( self.active_server, len(stats))) elif self.pressedkey == curses.KEY_UP or self.pressedkey == 65: # 'UP' > Up in the server list self.cursor_up(stats) logger.debug("Server {}/{} selected".format( self.cursor + 1, len(stats))) elif self.pressedkey == curses.KEY_DOWN or self.pressedkey == 66: # 'DOWN' > Down in the server list self.cursor_down(stats) logger.debug("Server {}/{} selected".format( self.cursor + 1, len(stats))) elif self.pressedkey == curses.KEY_PPAGE: # 'Page UP' > Prev page in the server list self.cursor_pageup(stats) logger.debug("PageUP: Server ({}/{}) pages.".format( self._current_page + 1, self._page_max)) elif self.pressedkey == curses.KEY_NPAGE: # 'Page Down' > Next page in the server list self.cursor_pagedown(stats) logger.debug("PageDown: Server {}/{} pages".format( self._current_page + 1, self._page_max)) # Return the key code return self.pressedkey
def init(self): """Init the connection to the Kafka server.""" if not self.export_enable: return None # Build the server URI with host and port server_uri = '{}:{}'.format(self.host, self.port) try: s = KafkaProducer( bootstrap_servers=server_uri, value_serializer=lambda v: json.dumps(v).encode('utf-8'), compression_type=self.compression) except Exception as e: logger.critical("Cannot connect to Kafka server %s (%s)" % (server_uri, e)) sys.exit(2) else: logger.info("Connected to the Kafka server %s" % server_uri) return s
def msg_curse(self, args=None): """Return the string to display in the curse interface.""" # Init the return message ret = [] if not self.stats or self.stats == {} or self.is_disable(): return ret # Generate the output if 'ami-id' in self.stats and 'region' in self.stats: msg = 'AWS EC2' ret.append(self.curse_add_line(msg, "TITLE")) msg = ' {} instance {} ({})'.format( to_ascii(self.stats['instance-type']), to_ascii(self.stats['instance-id']), to_ascii(self.stats['region'])) ret.append(self.curse_add_line(msg)) # Return the message with decoration logger.info(ret) return ret
def start(self, stats): """Start the bottle.""" # Init stats self.stats = stats # Init plugin list 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)
def main(): """Main entry point for Glances. Select the mode (standalone, client or server) Run it... """ # Log Glances and PSutil version logger.info('Start Glances {}'.format(__version__)) logger.info('{} {} and PSutil {} detected'.format( platform.python_implementation(), platform.python_version(), psutil_version)) # Share global var global core # Create the Glances main instance core = GlancesMain() config = core.get_config() args = core.get_args() # Catch the CTRL-C signal signal.signal(signal.SIGINT, __signal_handler) # Glances can be ran in standalone, client or server mode if core.is_standalone() and not WINDOWS: start_standalone(config=config, args=args) elif core.is_client() and not WINDOWS: if core.is_client_browser(): start_clientbrowser(config=config, args=args) else: start_client(config=config, args=args) elif core.is_server(): start_server(config=config, args=args) elif core.is_webserver() or (core.is_standalone() and WINDOWS): # Web server mode replace the standalone mode on Windows OS # In this case, try to start the web browser mode automaticaly if core.is_standalone() and WINDOWS: args.open_web_browser = True start_webserver(config=config, args=args)
def read(self): """Read the config file, if it exists. Using defaults otherwise.""" for config_file in self.config_file_paths(): logger.info('Search glances.conf file in {}'.format(config_file)) if os.path.exists(config_file): try: with open(config_file, encoding='utf-8') as f: self.parser.read_file(f) self.parser.read(f) logger.info( "Read configuration file '{}'".format(config_file)) except UnicodeDecodeError as err: logger.error( "Can not read configuration file '{}': {}".format( config_file, err)) sys.exit(1) # Save the loaded configuration file path (issue #374) self._loaded_config_file = config_file break # Set the default values for section not configured self.sections_set_default()
def init(self): """Init the connection to the InfluxDB server.""" if not self.export_enable: return None try: db = InfluxDBClient(host=self.host, port=self.port, username=self.user, password=self.password, database=self.db) get_all_db = [i['name'] for i in db.get_list_database()] self.version = INFLUXDB_09PLUS except InfluxDBClientError: # https://github.com/influxdb/influxdb-python/issues/138 logger.info("Trying fallback to InfluxDB v0.8") db = InfluxDBClient08(host=self.host, port=self.port, username=self.user, password=self.password, database=self.db) get_all_db = [i['name'] for i in db.get_list_database()] self.version = INFLUXDB_08 except InfluxDBClientError08 as e: logger.critical("Cannot connect to InfluxDB database '%s' (%s)" % (self.db, e)) sys.exit(2) if self.db in get_all_db: logger.info( "Stats will be exported to InfluxDB server: {0}".format( db._baseurl)) else: logger.critical( "InfluxDB database '%s' did not exist. Please create it" % self.db) sys.exit(2) return db
def __init__(self, config=None, args=None): """Init the CSV export IF.""" super(Export, self).__init__(config=config, args=args) # CSV file name self.csv_filename = args.export_csv_file # Set the CSV output file # (see https://github.com/nicolargo/glances/issues/1525) if not os.path.isfile(self.csv_filename) or args.export_csv_overwrite: # File did not exist, create it file_mode = 'w' self.old_header = None else: # A CSV file already exit, append new data file_mode = 'a' # Header will be check later # Get the existing one try: self.csv_file = open_csv_file(self.csv_filename, 'r') reader = csv.reader(self.csv_file) except IOError as e: logger.critical("Cannot open existing CSV file: {}".format(e)) sys.exit(2) self.old_header = next(reader, None) self.csv_file.close() try: self.csv_file = open_csv_file(self.csv_filename, file_mode) self.writer = csv.writer(self.csv_file) except IOError as e: logger.critical("Cannot create the CSV file: {}".format(e)) sys.exit(2) logger.info("Stats exported to CSV file: {}".format(self.csv_filename)) self.export_enable = True self.first_line = True
def add_service(self, zeroconf, srv_type, srv_name): """Method called when a new Zeroconf client is detected. Return True if the zeroconf client is a Glances server Note: the return code will never be used """ if srv_type != zeroconf_type: return False logger.debug("Check new Zeroconf server: %s / %s" % (srv_type, srv_name)) info = zeroconf.get_service_info(srv_type, srv_name) if info: new_server_ip = socket.inet_ntoa(info.address) new_server_port = info.port # Add server to the global dict self.servers.add_server(srv_name, new_server_ip, new_server_port) logger.info("New Glances server detected (%s from %s:%s)" % (srv_name, new_server_ip, new_server_port)) else: logger.warning( "New Glances server detected, but Zeroconf info failed to be grabbed") return True