Test Failed
Push — develop ( abf64f...1d1151 )
by Nicolas
02:59
created

glances/plugins/glances_mem.py (3 issues)

Checks old division

Minor
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 2019 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.compat import iterkeys
23
from glances.plugins.glances_plugin import GlancesPlugin
24
25
import psutil
26
27
# SNMP OID
28
# Total RAM in machine: .1.3.6.1.4.1.2021.4.5.0
29
# Total RAM used: .1.3.6.1.4.1.2021.4.6.0
30
# Total RAM Free: .1.3.6.1.4.1.2021.4.11.0
31
# Total RAM Shared: .1.3.6.1.4.1.2021.4.13.0
32
# Total RAM Buffered: .1.3.6.1.4.1.2021.4.14.0
33
# Total Cached Memory: .1.3.6.1.4.1.2021.4.15.0
34
# Note: For Windows, stats are in the FS table
35
snmp_oid = {'default': {'total': '1.3.6.1.4.1.2021.4.5.0',
36
                        'free': '1.3.6.1.4.1.2021.4.11.0',
37
                        'shared': '1.3.6.1.4.1.2021.4.13.0',
38
                        'buffers': '1.3.6.1.4.1.2021.4.14.0',
39
                        'cached': '1.3.6.1.4.1.2021.4.15.0'},
40
            'windows': {'mnt_point': '1.3.6.1.2.1.25.2.3.1.3',
41
                        'alloc_unit': '1.3.6.1.2.1.25.2.3.1.4',
42
                        'size': '1.3.6.1.2.1.25.2.3.1.5',
43
                        'used': '1.3.6.1.2.1.25.2.3.1.6'},
44
            'esxi': {'mnt_point': '1.3.6.1.2.1.25.2.3.1.3',
45
                     'alloc_unit': '1.3.6.1.2.1.25.2.3.1.4',
46
                     'size': '1.3.6.1.2.1.25.2.3.1.5',
47
                     'used': '1.3.6.1.2.1.25.2.3.1.6'}}
48
49
# Define the history items list
50
# All items in this list will be historised if the --enable-history tag is set
51
items_history_list = [{'name': 'percent',
52
                       'description': 'RAM memory usage',
53
                       'y_unit': '%'}]
54
55
56
class Plugin(GlancesPlugin):
57
    """Glances' memory plugin.
58
59
    stats is a dict
60
    """
61
62
    def __init__(self, args=None, config=None):
63
        """Init the plugin."""
64
        super(Plugin, self).__init__(args=args,
65
                                     config=config,
66
                                     items_history_list=items_history_list)
67
68
        # We want to display the stat in the curse interface
69
        self.display_curse = True
70
71
    @GlancesPlugin._check_decorator
72
    @GlancesPlugin._log_result_decorator
73
    def update(self):
74
        """Update RAM memory stats using the input method."""
75
        # Init new stats
76
        stats = self.get_init_value()
77
78
        if self.input_method == 'local':
79
            # Update stats using the standard system lib
80
            # Grab MEM using the psutil virtual_memory method
81
            vm_stats = psutil.virtual_memory()
82
83
            # Get all the memory stats (copy/paste of the psutil documentation)
84
            # total: total physical memory available.
85
            # 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.
86
            # percent: the percentage usage calculated as (total - available) / total * 100.
87
            # used: memory used, calculated differently depending on the platform and designed for informational purposes only.
88
            # 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).
89
            # Platform-specific fields:
90
            # active: (UNIX): memory currently in use or very recently used, and so it is in RAM.
91
            # inactive: (UNIX): memory that is marked as not used.
92
            # buffers: (Linux, BSD): cache for things like file system metadata.
93
            # cached: (Linux, BSD): cache for various things.
94
            # wired: (BSD, macOS): memory that is marked to always stay in RAM. It is never moved to disk.
95
            # shared: (BSD): memory that may be simultaneously accessed by multiple processes.
96
            self.reset()
97
            for mem in ['total', 'available', 'percent', 'used', 'free',
98
                        'active', 'inactive', 'buffers', 'cached',
99
                        'wired', 'shared']:
100
                if hasattr(vm_stats, mem):
101
                    stats[mem] = getattr(vm_stats, mem)
102
103
            # Use the 'free'/htop calculation
104
            # free=available+buffer+cached
105
            stats['free'] = stats['available']
106
            if hasattr(stats, 'buffers'):
107
                stats['free'] += stats['buffers']
108
            if hasattr(stats, 'cached'):
109
                stats['free'] += stats['cached']
110
            # used=total-free
111
            stats['used'] = stats['total'] - stats['free']
112
        elif self.input_method == 'snmp':
113
            # Update stats using SNMP
114
            if self.short_system_name in ('windows', 'esxi'):
115
                # Mem stats for Windows|Vmware Esxi are stored in the FS table
116
                try:
117
                    fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
118
                                                  bulk=True)
119
                except KeyError:
120
                    self.reset()
121
                else:
122
                    for fs in fs_stat:
123
                        # The Physical Memory (Windows) or Real Memory (VMware)
