Merge branch 'refs/heads/develop' into 2637-docker-memory-usage-is-incorrect

# Conflicts:
#	glances/outputs/static/public/glances.js
This commit is contained in:
Bharath Vignesh J K 2024-05-01 23:21:35 +05:30
commit 5b54ef9baf
20 changed files with 126 additions and 101 deletions

View File

@ -47,33 +47,38 @@ jobs:
run: | run: |
python ./unitest.py python ./unitest.py
test-windows: # Error appear with h11, not related to Glances
# Should be tested if correction is done
# Installed c:\hostedtoolcache\windows\python\3.9.13\x64\lib\site-packages\exceptiongroup-1.2.1-py3.9.egg
# error: h11 0.14.0 is installed but h11<0.13,>=0.11 is required by {'httpcore'}
# Error: Process completed with exit code 1.
# test-windows:
# https://github.com/actions/runner-images?tab=readme-ov-file#available-images # # https://github.com/actions/runner-images?tab=readme-ov-file#available-images
runs-on: windows-latest # runs-on: windows-latest
strategy: # strategy:
matrix: # matrix:
# Python version "3.12" introduce this issue: # # Python version "3.12" introduce this issue:
# https://github.com/nicolargo/glances/actions/runs/6439648370/job/17487567454 # # https://github.com/nicolargo/glances/actions/runs/6439648370/job/17487567454
python-version: ["3.8", "3.9", "3.10", "3.11"] # python-version: ["3.8", "3.9", "3.10", "3.11"]
steps: # steps:
- uses: actions/checkout@v4 # - uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }} # - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4 # uses: actions/setup-python@v4
with: # with:
python-version: ${{ matrix.python-version }} # python-version: ${{ matrix.python-version }}
- name: Install dependencies # - name: Install dependencies
run: | # run: |
python -m pip install --upgrade pip # python -m pip install --upgrade pip
if (Test-Path -PathType Leaf "requirements.txt") { python -m pip install -r requirements.txt } # if (Test-Path -PathType Leaf "requirements.txt") { python -m pip install -r requirements.txt }
python setup.py install # python setup.py install
- name: Unitary tests # - name: Unitary tests
run: | # run: |
python ./unitest.py # python ./unitest.py
test-macos: test-macos:

View File

