glances_mem.py 10.9 KB
Newer Older
A
Alessio Sergi 已提交
1 2
# -*- coding: utf-8 -*-
#
3
# This file is part of Glances.
A
Alessio Sergi 已提交
4
#
N
nicolargo 已提交
5
# Copyright (C) 2019 Nicolargo <nicolas@nicolargo.com>
A
Alessio Sergi 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18
#
# Glances is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Glances is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
A
PEP 257  
Alessio Sergi 已提交
19 20

"""Virtual memory plugin."""
A
Alessio Sergi 已提交
21

22
from glances.logger import logger
23
from glances.compat import iterkeys
24 25
from glances.plugins.glances_plugin import GlancesPlugin

A
flake8  
Alessio Sergi 已提交
26 27
import psutil

N
Nicolargo 已提交
28 29 30 31 32 33 34
# 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
N
Nicolargo 已提交
35 36 37 38 39 40 41 42 43
# 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',
N
Nicolargo 已提交
44 45 46 47 48
                        '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'}}
A
Alessio Sergi 已提交
49

50 51
# Define the history items list
# All items in this list will be historised if the --enable-history tag is set
52 53 54
items_history_list = [{'name': 'percent',
                       'description': 'RAM memory usage',
                       'y_unit': '%'}]
55

N
Nicolargo 已提交
56

A
Alessio Sergi 已提交
57
class Plugin(GlancesPlugin):
A
PEP 257  
Alessio Sergi 已提交
58
    """Glances' memory plugin.
A
Alessio Sergi 已提交
59 60 61 62

    stats is a dict
    """

63
    def __init__(self, args=None, config=None):
A
PEP 257  
Alessio Sergi 已提交
64
        """Init the plugin."""
65
        super(Plugin, self).__init__(args=args,
66
                                     config=config,
67
                                     items_history_list=items_history_list)
A
Alessio Sergi 已提交
68 69 70 71

        # We want to display the stat in the curse interface
        self.display_curse = True

72
    @GlancesPlugin._check_decorator
73
    @GlancesPlugin._log_result_decorator
A
Alessio Sergi 已提交
74
    def update(self):
A
PEP 257  
Alessio Sergi 已提交
75
        """Update RAM memory stats using the input method."""
76 77
        # Init new stats
        stats = self.get_init_value()
78

79
        if self.input_method == 'local':
80
            # Update stats using the standard system lib
A
Alessio Sergi 已提交
81
            # Grab MEM using the psutil virtual_memory method
82 83
            vm_stats = psutil.virtual_memory()

A
Alessio Sergi 已提交
84
            # Get all the memory stats (copy/paste of the psutil documentation)
85 86 87 88 89 90 91 92 93 94
            # 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.
A
Alessio Sergi 已提交
95
            # wired: (BSD, macOS): memory that is marked to always stay in RAM. It is never moved to disk.
96
            # shared: (BSD): memory that may be simultaneously accessed by multiple processes.
N
Nicolargo 已提交
97
            self.reset()
98 99 100 101
            for mem in ['total', 'available', 'percent', 'used', 'free',
                        'active', 'inactive', 'buffers', 'cached',
                        'wired', 'shared']:
                if hasattr(vm_stats, mem):
102
                    stats[mem] = getattr(vm_stats, mem)
103 104 105

            # Use the 'free'/htop calculation
            # free=available+buffer+cached
106 107 108 109 110
            stats['free'] = stats['available']
            if hasattr(stats, 'buffers'):
                stats['free'] += stats['buffers']
            if hasattr(stats, 'cached'):
                stats['free'] += stats['cached']
111
            # used=total-free
112
            stats['used'] = stats['total'] - stats['free']
113
        elif self.input_method == 'snmp':
114
            # Update stats using SNMP
115
            if self.short_system_name in ('windows', 'esxi'):
N
Nicolargo 已提交
116
                # Mem stats for Windows|Vmware Esxi are stored in the FS table
N
Nicolargo 已提交
117
                try:
118
                    fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
N
Nicolargo 已提交
119 120 121 122
                                                  bulk=True)
                except KeyError:
                    self.reset()
                else:
N
Nicolargo 已提交
123
                    for fs in fs_stat:
A
flake8  
Alessio Sergi 已提交
124
                        # The Physical Memory (Windows) or Real Memory (VMware)
N
Nicolargo 已提交
125 126
                        # gives statistics on RAM usage and availability.
                        if fs in ('Physical Memory', 'Real Memory'):
127 128 129 130
                            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']
N
Nicolargo 已提交
131 132 133
                            break
            else:
                # Default behavor for others OS
134
                stats = self.get_stats_snmp(snmp_oid=snmp_oid['default'])
N
Nicolargo 已提交
135

136
                if stats['total'] == '':
N
Nicolargo 已提交
137 138 139
                    self.reset()
                    return self.stats

140 141 142
                for key in iterkeys(stats):
                    if stats[key] != '':
                        stats[key] = float(stats[key]) * 1024
