First version of the Curses interface for the new GPU plugin

This commit is contained in:
nicolargo 2016-12-18 20:14:08 +01:00
parent 781dfeef0c
commit 37d7e942f1
4 changed files with 129 additions and 28 deletions

View File

@ -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

View File

@ -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,

View File

@ -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 (<SUMMARY>+CPU|PERCPU+LOAD+MEM+SWAP)
# ========================================================
# ==============================================================
# Display second line (<SUMMARY>+CPU|PERCPU+<GPU>+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"])

View File

@ -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