@ -87,7 +87,6 @@ Requirements
- ``defusedxml`` (in order to monkey patch xmlrpc) - ``defusedxml`` (in order to monkey patch xmlrpc)
- ``packaging`` (for the version comparison) - ``packaging`` (for the version comparison)
- ``ujson`` (an optimized alternative to the standard json module) - ``ujson`` (an optimized alternative to the standard json module)
- ``pytz`` (for the timezone support)
- ``pydantic`` (for the data validation support) - ``pydantic`` (for the data validation support)
*Note for Python 2 users* *Note for Python 2 users*
@ -122,7 +121,7 @@ Optional dependencies:
- ``pygal`` (for the graph export module) - ``pygal`` (for the graph export module)
- ``pymdstat`` (for RAID support) [Linux-only] - ``pymdstat`` (for RAID support) [Linux-only]
- ``pymongo`` (for the MongoDB export module) - ``pymongo`` (for the MongoDB export module)
- ``pysnmp`` (for SNMP support) - ``pysnmp-lextudio`` (for SNMP support)
- ``pySMART.smartx`` (for HDD Smart support) [Linux-only] - ``pySMART.smartx`` (for HDD Smart support) [Linux-only]
- ``pyzmq`` (for the ZeroMQ export module) - ``pyzmq`` (for the ZeroMQ export module)
- ``requests`` (for the Ports, Cloud plugins and RESTful export module) - ``requests`` (for the Ports, Cloud plugins and RESTful export module)
@ -136,26 +135,27 @@ Installation
There are several methods to test/install Glances on your system. Choose your weapon! There are several methods to test/install Glances on your system. Choose your weapon!
PyPI: The standard way PyPI: Pip, the standard way
---------------------- ---------------------------
Glances is on ``PyPI``. By using PyPI, you will be using the latest Glances is on ``PyPI``. By using PyPI, you will be using the latest
stable version. stable version.
To install Glances, simply use ``pip``: To install Glances, simply use the ``pip`` command line.
Warning: on modern Linux operating systems, you may have an externally-managed-environment
error message when you try to use ``pip``. In this case, go to the the PipX section bellow.
.. code-block:: console .. code-block:: console
pip install --user glances pip install --user glances
*Note*: Python headers are required to install `psutil`_, a Glances *Note*: Python headers are required to install `psutil`_, a Glances
dependency. For example, on Debian/Ubuntu **the simplest** is ``apt install python3-psutil`` or alternatively need to install first dependency. For example, on Debian/Ubuntu **the simplest** is
``apt install python3-psutil`` or alternatively need to install first
the *python-dev* package and gcc (*python-devel* on Fedora/CentOS/RHEL). the *python-dev* package and gcc (*python-devel* on Fedora/CentOS/RHEL).
For Windows, just install psutil from the binary installation file. For Windows, just install psutil from the binary installation file.
*Note 2 (for the Wifi plugin)*: If you want to use the Wifi plugin, you need
to install the *wireless-tools* package on your system.
By default, Glances is installed without the Web interface dependencies. By default, Glances is installed without the Web interface dependencies.
To install it, use the following command: To install it, use the following command:
@ -182,24 +182,18 @@ If you want to test the develop version (could be instable), enter:
pip install --user -i https://test.pypi.org/simple/ Glances pip install --user -i https://test.pypi.org/simple/ Glances
Glances Auto Install script: the easy way PyPI: PipX, the alternative way
----------------------------------------- -------------------------------
To install both dependencies and the latest Glances production ready version Install PipX on your system (apt install pipx on Ubuntu).
(aka *master* branch), just enter the following command line:
Install Glances (with all features):
.. code-block:: console .. code-block:: console
curl -L https://bit.ly/glances | /bin/bash pipx install 'glances[all]'
or The glances script will be installed in the ~/.local/bin folder.
.. code-block:: console
wget -O- https://bit.ly/glances | /bin/bash
*Note*: This is only supported on some GNU/Linux distributions and Mac OS X.
If you want to support other distributions, please contribute to `glancesautoinstall`_.
Docker: the cloudy way Docker: the cloudy way
---------------------- ----------------------
@ -257,8 +251,8 @@ Run the container in *Web server mode*:
For a full list of options, see the Glances `Docker`_ documentation page. For a full list of options, see the Glances `Docker`_ documentation page.
GNU/Linux GNU/Linux package
--------- -----------------
`Glances` is available on many Linux distributions, so you should be `Glances` is available on many Linux distributions, so you should be
able to install it using your favorite package manager. Be aware that able to install it using your favorite package manager. Be aware that

View File

@ -239,7 +239,7 @@ public_template={continent_name}/{country_name}/{city_name}
[connections] [connections]
# Display additional information about TCP connections # Display additional information about TCP connections
# This plugin is disabled by default # This plugin is disabled by default because it consumes lots of CPU
disable=True disable=True
# nf_conntrack thresholds in % # nf_conntrack thresholds in %
nf_conntrack_percent_careful=70 nf_conntrack_percent_careful=70
@ -333,8 +333,9 @@ port=7634
[sensors] [sensors]
# Documentation: https://glances.readthedocs.io/en/latest/aoa/sensors.html # Documentation: https://glances.readthedocs.io/en/latest/aoa/sensors.html
disable=False disable=False
# By default refresh every refresh time * 2 # Set the refresh multiplicator for the sensors
#refresh=6 # By default refresh every Glances refresh * 3 (increase to reduce CPU consumption)
#refresh=3
# Hide some sensors (comma separated list of regexp) # Hide some sensors (comma separated list of regexp)
hide=unknown.* hide=unknown.*
# Show only the following sensors (comma separated list of regexp) # Show only the following sensors (comma separated list of regexp)

View File