N
Nicolargo 已提交
143 144

                # Use the 'free'/htop calculation
145
                stats['free'] = stats['free'] - stats['total'] + (stats['buffers'] + stats['cached'])
N
Nicolargo 已提交
146 147

                # used=total-free
148
                stats['used'] = stats['total'] - stats['free']
N
Nicolargo 已提交
149 150

                # percent: the percentage usage calculated as (total - available) / total * 100.
151 152 153 154
                stats['percent'] = float((stats['total'] - stats['free']) / stats['total'] * 100)

        # Update the stats
        self.stats = stats
155 156

        return self.stats
A
Alessio Sergi 已提交
157

158
    def update_views(self):
A
PEP 257  
Alessio Sergi 已提交
159
        """Update stats views."""
160
        # Call the father's method
A
Alessio Sergi 已提交
161
        super(Plugin, self).update_views()
162 163 164

        # Add specifics informations
        # Alert and log
165
        self.views['percent']['decoration'] = self.get_alert_log(self.stats['used'], maximum=self.stats['total'])
166 167 168 169 170
        # Optional
        for key in ['active', 'inactive', 'buffers', 'cached']:
            if key in self.stats:
                self.views[key]['optional'] = True

171
    def msg_curse(self, args=None, max_width=None):
A
PEP 257  
Alessio Sergi 已提交
172
        """Return the dict to display in the curse interface."""
A
Alessio Sergi 已提交
173 174 175
        # Init the return message
        ret = []

176
        # Only process if stats exist and plugin not disabled
N
nicolargo 已提交
177
        if not self.stats or self.is_disable():
178 179
            return ret

A
Alessio Sergi 已提交
180 181
        # Build the string message
        # Header
182
        msg = '{}'.format('MEM')
A
Alessio Sergi 已提交
183
        ret.append(self.curse_add_line(msg, "TITLE"))
184 185
        msg = ' {:2}'.format(self.trend_msg(self.get_trend('percent')))
        ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
186
        # Percent memory usage
187
        msg = '{:>7.1%}'.format(self.stats['percent'] / 100)
188 189
        ret.append(self.curse_add_line(
            msg, self.get_views(key='percent', option='decoration')))
A
Alessio Sergi 已提交
190
        # Active memory usage
191
        if 'active' in self.stats:
192
            msg = '  {:9}'.format('active:')
193
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='active', option='optional')))
194
            msg = '{:>7}'.format(self.auto_unit(self.stats['active']))
195
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='active', option='optional')))
A
Alessio Sergi 已提交
196 197 198
        # New line
        ret.append(self.curse_new_line())
        # Total memory usage
199
        msg = '{:6}'.format('total:')
A
Alessio Sergi 已提交
200
        ret.append(self.curse_add_line(msg))
201
        msg = '{:>7}'.format(self.auto_unit(self.stats['total']))
A
Alessio Sergi 已提交
202 203
        ret.append(self.curse_add_line(msg))
        # Inactive memory usage
204
        if 'inactive' in self.stats:
205
            msg = '  {:9}'.format('inactive:')
206
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='inactive', option='optional')))
207
            msg = '{:>7}'.format(self.auto_unit(self.stats['inactive']))
208
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='inactive', option='optional')))
A
Alessio Sergi 已提交
209 210 211
        # New line
        ret.append(self.curse_new_line())
        # Used memory usage
212
        msg = '{:6}'.format('used:')
A
Alessio Sergi 已提交
213
        ret.append(self.curse_add_line(msg))
214
        msg = '{:>7}'.format(self.auto_unit(self.stats['used']))
A
Alessio Sergi 已提交
215
        ret.append(self.curse_add_line(
216
            msg, self.get_views(key='used', option='decoration')))
A
Alessio Sergi 已提交
217
        # Buffers memory usage
218
        if 'buffers' in self.stats:
219
            msg = '  {:9}'.format('buffers:')
220
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='buffers', option='optional')))
221
            msg = '{:>7}'.format(self.auto_unit(self.stats['buffers']))
222
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='buffers', option='optional')))
A
Alessio Sergi 已提交
223 224 225
        # New line
        ret.append(self.curse_new_line())
        # Free memory usage
226
        msg = '{:6}'.format('free:')
A
Alessio Sergi 已提交
227
        ret.append(self.curse_add_line(msg))
228
        msg = '{:>7}'.format(self.auto_unit(self.stats['free']))
A
Alessio Sergi 已提交
229 230
        ret.append(self.curse_add_line(msg))
        # Cached memory usage
231
        if 'cached' in self.stats:
232
            msg = '  {:9}'.format('cached:')
233
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='cached', option='optional')))
234
            msg = '{:>7}'.format(self.auto_unit(self.stats['cached']))
235
            ret.append(self.curse_add_line(msg, optional=self.get_views(key='cached', option='optional')))
A
Alessio Sergi 已提交
236 237

        return ret