summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornicolargo <nicolas@nicolargo.com>2023-05-19 17:32:41 +0200
committernicolargo <nicolas@nicolargo.com>2023-05-19 17:32:41 +0200
commita549b08107f600a73e67589afc80bad3c0f01a0d (patch)
tree269deebdbc1bb3fe0ce00cc7b1d0dadbfd44affc
parentdc1c7a1ea00dfb87fdc54a6235cfda67a5437403 (diff)
First version available on the (simple) mem plugin
-rw-r--r--glances/plugins/mem/model.py106
-rw-r--r--glances/plugins/plugin/model.py73
2 files changed, 116 insertions, 63 deletions
diff --git a/glances/plugins/mem/model.py b/glances/plugins/mem/model.py
index 52ceed0c..c3d6b5c5 100644
--- a/glances/plugins/mem/model.py
+++ b/glances/plugins/mem/model.py
@@ -12,12 +12,16 @@
from glances.globals import iterkeys
from glances.plugins.plugin.model import GlancesPluginModel
-import psutil
-
# Fields description
fields_description = {
- 'total': {'description': 'Total physical memory available.', 'unit': 'bytes', 'min_symbol': 'K'},
+ 'total': {
+ 'getter': 'psutil.virtual_memory',
+ 'description': 'Total physical memory available.',
+ 'unit': 'bytes',
+ 'min_symbol': 'K'
+ },
'available': {
+ 'getter': 'psutil.virtual_memory',
'description': 'The actual amount of available memory that can be given instantly \
to processes that request more memory in bytes; this is calculated by summing \
different memory values depending on the platform (e.g. free + buffers + cached on Linux) \
@@ -26,49 +30,62 @@ and it is supposed to be used to monitor actual memory usage in a cross platform
'min_symbol': 'K',
},
'percent': {
+ 'getter': 'psutil.virtual_memory',
'description': 'The percentage usage calculated as (total - available) / total * 100.',
'unit': 'percent',
},
- 'used': {
- 'description': 'Memory used, calculated differently depending on the platform and \
-designed for informational purposes only.',
- 'unit': 'bytes',
- 'min_symbol': 'K',
- },
- 'free': {
- 'description': 'Memory not being used at all (zeroed) that is readily available; \
-note that this doesn\'t reflect the actual memory available (use \'available\' instead).',
- 'unit': 'bytes',
- 'min_symbol': 'K',
- },
'active': {
+ 'getter': 'psutil.virtual_memory',
'description': '*(UNIX)*: memory currently in use or very recently used, and so it is in RAM.',
'unit': 'bytes',
'min_symbol': 'K',
},
'inactive': {
+ 'getter': 'psutil.virtual_memory',
'description': '*(UNIX)*: memory that is marked as not used.',
'unit': 'bytes',
'min_symbol': 'K',
'short_name': 'inacti',
},
'buffers': {
+ 'getter': 'psutil.virtual_memory',
'description': '*(Linux, BSD)*: cache for things like file system metadata.',
'unit': 'bytes',
'min_symbol': 'K',
'short_name': 'buffer',
},
- 'cached': {'description': '*(Linux, BSD)*: cache for various things.', 'unit': 'bytes', 'min_symbol': 'K'},
+ 'cached': {
+ 'getter': 'psutil.virtual_memory',
+ 'description': '*(Linux, BSD)*: cache for various things.',
+ 'unit': 'bytes',
+ 'min_symbol': 'K'
+ },
'wired': {
+ 'getter': 'psutil.virtual_memory',
'description': '*(BSD, macOS)*: memory that is marked to always stay in RAM. It is never moved to disk.',
'unit': 'bytes',
'min_symbol': 'K',
},
'shared': {
+ 'getter': 'psutil.virtual_memory',
'description': '*(BSD)*: memory that may be simultaneously accessed by multiple processes.',
'unit': 'bytes',
'min_symbol': 'K',
},
+ 'free': {
+ 'getter': 'compute',
+ 'description': 'Memory not being used at all (zeroed) that is readily available; \
+note that this doesn\'t reflect the actual memory available (use \'available\' instead).',
+ 'unit': 'bytes',
+ 'min_symbol': 'K',
+ },
+ 'used': {
+ 'getter': 'compute',
+ 'description': 'Memory used, calculated differently depending on the platform and \
+designed for informational purposes only.',
+ 'unit': 'bytes',
+ 'min_symbol': 'K',
+ },
}
# SNMP OID
@@ -115,25 +132,42 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(PluginModel, self).__init__(
- args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
+ args=args,
+ config=config,
+ items_history_list=items_history_list,
+ fields_description=fields_description
)
# We want to display the stat in the curse interface
self.display_curse = True
+ def free(self, stats):
+ """Return the free memory compute parameter"""
+ # Use the 'free'/htop calculation
+ # free=available+buffer+cached
+ ret = stats['available']
+ if hasattr(stats, 'buffers'):
+ ret += stats['buffers']
+ if hasattr(stats, 'cached'):
+ ret += stats['cached']
+ return ret
+
+ def used(self, stats):
+ """Return the used memory compute parameter"""
+ return stats['total'] - stats['free']
+
@GlancesPluginModel._check_decorator
@GlancesPluginModel._log_result_decorator
def update(self):
"""Update RAM memory stats using the input method."""
+
# Init new stats
stats = self.get_init_value()
if self.input_method == 'local':
- # Update stats using the standard system lib
- # Grab MEM using the psutil virtual_memory method
- vm_stats = psutil.virtual_memory()
-
- # Get all the memory stats (copy/paste of the psutil documentation)
+ #
+ # Under the wood, Glances use psutil.virtual_memory() to get the memory stats
+ #
# total: total physical memory available.
# available: the actual amount of available memory that can be given instantly
# to processes that request more memory in bytes; this is calculated by summing
@@ -151,32 +185,8 @@ class PluginModel(GlancesPluginModel):
# cached: (Linux, BSD): cache for various things.
# wired: (BSD, macOS): memory that is marked to always stay in RAM. It is never moved to disk.
# shared: (BSD): memory that may be simultaneously accessed by multiple processes.
- self.reset()
- for mem in [
- 'total',
- 'available',
- 'percent',
- 'used',
- 'free',
- 'active',
- 'inactive',
- 'buffers',
- 'cached',
- 'wired',
- 'shared',
- ]:
- if hasattr(vm_stats, mem):
- stats[mem] = getattr(vm_stats, mem)
-
- # Use the 'free'/htop calculation
- # free=available+buffer+cached
- stats['free'] = stats['available']
- if hasattr(stats, 'buffers'):
- stats['free'] += stats['buffers']
- if hasattr(stats, 'cached'):
- stats['free'] += stats['cached']
- # used=total-free
- stats['used'] = stats['total'] - stats['free']
+ stats = self.update_local(stats)
+
elif self.input_method == 'snmp':
# Update stats using SNMP
if self.short_system_name in ('windows', 'esxi'):
diff --git a/glances/plugins/plugin/model.py b/glances/plugins/plugin/model.py
index 3c2eb4b4..af6993b5 100644
--- a/glances/plugins/plugin/model.py
+++ b/glances/plugins/plugin/model.py
@@ -16,6 +16,9 @@ I am your father...
import re
import copy
+# PsUtil is call "dynamically", so the import should not be removed
+import psutil
+
from glances.globals import iterkeys, itervalues, listkeys, mean, nativestr, json_dumps, json_dumps_dictlist
from glances.actions import GlancesActions
from glances.history import GlancesHistory
@@ -111,6 +114,21 @@ class GlancesPluginModel(object):
# Init stats description
self.fields_description = fields_description
+ if fields_description:
+ # Return a list of getter (external functions to run to get the stats)
+ self.getters = list(set([fields_description[i]['getter'] for i in fields_description
+ if (i in fields_description and
+ 'getter' in fields_description[i] and
+ fields_description[i]['getter'] not in ('compute'))]))
+ # Return a list of internal functions to run to compute the stats
+ # Computation is done after the getters
+ self.computes = [i for i in fields_description
+ if (i in fields_description and
+ 'getter' in fields_description[i] and
+ fields_description[i]['getter'] in ('compute'))]
+ else:
+ self.getters = []
+ self.computes = []
# Init the stats
self.stats_init_value = stats_init_value
@@ -175,6 +193,31 @@ class GlancesPluginModel(object):
logger.debug("Reset history for plugin {} (items: {})".format(self.plugin_name, reset_list))
self.stats_history.reset()
+ def update_getters(self, stats):
+ """Return the stats updated from the getters functions."""
+ for g in self.getters:
+ # For each "getter", the Python function is called
+ g_call = getattr(globals()[g.split('.')[0]], g.split('.')[1])()
+ # The result is stored in the stats dict
+ # (only if the field is in the self.fields_description)
+ for name in [f for f in g_call._fields if f in self.fields_description]:
+ stats[name] = getattr(g_call, name)
+ return stats
+
+ def update_computes(self, stats):
+ """Return the stats updated from the computes functions."""
+ for c in self.computes:
+ stats[c] = getattr(self, c)(stats)
+ return stats
+
+ def update_local(self, stats):
+ """Return the stats updated by getters and computes."""
+ # Update the stats from the getters
+ stats = self.update_getters(stats)
+ # Update the stats from the computes
+ stats = self.update_computes(stats)
+ return stats
+
def update_stats_history(self):
"""Update stats history."""
# Build the history
@@ -483,9 +526,9 @@ class GlancesPluginModel(object):
'splittable': False,
'hidden': False,
'_zero': self.views[i[self.get_key()]][key]['_zero']
- if i[self.get_key()] in self.views
- and key in self.views[i[self.get_key()]]
- and 'zero' in self.views[i[self.get_key()]][key]
+ if i[self.get_key()] in self.views and
+ key in self.views[i[self.get_key()]] and
+ 'zero' in self.views[i[self.get_key()]][key]
else True,
}
ret[i[self.get_key()]][key] = value
@@ -962,9 +1005,9 @@ class GlancesPluginModel(object):
# Check if unit is defined and get the short unit char in the unit_sort dict
if (
- key in self.fields_description
- and 'unit' in self.fields_description[key]
- and self.fields_description[key]['unit'] in fields_unit_short
+ key in self.fields_description and
+ 'unit' in self.fields_description[key] and
+ self.fields_description[key]['unit'] in fields_unit_short
):
# Get the shortname
unit_short = fields_unit_short[self.fields_description[key]['unit']]
@@ -973,9 +1016,9 @@ class GlancesPluginModel(object):
# Check if unit is defined and get the unit type unit_type dict
if (
- key in self.fields_description
- and 'unit' in self.fields_description[key]
- and self.fields_description[key]['unit'] in fields_unit_type
+ key in self.fields_description and
+ 'unit' in self.fields_description[key] and
+ self.fields_description[key]['unit'] in fields_unit_type
):
# Get the shortname
unit_type = fields_unit_type[self.fields_description[key]['unit']]
@@ -984,9 +1027,9 @@ class GlancesPluginModel(object):
# Is it a rate ? Yes, compute it thanks to the time_since_update key
if (
- key in self.fields_description
- and 'rate' in self.fields_description[key]
- and self.fields_description[key]['rate'] is True
+ key in self.fields_description and
+ 'rate' in self.fields_description[key] and
+ self.fields_description[key]['rate'] is True
):
value = self.stats[key] // self.stats['time_since_update']
else:
@@ -1010,8 +1053,8 @@ class GlancesPluginModel(object):
msg_value = (
msg_template.format(
self.auto_unit(int(value), min_symbol=self.fields_description[key]['min_symbol']), unit_short
- )
- + trailer
+ ) +
+ trailer
)
else:
msg_value = msg_template.format(int(value), unit_short) + trailer
@@ -1055,7 +1098,7 @@ class GlancesPluginModel(object):
"""
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
if min_symbol in symbols:
- symbols = symbols[symbols.index(min_symbol) :]
+ symbols = symbols[symbols.index(min_symbol):]
prefix = {
'Y': 1208925819614629174706176,
'Z': 1180591620717411303424,