Issues (48)

glances/plugins/mem/__init__.py (1 issue)

1
#
2
# This file is part of Glances.
3
#
4
# SPDX-FileCopyrightText: 2022 Nicolas Hennion <[email protected]>
5
#
6
# SPDX-License-Identifier: LGPL-3.0-only
7
#
8
9
"""Virtual memory plugin."""
10
11
import psutil
12
13
from glances.plugins.plugin.model import GlancesPluginModel
14
15
# Fields description
16
fields_description = {
17
    'total': {'description': 'Total physical memory available.', 'unit': 'bytes', 'min_symbol': 'K'},
18
    'available': {
19
        'description': 'The actual amount of available memory that can be given instantly \
20
to processes that request more memory in bytes; this is calculated by summing \
21
different memory values depending on the platform (e.g. free + buffers + cached on Linux) \
22
and it is supposed to be used to monitor actual memory usage in a cross platform fashion.',
23
        'unit': 'bytes',
24
        'min_symbol': 'K',
25
    },
26
    'percent': {
27
        'description': 'The percentage usage calculated as (total - available) / total * 100.',
28
        'unit': 'percent',
29
    },
30
    'used': {
31
        'description': 'Memory used, calculated differently depending on the platform and \
32
designed for informational purposes only.',
33
        'unit': 'bytes',
34
        'min_symbol': 'K',
35
    },
36
    'free': {
37
        'description': 'Memory not being used at all (zeroed) that is readily available; \
38
note that this doesn\'t reflect the actual memory available (use \'available\' instead).',
39
        'unit': 'bytes',
40
        'min_symbol': 'K',
41
    },
42
    'active': {
43
        'description': '*(UNIX)*: memory currently in use or very recently used, and so it is in RAM.',
44
        'unit': 'bytes',
45
        'min_symbol': 'K',
46
    },
47
    'inactive': {
48
        'description': '*(UNIX)*: memory that is marked as not used.',
49
        'unit': 'bytes',
50
        'min_symbol': 'K',
51
        'short_name': 'inacti',
52
    },
53
    'buffers': {
54
        'description': '*(Linux, BSD)*: cache for things like file system metadata.',
55
        'unit': 'bytes',
56
        'min_symbol': 'K',
57
        'short_name': 'buffer',
58
    },
59
    'cached': {'description': '*(Linux, BSD)*: cache for various things.', 'unit': 'bytes', 'min_symbol': 'K'},
60
    'wired': {
61
        'description': '*(BSD, macOS)*: memory that is marked to always stay in RAM. It is never moved to disk.',
62
        'unit': 'bytes',
63
        'min_symbol': 'K',
64
    },
65
    'shared': {
66
        'description': '*(BSD)*: memory that may be simultaneously accessed by multiple processes.',
67
        'unit': 'bytes',
68
        'min_symbol': 'K',
69
    },
70
}
71
72
# SNMP OID
73
# Total RAM in machine: .1.3.6.1.4.1.2021.4.5.0
74
# Total RAM used: .1.3.6.1.4.1.2021.4.6.0
75
# Total RAM Free: .1.3.6.1.4.1.2021.4.11.0
76
# Total RAM Shared: .1.3.6.1.4.1.2021.4.13.0
77
# Total RAM Buffered: .1.3.6.1.4.1.2021.4.14.0
78
# Total Cached Memory: .1.3.6.1.4.1.2021.4.15.0
79
# Note: For Windows, stats are in the FS table
80
snmp_oid = {
81
    'default': {
82
        'total': '1.3.6.1.4.1.2021.4.5.0',
83
        'free': '1.3.6.1.4.1.2021.4.11.0',
84
        'shared': '1.3.6.1.4.1.2021.4.13.0',
85
        'buffers': '1.3.6.1.4.1.2021.4.14.0',
86
        'cached': '1.3.6.1.4.1.2021.4.15.0',
87
    },
88
    'windows': {
89
        'mnt_point': '1.3.6.1.2.1.25.2.3.1.3',
90
        'alloc_unit': '1.3.6.1.2.1.25.2.3.1.4',
91
        'size': '1.3.6.1.2.1.25.2.3.1.5',
92
        'used': '1.3.6.1.2.1.25.2.3.1.6',
93
    },
94
    'esxi': {
95
        'mnt_point': '1.3.6.1.2.1.25.2.3.1.3',
96
        'alloc_unit': '1.3.6.1.2.1.25.2.3.1.4',
97
        'size': '1.3.6.1.2.1.25.2.3.1.5',
98
        'used': '1.3.6.1.2.1.25.2.3.1.6',
99
    },
100
}
101
102
# Define the history items list
103
# All items in this list will be historised if the --enable-history tag is set
104
items_history_list = [{'name': 'percent', 'description': 'RAM memory usage', 'y_unit': '%'}]
105
106
107
class PluginModel(GlancesPluginModel):
108
    """Glances' memory plugin.
109
110
    stats is a dict
111
    """
112
113
    def __init__(self, args=None, config=None):
114
        """Init the plugin."""
115
        super().__init__(
116
            args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
117
        )
118
119
        # We want to display the stat in the curse interface
120
        self.display_curse = True
121
122
    @GlancesPluginModel._check_decorator
123
    @GlancesPluginModel._log_result_decorator
124
    def update(self):
125
        """Update RAM memory stats using the input method."""
126
        # Init new stats
127
        stats = self.get_init_value()
128
129
        if self.input_method == 'local':
130
            # Update stats using the standard system lib
131
            # Grab MEM using the psutil virtual_memory method
132
            vm_stats = psutil.virtual_memory()
133
134
            # Get all the memory stats (copy/paste of the psutil documentation)
135
            # total: total physical memory available.
136
            # available: the actual amount of available memory that can be given instantly