@ -239,7 +239,7 @@ public_template={continent_name}/{country_name}/{city_name}
[connections] [connections]
# Display additional information about TCP connections # Display additional information about TCP connections
# This plugin is disabled by default # This plugin is disabled by default because it consumes lots of CPU
disable=True disable=True
# nf_conntrack thresholds in % # nf_conntrack thresholds in %
nf_conntrack_percent_careful=70 nf_conntrack_percent_careful=70
@ -333,8 +333,9 @@ port=7634
[sensors] [sensors]
# Documentation: https://glances.readthedocs.io/en/latest/aoa/sensors.html # Documentation: https://glances.readthedocs.io/en/latest/aoa/sensors.html
disable=False disable=False
# By default refresh every refresh time * 2 # Set the refresh multiplicator for the sensors
#refresh=6 # By default refresh every Glances refresh * 3 (increase to reduce CPU consumption)
#refresh=3
# Hide some sensors (comma separated list of regexp) # Hide some sensors (comma separated list of regexp)
hide=unknown.* hide=unknown.*
# Show only the following sensors (comma separated list of regexp) # Show only the following sensors (comma separated list of regexp)

View File

@ -1130,6 +1130,10 @@ class _GlancesCurses(object):
update the terminal.""" update the terminal."""
self.term_window.erase() self.term_window.erase()
def refresh(self):
"""Refresh the windows"""
self.term_window.refresh()
def flush(self, stats, cs_status=None): def flush(self, stats, cs_status=None):
"""Erase and update the screen. """Erase and update the screen.
@ -1139,8 +1143,10 @@ class _GlancesCurses(object):
"Connected": Client is connected to the server "Connected": Client is connected to the server
"Disconnected": Client is disconnected from the server "Disconnected": Client is disconnected from the server
""" """
# See https://stackoverflow.com/a/43486979/1919431
self.erase() self.erase()
self.display(stats, cs_status=cs_status) self.display(stats, cs_status=cs_status)
self.refresh()
def update(self, stats, duration=3, cs_status=None, return_to_browser=False): def update(self, stats, duration=3, cs_status=None, return_to_browser=False):
"""Update the screen. """Update the screen.

View File

@ -29,7 +29,7 @@
> >
MEM MEM
</div> </div>
<div class="table-cell">/MAX</div> <div class="table-cell text-left">/MAX</div>
<div class="table-cell">IOR/s</div> <div class="table-cell">IOR/s</div>
<div class="table-cell">IOW/s</div> <div class="table-cell">IOW/s</div>
<div class="table-cell">RX/s</div> <div class="table-cell">RX/s</div>
@ -56,8 +56,8 @@
<div class="table-cell"> <div class="table-cell">
{{ $filters.bytes(container.memory_usage) }} {{ $filters.bytes(container.memory_usage) }}
</div> </div>
<div class="table-cell"> <div class="table-cell text-left">
{{ $filters.bytes(container.limit) }} /{{ $filters.bytes(container.limit) }}
</div> </div>
<div class="table-cell"> <div class="table-cell">
{{ $filters.bytes(container.io_rx) }} {{ $filters.bytes(container.io_rx) }}

View File

