summaryrefslogtreecommitdiffstats
path: root/glances/plugins/mem/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'glances/plugins/mem/__init__.py')
-rw-r--r--glances/plugins/mem/__init__.py282
1 files changed, 282 insertions, 0 deletions
diff --git a/glances/plugins/mem/__init__.py b/glances/plugins/mem/__init__.py
index e69de29b..52ceed0c 100644
--- a/glances/plugins/mem/__init__.py
+++ b/glances/plugins/mem/__init__.py
@@ -0,0 +1,282 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of Glances.
+#
+# SPDX-FileCopyrightText: 2022 Nicolas Hennion <nicolas@nicolargo.com>
+#
+# SPDX-License-Identifier: LGPL-3.0-only
+#
+
+"""Virtual memory plugin."""
+
+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'},
+ 'available': {
+ '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) \
+and it is supposed to be used to monitor actual memory usage in a cross platform fashion.',
+ 'unit': 'bytes',
+ 'min_symbol': 'K',
+ },
+ 'percent': {
+ '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': {
+ 'description': '*(UNIX)*: memory currently in use or very recently used, and so it is in RAM.',
+ 'unit': 'bytes',
+ 'min_symbol': 'K',
+ },
+ 'inactive': {
+ 'description': '*(UNIX)*: memory that is marked as not used.',
+ 'unit': 'bytes',
+ 'min_symbol': 'K',
+ 'short_name': 'inacti',
+ },
+ 'buffers': {
+ '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'},
+ 'wired': {
+ 'description': '*(BSD, macOS)*: memory that is marked to always stay in RAM. It is never moved to disk.',
+ 'unit': 'bytes',
+ 'min_symbol': 'K',
+ },
+ 'shared': {
+ 'description': '*(BSD)*: memory that may be simultaneously accessed by multiple processes.',
+ 'unit': 'bytes',
+ 'min_symbol': 'K',
+ },
+}
+
+# SNMP OID
+# Total RAM in machine: .1.3.6.1.4.1.2021.4.5.0
+# Total RAM used: .1.3.6.1.4.1.2021.4.6.0
+# Total RAM Free: .1.3.6.1.4.1.2021.4.11.0
+# Total RAM Shared: .1.3.6.1.4.1.2021.4.13.0
+# Total RAM Buffered: .1.3.6.1.4.1.2021.4.14.0
+# Total Cached Memory: .1.3.6.1.4.1.2021.4.15.0
+# Note: For Windows, stats are in the FS table
+snmp_oid = {
+ 'default': {
+ 'total': '1.3.6.1.4.1.2021.4.5.0',
+ 'free': '1.3.6.1.4.1.2021.4.11.0',
+ 'shared': '1.3.6.1.4.1.2021.4.13.0',
+ 'buffers': '1.3.6.1.4.1.2021.4.14.0',
+ 'cached': '1.3.6.1.4.1.2021.4.15.0',
+ },
+ 'windows': {
+ 'mnt_point': '1.3.6.1.2.1.25.2.3.1.3',
+ 'alloc_unit': '1.3.6.1.2.1.25.2.3.1.4',
+ 'size': '1.3.6.1.2.1.25.2.3.1.5',
+ 'used': '1.3.6.1.2.1.25.2.3.1.6',
+ },
+ 'esxi': {
+ 'mnt_point': '1.3.6.1.2.1.25.2.3.1.3',
+ 'alloc_unit': '1.3.6.1.2.1.25.2.3.1.4',
+ 'size': '1.3.6.1.2.1.25.2.3.1.5',
+ 'used': '1.3.6.1.2.1.25.2.3.1.6',
+ },
+}
+
+# Define the history items list
+# All items in this list will be historised if the --enable-history tag is set
+items_history_list = [{'name': 'percent', 'description': 'RAM memory usage', 'y_unit': '%'}]
+
+
+class PluginModel(GlancesPluginModel):
+ """Glances' memory plugin.
+
+ stats is a dict
+ """
+
+ 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
+ )
+
+ # We want to display the stat in the curse interface
+ self.display_curse = True
+
+ @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)
+ # 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
+ # different memory values depending on the platform (e.g. free + buffers + cached on Linux)
+ # and it is supposed to be used to monitor actual memory usage in a cross platform fashion.
+ # percent: the percentage usage calculated as (total - available) / total * 100.
+ # used: memory used, calculated differently depending on the platform and designed for informational
+ # purposes only.
+ # free: memory not being used at all (zeroed) that is readily available; note that this doesn't
+ # reflect the actual memory available (use ‘available’ instead).
+ # Platform-specific fields:
+ # active: (UNIX): memory currently in use or very recently used, and so it is in RAM.
+ # inactive: (UNIX): memory that is marked as not used.
+ # buffers: (Linux, BSD): cache for things like file system metadata.
+ # 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']
+ elif self.input_method == 'snmp':
+ # Update stats using SNMP
+ if self.short_system_name in ('windows', 'esxi'):
+ # Mem stats for Windows|Vmware Esxi are stored in the FS table
+ try:
+ fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name], bulk=True)
+ except KeyError:
+ self.reset()
+ else:
+ for fs in fs_stat:
+ # The Physical Memory (Windows) or Real Memory (VMware)
+ # gives statistics on RAM usage and availability.
+ if fs in ('Physical Memory', 'Real Memory'):
+ stats['total'] = int(fs_stat[fs]['size']) * int(fs_stat[fs]['alloc_unit'])
+ stats['used'] = int(fs_stat[fs]['used']) * int(fs_stat[fs]['alloc_unit'])
+ stats['percent'] = float(stats['used'] * 100 / stats['total'])
+ stats['free'] = stats['total'] - stats['used']
+ break
+ else:
+ # Default behavior for others OS
+ stats = self.get_stats_snmp(snmp_oid=snmp_oid['default'])
+
+ if stats['total'] == '':
+ self.reset()
+ return self.stats
+
+ for key in iterkeys(stats):
+ if stats[key] != '':
+ stats[key] = float(stats[key]) * 1024
+
+ # Use the 'free'/htop calculation
+ stats['free'] = stats['free'] - stats['total'] + (stats['buffers'] + stats['cached'])
+
+ # used=total-free
+ stats['used'] = stats['total'] - stats['free']
+
+ # percent: the percentage usage calculated as (total - available) / total * 100.
+ stats['percent'] = float((stats['total'] - stats['free']) / stats['total'] * 100)
+
+ # Update the stats
+ self.stats = stats
+
+ return self.stats
+
+ def update_views(self):
+ """Update stats views."""
+ # Call the father's method
+ super(PluginModel, self).update_views()
+
+ # Add specifics information
+ # Alert and log
+ self.views['percent']['decoration'] = self.get_alert_log(self.stats['used'], maximum=self.stats['total'])
+ # Optional
+ for key in ['active', 'inactive', 'buffers', 'cached']:
+ if key in self.stats:
+ self.views[key]['optional'] = True
+
+ def msg_curse(self, args=None, max_width=None):
+ """Return the dict to display in the curse interface."""
+ # Init the return message
+ ret = []
+
+ # Only process if stats exist and plugin not disabled
+ if not self.stats or self.is_disabled():
+ return ret
+
+ # First line
+ # total% + active
+ msg = '{}'.format('MEM')
+ ret.append(self.curse_add_line(msg, "TITLE"))
+ msg = ' {:2}'.format(self.trend_msg(self.get_trend('percent')))
+ ret.append(self.curse_add_line(msg))
+ # Percent memory usage
+ msg = '{:>7.1%}'.format(self.stats['percent'] / 100)
+ ret.append(self.curse_add_line(msg, self.get_views(key='percent', option='decoration')))
+ # Active memory usage
+ ret.extend(self.curse_add_stat('active', width=16, header=' '))
+
+ # Second line
+ # total + inactive
+ ret.append(self.curse_new_line())
+ # Total memory usage
+ ret.extend(self.curse_add_stat('total', width=15))
+ # Inactive memory usage
+ ret.extend(self.curse_add_stat('inactive', width=16, header=' '))
+
+ # Third line
+ # used + buffers
+ ret.append(self.curse_new_line())
+ # Used memory usage
+ ret.extend(self.curse_add_stat('used', width=15))
+ # Buffers memory usage
+ ret.extend(self.curse_add_stat('buffers', width=16, header=' '))
+
+ # Fourth line
+ # free + cached
+ ret.append(self.curse_new_line())
+ # Free memory usage
+ ret.extend(self.curse_add_stat('free', width=15))
+ # Cached memory usage
+ ret.extend(self.curse_add_stat('cached', width=16, header=' '))
+
+ return ret