mirror of https://github.com/nicolargo/glances
Merge branch 'feature/issue403' into develop
commit
37edbfc5a1
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue