def get_stats_display_width(self, curse_msg, without_option=False): """Return the width of the formatted curses message.""" try: if without_option: # Size without options c = len( max(''.join([ (u(u(nativestr(i['msg'])).encode('ascii', 'replace')) if not i['optional'] else "") for i in curse_msg['msgdict'] ]).split('\n'), key=len)) else: # Size with all options c = len( max(''.join([ u(u(nativestr(i['msg'])).encode('ascii', 'replace')) for i in curse_msg['msgdict'] ]).split('\n'), key=len)) except Exception as e: logger.debug('ERROR: Can not compute plugin width ({})'.format(e)) return 0 else: return c
def update_stats_history(self): """Update stats history.""" # If the plugin data is a dict, the dict's key should be used if self.get_key() is None: item_name = '' else: item_name = self.get_key() # Build the history if self.get_export() and self.history_enable(): for i in self.get_items_history_list(): if isinstance(self.get_export(), list): # Stats is a list of data # Iter throught it (for exemple, iter throught network # interface) for l in self.get_export(): self.stats_history.add( nativestr(l[item_name]) + '_' + nativestr(i['name']), l[i['name']], description=i['description'], history_max_size=self._limits['history_size']) else: # Stats is not a list # Add the item to the history directly self.stats_history.add( nativestr(i['name']), self.get_export()[i['name']], description=i['description'], history_max_size=self._limits['history_size'])
def update_stats_history(self): """Update stats history.""" # If the plugin data is a dict, the dict's key should be used if self.get_key() is None: item_name = '' else: item_name = self.get_key() # Build the history if self.get_export() and self._history_enable(): for i in self.get_items_history_list(): if isinstance(self.get_export(), list): # Stats is a list of data # Iter throught it (for exemple, iter throught network # interface) for l in self.get_export(): self.stats_history.add( nativestr(l[item_name]) + '_' + nativestr(i['name']), l[i['name']], description=i['description'], history_max_size=self._limits['history_size']) else: # Stats is not a list # Add the item to the history directly self.stats_history.add(nativestr(i['name']), self.get_export()[i['name']], description=i['description'], history_max_size=self._limits['history_size'])
def __update__(self): """Update the stats.""" # Reset the list self.reset() # Only update if --disable-hddtemp is not set if self.args is None or self.args.disable_hddtemp: return # Fetch the data # data = ("|/dev/sda|WDC WD2500JS-75MHB0|44|C|" # "|/dev/sdb|WDC WD2500JS-75MHB0|35|C|" # "|/dev/sdc|WDC WD3200AAKS-75B3A0|45|C|" # "|/dev/sdd|WDC WD3200AAKS-75B3A0|45|C|" # "|/dev/sde|WDC WD3200AAKS-75B3A0|43|C|" # "|/dev/sdf|???|ERR|*|" # "|/dev/sdg|HGST HTS541010A9E680|SLP|*|" # "|/dev/sdh|HGST HTS541010A9E680|UNK|*|") data = self.fetch() # Exit if no data if data == "": return # Safety check to avoid malformed data # Considering the size of "|/dev/sda||0||" as the minimum if len(data) < 14: data = self.cache if len(self.cache) > 0 else self.fetch() self.cache = data try: fields = data.split(b'|') except TypeError: fields = "" devices = (len(fields) - 1) // 5 for item in range(devices): offset = item * 5 hddtemp_current = {} device = os.path.basename(nativestr(fields[offset + 1])) temperature = fields[offset + 3] unit = nativestr(fields[offset + 4]) hddtemp_current['label'] = device try: hddtemp_current['value'] = float(temperature) except ValueError: # Temperature could be 'ERR', 'SLP' or 'UNK' (see issue #824) # Improper bytes/unicode in glances_hddtemp.py (see issue #887) hddtemp_current['value'] = nativestr(temperature) hddtemp_current['unit'] = unit self.hddtemp_list.append(hddtemp_current)
def __secure_popen(cmd): """A more or less secure way to execute system command Manage redirection (>) and pipes (|) """ ret = '' # Split by redirection '>' cmd_split_redirect = cmd.split('>') if len(cmd_split_redirect) > 2: return 'Glances error: Only one file redirection allowed ({})'.format( cmd) elif len(cmd_split_redirect) == 2: stdout_redirect = cmd_split_redirect[1].strip() cmd = cmd_split_redirect[0] else: stdout_redirect = None sub_cmd_stdin = None p_last = None # Split by pipe '|' for sub_cmd in cmd.split('|'): # Split by space ' ' sub_cmd_split = [i for i in sub_cmd.split(' ') if i] p = Popen(sub_cmd_split, shell=False, stdin=sub_cmd_stdin, stdout=PIPE, stderr=PIPE) if p_last is not None: # Allow p_last to receive a SIGPIPE if p exits. p_last.stdout.close() p_last = p sub_cmd_stdin = p.stdout p_ret = p_last.communicate() if nativestr(p_ret[1]) == '': # No error ret = nativestr(p_ret[0]) if stdout_redirect is not None: # Write result to redirection file with open(stdout_redirect, "w") as stdout_redirect_file: stdout_redirect_file.write(ret) else: # Error ret = nativestr(p_ret[1]) return ret
def __set_folder_list(self, section): """Init the monitored folder list. The list is defined in the Glances configuration file. """ for l in range(1, self.__folder_list_max_size + 1): value = {} key = 'folder_' + str(l) + '_' # Path is mandatory value['indice'] = str(l) value['path'] = self.config.get_value(section, key + 'path') if value['path'] is None: continue else: value['path'] = nativestr(value['path']) # Optional conf keys for i in ['careful', 'warning', 'critical']: # Read threshold value[i] = self.config.get_value(section, key + i) if value[i] is not None: logger.debug("{} threshold for folder {} is {}".format(i, value["path"], value[i])) # Read action action = self.config.get_value(section, key + i + '_action') if action is not None: value[i + '_action'] = action logger.debug("{} action for folder {} is {}".format(i, value["path"], value[i + '_action'])) # Add the item to the list self.__folder_list.append(value)
def msg_curse(self, args=None, max_width=None): """Return the dict to display in the curse interface.""" # Init the return message ret = [] # Only process if stats exist and display plugin enable... if not self.stats or self.is_disable(): return ret # Max size for the interface name name_max_width = max_width - 7 # Header msg = '{:{width}}'.format('FOLDERS', width=name_max_width) ret.append(self.curse_add_line(msg, "TITLE")) # Data for i in self.stats: ret.append(self.curse_new_line()) if len(i['path']) > name_max_width: # Cut path if it is too long path = '_' + i['path'][-name_max_width + 1:] else: path = i['path'] msg = '{:{width}}'.format(nativestr(path), width=name_max_width) ret.append(self.curse_add_line(msg)) try: msg = '{:>9}'.format(self.auto_unit(i['size'])) except (TypeError, ValueError): msg = '{:>9}'.format(i['size']) ret.append( self.curse_add_line( msg, self.get_alert(i, header='folder_' + i['indice']))) return ret
def __set_folder_list(self, section): """Init the monitored folder list. The list is defined in the Glances configuration file. """ for l in range(1, self.__folder_list_max_size + 1): value = {} key = 'folder_' + str(l) + '_' # Path is mandatory value['path'] = self.config.get_value(section, key + 'path') if value['path'] is None: continue else: value['path'] = nativestr(value['path']) # Optional conf keys for i in ['careful', 'warning', 'critical']: value[i] = self.config.get_value(section, key + i) if value[i] is None: logger.debug("No {} threshold for folder {}".format( i, value["path"])) # Add the item to the list self.__folder_list.append(value)
def msg_curse(self, args=None, max_width=None): """Return the dict to display in the curse interface.""" # Init the return message ret = [] # Only process if stats exist and display plugin enable... if not self.stats or self.is_disable(): return ret # Max size for the interface name name_max_width = max_width - 7 # Header msg = '{:{width}}'.format('FOLDERS', width=name_max_width) ret.append(self.curse_add_line(msg, "TITLE")) # Data for i in self.stats: ret.append(self.curse_new_line()) if len(i['path']) > name_max_width: # Cut path if it is too long path = '_' + i['path'][-name_max_width + 1:] else: path = i['path'] msg = '{:{width}}'.format(nativestr(path), width=name_max_width) ret.append(self.curse_add_line(msg)) try: msg = '{:>9}'.format(self.auto_unit(i['size'])) except (TypeError, ValueError): msg = '{:>9}'.format(i['size']) ret.append(self.curse_add_line(msg, self.get_alert(i))) return ret
def _update_pypi_version(self): """Get the latest PyPI version (as a string) via the RESTful JSON API""" logger.debug( "Get latest Glances version from the PyPI RESTful API ({})".format( PYPI_API_URL)) # Update the current time self.data[u'refresh_date'] = datetime.now() try: res = urlopen(PYPI_API_URL, timeout=3).read() except (HTTPError, URLError, CertificateError) as e: logger.debug( "Cannot get Glances version from the PyPI RESTful API ({})". format(e)) else: self.data[u'latest_version'] = json.loads( nativestr(res))['info']['version'] logger.debug("Save Glances version to the cache file") # Save result to the cache file # Note: also saved if the Glances PyPI version cannot be grabbed self._save_cache() return self.data
def get(self): """Return the sparkline.""" ret = sparklines(self.percents)[0] if self.__with_text: percents_without_none = [x for x in self.percents if x is not None] if len(percents_without_none) > 0: ret = '{}{:5.1f}%'.format(ret, percents_without_none[-1]) return nativestr(ret)
def msg_curse(self, args=None, max_width=None): """Return the dict to display in the curse interface.""" # Init the return message ret = [] # Only process if stats exist and display plugin enable... if not self.stats or self.is_disable(): return ret # Max size for the interface name name_max_width = max_width - 12 # Build the string message # Header msg = '{:{width}}'.format('FILE SYS', width=name_max_width) ret.append(self.curse_add_line(msg, "TITLE")) if args.fs_free_space: msg = '{:>7}'.format('Free') else: msg = '{:>7}'.format('Used') ret.append(self.curse_add_line(msg)) msg = '{:>7}'.format('Total') ret.append(self.curse_add_line(msg)) # Filesystem list (sorted by name) for i in sorted(self.stats, key=operator.itemgetter(self.get_key())): # New line ret.append(self.curse_new_line()) if i['device_name'] == '' or i['device_name'] == 'none': mnt_point = i['mnt_point'][-name_max_width + 1:] elif len(i['mnt_point']) + len( i['device_name'].split('/')[-1]) <= name_max_width - 3: # If possible concatenate mode info... Glances touch inside :) mnt_point = i['mnt_point'] + ' (' + i['device_name'].split( '/')[-1] + ')' elif len(i['mnt_point']) > name_max_width: # Cut mount point name if it is too long mnt_point = '_' + i['mnt_point'][-name_max_width + 1:] else: mnt_point = i['mnt_point'] msg = '{:{width}}'.format(nativestr(mnt_point), width=name_max_width) ret.append(self.curse_add_line(msg)) if args.fs_free_space: msg = '{:>7}'.format(self.auto_unit(i['free'])) else: msg = '{:>7}'.format(self.auto_unit(i['used'])) ret.append( self.curse_add_line( msg, self.get_views(item=i[self.get_key()], key='used', option='decoration'))) msg = '{:>7}'.format(self.auto_unit(i['size'])) ret.append(self.curse_add_line(msg)) return ret
def msg_curse(self, args=None, max_width=None): """Return the dict to display in the curse interface.""" # Init the return message ret = [] # Only process if stats exist and display plugin enable... if not self.stats or self.is_disable(): return ret # Header if self.net_connections_enabled or self.nf_conntrack_enabled: msg = '{}'.format('TCP CONNECTIONS') ret.append(self.curse_add_line(msg, "TITLE")) # Connections status if self.net_connections_enabled: for s in [ psutil.CONN_LISTEN, 'initiated', psutil.CONN_ESTABLISHED, 'terminated' ]: ret.append(self.curse_new_line()) msg = '{:{width}}'.format(nativestr(s).capitalize(), width=len(s)) ret.append(self.curse_add_line(msg)) msg = '{:>{width}}'.format(self.stats[s], width=max_width - len(s) + 2) ret.append(self.curse_add_line(msg)) # Connections track if self.nf_conntrack_enabled: s = 'Tracked' ret.append(self.curse_new_line()) msg = '{:{width}}'.format(nativestr(s).capitalize(), width=len(s)) ret.append(self.curse_add_line(msg)) msg = '{:>{width}}'.format('{:0.0f}/{:0.0f}'.format( self.stats['nf_conntrack_count'], self.stats['nf_conntrack_max']), width=max_width - len(s) + 2) ret.append( self.curse_add_line( msg, self.get_views(key='nf_conntrack_percent', option='decoration'))) return ret
def msg_curse(self, args=None, max_width=None): """Return the dict to display in the curse interface.""" # Init the return message ret = [] # Only process if stats exist and display plugin enable... if not self.stats or self.is_disable(): return ret # Max size for the interface name name_max_width = max_width - 12 # Build the string message # Header msg = '{:{width}}'.format('FILE SYS', width=name_max_width) ret.append(self.curse_add_line(msg, "TITLE")) if args.fs_free_space: msg = '{:>7}'.format('Free') else: msg = '{:>7}'.format('Used') ret.append(self.curse_add_line(msg)) msg = '{:>7}'.format('Total') ret.append(self.curse_add_line(msg)) # Filesystem list (sorted by name) for i in sorted(self.stats, key=operator.itemgetter(self.get_key())): # New line ret.append(self.curse_new_line()) if i['device_name'] == '' or i['device_name'] == 'none': mnt_point = i['mnt_point'][-name_max_width + 1:] elif len(i['mnt_point']) + len(i['device_name'].split('/')[-1]) <= name_max_width - 3: # If possible concatenate mode info... Glances touch inside :) mnt_point = i['mnt_point'] + ' (' + i['device_name'].split('/')[-1] + ')' elif len(i['mnt_point']) > name_max_width: # Cut mount point name if it is too long mnt_point = '_' + i['mnt_point'][-name_max_width + 1:] else: mnt_point = i['mnt_point'] msg = '{:{width}}'.format(nativestr(mnt_point), width=name_max_width) ret.append(self.curse_add_line(msg)) if args.fs_free_space: msg = '{:>7}'.format(self.auto_unit(i['free'])) else: msg = '{:>7}'.format(self.auto_unit(i['used'])) ret.append(self.curse_add_line(msg, self.get_views(item=i[self.get_key()], key='used', option='decoration'))) msg = '{:>7}'.format(self.auto_unit(i['size'])) ret.append(self.curse_add_line(msg)) return ret
def __update__(self): """Update the stats.""" # Reset the list self.reset() # Only update if --disable-hddtemp is not set if self.args is None or self.args.disable_hddtemp: return # Fetch the data data = self.fetch() # Exit if no data if data == "": return # Safety check to avoid malformed data # Considering the size of "|/dev/sda||0||" as the minimum if len(data) < 14: data = self.cache if len(self.cache) > 0 else self.fetch() self.cache = data try: fields = data.split(b'|') except TypeError: fields = "" devices = (len(fields) - 1) // 5 for item in range(devices): offset = item * 5 hddtemp_current = {} device = os.path.basename(nativestr(fields[offset + 1])) temperature = fields[offset + 3] unit = nativestr(fields[offset + 4]) hddtemp_current['label'] = device hddtemp_current['value'] = float( temperature) if temperature != b'ERR' else temperature hddtemp_current['unit'] = unit self.hddtemp_list.append(hddtemp_current)
def __update__(self): """Update the stats.""" # Reset the list self.reset() # Only update if --disable-hddtemp is not set if self.args is None or self.args.disable_hddtemp: return # Fetch the data data = self.fetch() # Exit if no data if data == "": return # Safety check to avoid malformed data # Considering the size of "|/dev/sda||0||" as the minimum if len(data) < 14: data = self.cache if len(self.cache) > 0 else self.fetch() self.cache = data try: fields = data.split(b'|') except TypeError: fields = "" devices = (len(fields) - 1) // 5 for item in range(devices): offset = item * 5 hddtemp_current = {} device = os.path.basename(nativestr(fields[offset + 1])) temperature = fields[offset + 3] unit = nativestr(fields[offset + 4]) hddtemp_current['label'] = device # Temperature could be 'ERR' or 'SLP' (see issue#824) hddtemp_current['value'] = float(temperature) if isinstance(temperature, numbers.Number) else temperature hddtemp_current['unit'] = unit self.hddtemp_list.append(hddtemp_current)
def msg_curse(self, args=None, max_width=None): """Return the dict to display in the curse interface.""" # Init the return message ret = [] # Only process if stats exist and display plugin enable... if not self.stats or import_error_tag or self.is_disable(): return ret # Max size for the interface name ifname_max_width = max_width - 5 # Build the string message # Header msg = '{:{width}}'.format('WIFI', width=ifname_max_width) ret.append(self.curse_add_line(msg, "TITLE")) msg = '{:>7}'.format('dBm') ret.append(self.curse_add_line(msg)) # Hotspot list (sorted by name) for i in sorted(self.stats, key=operator.itemgetter(self.get_key())): # Do not display hotspot with no name (/ssid)... # of ssid None... See issue #1151 if i['ssid'] == '' or i['ssid'] is None: continue ret.append(self.curse_new_line()) # New hotspot hotspotname = i['ssid'] # Add the encryption type (if it is available) if i['encrypted']: hotspotname += ' {}'.format(i['encryption_type']) # Cut hotspotname if it is too long if len(hotspotname) > ifname_max_width: hotspotname = '_' + hotspotname[-ifname_max_width + 1:] # Add the new hotspot to the message msg = '{:{width}}'.format(nativestr(hotspotname), width=ifname_max_width) ret.append(self.curse_add_line(msg)) msg = '{:>7}'.format(i['signal'], width=ifname_max_width) ret.append( self.curse_add_line( msg, self.get_views(item=i[self.get_key()], key='signal', option='decoration'))) return ret
def msg_curse(self, args=None, max_width=None): """Return the dict to display in the curse interface.""" # Init the return message ret = [] # Only process if stats exist and display plugin enable... if not self.stats or import_error_tag or self.is_disable(): return ret # Max size for the interface name ifname_max_width = max_width - 5 # Build the string message # Header msg = '{:{width}}'.format('WIFI', width=ifname_max_width) ret.append(self.curse_add_line(msg, "TITLE")) msg = '{:>7}'.format('dBm') ret.append(self.curse_add_line(msg)) # Hotspot list (sorted by name) for i in sorted(self.stats, key=operator.itemgetter(self.get_key())): # Do not display hotspot with no name (/ssid)... # of ssid None... See issue #1151 if i['ssid'] == '' or i['ssid'] is None: continue ret.append(self.curse_new_line()) # New hotspot hotspotname = i['ssid'] # Add the encryption type (if it is available) if i['encrypted']: hotspotname += ' {}'.format(i['encryption_type']) # Cut hotspotname if it is too long if len(hotspotname) > ifname_max_width: hotspotname = '_' + hotspotname[-ifname_max_width + 1:] # Add the new hotspot to the message msg = '{:{width}}'.format(nativestr(hotspotname), width=ifname_max_width) ret.append(self.curse_add_line(msg)) msg = '{:>7}'.format(i['signal'], width=ifname_max_width) ret.append(self.curse_add_line(msg, self.get_views(item=i[self.get_key()], key='signal', option='decoration'))) return ret
def _update_pypi_version(self): """Get the latest PyPI version (as a string) via the RESTful JSON API""" logger.debug("Get latest Glances version from the PyPI RESTful API ({})".format(PYPI_API_URL)) # Update the current time self.data[u'refresh_date'] = datetime.now() try: res = urlopen(PYPI_API_URL, timeout=3).read() except (HTTPError, URLError) as e: logger.debug("Cannot get Glances version from the PyPI RESTful API ({})".format(e)) else: self.data[u'latest_version'] = json.loads(nativestr(res))['info']['version'] logger.debug("Save Glances version to the cache file") # Save result to the cache file # Note: also saved if the Glances PyPI version cannot be grabbed self._save_cache() return self.data
def __set_folder_list(self, section): """Init the monitored folder list. The list is defined in the Glances configuration file. """ for l in range(1, self.__folder_list_max_size + 1): value = {} key = 'folder_' + str(l) + '_' # Path is mandatory value['indice'] = str(l) value['path'] = self.config.get_value(section, key + 'path') if value['path'] is None: continue else: value['path'] = nativestr(value['path']) # Optional conf keys # Refresh time value['refresh'] = int( self.config.get_value(section, key + 'refresh', default=self.__default_refresh)) self.timer_folders.append(Timer(value['refresh'])) # Thesholds for i in ['careful', 'warning', 'critical']: # Read threshold value[i] = self.config.get_value(section, key + i) if value[i] is not None: logger.debug("{} threshold for folder {} is {}".format( i, value["path"], value[i])) # Read action action = self.config.get_value(section, key + i + '_action') if action is not None: value[i + '_action'] = action logger.debug("{} action for folder {} is {}".format( i, value["path"], value[i + '_action'])) # Add the item to the list self.__folder_list.append(value)
def msg_curse(self, args=None, max_width=None): """Return the dict to display in the curse interface.""" # Init the return message ret = [] # Only process if stats exist and display plugin enable... if not self.stats or self.is_disable(): return ret # Max size for the interface name name_max_width = max_width - 12 # Header msg = '{:{width}}'.format('DISK I/O', width=name_max_width) ret.append(self.curse_add_line(msg, "TITLE")) if args.diskio_iops: msg = '{:>7}'.format('IOR/s') ret.append(self.curse_add_line(msg)) msg = '{:>7}'.format('IOW/s') ret.append(self.curse_add_line(msg)) else: msg = '{:>7}'.format('R/s') ret.append(self.curse_add_line(msg)) msg = '{:>7}'.format('W/s') ret.append(self.curse_add_line(msg)) # Disk list (sorted by name) for i in self.sorted_stats(): # Is there an alias for the disk name ? disk_real_name = i['disk_name'] disk_name = self.has_alias(i['disk_name']) if disk_name is None: disk_name = disk_real_name # New line ret.append(self.curse_new_line()) if len(disk_name) > name_max_width: # Cut disk name if it is too long disk_name = '_' + disk_name[-name_max_width:] msg = '{:{width}}'.format(nativestr(disk_name), width=name_max_width) ret.append(self.curse_add_line(msg)) if args.diskio_iops: # count txps = self.auto_unit( int(i['read_count'] // i['time_since_update'])) rxps = self.auto_unit( int(i['write_count'] // i['time_since_update'])) msg = '{:>7}'.format(txps) ret.append(self.curse_add_line(msg, self.get_views(item=i[self.get_key()], key='read_count', option='decoration'))) msg = '{:>7}'.format(rxps) ret.append(self.curse_add_line(msg, self.get_views(item=i[self.get_key()], key='write_count', option='decoration'))) else: # Bitrate txps = self.auto_unit( int(i['read_bytes'] // i['time_since_update'])) rxps = self.auto_unit( int(i['write_bytes'] // i['time_since_update'])) msg = '{:>7}'.format(txps) ret.append(self.curse_add_line(msg, self.get_views(item=i[self.get_key()], key='read_bytes', option='decoration'))) msg = '{:>7}'.format(rxps) ret.append(self.curse_add_line(msg, self.get_views(item=i[self.get_key()], key='write_bytes', option='decoration'))) return ret
def get_device_name(device_handle): """Get GPU device name.""" try: return nativestr(pynvml.nvmlDeviceGetName(device_handle)) except pynvml.NVMlError: return "NVIDIA"
def update(self): """Update Docker stats using the input method.""" # Init new stats stats = self.get_init_value() # The Docker-py lib is mandatory if import_error_tag: return self.stats if self.input_method == 'local': # Update stats # Docker version # Exemple: { # "KernelVersion": "3.16.4-tinycore64", # "Arch": "amd64", # "ApiVersion": "1.15", # "Version": "1.3.0", # "GitCommit": "c78088f", # "Os": "linux", # "GoVersion": "go1.3.3" # } try: stats['version'] = self.docker_client.version() except Exception as e: # Correct issue#649 logger.error("{} plugin - Cannot get Docker version ({})".format(self.plugin_name, e)) return self.stats # Update current containers list try: # Issue #1152: Docker module doesn't export details about stopped containers # The Docker/all key of the configuration file should be set to True containers = self.docker_client.containers.list(all=self._all_tag()) or [] except Exception as e: logger.error("{} plugin - Cannot get containers list ({})".format(self.plugin_name, e)) return self.stats # Start new thread for new container for container in containers: if container.id not in self.thread_list: # Thread did not exist in the internal dict # Create it and add it to the internal dict logger.debug("{} plugin - Create thread for container {}".format(self.plugin_name, container.id[:12])) t = ThreadDockerGrabber(container) self.thread_list[container.id] = t t.start() # Stop threads for non-existing containers nonexisting_containers = set(iterkeys(self.thread_list)) - set([c.id for c in containers]) for container_id in nonexisting_containers: # Stop the thread logger.debug("{} plugin - Stop thread for old container {}".format(self.plugin_name, container_id[:12])) self.thread_list[container_id].stop() # Delete the item from the dict del self.thread_list[container_id] # Get stats for all containers stats['containers'] = [] for container in containers: # Init the stats for the current container container_stats = {} # The key is the container name and not the Id container_stats['key'] = self.get_key() # Export name (first name in the Names list, without the /) container_stats['name'] = nativestr(container.name) # Export global Names (used by the WebUI) container_stats['Names'] = [ nativestr(container.name)] # Container Id container_stats['Id'] = container.id # Container Image container_stats['Image'] = container.image.tags # Global stats (from attrs) container_stats['Status'] = container.attrs['State']['Status'] container_stats['Command'] = container.attrs['Config']['Entrypoint'] # Standards stats if container_stats['Status'] in ('running', 'paused'): container_stats['cpu'] = self.get_docker_cpu(container.id, self.thread_list[container.id].stats) container_stats['cpu_percent'] = container_stats['cpu'].get('total', None) container_stats['memory'] = self.get_docker_memory(container.id, self.thread_list[container.id].stats) container_stats['memory_usage'] = container_stats['memory'].get('usage', None) container_stats['io'] = self.get_docker_io(container.id, self.thread_list[container.id].stats) container_stats['io_r'] = container_stats['io'].get('ior', None) container_stats['io_w'] = container_stats['io'].get('iow', None) container_stats['network'] = self.get_docker_network(container.id, self.thread_list[container.id].stats) container_stats['network_rx'] = container_stats['network'].get('rx', None) container_stats['network_tx'] = container_stats['network'].get('tx', None) else: container_stats['cpu'] = {} container_stats['cpu_percent'] = None container_stats['memory'] = {} container_stats['memory_percent'] = None container_stats['io'] = {} container_stats['io_r'] = None container_stats['io_w'] = None container_stats['network'] = {} container_stats['network_rx'] = None container_stats['network_tx'] = None # Add current container stats to the stats list stats['containers'].append(container_stats) elif self.input_method == 'snmp': # Update stats using SNMP # Not available pass # Update the stats self.stats = stats return self.stats
def update(self): """Update Docker stats using the input method.""" # Init new stats stats = self.get_init_value() # The Docker-py lib is mandatory if import_error_tag: return self.stats if self.input_method == 'local': # Update stats # Docker version # Exemple: { # "KernelVersion": "3.16.4-tinycore64", # "Arch": "amd64", # "ApiVersion": "1.15", # "Version": "1.3.0", # "GitCommit": "c78088f", # "Os": "linux", # "GoVersion": "go1.3.3" # } try: stats['version'] = self.docker_client.version() except Exception as e: # Correct issue#649 logger.error( "{} plugin - Cannot get Docker version ({})".format( self.plugin_name, e)) # We may have lost connection remove version info if 'version' in self.stats: del self.stats['version'] self.stats['containers'] = [] return self.stats # Update current containers list try: # Issue #1152: Docker module doesn't export details about stopped containers # The Docker/all key of the configuration file should be set to True containers = self.docker_client.containers.list( all=self._all_tag()) or [] except Exception as e: logger.error( "{} plugin - Cannot get containers list ({})".format( self.plugin_name, e)) # We may have lost connection empty the containers list. self.stats['containers'] = [] return self.stats # Start new thread for new container for container in containers: if container.id not in self.thread_list: # Thread did not exist in the internal dict # Create it and add it to the internal dict logger.debug( "{} plugin - Create thread for container {}".format( self.plugin_name, container.id[:12])) t = ThreadDockerGrabber(container) self.thread_list[container.id] = t t.start() # Stop threads for non-existing containers nonexisting_containers = set(iterkeys(self.thread_list)) - set( [c.id for c in containers]) for container_id in nonexisting_containers: # Stop the thread logger.debug( "{} plugin - Stop thread for old container {}".format( self.plugin_name, container_id[:12])) self.thread_list[container_id].stop() # Delete the item from the dict del self.thread_list[container_id] # Get stats for all containers stats['containers'] = [] for container in containers: # Init the stats for the current container container_stats = {} # The key is the container name and not the Id container_stats['key'] = self.get_key() # Export name (first name in the Names list, without the /) container_stats['name'] = nativestr(container.name) # Export global Names (used by the WebUI) container_stats['Names'] = [nativestr(container.name)] # Container Id container_stats['Id'] = container.id # Container Image container_stats['Image'] = container.image.tags # Global stats (from attrs) container_stats['Status'] = container.attrs['State']['Status'] container_stats['Command'] = container.attrs['Config'][ 'Entrypoint'] # Standards stats if container_stats['Status'] in ('running', 'paused'): container_stats['cpu'] = self.get_docker_cpu( container.id, self.thread_list[container.id].stats) container_stats['cpu_percent'] = container_stats[ 'cpu'].get('total', None) container_stats['memory'] = self.get_docker_memory( container.id, self.thread_list[container.id].stats) container_stats['memory_usage'] = container_stats[ 'memory'].get('usage', None) container_stats['io'] = self.get_docker_io( container.id, self.thread_list[container.id].stats) container_stats['io_r'] = container_stats['io'].get( 'ior', None) container_stats['io_w'] = container_stats['io'].get( 'iow', None) container_stats['network'] = self.get_docker_network( container.id, self.thread_list[container.id].stats) container_stats['network_rx'] = container_stats[ 'network'].get('rx', None) container_stats['network_tx'] = container_stats[ 'network'].get('tx', None) else: container_stats['cpu'] = {} container_stats['cpu_percent'] = None container_stats['memory'] = {} container_stats['memory_percent'] = None container_stats['io'] = {} container_stats['io_r'] = None container_stats['io_w'] = None container_stats['network'] = {} container_stats['network_rx'] = None container_stats['network_tx'] = None # Add current container stats to the stats list stats['containers'].append(container_stats) elif self.input_method == 'snmp': # Update stats using SNMP # Not available pass # Sort and update the stats self.stats = sort_stats(stats) return self.stats