提交 83026909 编写于 作者: N Nicolargo

Add the process filter feature

上级 37edbfc5
......@@ -111,6 +111,8 @@ class GlancesMain(object):
parser.add_argument('-w', '--webserver', action='store_true', default=False,
dest='webserver', help=_('run Glances in web server mode'))
# Display (Curses) options
parser.add_argument('-f', '--process-filter', default=None, type=str,
dest='process_filter', help=_('set the process filter patern (regular expression)'))
parser.add_argument('-b', '--byte', action='store_true', default=False,
dest='byte', help=_('display network rate in byte per second'))
parser.add_argument('-1', '--percpu', action='store_true', default=False,
......@@ -141,7 +143,7 @@ class GlancesMain(object):
# In web server mode, defaul refresh time: 5 sec
if args.webserver:
args.time = 5
args.time = 5
# Server or client login/password
args.username = self.username
......
......@@ -17,10 +17,13 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Import Glances lib
from glances.core.glances_globals import is_linux, is_bsd, is_mac, is_windows, logger
from glances.core.glances_timer import Timer, getTimeSinceLastUpdate
# Import Python lib
import psutil
import re
class GlancesProcesses(object):
......@@ -59,6 +62,13 @@ class GlancesProcesses(object):
# None if no limit
self.max_processes = None
# Process filter is a regular expression
self.process_filter = None
self.process_filter_re = None
# !!! ONLY FOR TEST
# self.set_process_filter('.*python.*')
def enable(self):
"""Enable process stats."""
self.disable_tag = False
......@@ -86,6 +96,37 @@ class GlancesProcesses(object):
"""Get the maximum number of processes showed in the UI interfaces"""
return self.max_processes
def set_process_filter(self, value):
"""Set the process filter"""
logger.info(_("Set process filter to %s") % value)
self.process_filter = value
if value is not None:
try:
self.process_filter_re = re.compile(value)
logger.debug(_("Process filter regular expression compilation OK: %s") % self.get_process_filter())
except:
logger.error(_("Can not compile process filter regular expression: %s") % value)
self.process_filter_re = None
else:
self.process_filter_re = None
return self.process_filter
def get_process_filter(self):
"""Get the process filter"""
return self.process_filter
def get_process_filter_re(self):
"""Get the process regular expression compiled"""
return self.process_filter_re
def is_filtered(self, value):
"""Return True if the value should be filtered"""
if self.get_process_filter() is None:
# No filter => Not filtered
return False
else:
return self.get_process_filter_re().match(value) is None
def __get_process_stats(self, proc,
mandatory_stats=True,
standard_stats=True,
......@@ -237,9 +278,12 @@ class GlancesProcesses(object):
for proc in psutil.process_iter():
# If self.get_max_processes() is None: Only retreive mandatory stats
# Else: retreive mandatoryadn standard stast
processdict[proc] = self.__get_process_stats(proc,
mandatory_stats=True,
standard_stats=self.get_max_processes() is None)
s = self.__get_process_stats(proc,
mandatory_stats=True,
standard_stats=self.get_max_processes() is None)
if self.is_filtered(s['name']):
continue
processdict[proc] = s
# ignore the 'idle' process on Windows and *BSD
# ignore the 'kernel_task' process on OS X
# waiting for upstream patch from psutil
......
......@@ -51,6 +51,10 @@ class GlancesStandalone(object):
logger.debug(_("Extended stats for top process is enabled (default behavor)"))
glances_processes.enable_extended()
# Manage optionnal process filter
if args.process_filter is not None:
glances_processes.set_process_filter(args.process_filter)
# Initial system informations update
self.stats.update()
......
......@@ -31,6 +31,7 @@ if not is_windows:
try:
import curses
import curses.panel
from curses.textpad import Textbox, rectangle
except ImportError:
logger.critical('Curses module not found. Glances cannot start in standalone mode.')
sys.exit(1)
......@@ -70,11 +71,7 @@ class GlancesCurses(object):
curses.noecho()
if hasattr(curses, 'cbreak'):
curses.cbreak()
if hasattr(curses, 'curs_set'):
try:
curses.curs_set(0)
except Exception:
pass
self.set_cursor(0)
# Init colors
self.hascolors = False
......@@ -136,6 +133,7 @@ class GlancesCurses(object):
'BOLD': A_BOLD,
'SORT': A_BOLD,
'OK': self.default_color2,
'FILTER': self.ifCAREFUL_color2,
'TITLE': self.title_color,
'PROCESS': self.default_color2,
'STATUS': self.default_color2,
......@@ -158,6 +156,9 @@ class GlancesCurses(object):
# Init process sort method
self.args.process_sorted_by = 'auto'
# Init edit filter tag
self.edit_filter = False
# Catch key pressed with non blocking mode
self.term_window.keypad(1)
self.term_window.nodelay(1)
......@@ -174,6 +175,18 @@ class GlancesCurses(object):
args.enable_history = False
logger.error('Stats history disabled because graph lib is not available')
def set_cursor(self, value):
"""Configure the cursor
0: invisible
1: visible
2: very visible
"""
if hasattr(curses, 'curs_set'):
try:
curses.curs_set(value)
except Exception:
pass
def __get_key(self, window):
# Catch ESC key AND numlock key (issue #163)
keycode = [0, 0]
......@@ -197,6 +210,9 @@ class GlancesCurses(object):
self.end()
logger.info("Stop Glances")
sys.exit(0)
elif self.pressedkey == 10:
# 'ENTER' > Edit the process filter
self.edit_filter = not self.edit_filter
elif self.pressedkey == ord('1'):
# '1' > Switch between CPU and PerCPU information
self.args.percpu = not self.args.percpu
......@@ -463,19 +479,41 @@ class GlancesCurses(object):
self.history_tag = False
self.reset_history_tag = False
# Display edit filter popup
if self.edit_filter:
new_filter = self.display_popup(_("Filter: "),
is_input=True,
input_value=glances_processes.get_process_filter())
glances_processes.set_process_filter(new_filter)
self.edit_filter = False
return True
def display_popup(self, message, size_x=None, size_y=None, duration=3):
def display_popup(self, message,
size_x=None, size_y=None,
duration=3,
is_input=False,
input_size=20,
input_value=None):
"""
Display a centered popup with the given message during duration seconds
If size_x and size_y: set the popup size
else set it automatically
Return True if the popup could be displayed
If is_input is False:
Display a centered popup with the given message during duration seconds
If size_x and size_y: set the popup size
else set it automatically
Return True if the popup could be displayed
If is_input is True:
Display a centered popup with the given message and a input field
If size_x and size_y: set the popup size
else set it automatically
Return the input string or None if the field is empty
"""
# Center the popup
if size_x is None:
size_x = len(message) + 4
# Add space for the input field
if is_input:
size_x += input_size
if size_y is None:
size_y = message.count('\n') + 1 + 4
screen_x = self.screen.getmaxyx()[1]
......@@ -488,7 +526,7 @@ class GlancesCurses(object):
# Create the popup
popup = curses.newwin(size_y, size_x, pos_y, pos_x)
# Fill the popup
popup.border()
......@@ -498,11 +536,32 @@ class GlancesCurses(object):
popup.addnstr(2 + y, 2, m, len(m))
y += 1
# Display the popup
popup.refresh()
curses.napms(duration * 1000)
return True
if is_input:
# Create a subwindow for the text field
subpop = popup.derwin(1, input_size, 2, 2 + len(m))
subpop.attron(self.__colors_list['FILTER'])
# Init the field with the current value
if input_value is not None:
subpop.addnstr(0, 0, input_value, len(input_value))
# Display the popup
popup.refresh()
subpop.refresh()
# Create the textbox inside the subwindows
self.set_cursor(2)
textbox = glances_textbox(subpop, insert_mode=False)
textbox.edit()
self.set_cursor(0)
if textbox.gather() != '':
logger.debug(_("User enters the following process filter patern: %s") % textbox.gather())
return textbox.gather()[:-1]
else:
logger.debug(_("User clears the process filter patern"))
return None
else:
# Display the popup
popup.refresh()
curses.napms(duration * 1000)
return True
def display_plugin(self, plugin_stats,
display_optional=True,
......@@ -648,3 +707,16 @@ class GlancesCurses(object):
return 0
else:
return c + 1
class glances_textbox(Textbox):
"""
"""
def __init__(*args, **kwargs):
Textbox.__init__(*args, **kwargs)
def do_command(self, ch):
if ch == 10: # Enter
return 0
if ch == 127: # Enter
return 8
return Textbox.do_command(self, ch)
\ No newline at end of file
......@@ -129,5 +129,11 @@ class Plugin(GlancesPlugin):
msg = msg_col2.format("q", _("Quit (Esc and Ctrl-C also work)"))
ret.append(self.curse_add_line(msg))
ret.append(self.curse_new_line())
ret.append(self.curse_new_line())
msg = '{0}: {1}'.format("ENTER", _("Edit the process filter patern"))
ret.append(self.curse_add_line(msg))
# Return the message with decoration
return ret
......@@ -69,9 +69,6 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist and display plugin enable...
# if self.stats == {} or args.disable_process:
# return ret
if args.disable_process:
msg = _("PROCESSES DISABLED (press 'z' to display)")
ret.append(self.curse_add_line(msg))
......@@ -80,6 +77,16 @@ class Plugin(GlancesPlugin):
if self.stats == {}:
return ret
# Display the filter (if it exists)
if glances_processes.get_process_filter() is not None:
msg = _("Processes filter:")
ret.append(self.curse_add_line(msg, "TITLE"))
msg = _(" {0} ").format(glances_processes.get_process_filter())
ret.append(self.curse_add_line(msg, "FILTER"))
msg = _("(press ENTER to edit)")
ret.append(self.curse_add_line(msg))
ret.append(self.curse_new_line())
# Build the string message
# Header
msg = _("TASKS ")
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册