@ -106,30 +106,46 @@ class PluginModel(GlancesPluginModel):
('toggle_linux_percentage', msg_col.format('0', 'Load, Linux/percentage')), ('toggle_linux_percentage', msg_col.format('0', 'Load, Linux/percentage')),
('toggle_cpu_individual_combined', msg_col.format('1', 'CPU, individual/combined')), ('toggle_cpu_individual_combined', msg_col.format('1', 'CPU, individual/combined')),
('toggle_gpu_individual_combined', msg_col.format('6', 'GPU, individual/combined')), ('toggle_gpu_individual_combined', msg_col.format('6', 'GPU, individual/combined')),
('toggle_short_full', (
msg_col.format('S', 'toggle_short_full',
'Process names, short/full') if self.args.webserver else msg_col.format('/', 'Process names, short/full')), (
msg_col.format('S', 'Process names, short/full')
if self.args and self.args.webserver
else msg_col.format('/', 'Process names, short/full')
),
),
('header_miscellaneous', msg_header.format('MISCELLANEOUS:')), ('header_miscellaneous', msg_header.format('MISCELLANEOUS:')),
('misc_erase_process_filter', (
'' if self.args.webserver else msg_col.format('E', 'Erase process filter')), 'misc_erase_process_filter',
('misc_generate_history_graphs', '' if self.args and self.args.webserver else msg_col.format('E', 'Erase process filter'),
'' if self.args.webserver else msg_col.format('g', 'Generate history graphs')), ),
(
'misc_generate_history_graphs',
'' if self.args and self.args.webserver else msg_col.format('g', 'Generate history graphs'),
),
('misc_help', msg_col.format('h', 'HELP')), ('misc_help', msg_col.format('h', 'HELP')),
('misc_accumulate_processes_by_program', (
'' if self.args.webserver else msg_col.format('j', 'Display threads or programs')), 'misc_accumulate_processes_by_program',
'' if self.args and self.args.webserver else msg_col.format('j', 'Display threads or programs'),
),
('misc_increase_nice_process', msg_col.format('+', 'Increase nice process')), ('misc_increase_nice_process', msg_col.format('+', 'Increase nice process')),
('misc_decrease_nice_process', msg_col.format('-', 'Decrease nice process (need admin rights)')), ('misc_decrease_nice_process', msg_col.format('-', 'Decrease nice process (need admin rights)')),
('misc_kill_process', ('misc_kill_process', '' if self.args and self.args.webserver else msg_col.format('k', 'Kill process')),
'' if self.args.webserver else msg_col.format('k', 'Kill process')), (
('misc_reset_processes_summary_min_max', 'misc_reset_processes_summary_min_max',
'' if self.args.webserver else msg_col.format('M', 'Reset processes summary min/max')), '' if self.args and self.args.webserver else msg_col.format('M', 'Reset processes summary min/max'),
('misc_quit', ),
'' if self.args.webserver else msg_col.format('q', 'QUIT (or Esc or Ctrl-C)')), (
'misc_quit',
'' if self.args and self.args.webserver else msg_col.format('q', 'QUIT (or Esc or Ctrl-C)'),
),
('misc_reset_history', msg_col.format('r', 'Reset history')), ('misc_reset_history', msg_col.format('r', 'Reset history')),
('misc_delete_warning_alerts', msg_col.format('w', 'Delete warning alerts')), ('misc_delete_warning_alerts', msg_col.format('w', 'Delete warning alerts')),
('misc_delete_warning_and_critical_alerts', msg_col.format('x', 'Delete warning & critical alerts')), ('misc_delete_warning_and_critical_alerts', msg_col.format('x', 'Delete warning & critical alerts')),
('misc_edit_process_filter_pattern', (
'' if self.args.webserver else ' ENTER: Edit process filter pattern'), 'misc_edit_process_filter_pattern',
'' if self.args and self.args.webserver else ' ENTER: Edit process filter pattern',
),
] ]
) )

View File

@ -204,12 +204,8 @@ class PluginModel(GlancesPluginModel):
self.reset() self.reset()
return self.stats return self.stats
for key in iterkeys(stats): for k in stats:
if stats[key] != '': stats[k] = int(stats[k]) * 1024
stats[key] = float(stats[key]) * 1024
# Use the 'free'/htop calculation
stats['free'] = stats['free'] - stats['total'] + (stats['buffers'] + stats['cached'])
# used=total-free # used=total-free
stats['used'] = stats['total'] - stats['free'] stats['used'] = stats['total'] - stats['free']

View File

@ -53,9 +53,9 @@ class PluginModel(GlancesPluginModel):
# Set the message position # Set the message position
self.align = 'bottom' self.align = 'bottom'
if args.strftime_format: if args and args.strftime_format:
self.strftime = args.strftime_format self.strftime = args.strftime_format
elif config is not None: elif config:
if 'global' in config.as_dict(): if 'global' in config.as_dict():
self.strftime = config.as_dict()['global']['strftime_format'] self.strftime = config.as_dict()['global']['strftime_format']

View File

