Merge branch 'feature/issue403' into develop

pull/416/head
Nicolargo 2014-08-15 11:45:51 +02:00
commit 37edbfc5a1
8 changed files with 158 additions and 17 deletions

View File

@ -66,11 +66,10 @@ logger = glancesLogger()
# Instances shared between all Glances scripts
# ============================================
# glances_processes for processcount and processlist plugins
# Glances_processes for processcount and processlist plugins
from glances.core.glances_processes import GlancesProcesses
glances_processes = GlancesProcesses()
# The global instance for the logs
from glances.core.glances_logs import GlancesLogs
glances_logs = GlancesLogs()

View File

@ -74,6 +74,8 @@ class GlancesMain(object):
dest='disable_sensors', help=_('disable sensors module'))
parser.add_argument('--disable-process', action='store_true', default=False,
dest='disable_process', help=_('disable process module'))
parser.add_argument('--disable-process-extended', action='store_true', default=False,
dest='disable_process_extended', help=_('disable extended stats on top process'))
parser.add_argument('--disable-log', action='store_true', default=False,
dest='disable_log', help=_('disable log module'))
# CSV output feature

View File

@ -52,6 +52,9 @@ class GlancesProcesses(object):
# Default is to enable the processes stats
self.disable_tag = False
# Extended stats for top process is enable by default
self.disable_extended_tag = False
# Maximum number of processes showed in the UI interface
# None if no limit
self.max_processes = None
@ -65,6 +68,15 @@ class GlancesProcesses(object):
"""Disable process stats."""
self.disable_tag = True
def enable_extended(self):
"""Enable extended process stats."""
self.disable_extended_tag = False
self.update()
def disable_extended(self):
"""Disable extended process stats."""
self.disable_extended_tag = True
def set_max_processes(self, value):
"""Set the maximum number of processes showed in the UI interfaces"""
self.max_processes = value
@ -84,14 +96,16 @@ class GlancesProcesses(object):
=> cpu_percent, memory_percent, io_counters, name
standard_stats: for all the displayed processes
=> username, cmdline, status, memory_info, cpu_times
extended_stats: only for top processes (!!! to be implemented)
=> connections (UDP/TCP), memory_swap
extended_stats: only for top processes (see issue #403)
=> connections (UDP/TCP), memory_swap...
"""
# Process ID (always)
procstat = proc.as_dict(attrs=['pid'])
if mandatory_stats:
procstat['mandatory_stats'] = True
# Process CPU, MEM percent and name
procstat.update(proc.as_dict(attrs=['cpu_percent', 'memory_percent', 'name'], ad_value=''))
@ -126,6 +140,8 @@ class GlancesProcesses(object):
procstat['io_counters'] += [io_tag]
if standard_stats:
procstat['standard_stats'] = True
# Process username (cached with internal cache)
try:
self.username_cache[procstat['pid']]
@ -154,21 +170,50 @@ class GlancesProcesses(object):
procstat.update(proc.as_dict(attrs=['status', 'nice', 'memory_info', 'cpu_times']))
procstat['status'] = str(procstat['status'])[:1].upper()
if extended_stats:
# Process network connections (TCP and UDP) (Experimental)
if extended_stats and not self.disable_extended_tag:
procstat['extended_stats'] = True
# CPU affinity
# Memory extended
# Number of context switch
# Number of file descriptors (Unix only)
# Threads number
procstat.update(proc.as_dict(attrs=['cpu_affinity',
'memory_info_ex',
'num_ctx_switches',
'num_fds',
'num_threads']))
# Number of handles (Windows only)
if is_windows:
procstat.update(proc.as_dict(attrs=['num_handles']))
else:
procstat['num_handles'] = None
# SWAP memory (Only on Linux based OS)
# http://www.cyberciti.biz/faq/linux-which-process-is-using-swap/
if is_linux:
try:
procstat['memory_swap'] = sum([v.swap for v in proc.memory_maps()])
except psutil.AccessDenied:
procstat['memory_swap'] = None
# Process network connections (TCP and UDP)
try:
procstat['tcp'] = len(proc.connections(kind="tcp"))
procstat['udp'] = len(proc.connections(kind="udp"))
except:
procstat['tcp'] = 0
procstat['udp'] = 0
procstat['tcp'] = None
procstat['udp'] = None
# SWAP memory
# Only on Linux based OS
# http://www.cyberciti.biz/faq/linux-which-process-is-using-swap/
if is_linux:
logger.debug(proc.memory_maps())
procstat['memory_swap'] = sum([ v.swap for v in proc.memory_maps() ])
# IO Nice
# http://pythonhosted.org/psutil/#psutil.Process.ionice
if is_linux or is_windows:
procstat.update(proc.as_dict(attrs=['ionice']))
else:
procstat['ionice'] = None
#logger.debug(procstat)
return procstat
@ -220,17 +265,22 @@ class GlancesProcesses(object):
# Sort the internal dict and cut the top N (Return a list of tuple)
# tuple=key (proc), dict (returned by __get_process_stats)
processiter = sorted(processdict.items(), key=lambda x: x[1][self.getsortkey()], reverse=True)
first = True
for i in processiter[0:self.get_max_processes()]:
# Already existing mandatory stats
procstat = i[1]
# Update with standard stats
# and extended stats but only for TOP (first) process
procstat.update(self.__get_process_stats(i[0],
mandatory_stats=False,
standard_stats=True))
standard_stats=True,
extended_stats=first))
# Add a specific time_since_update stats for bitrate
procstat['time_since_update'] = time_since_update
# Update process list
self.processlist.append(procstat)
# Next...
first = False
else:
# Get all the processes
for i in processdict.items():

View File

@ -43,6 +43,14 @@ class GlancesStandalone(object):
logger.warning(_("Maximum displayed processes is not configured (high CPU consumption)"))
glances_processes.set_max_processes(max_processes)
# If process extended stats is disabled by user
if args.disable_process_extended:
logger.info(_("Extended stats for top process is disabled"))
glances_processes.disable_extended()
else:
logger.debug(_("Extended stats for top process is enabled (default behavor)"))
glances_processes.enable_extended()
# Initial system informations update
self.stats.update()

View File

@ -215,6 +215,13 @@ class GlancesCurses(object):
elif self.pressedkey == ord('d'):
# 'd' > Show/hide disk I/O stats
self.args.disable_diskio = not self.args.disable_diskio
elif self.pressedkey == ord('e'):
# 'e' > Enable/Disable extended stats for top process
self.args.disable_process_extended = not self.args.disable_process_extended
if self.args.disable_process_extended:
glances_processes.disable_extended()
else:
glances_processes.enable_extended()
elif self.pressedkey == ord('f'):
# 'f' > Show/hide fs stats
self.args.disable_fs = not self.args.disable_fs

View File

@ -124,7 +124,9 @@ class Plugin(GlancesPlugin):
msg = msg_col2.format("r", _("Reset history"))
ret.append(self.curse_add_line(msg))
ret.append(self.curse_new_line())
msg = msg_col.format("q", _("Quit (Esc and Ctrl-C also work)"))
msg = msg_col.format("e", _("Enable/disable top extended stats"))
ret.append(self.curse_add_line(msg))
msg = msg_col2.format("q", _("Quit (Esc and Ctrl-C also work)"))
ret.append(self.curse_add_line(msg))
# Return the message with decoration

View File

@ -40,7 +40,7 @@ class GlancesPlugin(object):
"""Init the plugin of plugins class."""
# Plugin name (= module name without glances_)
self.plugin_name = self.__class__.__module__[len('glances_'):]
logger.debug(_("Init plugin %s") % self.plugin_name)
# logger.debug(_("Init plugin %s") % self.plugin_name)
# Init the args
self.args = args

View File

@ -110,6 +110,7 @@ class Plugin(GlancesPlugin):
tag_proc_time = True
# Loop over processes (sorted by the sort key previously compute)
first = True
for p in self.sortlist(process_sort_key):
ret.append(self.curse_new_line())
# CPU
@ -237,6 +238,78 @@ class Plugin(GlancesPlugin):
except UnicodeEncodeError:
ret.append(self.curse_add_line("", splittable=True))
# Add extended stats but only for the top processes
# !!! CPU consumption ???
# TODO: extended stats into the web interface
if first and 'extended_stats' in p:
# Left padding
xpad = ' ' * 13
# First line is CPU affinity
if p['cpu_affinity'] is not None:
ret.append(self.curse_new_line())
msg = xpad + _('CPU affinity: ') + str(len(p['cpu_affinity'])) + _(' cores')
ret.append(self.curse_add_line(msg))
# Second line is memory info
if p['memory_info_ex'] is not None:
ret.append(self.curse_new_line())
msg = xpad + _('Memory info: ')
for k, v in p['memory_info_ex']._asdict().items():
# Ignore rss and vms (already displayed)
if k not in ['rss', 'vms'] and v is not None:
msg += k + ' ' + self.auto_unit(v, low_precision=False) + ' '
if p['memory_swap'] is not None:
msg += _('swap ') + self.auto_unit(p['memory_swap'], low_precision=False)
ret.append(self.curse_add_line(msg))
# Third line is for openned files/network sessions
ret.append(self.curse_new_line())
msg = xpad + _('Openned: ')
if p['num_threads'] is not None:
msg += _('threads ') + str(p['num_threads']) + ' '
if p['num_fds'] is not None:
msg += _('files ') + str(p['num_fds']) + ' '
if p['num_handles'] is not None:
msg += _('handles ') + str(p['num_handles']) + ' '
if p['tcp'] is not None:
msg += _('TCP ') + str(p['tcp']) + ' '
if p['udp'] is not None:
msg += _('UDP ') + str(p['udp']) + ' '
ret.append(self.curse_add_line(msg))
# Fouth line is IO nice level (only Linux and Windows OS)
if p['ionice'] is not None:
ret.append(self.curse_new_line())
msg = xpad + _('IO nice: ')
k = _('Class is ')
v = p['ionice'].ioclass
# Linux: The scheduling class. 0 for none, 1 for real time, 2 for best-effort, 3 for idle.
# Windows: On Windows only ioclass is used and it can be set to 2 (normal), 1 (low) or 0 (very low).
if is_windows:
if v == 0:
msg += k + 'Very Low'
elif v == 1:
msg += k + 'Low'
elif v == 2:
msg += _('No specific I/O priority')
else:
msg += k + str(v)
else:
if v == 0:
msg += _('No specific I/O priority')
elif v == 1:
msg += k + 'Real Time'
elif v == 2:
msg += k + 'Best Effort'
elif v == 3:
msg += k + 'IDLE'
else:
msg += k + str(v)
# value is a number which goes from 0 to 7.
# The higher the value, the lower the I/O priority of the process.
if hasattr(p['ionice'], 'value') and p['ionice'].value != 0:
msg += _(' (value %s/7)') % str(p['ionice'].value)
ret.append(self.curse_add_line(msg))
# End of extended stats
first = False
# Return the message with decoration
return ret