Filter processes by others stats (username) #748

This commit is contained in:
nicolargo 2016-05-23 14:31:37 +02:00
parent d0783a21e1
commit d1f9d30aae
7 changed files with 114 additions and 12 deletions

1
NEWS
View File

@ -10,6 +10,7 @@ Enhancements and new features:
* Add Application Monitoring Process plugin (issue #780)
* Improve IP plugin to display public IP address (issue #646)
* CPU additionnal stats monitoring: Context switch, Interrupts... (issue #810)
* Filter processes by others stats (username) (issue #748)
* [Folders] Differentiate permission issue and non-existence of a directory (issue #828)
* [Web UI] Add cpu name in quicklook plugin (issue #825)
* Allow theme to be set in configuration file (issue #862)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 157 KiB

View File

@ -79,6 +79,18 @@ Columns display
pressing on the ``'/'`` key
========================= ==============================================
Process filtering
-----------------
It's possible to filter the processes list using the ``ENTER`` key.
Filter syntax is the following (examples):
- python > Filter processes name or command line starting with *python* (regexp)
- .*python.* > Filter processes name or command line containing *python* (regexp)
- username:nicolargo > Processes of nicolargo user (key:regexp)
- cmdline:\/usr\/bin.* > Processes starting by */usr/bin*
Extended info
-------------

View File

@ -30,49 +30,116 @@ class GlancesFilter(object):
>>> f.filter = '.*python.*'
>>> f.filter
'.*python.*'
>>> f.key
None
>>> f.filter = 'user:nicolargo'
>>> f.filter
'nicolargo'
>>> f.key
'user'
>>> f.filter = 'username:.*nico.*'
>>> f.filter
'.*nico.*'
>>> f.key
'username'
"""
def __init__(self):
# Filter entered by the user (string)
self._filter_input = None
# Filter to apply
self._filter = None
# Filter regular expression
self._filter_re = None
# Dict key where the filter should be applied
# Default is None: search on command line and process name
self._filter_key = None
@property
def filter_input(self):
"""Return the filter given by the user (as a sting)"""
return self._filter_input
@property
def filter(self):
"""Return the filter (as a sting)"""
"""Return the current filter to be applied"""
return self._filter
@filter.setter
def filter(self, value):
"""Set the filter (as a sting) and compute the regular expression"""
self._filter = value
self._filter_re = None
"""Set the filter (as a sting) and compute the regular expression
A filter could be one of the following:
- python > Process name of cmd start with python
- .*python.* > Process name of cmd contain python
- username:nicolargo > Process of nicolargo user
"""
self._filter_input = value
if value is None:
self._filter = None
self._filter_key = None
else:
new_filter = value.split(':')
if len(new_filter) == 1:
self._filter = new_filter[0]
self._filter_key = None
else:
self._filter = new_filter[1]
self._filter_key = new_filter[0]
self._filter_re = None
if self.filter is not None:
logger.info("Set filter to {0}".format(self.filter))
logger.info("Set filter to {0} on key {1}".format(self.filter, self.filter_key))
# Compute the regular expression
try:
self._filter_re = re.compile(self.filter)
logger.debug("Filter regex compilation OK: {0}".format(self.filter))
except Exception as e:
logger.error("Cannot compile filter regex: {0} ({1})".format(self.filter, e))
self._filter = None
self._filter_re = None
self._filter_key = None
@property
def filter_re(self):
"""Return the filter regular expression"""
return self._filter_re
def is_filtered(self, process, key='cmdline'):
@property
def filter_key(self):
"""key where the filter should be applied"""
return self._filter_key
def is_filtered(self, process):
"""Return True if the process item match the current filter
The proces item is a dict.
The filter will be applyed on the process[key] (command line by default)
"""
if self.filter is None:
# No filter => Not filtered
return False
if self.filter_key is None:
# Apply filter on command line and process name
return self._is_process_filtered(process, key='cmdline') and self._is_process_filtered(process, key='name')
else:
# Apply filter on <key>
return self._is_process_filtered(process)
def _is_process_filtered(self, process, key=None):
"""Return True if the process[key] should be filtered according to the current filter"""
if key is None:
key = self.filter_key
try:
return self._filter_re.match(' '.join(process[key])) is None
# If the item process[key] is a list, convert it to a string
# in order to match it with the current regular expression
if isinstance(process[key], list):
value = ' '.join(process[key])
else:
value = process[key]
except KeyError:
# If the key did not exist
return False
try:
return self._filter_re.match(value) is None
except AttributeError:
# Filter processes crashs with a bad regular expression pattern (issue #665)
return False

View File

@ -781,8 +781,18 @@ class _GlancesCurses(object):
# Only in standalone mode (cs_status is None)
if self.edit_filter and cs_status is None:
new_filter = self.display_popup(
'Process filter pattern: ', is_input=True,
input_value=glances_processes.process_filter)
'Process filter pattern: \n' +
'\n' +
'Examples:\n' +
'- python\n' +
'- .*python.*\n' +
'- \/usr\/lib.*' +
'- name:.*nautilus.*\n' +
'- cmdline:.*glances.*\n' +
'- username:nicolargo\n' +
'- username:^root ',
is_input=True,
input_value=glances_processes.process_filter_input)
glances_processes.process_filter = new_filter
elif self.edit_filter and cs_status != 'None':
self.display_popup('Process filter only available in standalone mode')

View File

@ -84,6 +84,8 @@ class Plugin(GlancesPlugin):
msg = 'Processes filter:'
ret.append(self.curse_add_line(msg, "TITLE"))
msg = ' {0} '.format(glances_processes.process_filter)
if glances_processes.process_filter_key is not None:
msg += 'on column {0} '.format(glances_processes.process_filter_key)
ret.append(self.curse_add_line(msg, "FILTER"))
msg = '(\'ENTER\' to edit, \'E\' to reset)'
ret.append(self.curse_add_line(msg))

View File

@ -116,9 +116,14 @@ class GlancesProcesses(object):
"""Set the maximum number of processes showed in the UI."""
self._max_processes = value
@property
def process_filter_input(self):
"""Get the process filter (given by the user)."""
return self._filter.filter_input
@property
def process_filter(self):
"""Get the process filter."""
"""Get the process filter (current apply filter)."""
return self._filter.filter
@process_filter.setter
@ -126,6 +131,11 @@ class GlancesProcesses(object):
"""Set the process filter."""
self._filter.filter = value
@property
def process_filter_key(self):
"""Get the process filter key."""
return self._filter.filter_key
@property
def process_filter_re(self):
"""Get the process regular expression compiled."""
@ -398,7 +408,7 @@ class GlancesProcesses(object):
OSX and s['name'] == 'kernel_task'):
continue
# Continue to the next process if it has to be filtered
if s is None or (self._filter.is_filtered(s, 'cmdline') and self._filter.is_filtered(s, 'name')):
if s is None or self._filter.is_filtered(s):
excluded_processes.add(proc)
continue