137
            # to processes that request more memory in bytes; this is calculated by summing
138
            # different memory values depending on the platform (e.g. free + buffers + cached on Linux)
139
            # and it is supposed to be used to monitor actual memory usage in a cross platform fashion.
140
            # percent: the percentage usage calculated as (total - available) / total * 100.
141
            # used: memory used, calculated differently depending on the platform and designed for informational
142
            # purposes only.
143
            # free: memory not being used at all (zeroed) that is readily available; note that this doesn't
144
            # reflect the actual memory available (use ‘available’ instead).
145
            # Platform-specific fields:
146
            # active: (UNIX): memory currently in use or very recently used, and so it is in RAM.
147
            # inactive: (UNIX): memory that is marked as not used.
148
            # buffers: (Linux, BSD): cache for things like file system metadata.
149
            # cached: (Linux, BSD): cache for various things.
150
            # wired: (BSD, macOS): memory that is marked to always stay in RAM. It is never moved to disk.
151
            # shared: (BSD): memory that may be simultaneously accessed by multiple processes.
152
            self.reset()
153
            for mem in [
154
                'total',
155
                'available',
156
                'percent',
157
                'used',
158
                'free',
159
                'active',
160
                'inactive',
161
                'buffers',
162
                'cached',
163
                'wired',
164
                'shared',
165
            ]:
166
                if hasattr(vm_stats, mem):
167
                    stats[mem] = getattr(vm_stats, mem)
168
169
            # Use the 'free'/htop calculation
170
            # free=available+buffer+cached
171
            stats['free'] = stats['available']
172
            if hasattr(stats, 'buffers'):
173
                stats['free'] += stats['buffers']
174
            if hasattr(stats, 'cached'):
175
                stats['free'] += stats['cached']
176
            # used=total-free
177
            stats['used'] = stats['total'] - stats['free']
178
        elif self.input_method == 'snmp':
179
            # Update stats using SNMP
180 View Code Duplication
            if self.short_system_name in ('windows', 'esxi'):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
181
                # Mem stats for Windows|Vmware Esxi are stored in the FS table
182
                try:
183
                    fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name], bulk=True)
184
                except KeyError:
185
                    self.reset()
186
                else:
187
                    for fs in fs_stat:
188
                        # The Physical Memory (Windows) or Real Memory (VMware)
189
                        # gives statistics on RAM usage and availability.
190
                        if fs in ('Physical Memory', 'Real Memory'):
191
                            stats['total'] = int(fs_stat[fs]['size']) * int(fs_stat[fs]['alloc_unit'])
192
                            stats['used'] = int(fs_stat[fs]['used']) * int(fs_stat[fs]['alloc_unit'])
193
                            stats['percent'] = float(stats['used'] * 100 / stats['total'])
194
                            stats['free'] = stats['total'] - stats['used']
195
                            break
196
            else:
197
                # Default behavior for others OS
198
                stats = self.get_stats_snmp(snmp_oid=snmp_oid['default'])
199
200
                if stats['total'] == '':
201
                    self.reset()
202
                    return self.stats
203
204
                for k in stats:
205
                    stats[k] = int(stats[k]) * 1024
206
207
                # used=total-free
208
                stats['used'] = stats['total'] - stats['free']
209
210
                # percent: the percentage usage calculated as (total - available) / total * 100.
211
                stats['percent'] = float((stats['total'] - stats['free']) / stats['total'] * 100)
212
213
        # Update the stats
214
        self.stats = stats
215
216
        return self.stats
217
218
    def update_views(self):
219
        """Update stats views."""
220
        # Call the father's method
221
        super().update_views()
222
223
        # Add specifics information
224
        # Alert and log
225
        self.views['percent']['decoration'] = self.get_alert_log(self.stats['used'], maximum=self.stats['total'])
226
        # Optional
227
        for key in ['active', 'inactive', 'buffers', 'cached']:
228
            if key in self.stats:
229
                self.views[key]['optional'] = True
230
231
    def msg_curse(self, args=None, max_width=None):
232
        """Return the dict to display in the curse interface."""
233
        # Init the return message
234
        ret = []
235
236
        # Only process if stats exist and plugin not disabled
237
        if not self.stats or self.is_disabled():
238
            return ret
239
240
        # First line
241
        # total% + active
242
        msg = '{}'.format('MEM')
243
        ret.append(self.curse_add_line(msg, "TITLE"))
244
        msg = ' {:2}'.format(self.trend_msg(self.get_trend('percent')))
245
        ret.append(self.curse_add_line(msg))
246
        # Percent memory usage
247
        msg = '{:>7.1%}'.format(self.stats['percent'] / 100)
248
        ret.append(self.curse_add_line(msg, self.get_views(key='percent', option='decoration')))
249
        # Active memory usage
250
        ret.extend(self.curse_add_stat('active', width=16, header='  '))
251
252
        # Second line
253
        # total + inactive
254
        ret.append(self.curse_new_line())
255
        # Total memory usage
256
        ret.extend(self.curse_add_stat('total', width=15))
257
        # Inactive memory usage
258
        ret.extend(self.curse_add_stat('inactive', width=16, header='  '))
259
260
        # Third line
261
        # used + buffers
262
        ret.append(self.curse_new_line())
263
        # Used memory usage
264
        ret.extend(self.curse_add_stat('used', width=15))
265
        # Buffers memory usage
266
        ret.extend(self.curse_add_stat('buffers', width=16, header='  '))
267
268
        # Fourth line
269
        # free + cached
270
        ret.append(self.curse_new_line())
271
        # Free memory usage
272
        ret.extend(self.curse_add_stat('free', width=15))
273
        # Cached memory usage
274
        ret.extend(self.curse_add_stat('cached', width=16, header='  '))
275
276
        return ret
277