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
|
# 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
|
from glances.core.glances_processes import GlancesProcesses
|
||||||
glances_processes = GlancesProcesses()
|
glances_processes = GlancesProcesses()
|
||||||
|
|
||||||
# The global instance for the logs
|
# The global instance for the logs
|
||||||
from glances.core.glances_logs import GlancesLogs
|
from glances.core.glances_logs import GlancesLogs
|
||||||
glances_logs = GlancesLogs()
|
glances_logs = GlancesLogs()
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,8 @@ class GlancesMain(object):
|
||||||
dest='disable_sensors', help=_('disable sensors module'))
|
dest='disable_sensors', help=_('disable sensors module'))
|
||||||
parser.add_argument('--disable-process', action='store_true', default=False,
|
parser.add_argument('--disable-process', action='store_true', default=False,
|
||||||
dest='disable_process', help=_('disable process module'))
|
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,
|
parser.add_argument('--disable-log', action='store_true', default=False,
|
||||||
dest='disable_log', help=_('disable log module'))
|
dest='disable_log', help=_('disable log module'))
|
||||||
# CSV output feature
|
# CSV output feature
|
||||||
|
|
|
@ -52,6 +52,9 @@ class GlancesProcesses(object):
|
||||||
# Default is to enable the processes stats
|
# Default is to enable the processes stats
|
||||||
self.disable_tag = False
|
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
|
# Maximum number of processes showed in the UI interface
|
||||||
# None if no limit
|
# None if no limit
|
||||||
self.max_processes = None
|
self.max_processes = None
|
||||||
|
@ -65,6 +68,15 @@ class GlancesProcesses(object):
|
||||||
"""Disable process stats."""
|
"""Disable process stats."""
|
||||||
self.disable_tag = True
|
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):
|
def set_max_processes(self, value):
|
||||||
"""Set the maximum number of processes showed in the UI interfaces"""
|
"""Set the maximum number of processes showed in the UI interfaces"""
|
||||||
self.max_processes = value
|
self.max_processes = value
|
||||||
|
@ -84,14 +96,16 @@ class GlancesProcesses(object):
|
||||||
=> cpu_percent, memory_percent, io_counters, name
|
=> cpu_percent, memory_percent, io_counters, name
|
||||||
standard_stats: for all the displayed processes
|
standard_stats: for all the displayed processes
|
||||||
=> username, cmdline, status, memory_info, cpu_times
|
=> username, cmdline, status, memory_info, cpu_times
|
||||||
extended_stats: only for top processes (!!! to be implemented)
|
extended_stats: only for top processes (see issue #403)
|
||||||
=> connections (UDP/TCP), memory_swap
|
=> connections (UDP/TCP), memory_swap...
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Process ID (always)
|
# Process ID (always)
|
||||||
procstat = proc.as_dict(attrs=['pid'])
|
procstat = proc.as_dict(attrs=['pid'])
|
||||||
|
|
||||||
if mandatory_stats:
|
if mandatory_stats:
|
||||||
|
procstat['mandatory_stats'] = True
|
||||||
|
|
||||||
# Process CPU, MEM percent and name
|
# Process CPU, MEM percent and name
|
||||||
procstat.update(proc.as_dict(attrs=['cpu_percent', 'memory_percent', 'name'], ad_value=''))
|
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]
|
procstat['io_counters'] += [io_tag]
|
||||||
|
|
||||||
if standard_stats:
|
if standard_stats:
|
||||||
|
procstat['standard_stats'] = True
|
||||||
|
|
||||||
# Process username (cached with internal cache)
|
# Process username (cached with internal cache)
|
||||||
try:
|
try:
|
||||||
self.username_cache[procstat['pid']]
|
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.update(proc.as_dict(attrs=['status', 'nice', 'memory_info', 'cpu_times']))
|
||||||
procstat['status'] = str(procstat['status'])[:1].upper()
|
procstat['status'] = str(procstat['status'])[:1].upper()
|
||||||
|
|
||||||
if extended_stats:
|
if extended_stats and not self.disable_extended_tag:
|
||||||
# Process network connections (TCP and UDP) (Experimental)
|
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:
|
try:
|
||||||
procstat['tcp'] = len(proc.connections(kind="tcp"))
|
procstat['tcp'] = len(proc.connections(kind="tcp"))
|
||||||
procstat['udp'] = len(proc.connections(kind="udp"))
|
procstat['udp'] = len(proc.connections(kind="udp"))
|
||||||
except:
|
except:
|
||||||
procstat['tcp'] = 0
|
procstat['tcp'] = None
|
||||||
procstat['udp'] = 0
|
procstat['udp'] = None
|
||||||
|
|
||||||
# SWAP memory
|
# IO Nice
|
||||||
# Only on Linux based OS
|
# http://pythonhosted.org/psutil/#psutil.Process.ionice
|
||||||
# http://www.cyberciti.biz/faq/linux-which-process-is-using-swap/
|
if is_linux or is_windows:
|
||||||
if is_linux:
|
procstat.update(proc.as_dict(attrs=['ionice']))
|
||||||
logger.debug(proc.memory_maps())
|
else:
|
||||||
procstat['memory_swap'] = sum([ v.swap for v in proc.memory_maps() ])
|
procstat['ionice'] = None
|
||||||
|
|
||||||
|
#logger.debug(procstat)
|
||||||
|
|
||||||
return procstat
|
return procstat
|
||||||
|
|
||||||
|
@ -220,17 +265,22 @@ class GlancesProcesses(object):
|
||||||
# Sort the internal dict and cut the top N (Return a list of tuple)
|
# Sort the internal dict and cut the top N (Return a list of tuple)
|
||||||
# tuple=key (proc), dict (returned by __get_process_stats)
|
# tuple=key (proc), dict (returned by __get_process_stats)
|
||||||
processiter = sorted(processdict.items(), key=lambda x: x[1][self.getsortkey()], reverse=True)
|
processiter = sorted(processdict.items(), key=lambda x: x[1][self.getsortkey()], reverse=True)
|
||||||
|
first = True
|
||||||
for i in processiter[0:self.get_max_processes()]:
|
for i in processiter[0:self.get_max_processes()]:
|
||||||
# Already existing mandatory stats
|
# Already existing mandatory stats
|
||||||
procstat = i[1]
|
procstat = i[1]
|
||||||
# Update with standard stats
|
# Update with standard stats
|
||||||
|
# and extended stats but only for TOP (first) process
|
||||||
procstat.update(self.__get_process_stats(i[0],
|
procstat.update(self.__get_process_stats(i[0],
|
||||||
mandatory_stats=False,
|
mandatory_stats=False,
|
||||||
standard_stats=True))
|
standard_stats=True,
|
||||||
|
extended_stats=first))
|
||||||
# Add a specific time_since_update stats for bitrate
|
# Add a specific time_since_update stats for bitrate
|
||||||
procstat['time_since_update'] = time_since_update
|
procstat['time_since_update'] = time_since_update
|
||||||
# Update process list
|
# Update process list
|
||||||
self.processlist.append(procstat)
|
self.processlist.append(procstat)
|
||||||
|
# Next...
|
||||||
|
first = False
|
||||||
else:
|
else:
|
||||||
# Get all the processes
|
# Get all the processes
|
||||||
for i in processdict.items():
|
for i in processdict.items():
|
||||||
|
|
|
@ -43,6 +43,14 @@ class GlancesStandalone(object):
|
||||||
logger.warning(_("Maximum displayed processes is not configured (high CPU consumption)"))
|
logger.warning(_("Maximum displayed processes is not configured (high CPU consumption)"))
|
||||||
glances_processes.set_max_processes(max_processes)
|
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
|
# Initial system informations update
|
||||||
self.stats.update()
|
self.stats.update()
|
||||||
|
|
||||||
|
|
|
@ -215,6 +215,13 @@ class GlancesCurses(object):
|
||||||
elif self.pressedkey == ord('d'):
|
elif self.pressedkey == ord('d'):
|
||||||
# 'd' > Show/hide disk I/O stats
|
# 'd' > Show/hide disk I/O stats
|
||||||
self.args.disable_diskio = not self.args.disable_diskio
|
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'):
|
elif self.pressedkey == ord('f'):
|
||||||
# 'f' > Show/hide fs stats
|
# 'f' > Show/hide fs stats
|
||||||
self.args.disable_fs = not self.args.disable_fs
|
self.args.disable_fs = not self.args.disable_fs
|
||||||
|
|
|
@ -124,7 +124,9 @@ class Plugin(GlancesPlugin):
|
||||||
msg = msg_col2.format("r", _("Reset history"))
|
msg = msg_col2.format("r", _("Reset history"))
|
||||||
ret.append(self.curse_add_line(msg))
|
ret.append(self.curse_add_line(msg))
|
||||||
ret.append(self.curse_new_line())
|
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))
|
ret.append(self.curse_add_line(msg))
|
||||||
|
|
||||||
# Return the message with decoration
|
# Return the message with decoration
|
||||||
|
|
|
@ -40,7 +40,7 @@ class GlancesPlugin(object):
|
||||||
"""Init the plugin of plugins class."""
|
"""Init the plugin of plugins class."""
|
||||||
# Plugin name (= module name without glances_)
|
# Plugin name (= module name without glances_)
|
||||||
self.plugin_name = self.__class__.__module__[len('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
|
# Init the args
|
||||||
self.args = args
|
self.args = args
|
||||||
|
|
|
@ -110,6 +110,7 @@ class Plugin(GlancesPlugin):
|
||||||
tag_proc_time = True
|
tag_proc_time = True
|
||||||
|
|
||||||
# Loop over processes (sorted by the sort key previously compute)
|
# Loop over processes (sorted by the sort key previously compute)
|
||||||
|
first = True
|
||||||
for p in self.sortlist(process_sort_key):
|
for p in self.sortlist(process_sort_key):
|
||||||
ret.append(self.curse_new_line())
|
ret.append(self.curse_new_line())
|
||||||
# CPU
|
# CPU
|
||||||
|
@ -237,6 +238,78 @@ class Plugin(GlancesPlugin):
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
ret.append(self.curse_add_line("", splittable=True))
|
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 the message with decoration
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue