diff options
author | nicolargo <nicolashennion@gmail.com> | 2015-09-09 14:50:12 +0200 |
---|---|---|
committer | nicolargo <nicolashennion@gmail.com> | 2015-09-09 14:50:12 +0200 |
commit | fdebbce53ed545064bafb86bec07bc0dabf132c3 (patch) | |
tree | 32d01bdedb5b38f22fc2903a1487bac616d6f8df | |
parent | 1427274e5083b4732fa49e47d6b1631d70de7f93 (diff) |
Add IOR and W to the Curses interface
-rw-r--r-- | glances/plugins/glances_docker.py | 123 |
1 files changed, 102 insertions, 21 deletions
diff --git a/glances/plugins/glances_docker.py b/glances/plugins/glances_docker.py index 53ffbc34..26b81905 100644 --- a/glances/plugins/glances_docker.py +++ b/glances/plugins/glances_docker.py @@ -225,6 +225,7 @@ class Plugin(GlancesPlugin): container['cpu'] = self.get_docker_cpu(container['Id'], self.thread_list[container['Id']].stats) container['memory'] = self.get_docker_memory(container['Id'], self.thread_list[container['Id']].stats) container['network'] = self.get_docker_network(container['Id'], self.thread_list[container['Id']].stats) + container['io'] = self.get_docker_io(container['Id'], self.thread_list[container['Id']].stats) elif self.input_method == 'snmp': # Update stats using SNMP @@ -308,13 +309,17 @@ class Plugin(GlancesPlugin): Input: id is the full container id Output: a dict {'time_since_update': 3000, 'rx': 10, 'tx': 65}. + with: + time_since_update: number of seconds elapsed between the latest grab + rx: Number of byte received + tx: Number of byte transmited """ # Init the returned dict network_new = {} # Read the rx/tx stats (in bytes) try: - netiocounters = all_stats["network"] + netcounters = all_stats["network"] except KeyError as e: # all_stats do not have NETWORK information logger.debug("Can not grab NET usage for container {0} ({1})".format(container_id, e)) @@ -322,17 +327,17 @@ class Plugin(GlancesPlugin): return network_new # Previous network interface stats are stored in the network_old variable - if not hasattr(self, 'netiocounters_old'): + if not hasattr(self, 'inetcounters_old'): # First call, we init the network_old var - self.netiocounters_old = {} + self.netcounters_old = {} try: - self.netiocounters_old[container_id] = netiocounters + self.netcounters_old[container_id] = netcounters except (IOError, UnboundLocalError): pass - if container_id not in self.netiocounters_old: + if container_id not in self.netcounters_old: try: - self.netiocounters_old[container_id] = netiocounters + self.netcounters_old[container_id] = netcounters except (IOError, UnboundLocalError): pass else: @@ -340,17 +345,79 @@ class Plugin(GlancesPlugin): # XML/RPC API, which would otherwise be overly difficult work # for users of the API network_new['time_since_update'] = getTimeSinceLastUpdate('docker_net_{0}'.format(container_id)) - network_new['rx'] = netiocounters["rx_bytes"] - self.netiocounters_old[container_id]["rx_bytes"] - network_new['tx'] = netiocounters["tx_bytes"] - self.netiocounters_old[container_id]["tx_bytes"] - network_new['cumulative_rx'] = netiocounters["rx_bytes"] - network_new['cumulative_tx'] = netiocounters["tx_bytes"] + network_new['rx'] = netcounters["rx_bytes"] - self.netcounters_old[container_id]["rx_bytes"] + network_new['tx'] = netcounters["tx_bytes"] - self.netcounters_old[container_id]["tx_bytes"] + network_new['cumulative_rx'] = netcounters["rx_bytes"] + network_new['cumulative_tx'] = netcounters["tx_bytes"] # Save stats to compute next bitrate - self.netiocounters_old[container_id] = netiocounters + self.netcounters_old[container_id] = netcounters # Return the stats return network_new + def get_docker_io(self, container_id, all_stats): + """Return the container IO usage using the Docker API (v1.0 or higher). + + Input: id is the full container id + Output: a dict {'time_since_update': 3000, 'ior': 10, 'iow': 65}. + with: + time_since_update: number of seconds elapsed between the latest grab + ior: Number of byte readed + iow: Number of byte written + """ + # Init the returned dict + io_new = {} + + # Read the ior/iow stats (in bytes) + try: + iocounters = all_stats["blkio_stats"] + except KeyError as e: + # all_stats do not have io information + logger.debug("Can not grab block IO usage for container {0} ({1})".format(container_id, e)) + # No fallback available... + return io_new + + # Previous io interface stats are stored in the io_old variable + if not hasattr(self, 'iocounters_old'): + # First call, we init the io_old var + self.iocounters_old = {} + try: + self.iocounters_old[container_id] = iocounters + except (IOError, UnboundLocalError): + pass + + if container_id not in self.iocounters_old: + try: + self.iocounters_old[container_id] = iocounters + except (IOError, UnboundLocalError): + pass + else: + # By storing time data we enable IoR/s and IoW/s calculations in the + # XML/RPC API, which would otherwise be overly difficult work + # for users of the API + try: + # Read IOR and IOW value in the structure list of dict + ior = [i for i in iocounters['io_service_bytes_recursive'] if i['op'] == 'Read'][0]['value'] + iow = [i for i in iocounters['io_service_bytes_recursive'] if i['op'] == 'Write'][0]['value'] + ior_old = [i for i in self.iocounters_old[container_id]['io_service_bytes_recursive'] if i['op'] == 'Read'][0]['value'] + iow_old = [i for i in self.iocounters_old[container_id]['io_service_bytes_recursive'] if i['op'] == 'Write'][0]['value'] + except (IndexError, KeyError) as e: + # all_stats do not have io information + logger.debug("Can not grab block IO usage for container {0} ({1})".format(container_id, e)) + else: + io_new['time_since_update'] = getTimeSinceLastUpdate('docker_io_{0}'.format(container_id)) + io_new['ior'] = ior - ior_old + io_new['iow'] = iow - iow_old + io_new['cumulative_ior'] = ior + io_new['cumulative_iow'] = iow + + # Save stats to compute next bitrate + self.iocounters_old[container_id] = iocounters + + # Return the stats + return io_new + def get_user_ticks(self): """Return the user ticks by reading the environment variable.""" return os.sysconf(os.sysconf_names['SC_CLK_TCK']) @@ -376,9 +443,11 @@ class Plugin(GlancesPlugin): ret.append(self.curse_new_line()) # Header ret.append(self.curse_new_line()) - msg = '{0:>14}'.format('Id') - ret.append(self.curse_add_line(msg)) - msg = ' {0:20}'.format('Name') + # msg = '{0:>14}'.format('Id') + # ret.append(self.curse_add_line(msg)) + # Get the maximum containers name (cutted to 20 char max) + name_max_width = min(20, len(max(self.stats['containers'], key=lambda x: len(x['name']))['name'])) + msg = ' {0:{width}}'.format('Name', width=name_max_width) ret.append(self.curse_add_line(msg)) msg = '{0:>26}'.format('Status') ret.append(self.curse_add_line(msg)) @@ -386,6 +455,10 @@ class Plugin(GlancesPlugin): ret.append(self.curse_add_line(msg)) msg = '{0:>7}'.format('MEM') ret.append(self.curse_add_line(msg)) + msg = '{0:>6}'.format('IOR/s') + ret.append(self.curse_add_line(msg)) + msg = '{0:>6}'.format('IOW/s') + ret.append(self.curse_add_line(msg)) msg = '{0:>6}'.format('Rx/s') ret.append(self.curse_add_line(msg)) msg = '{0:>6}'.format('Tx/s') @@ -396,15 +469,15 @@ class Plugin(GlancesPlugin): for container in self.stats['containers']: ret.append(self.curse_new_line()) # Id - msg = '{0:>14}'.format(container['Id'][0:12]) - ret.append(self.curse_add_line(msg)) + # msg = '{0:>14}'.format(container['Id'][0:12]) + # ret.append(self.curse_add_line(msg)) # Name - name = container['Names'][0] - if len(name) > 20: - name = '_' + name[-19:] + name = container['name'] + if len(name) > name_max_width: + name = '_' + name[-name_max_width + 1:] else: - name = name[:20] - msg = ' {0:20}'.format(name) + name = name[:name_max_width] + msg = ' {0:{width}}'.format(name, width=name_max_width) ret.append(self.curse_add_line(msg)) # Status status = self.container_alert(container['Status']) @@ -423,6 +496,14 @@ class Plugin(GlancesPlugin): except KeyError: msg = '{0:>7}'.format('?') ret.append(self.curse_add_line(msg)) + # IO R/W + for r in ['ior', 'iow']: + try: + value = self.auto_unit(int(container['io'][r] // container['io']['time_since_update'] * 8)) + "b" + msg = '{0:>6}'.format(value) + except KeyError: + msg = '{0:>6}'.format('?') + ret.append(self.curse_add_line(msg)) # NET RX/TX for r in ['rx', 'tx']: try: |