@ -108,7 +108,10 @@ class PluginModel(GlancesPluginModel):
self.display_curse = True self.display_curse = True
# Manage the maximum number of CPU to display (related to enhancement request #2734) # Manage the maximum number of CPU to display (related to enhancement request #2734)
self.max_cpu_display = config.get_int_value('percpu', 'max_cpu_display', 4) if config:
self.max_cpu_display = config.get_int_value('percpu', 'max_cpu_display', 4)
else:
self.max_cpu_display = 4
def get_key(self): def get_key(self):
"""Return the key of the list.""" """Return the key of the list."""

View File

@ -350,8 +350,10 @@ class GlancesPluginModel(object):
ret = {} ret = {}
if bulk: if bulk:
# Bulk request # Bulk request
snmp_result = snmp_client.getbulk_by_oid(0, 10, itervalues(*snmp_oid)) snmp_result = snmp_client.getbulk_by_oid(0,
10,
*list(itervalues(snmp_oid)))
logger.info(snmp_result)
if len(snmp_oid) == 1: if len(snmp_oid) == 1:
# Bulk command for only one OID # Bulk command for only one OID
# Note: key is the item indexed but the OID result # Note: key is the item indexed but the OID result
@ -379,7 +381,7 @@ class GlancesPluginModel(object):
index += 1 index += 1
else: else:
# Simple get request # Simple get request
snmp_result = snmp_client.get_by_oid(itervalues(*snmp_oid)) snmp_result = snmp_client.get_by_oid(*list(itervalues(snmp_oid)))
# Build the internal dict with the SNMP result # Build the internal dict with the SNMP result
for key in iterkeys(snmp_oid): for key in iterkeys(snmp_oid):
@ -623,7 +625,7 @@ class GlancesPluginModel(object):
"""Return the plugin refresh time""" """Return the plugin refresh time"""
ret = self.get_limits(item='refresh') ret = self.get_limits(item='refresh')
if ret is None: if ret is None:
ret = self.args.time ret = self.args.time if hasattr(self.args, 'time') else 2
return ret return ret
def get_refresh_time(self): def get_refresh_time(self):

View File

@ -194,7 +194,7 @@ class PluginModel(GlancesPluginModel):
config.as_dict()['processlist']['export'])) config.as_dict()['processlist']['export']))
# The default sort key could also be overwrite by command line (see #1903) # The default sort key could also be overwrite by command line (see #1903)
if args.sort_processes_key is not None: if args and args.sort_processes_key is not None:
glances_processes.set_sort_key(args.sort_processes_key, False) glances_processes.set_sort_key(args.sort_processes_key, False)
# Note: 'glances_processes' is already init in the processes.py script # Note: 'glances_processes' is already init in the processes.py script

View File

@ -92,7 +92,7 @@ class PluginModel(GlancesPluginModel):
self.display_curse = True self.display_curse = True
# Manage the maximum number of CPU to display (related to enhancement request #2734) # Manage the maximum number of CPU to display (related to enhancement request #2734)
self.max_cpu_display = config.get_int_value('percpu', 'max_cpu_display', 4) self.max_cpu_display = config.get_int_value('percpu', 'max_cpu_display', 4) if config else 4
# Define the stats list # Define the stats list
self.stats_list = self.get_conf_value('list', default=self.DEFAULT_STATS_LIST) self.stats_list = self.get_conf_value('list', default=self.DEFAULT_STATS_LIST)

View File

@ -26,6 +26,11 @@ SENSOR_TEMP_UNIT = 'C'
SENSOR_FAN_TYPE = 'fan_speed' SENSOR_FAN_TYPE = 'fan_speed'
SENSOR_FAN_UNIT = 'R' SENSOR_FAN_UNIT = 'R'
# Define the default refresh multiplicator
# Default value is 3 * Glances refresh time
# Can be overwritten by the refresh option in the sensors section of the glances.conf file
DEFAULT_REFRESH = 3
# Fields description # Fields description
# description: human readable description # description: human readable description
# short_name: shortname to use un UI # short_name: shortname to use un UI
@ -96,9 +101,8 @@ class PluginModel(GlancesPluginModel):
self.display_curse = True self.display_curse = True
# Not necessary to refresh every refresh time # Not necessary to refresh every refresh time
# By default set to refresh * 2 if args and self.get_refresh() == args.time:
if self.get_refresh() == args.time: self.set_refresh(self.get_refresh() * DEFAULT_REFRESH)
self.set_refresh(self.get_refresh() * 2)
def get_key(self): def get_key(self):
"""Return the key of the list.""" """Return the key of the list."""

