diff --git a/conf/glances.conf b/conf/glances.conf index 990da6ec..03e5baa6 100644 --- a/conf/glances.conf +++ b/conf/glances.conf @@ -71,16 +71,15 @@ system_careful=50 system_warning=70 system_critical=90 -[load] -# Define LOAD thresholds -# Value * number of cores -# Default values if not defined: 0.7/1.0/5.0 per number of cores -# Source: http://blog.scoutapp.com/articles/2009/07/31/understanding-load-averages -# http://www.linuxjournal.com/article/9001 -careful=0.7 -warning=1.0 -critical=5.0 -#log=False +[gpu] +# Default processor values if not defined: 50/70/90 +proc_careful=50 +proc_warning=70 +proc_critical=90 +# Default memory values if not defined: 50/70/90 +mem_careful=50 +mem_warning=70 +mem_critical=90 [mem] # Define RAM thresholds in % @@ -96,6 +95,17 @@ careful=50 warning=70 critical=90 +[load] +# Define LOAD thresholds +# Value * number of cores +# Default values if not defined: 0.7/1.0/5.0 per number of cores +# Source: http://blog.scoutapp.com/articles/2009/07/31/understanding-load-averages +# http://www.linuxjournal.com/article/9001 +careful=0.7 +warning=1.0 +critical=5.0 +#log=False + [network] # Default bitrate thresholds in % of the network interface speed # Default values if not defined: 70/80/90 diff --git a/glances/main.py b/glances/main.py index 64e7a90f..871f0756 100644 --- a/glances/main.py +++ b/glances/main.py @@ -113,6 +113,8 @@ Start the client browser (browser mode):\n\ dest='disable_folders', help='disable folder module') parser.add_argument('--disable-fs', action='store_true', default=False, dest='disable_fs', help='disable filesystem module') + parser.add_argument('--disable-gpu', action='store_true', default=False, + dest='disable_gpu', help='disable GPU module') parser.add_argument('--disable-hddtemp', action='store_true', default=False, dest='disable_hddtemp', help='disable HD temperature module') parser.add_argument('--disable-ip', action='store_true', default=False, diff --git a/glances/outputs/glances_curses.py b/glances/outputs/glances_curses.py index f752e0b2..ca776752 100644 --- a/glances/outputs/glances_curses.py +++ b/glances/outputs/glances_curses.py @@ -66,6 +66,7 @@ class _GlancesCurses(object): 'D': {'switch': 'disable_docker'}, 'e': {'switch': 'enable_process_extended'}, 'F': {'switch': 'fs_free_space'}, + 'G': {'switch': 'disable_gpu'}, 'h': {'switch': 'help_tag'}, 'I': {'switch': 'disable_ip'}, 'l': {'switch': 'disable_alert'}, @@ -470,6 +471,7 @@ class _GlancesCurses(object): ret["cpu"] = stats.get_plugin('percpu').get_stats_display(args=self.args) else: ret["cpu"] = stats.get_plugin('cpu').get_stats_display(args=self.args) + ret["gpu"] = stats.get_plugin('gpu').get_stats_display(args=self.args) ret["load"] = stats.get_plugin('load').get_stats_display(args=self.args) ret["mem"] = stats.get_plugin('mem').get_stats_display(args=self.args) ret["memswap"] = stats.get_plugin('memswap').get_stats_display(args=self.args) @@ -591,9 +593,9 @@ class _GlancesCurses(object): self.new_column() self.display_plugin(__stat_display["uptime"]) - # ======================================================== - # Display second line (+CPU|PERCPU+LOAD+MEM+SWAP) - # ======================================================== + # ============================================================== + # Display second line (+CPU|PERCPU++LOAD+MEM+SWAP) + # ============================================================== self.init_column() self.new_line() @@ -606,6 +608,10 @@ class _GlancesCurses(object): cpu_width = 0 else: cpu_width = self.get_stats_display_width(__stat_display["cpu"]) + if self.args.disable_gpu: + gpu_width = 0 + else: + gpu_width = self.get_stats_display_width(__stat_display["gpu"]) if self.args.disable_mem: mem_width = 0 else: @@ -620,11 +626,12 @@ class _GlancesCurses(object): load_width = self.get_stats_display_width(__stat_display["load"]) # Size of plugins but quicklook - stats_width = cpu_width + mem_width + swap_width + load_width + stats_width = cpu_width + gpu_width + mem_width + swap_width + load_width # Number of plugin but quicklook stats_number = ( int(not self.args.disable_cpu and __stat_display["cpu"]['msgdict'] != []) + + int(not self.args.disable_gpu and __stat_display["gpu"]['msgdict'] != []) + int(not self.args.disable_mem and __stat_display["mem"]['msgdict'] != []) + int(not self.args.disable_memswap and __stat_display["memswap"]['msgdict'] != []) + int(not self.args.disable_load and __stat_display["load"]['msgdict'] != [])) @@ -660,7 +667,7 @@ class _GlancesCurses(object): mem_width = 0 else: mem_width = self.get_stats_display_width(__stat_display["mem"], without_option=True) - stats_width = quicklook_width + 1 + cpu_width + mem_width + swap_width + load_width + stats_width = quicklook_width + 1 + cpu_width + gpu_width + mem_width + swap_width + load_width self.space_between_column = max(1, int((screen_x - stats_width) / (stats_number - 1))) # No space again ? Remove optionnal CPU stats if self.space_between_column < 3: @@ -669,7 +676,7 @@ class _GlancesCurses(object): cpu_width = 0 else: cpu_width = self.get_stats_display_width(__stat_display["cpu"], without_option=True) - stats_width = quicklook_width + 1 + cpu_width + mem_width + swap_width + load_width + stats_width = quicklook_width + 1 + cpu_width + gpu_width + mem_width + swap_width + load_width self.space_between_column = max(1, int((screen_x - stats_width) / (stats_number - 1))) else: self.space_between_column = 0 @@ -677,6 +684,8 @@ class _GlancesCurses(object): # Display CPU, MEM, SWAP and LOAD self.display_plugin(__stat_display["cpu"], display_optional=display_optional_cpu) self.new_column() + self.display_plugin(__stat_display["gpu"]) + self.new_column() self.display_plugin(__stat_display["mem"], display_optional=display_optional_mem) self.new_column() self.display_plugin(__stat_display["memswap"]) diff --git a/glances/plugins/glances_gpu.py b/glances/plugins/glances_gpu.py index 33ddd9c7..be2d46b9 100644 --- a/glances/plugins/glances_gpu.py +++ b/glances/plugins/glances_gpu.py @@ -46,8 +46,7 @@ class Plugin(GlancesPlugin): self.init_nvidia() # We want to display the stat in the curse interface - # !!! TODO: Not implemented yeat - self.display_curse = False + self.display_curse = True # Init the stats self.reset() @@ -71,6 +70,10 @@ class Plugin(GlancesPlugin): return self.nvml_ready + def get_key(self): + """Return the key of the list.""" + return 'gpu_id' + @GlancesPlugin._check_decorator @GlancesPlugin._log_result_decorator def update(self): @@ -78,6 +81,12 @@ class Plugin(GlancesPlugin): self.reset() + # !!! JUST FOR TEST + # self.stats = [{"key": "gpu_id", "mem": None, "proc": 60, "gpu_id": 0, "name": "GeForce GTX 560 Ti"}] + # self.stats = [{"key": "gpu_id", "mem": 30, "proc": 60, "gpu_id": 0, "name": "GeForce GTX 560 Ti"}, + # {"key": "gpu_id", "mem": 70, "proc": 80, "gpu_id": 1, "name": "GeForce GTX 560 Ti"}] + # !!! TO BE REMOVED + if not self.nvml_ready: return self.stats @@ -87,14 +96,85 @@ class Plugin(GlancesPlugin): # not available pass - # Update the view - # self.update_views() - return self.stats - def get_key(self): - """Return the key of the list.""" - return 'gpu_id' + def update_views(self): + """Update stats views.""" + # Call the father's method + super(Plugin, self).update_views() + + # Add specifics informations + # Alert + for i in self.stats: + # Init the views for the current GPU + self.views[i[self.get_key()]] = {'proc': {}, 'mem': {}} + # Processor alert + if 'proc' in i: + alert = self.get_alert(i['proc'], header='proc') + self.views[i[self.get_key()]]['proc']['decoration'] = alert + # Memory alert + if 'mem' in i: + alert = self.get_alert(i['mem'], header='mem') + self.views[i[self.get_key()]]['mem']['decoration'] = alert + + return True + + def msg_curse(self, args=None): + """Return the dict to display in the curse interface.""" + # Init the return message + ret = [] + + # Only process if stats exist, not empty (issue #871) and plugin not disabled + if not self.stats or (self.stats == []) or self.is_disable(): + return ret + + # Build the string message + if len(self.stats) == 1: + # Mono GPU + gpu_stats = self.stats[0] + # Header + header = '{} {}'.format('GPU', gpu_stats['name']) + msg = header[:16] + ret.append(self.curse_add_line(msg, "TITLE")) + # New line + ret.append(self.curse_new_line()) + # GPU CPU + msg = '{:8}'.format('proc:') + ret.append(self.curse_add_line(msg)) + msg = '{:>7d}%'.format(int(gpu_stats['proc'])) + ret.append(self.curse_add_line( + msg, self.get_views(item=gpu_stats[self.get_key()], + key='proc', + option='decoration'))) + # New line + ret.append(self.curse_new_line()) + # GPU MEM + msg = '{:8}'.format('mem:') + ret.append(self.curse_add_line(msg)) + if gpu_stats['mem'] is None: + msg = '{:>8}'.format('N/A') + else: + msg = '{:>7d%}'.format(int(gpu_stats['mem'])) + ret.append(self.curse_add_line( + msg, self.get_views(item=gpu_stats[self.get_key()], + key='mem', + option='decoration'))) + else: + # Multi GPU + # Header + header = '{} {}'.format(len(self.stats), 'GPUs') + msg = header[:16] + ret.append(self.curse_add_line(msg, "TITLE")) + for gpu_stats in self.stats: + # New line + ret.append(self.curse_new_line()) + # GPU ID + PROC + MEM + msg = '{}: {:>3}% mem: {:>3}%'.format(gpu_stats['gpu_id'], + gpu_stats['proc'], + gpu_stats['proc'],) + ret.append(self.curse_add_line(msg)) + + return ret def get_device_handles(self): """ @@ -115,9 +195,9 @@ class Plugin(GlancesPlugin): # GPU name device_stats['name'] = self.get_device_name(device_handle) # Memory consumption in % (not available on all GPU) - device_stats['memory_percent'] = self.get_memory_percent(device_handle) + device_stats['mem'] = self.get_mem(device_handle) # Processor consumption in % - device_stats['processor_percent'] = self.get_processor_percent(device_handle) + device_stats['proc'] = self.get_proc(device_handle) stats.append(device_stats) return stats @@ -129,7 +209,7 @@ class Plugin(GlancesPlugin): except pynvml.NVMlError: return "NVIDIA GPU" - def get_memory_percent(self, device_handle): + def get_mem(self, device_handle): """Get GPU device memory consumption in percent""" try: return pynvml.nvmlDeviceGetUtilizationRates(device_handle).memory @@ -140,7 +220,7 @@ class Plugin(GlancesPlugin): except pynvml.NVMLError: return None - def get_processor_percent(self, device_handle): + def get_proc(self, device_handle): """Get GPU device CPU consumption in percent""" try: return pynvml.nvmlDeviceGetUtilizationRates(device_handle).gpu