First running version. Need more test. And WebUI

This commit is contained in:
nicolargo 2022-03-12 15:20:57 +01:00
parent 7e2af7bd63
commit 6d9bac1867
14 changed files with 643 additions and 562 deletions

View File

@ -59,6 +59,9 @@ You can also set the sort key in the UI:
* - i * - i
- --sort-processes io_counters - --sort-processes io_counters
- Sort by DISK I/O - Sort by DISK I/O
* - j
- --programs
- Processes are display per program name (not per thread)
* - m * - m
- --sort-processes memory_percent - --sort-processes memory_percent
- Sort by MEM - Sort by MEM

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText. .\" Man page generated from reStructuredText.
. .
.TH "GLANCES" "1" "Jan 15, 2022" "3.2.5_beta01" "Glances" .TH "GLANCES" "1" "Mar 12, 2022" "3.2.5_beta01" "Glances"
.SH NAME .SH NAME
glances \- An eye on your system glances \- An eye on your system
. .

View File

@ -253,6 +253,14 @@ Examples of use:
choices=sort_processes_key_list, choices=sort_processes_key_list,
help='Sort processes by: {}'.format(', '.join(sort_processes_key_list)), help='Sort processes by: {}'.format(', '.join(sort_processes_key_list)),
) )
# Display processes list by program name and not by thread
parser.add_argument(
'--programs',
action='store_true',
default=False,
dest='programs',
help='Show processes as programs (not threads)',
)
# Export modules feature # Export modules feature
parser.add_argument('--export', dest='export', help='enable export module (comma separed list)') parser.add_argument('--export', dest='export', help='enable export module (comma separed list)')
parser.add_argument( parser.add_argument(

View File

@ -77,6 +77,7 @@ class _GlancesCurses(object):
'h': {'switch': 'help_tag'}, 'h': {'switch': 'help_tag'},
'i': {'sort_key': 'io_counters'}, 'i': {'sort_key': 'io_counters'},
'I': {'switch': 'disable_ip'}, 'I': {'switch': 'disable_ip'},
'j': {'switch': 'programs'},
# 'k' > Kill selected process # 'k' > Kill selected process
'K': {'switch': 'disable_connections'}, 'K': {'switch': 'disable_connections'},
'l': {'switch': 'disable_alert'}, 'l': {'switch': 'disable_alert'},

View File

@ -46,6 +46,14 @@ export default function GlancesController($scope, GlancesStats, hotkeys, ARGUMEN
} }
}); });
// j => Processes are display per program name (not per thread)
hotkeys.add({
combo: 'j',
callback: function () {
ARGUMENTS.programs = !ARGUMENTS.programs;
}
});
// k => Show/hide connections stats // k => Show/hide connections stats
hotkeys.add({ hotkeys.add({
combo: 'k', combo: 'k',

Binary file not shown.

Binary file not shown.

View File

@ -110,6 +110,7 @@ class Plugin(GlancesPlugin):
self.view_data['show_hide_raid_plugin'] = msg_col.format('R', 'Show/hide RAID plugin') self.view_data['show_hide_raid_plugin'] = msg_col.format('R', 'Show/hide RAID plugin')
self.view_data['show_hide_wifi_module'] = msg_col2.format('W', 'Show/hide Wifi module') self.view_data['show_hide_wifi_module'] = msg_col2.format('W', 'Show/hide Wifi module')
self.view_data['enable_disable_all_but_quick_look_and_load_module'] = msg_col2.format('4', 'Enable/disable all but quick look and load module') self.view_data['enable_disable_all_but_quick_look_and_load_module'] = msg_col2.format('4', 'Enable/disable all but quick look and load module')
self.view_data['programs'] = msg_col2.format('j', 'Show processes as programs')
def get_view_data(self, args=None): def get_view_data(self, args=None):
@ -203,7 +204,8 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(self.view_data['enable_disable_all_but_quick_look_and_load_module'])) ret.append(self.curse_add_line(self.view_data['enable_disable_all_but_quick_look_and_load_module']))
ret.append(self.curse_new_line()) ret.append(self.curse_new_line())
ret.append(self.curse_add_line(self.view_data['quit'])) ret.append(self.curse_add_line(self.view_data['quit']))
ret.append(self.curse_add_line(self.view_data['programs']))
ret.append(self.curse_new_line()) ret.append(self.curse_new_line())
ret.append(self.curse_new_line()) ret.append(self.curse_new_line())

View File

@ -141,16 +141,17 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
# Display sort information # Display sort information
msg = 'Programs' if self.args.programs else 'Threads'
try: try:
sort_human = self.sort_for_human[glances_processes.sort_key] sort_human = self.sort_for_human[glances_processes.sort_key]
except KeyError: except KeyError:
sort_human = glances_processes.sort_key sort_human = glances_processes.sort_key
if glances_processes.auto_sort: if glances_processes.auto_sort:
msg = 'sorted automatically' msg += ' sorted automatically'
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
msg = ' by {}'.format(sort_human) msg = ' by {}'.format(sort_human)
else: else:
msg = 'sorted by {}'.format(sort_human) msg += ' sorted by {}'.format(sort_human)
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
# Return the message with decoration # Return the message with decoration

View File

@ -29,6 +29,7 @@ from glances.processes import glances_processes, sort_stats
from glances.outputs.glances_unicode import unicode_message from glances.outputs.glances_unicode import unicode_message
from glances.plugins.glances_core import Plugin as CorePlugin from glances.plugins.glances_core import Plugin as CorePlugin
from glances.plugins.glances_plugin import GlancesPlugin from glances.plugins.glances_plugin import GlancesPlugin
from glances.programs import processes_to_programs
def seconds_to_hms(input_seconds): def seconds_to_hms(input_seconds):
@ -156,6 +157,9 @@ class Plugin(GlancesPlugin):
# Note: Update is done in the processcount plugin # Note: Update is done in the processcount plugin
# Just return the processes list # Just return the processes list
stats = glances_processes.getlist() stats = glances_processes.getlist()
if self.args.programs:
stats = processes_to_programs(stats)
elif self.input_method == 'snmp': elif self.input_method == 'snmp':
# No SNMP grab for processes # No SNMP grab for processes

View File

@ -23,6 +23,7 @@ from glances.compat import iterkeys
from glances.globals import BSD, LINUX, MACOS, WINDOWS from glances.globals import BSD, LINUX, MACOS, WINDOWS
from glances.timer import Timer, getTimeSinceLastUpdate from glances.timer import Timer, getTimeSinceLastUpdate
from glances.filter import GlancesFilter from glances.filter import GlancesFilter
from glances.programs import processes_to_programs
from glances.logger import logger from glances.logger import logger
import psutil import psutil

74
glances/programs.py Normal file
View File

@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
# Copyright (C) 2022 Nicolargo <nicolas@nicolargo.com>
#
# Glances is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Glances is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# 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/>.
# from glances.logger import logger
# This constant defines the list of available processes sort key
sort_programs_key_list = ['cpu_percent', 'memory_percent', 'cpu_times', 'io_counters', 'name']
def processes_to_programs(processes):
"""Convert a list of processes to a list of programs.
Each program is a dict containing the following keys:
- 'name': program name
- 'cpu_percent': process cpu percent (sum of all processes)
- 'memory_percent': process memory percent (sum of all processes)
- 'cpu_times': process cpu times (sum of all processes)
- 'memory_info': process memory info (sum of all processes)
- 'io_counters': process io counters (sum of all processes)
- 'children': list of children processes
"""
# Start to build a dict of programs (key is program name)
programs_dict = {}
for p in processes:
if p['name'] not in programs_dict:
# Create a new entry in the dict (new program)
programs_dict[p['name']] = {
'cpu_percent': p['cpu_percent'],
'memory_percent': p['memory_percent'],
'cpu_times': p['cpu_times'],
'memory_info': p['memory_info'],
'io_counters': p['io_counters'],
'children': [p['pid']],
'time_since_update': p['time_since_update'],
# Others keys are not used
# but should be set to be compliant with the existing process_list
'name': p['name'],
'cmdline': [p['name']],
'pid': '_',
'username': p['username'],
'nice': p['nice'],
'status': p['status'],
}
else:
# Update a existing entry in the dict (existing program)
programs_dict[p['name']]['cpu_percent'] += p['cpu_percent']
programs_dict[p['name']]['memory_percent'] += p['memory_percent']
programs_dict[p['name']]['cpu_times'] += p['cpu_times']
programs_dict[p['name']]['memory_info'] += p['memory_info']
programs_dict[p['name']]['io_counters'] += p['io_counters']
programs_dict[p['name']]['children'].append(p['pid'])
# If all the subprocess has the same value, display it
programs_dict[p['name']]['username'] = p['username'] if p['username'] == programs_dict[p['name']]['username'] else '_'
programs_dict[p['name']]['nice'] = p['nice'] if p['nice'] == programs_dict[p['name']]['nice'] else '_'
programs_dict[p['name']]['status'] = p['status'] if p['status'] == programs_dict[p['name']]['status'] else '_'
# Convert the dict to a list of programs
return [programs_dict[p] for p in programs_dict]

View File

@ -34,6 +34,7 @@ from glances.thresholds import GlancesThresholdWarning
from glances.thresholds import GlancesThresholdCritical from glances.thresholds import GlancesThresholdCritical
from glances.thresholds import GlancesThresholds from glances.thresholds import GlancesThresholds
from glances.plugins.glances_plugin import GlancesPlugin from glances.plugins.glances_plugin import GlancesPlugin
from glances.programs import processes_to_programs
from glances.compat import subsample, range from glances.compat import subsample, range
from glances.secure import secure_popen from glances.secure import secure_popen
from glances.compat import PY3 from glances.compat import PY3
@ -283,6 +284,16 @@ class TestGlances(unittest.TestCase):
print('INFO: SMART stats: %s' % stats_grab) print('INFO: SMART stats: %s' % stats_grab)
def test_017_programs(self):
"""Check Programs function (it's not a plugin)."""
# stats_to_check = [ ]
print('INFO: [TEST_010] Check PROGRAM stats')
stats_grab = processes_to_programs(stats.get_plugin('processlist').get_raw())
self.assertTrue(type(stats_grab) is list, msg='Programs stats is not a list')
print('INFO: PROGRAM list stats: %s items in the list' % len(stats_grab))
# Check if number of processes in the list equal counter
# self.assertEqual(total, len(stats_grab))
def test_094_thresholds(self): def test_094_thresholds(self):
"""Test thresholds classes""" """Test thresholds classes"""
print('INFO: [TEST_094] Thresholds') print('INFO: [TEST_094] Thresholds')