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
#
5
# Copyright (C) 2015 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.compat import iterkeys
23 24
from glances.plugins.glances_plugin import GlancesPlugin

A
flake8  
Alessio Sergi 已提交
25 26
import psutil

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

49 50 51
# Define the history items list
# All items in this list will be historised if the --enable-history tag is set
# 'color' define the graph color in #RGB format
N
Nicolargo 已提交
52
items_history_list = [{'name': 'percent', 'color': '#00FF00', 'y_unit': '%'}]
53

N
Nicolargo 已提交
54

A
Alessio Sergi 已提交
55
class Plugin(GlancesPlugin):
A
PEP 257  
Alessio Sergi 已提交
56 57

    """Glances' memory plugin.
A
Alessio Sergi 已提交
58 59 60 61

    stats is a dict
    """

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

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

69
        # Init the stats
A
Alessio Sergi 已提交
70
        self.reset()
71 72

    def reset(self):
A
PEP 257  
Alessio Sergi 已提交
73
        """Reset/init the stats."""
74 75
        self.stats = {}

76
    @GlancesPlugin._log_result_decorator
A
Alessio Sergi 已提交
77
    def update(self):
A
PEP 257  
Alessio Sergi 已提交
78
        """Update RAM memory stats using the input method."""
79 80 81
        # Reset stats
        self.reset()

82
        if self.input_method == 'local':
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
            # 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, OSX): 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.
N
Nicolargo 已提交
100
            self.reset()
101 102 103 104 105 106 107 108 109
            for mem in ['total', 'available', 'percent', 'used', 'free',
                        'active', 'inactive', 'buffers', 'cached',
                        'wired', 'shared']:
                if hasattr(vm_stats, mem):
                    self.stats[mem] = getattr(vm_stats, mem)

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

                if self.stats['total'] == '':
                    self.reset()
                    return self.stats

A
Alessio Sergi 已提交
143
                for key in iterkeys(self.stats):
N
Nicolargo 已提交
144 145 146 147 148 149 150 151 152 153 154
                    if self.stats[key] != '':
                        self.stats[key] = float(self.stats[key]) * 1024

                # Use the 'free'/htop calculation
                self.stats['free'] = self.stats['free'] - self.stats['total'] + (self.stats['buffers'] + self.stats['cached'])

                # used=total-free
                self.stats['used'] = self.stats['total'] - self.stats['free']

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

156 157 158
        # Update the history list
        self.update_stats_history()

159 160 161
        # Update the view
        self.update_views()

162
        return self.stats
A
Alessio Sergi 已提交
163

164
    def update_views(self):
A
PEP 257  
Alessio Sergi 已提交
165
        """Update stats views."""
166
        # Call the father's method
A
Alessio Sergi 已提交
167
        super(Plugin, self).update_views()
168 169 170

        # Add specifics informations
        # Alert and log
171
        self.views['used']['decoration'] = self.get_alert_log(self.stats['used'], maximum=self.stats['total'])
172 173 174 175 176
        # Optional
        for key in ['active', 'inactive', 'buffers', 'cached']:
            if key in self.stats:
                self.views[key]['optional'] = True

A
Alessio Sergi 已提交
177
    def msg_curse(self, args=None):
A
PEP 257  
Alessio Sergi 已提交
178
        """Return the dict to display in the curse interface."""
A
Alessio Sergi 已提交
179 180 181
        # Init the return message
        ret = []

182 183
        # Only process if stats exist and plugin not disabled
        if not self.stats or args.disable_mem:
184 185
            return ret

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

        return ret