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
- --sort-processes io_counters
- Sort by DISK I/O
* - j
- --programs
- Processes are display per program name (not per thread)
* - m
- --sort-processes memory_percent
- Sort by MEM

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
.\" 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
glances \- An eye on your system
.

View File

@ -253,6 +253,14 @@ Examples of use:
choices=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
parser.add_argument('--export', dest='export', help='enable export module (comma separed list)')
parser.add_argument(

View File

@ -77,6 +77,7 @@ class _GlancesCurses(object):
'h': {'switch': 'help_tag'},
'i': {'sort_key': 'io_counters'},
'I': {'switch': 'disable_ip'},
'j': {'switch': 'programs'},
# 'k' > Kill selected process
'K': {'switch': 'disable_connections'},
'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
hotkeys.add({
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_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['programs'] = msg_col2.format('j', 'Show processes as programs')
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_new_line())
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())

View File

@ -141,16 +141,17 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg))
# Display sort information
msg = 'Programs' if self.args.programs else 'Threads'
try:
sort_human = self.sort_for_human[glances_processes.sort_key]
except KeyError:
sort_human = glances_processes.sort_key
if glances_processes.auto_sort:
msg = 'sorted automatically'
msg += ' sorted automatically'
ret.append(self.curse_add_line(msg))
msg = ' by {}'.format(sort_human)
else:
msg = 'sorted by {}'.format(sort_human)
msg += ' sorted by {}'.format(sort_human)
ret.append(self.curse_add_line(msg))
# 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.plugins.glances_core import Plugin as CorePlugin
from glances.plugins.glances_plugin import GlancesPlugin
from glances.programs import processes_to_programs
def seconds_to_hms(input_seconds):
@ -156,6 +157,9 @@ class Plugin(GlancesPlugin):
# Note: Update is done in the processcount plugin
# Just return the processes list
stats = glances_processes.getlist()
if self.args.programs:
stats = processes_to_programs(stats)
elif self.input_method == 'snmp':
# 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.timer import Timer, getTimeSinceLastUpdate
from glances.filter import GlancesFilter
from glances.programs import processes_to_programs
from glances.logger import logger
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 GlancesThresholds
from glances.plugins.glances_plugin import GlancesPlugin
from glances.programs import processes_to_programs
from glances.compat import subsample, range
from glances.secure import secure_popen
from glances.compat import PY3
@ -283,6 +284,16 @@ class TestGlances(unittest.TestCase):
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):
"""Test thresholds classes"""
print('INFO: [TEST_094] Thresholds')