View File

@ -125,7 +125,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None, stats_init_value=[]): def __init__(self, args=None, config=None, stats_init_value=[]):
"""Init the plugin.""" """Init the plugin."""
# check if user is admin # check if user is admin
if not is_admin(): if not is_admin() and args:
disable(args, "smart") disable(args, "smart")
logger.debug("Current user is not admin, HDD SMART plugin disabled.") logger.debug("Current user is not admin, HDD SMART plugin disabled.")

View File

@ -132,7 +132,7 @@ class PluginModel(GlancesPluginModel):
self.set_refresh(60) self.set_refresh(60)
# Get the default message (if defined) # Get the default message (if defined)
self.system_info_msg = config.get_value('system', 'system_info_msg') self.system_info_msg = config.get_value('system', 'system_info_msg') if config else None
@GlancesPluginModel._check_decorator @GlancesPluginModel._check_decorator
@GlancesPluginModel._log_result_decorator @GlancesPluginModel._log_result_decorator

View File

@ -41,12 +41,9 @@ class GlancesSNMPClient(object):
ret = {} ret = {}
for name, val in varBinds: for name, val in varBinds:
if str(val) == '': if str(val) == '':
ret[name.prettyPrint()] = '' ret[str(name)] = ''
else: else:
ret[name.prettyPrint()] = val.prettyPrint() ret[str(name)] = val.prettyPrint()
# In Python 3, prettyPrint() return 'b'linux'' instead of 'linux'
if ret[name.prettyPrint()].startswith('b\''):
ret[name.prettyPrint()] = ret[name.prettyPrint()][2:-1]
return ret return ret
def __get_result__(self, errorIndication, errorStatus, errorIndex, varBinds): def __get_result__(self, errorIndication, errorStatus, errorIndex, varBinds):

View File

@ -148,15 +148,16 @@ class GlancesStandalone(object):
logger.debug('Stats updated duration: {} seconds'.format(counter.get())) logger.debug('Stats updated duration: {} seconds'.format(counter.get()))
# Patch for issue1326 to avoid < 0 refresh # Patch for issue1326 to avoid < 0 refresh
adapted_refresh = self.refresh_time - counter.get() adapted_refresh = (self.refresh_time - counter.get()) if (self.refresh_time - counter.get()) > 0 else 0
adapted_refresh = adapted_refresh if adapted_refresh > 0 else 0
# Display stats # Display stats
# and wait refresh_time - counter # and wait refresh_time - counter
if not self.quiet: if not self.quiet:
# The update function return True if an exit key 'q' or 'ESC' # The update function return True if an exit key 'q' or 'ESC'
# has been pressed. # has been pressed.
counter_display = Counter()
ret = not self.screen.update(self.stats, duration=adapted_refresh) ret = not self.screen.update(self.stats, duration=adapted_refresh)
logger.debug('Stats display duration: {} seconds'.format(counter_display.get() - adapted_refresh))
else: else:
# Nothing is displayed # Nothing is displayed
# Break should be done via a signal (CTRL-C) # Break should be done via a signal (CTRL-C)

View File

@ -27,7 +27,7 @@ pygal
pymdstat pymdstat
pymongo; python_version >= "3.7" pymongo; python_version >= "3.7"
nvidia-ml-py; python_version >= "3.5" nvidia-ml-py; python_version >= "3.5"
pysnmp pysnmp-lextudio; python_version >= "3.7"
pySMART.smartx pySMART.smartx
python-dateutil python-dateutil
pyzmq pyzmq

View File

@ -2,5 +2,4 @@ psutil>=5.6.7
defusedxml defusedxml
packaging packaging
ujson>=5.4.0 ujson>=5.4.0
pytz
pydantic pydantic