124
                        # gives statistics on RAM usage and availability.
125
                        if fs in ('Physical Memory', 'Real Memory'):
126
                            stats['total'] = int(fs_stat[fs]['size']) * int(fs_stat[fs]['alloc_unit'])
127
                            stats['used'] = int(fs_stat[fs]['used']) * int(fs_stat[fs]['alloc_unit'])
128
                            stats['percent'] = float(stats['used'] * 100 / stats['total'])
0 ignored issues
show
division w/o __future__ statement
Loading history...
129
                            stats['free'] = stats['total'] - stats['used']
130
                            break
131
            else:
132
                # Default behavor for others OS
133
                stats = self.get_stats_snmp(snmp_oid=snmp_oid['default'])
134
135
                if stats['total'] == '':
136
                    self.reset()
137
                    return self.stats
138
139
                for key in iterkeys(stats):
140
                    if stats[key] != '':
141
                        stats[key] = float(stats[key]) * 1024
142
143
                # Use the 'free'/htop calculation
144
                stats['free'] = stats['free'] - stats['total'] + (stats['buffers'] + stats['cached'])
145
146
                # used=total-free
147
                stats['used'] = stats['total'] - stats['free']
148
149
                # percent: the percentage usage calculated as (total - available) / total * 100.
150
                stats['percent'] = float((stats['total'] - stats['free']) / stats['total'] * 100)
0 ignored issues
show
division w/o __future__ statement
Loading history...
151
152
        # Update the stats
153
        self.stats = stats
154
155
        return self.stats
156
157
    def update_views(self):
158
        """Update stats views."""
159
        # Call the father's method
160
        super(Plugin, self).update_views()
161
162
        # Add specifics informations
163
        # Alert and log
164
        self.views['used']['decoration'] = self.get_alert_log(self.stats['used'], maximum=self.stats['total'])
165
        # Optional
166
        for key in ['active', 'inactive', 'buffers', 'cached']:
167
            if key in self.stats:
168
                self.views[key]['optional'] = True
169
170
    def msg_curse(self, args=None, max_width=None):
171
        """Return the dict to display in the curse interface."""
172
        # Init the return message
173
        ret = []
174
175
        # Only process if stats exist and plugin not disabled
176
        if not self.stats or self.is_disable():
177
            return ret
178
179
        # Build the string message
180
        # Header
181
        msg = '{}'.format('MEM')
182
        ret.append(self.curse_add_line(msg, "TITLE"))
183
        msg = ' {:2}'.format(self.trend_msg(self.get_trend('percent')))
184
        ret.append(self.curse_add_line(msg))
185
        # Percent memory usage
186
        msg = '{:>7.1%}'.format(self.stats['percent'] / 100)
0 ignored issues
show
division w/o __future__ statement
Loading history...
187
        ret.append(self.curse_add_line(msg))
188
        # Active memory usage
189
        if 'active' in self.stats:
190
            msg = '  {:9}'.format('active:')
191
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='active', option='optional')))
192
            msg = '{:>7}'.format(self.auto_unit(self.stats['active']))
193
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='active', option='optional')))
194
        # New line
195
        ret.append(self.curse_new_line())
196
        # Total memory usage
197
        msg = '{:6}'.format('total:')
198
        ret.append(self.curse_add_line(msg))
199
        msg = '{:>7}'.format(self.auto_unit(self.stats['total']))
200
        ret.append(self.curse_add_line(msg))
201
        # Inactive memory usage
202
        if 'inactive' in self.stats:
203
            msg = '  {:9}'.format('inactive:')
204
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='inactive', option='optional')))
205
            msg = '{:>7}'.format(self.auto_unit(self.stats['inactive']))
206
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='inactive', option='optional')))
207
        # New line
208
        ret.append(self.curse_new_line())
209
        # Used memory usage
210
        msg = '{:6}'.format('used:')
211
        ret.append(self.curse_add_line(msg))
212
        msg = '{:>7}'.format(self.auto_unit(self.stats['used']))
213
        ret.append(self.curse_add_line(
214
            msg, self.get_views(key='used', option='decoration')))
215
        # Buffers memory usage
216
        if 'buffers' in self.stats:
217
            msg = '  {:9}'.format('buffers:')
218
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='buffers', option='optional')))
219
            msg = '{:>7}'.format(self.auto_unit(self.stats['buffers']))
220
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='buffers', option='optional')))
221
        # New line
222
        ret.append(self.curse_new_line())
223
        # Free memory usage
224
        msg = '{:6}'.format('free:')
225
        ret.append(self.curse_add_line(msg))
226
        msg = '{:>7}'.format(self.auto_unit(self.stats['free']))
227
        ret.append(self.curse_add_line(msg))
228
        # Cached memory usage
229
        if 'cached' in self.stats:
230
            msg = '  {:9}'.format('cached:')
231
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='cached', option='optional')))
232
            msg = '{:>7}'.format(self.auto_unit(self.stats['cached']))
233
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='cached', option='optional')))
234
235
        return ret
236