Test Failed
Push — master ( b164e3...65b0d8 )
by Nicolas
03:39
created

glances.plugins.glances_mem.Plugin.msg_curse()   A

Complexity

Conditions 3

Size

Total Lines 47
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

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