glances_memswap.py 8.3 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

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

22
from glances.compat import iterkeys
N
nicolargo 已提交
23
from glances.timer import getTimeSinceLastUpdate
24 25
from glances.plugins.glances_plugin import GlancesPlugin

A
flake8  
Alessio Sergi 已提交
26 27
import psutil

N
nicolargo 已提交
28 29 30
# Fields description
fields_description = {
    'total': {'description': 'Total swap memory.',
N
nicolargo 已提交
31 32
              'unit': 'bytes',
              'min_symbol': 'K'},
N
nicolargo 已提交
33
    'used': {'description': 'Used swap memory.',
N
nicolargo 已提交
34 35
             'unit': 'bytes',
             'min_symbol': 'K'},
N
nicolargo 已提交
36
    'free': {'description': 'Free swap memory.',
N
nicolargo 已提交
37 38
             'unit': 'bytes',
             'min_symbol': 'K'},
N
nicolargo 已提交
39 40 41
    'percent': {'description': 'Used swap memory in percentage.',
                'unit': 'percent'},
    'sin': {'description': 'The number of bytes the system has swapped in from disk (cumulative).',
N
nicolargo 已提交
42 43
            'unit': 'bytes',
            'min_symbol': 'K'},
N
nicolargo 已提交
44
    'sout': {'description': 'The number of bytes the system has swapped out from disk (cumulative).',
N
nicolargo 已提交
45 46
             'unit': 'bytes',
             'min_symbol': 'K'},
N
nicolargo 已提交
47 48 49 50
    'time_since_update': {'description': 'Number of seconds since last update.',
                          'unit': 'seconds'},
}

N
Nicolargo 已提交
51 52 53
# SNMP OID
# Total Swap Size: .1.3.6.1.4.1.2021.4.3.0
# Available Swap Space: .1.3.6.1.4.1.2021.4.4.0
54 55 56 57 58
snmp_oid = {'default': {'total': '1.3.6.1.4.1.2021.4.3.0',
                        'free': '1.3.6.1.4.1.2021.4.4.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',
59 60 61 62
                        'used': '1.3.6.1.2.1.25.2.3.1.6'}}

# Define the history items list
# All items in this list will be historised if the --enable-history tag is set
63 64 65
items_history_list = [{'name': 'percent',
                       'description': 'Swap memory usage',
                       'y_unit': '%'}]
N
Nicolargo 已提交
66

A
Alessio Sergi 已提交
67 68

class Plugin(GlancesPlugin):
A
PEP 257  
Alessio Sergi 已提交
69
    """Glances swap memory plugin.
A
Alessio Sergi 已提交
70 71 72 73

    stats is a dict
    """

74
    def __init__(self, args=None, config=None):
A
PEP 257  
Alessio Sergi 已提交
75
        """Init the plugin."""
76
        super(Plugin, self).__init__(args=args,
77
                                     config=config,
N
nicolargo 已提交
78 79
                                     items_history_list=items_history_list,
                                     fields_description=fields_description)
A
Alessio Sergi 已提交
80 81 82 83

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

84
    @GlancesPlugin._check_decorator
85
    @GlancesPlugin._log_result_decorator
A
Alessio Sergi 已提交
86
    def update(self):
A
PEP 257  
Alessio Sergi 已提交
87
        """Update swap memory stats using the input method."""
88 89
        # Init new stats
        stats = self.get_init_value()
90

91
        if self.input_method == 'local':
92
            # Update stats using the standard system lib
A
Alessio Sergi 已提交
93
            # Grab SWAP using the psutil swap_memory method
94 95 96 97 98 99 100 101 102 103 104 105
            try:
                sm_stats = psutil.swap_memory()
            except RuntimeError:
                # Crash on startup on Illumos when no swap is configured #1767
                pass
            else:
                # Get all the swap stats (copy/paste of the psutil documentation)
                # total: total swap memory in bytes
                # used: used swap memory in bytes
                # free: free swap memory in bytes
                # percent: the percentage usage
                # sin: the number of bytes the system has swapped in from disk (cumulative)
N
nicolargo 已提交
106
                # sout: the number of bytes the system has swapped out from disk (cumulative)
107
                for swap in ['total', 'used', 'free', 'percent',
N
nicolargo 已提交
108
                             'sin', 'sout']:
109 110
                    if hasattr(sm_stats, swap):
                        stats[swap] = getattr(sm_stats, swap)
N
nicolargo 已提交
111 112 113 114 115

                # By storing time data we enable sin/s and sout/s calculations in the
                # XML/RPC API, which would otherwise be overly difficult work
                # for users of the API
                stats['time_since_update'] = getTimeSinceLastUpdate('memswap')
116
        elif self.input_method == 'snmp':
117
            # Update stats using SNMP
118
            if self.short_system_name == 'windows':
119 120
                # Mem stats for Windows OS are stored in the FS table
                try:
121
                    fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
122 123 124 125
                                                  bulk=True)
                except KeyError:
                    self.reset()
                else:
126
                    for fs in fs_stat:
A
flake8  
Alessio Sergi 已提交
127 128 129 130
                        # The virtual memory concept is used by the operating
                        # system to extend (virtually) the physical memory and
                        # thus to run more programs by swapping unused memory
                        # zone (page) to a disk file.
131
                        if fs == 'Virtual Memory':
132
                            stats['total'] = int(
133
                                fs_stat[fs]['size']) * int(fs_stat[fs]['alloc_unit'])
134
                            stats['used'] = int(
135
                                fs_stat[fs]['used']) * int(fs_stat[fs]['alloc_unit'])
136 137 138
                            stats['percent'] = float(
                                stats['used'] * 100 / stats['total'])
                            stats['free'] = stats['total'] - stats['used']
139
                            break
140
            else:
141
                stats = self.get_stats_snmp(snmp_oid=snmp_oid['default'])
142

143
                if stats['total'] == '':
144
                    self.reset()
145
                    return stats
146

147 148 149
                for key in iterkeys(stats):
                    if stats[key] != '':
                        stats[key] = float(stats[key]) * 1024
150 151

                # used=total-free
152
                stats['used'] = stats['total'] - stats['free']
153

154 155
                # percent: the percentage usage calculated as (total -
                # available) / total * 100.
156 157 158 159 160
                stats['percent'] = float(
                    (stats['total'] - stats['free']) / stats['total'] * 100)

        # Update the stats
        self.stats = stats
161

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 172
        if 'used' in self.stats and 'total' in self.stats and 'percent' in self.stats:
            self.views['percent']['decoration'] = self.get_alert_log(self.stats['used'], maximum=self.stats['total'])
173

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

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

N
nicolargo 已提交
183 184
        # First line
        # total%
185
        msg = '{}'.format('SWAP')
A
Alessio Sergi 已提交
186
        ret.append(self.curse_add_line(msg, "TITLE"))
187
        msg = ' {:3}'.format(self.trend_msg(self.get_trend('percent')))
188
        ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
189
        # Percent memory usage
190
        msg = '{:>6.1%}'.format(self.stats['percent'] / 100)
191 192
        ret.append(self.curse_add_line(
            msg, self.get_views(key='percent', option='decoration')))
N
nicolargo 已提交
193 194 195

        # Second line
        # total
A
Alessio Sergi 已提交
196 197
        ret.append(self.curse_new_line())
        # Total memory usage
N
nicolargo 已提交
198 199 200 201
        ret.extend(self.curse_add_stat('total', width=16))

        # Third line
        # used
A
Alessio Sergi 已提交
202 203
        ret.append(self.curse_new_line())
        # Used memory usage
N
nicolargo 已提交
204 205 206 207
        ret.extend(self.curse_add_stat('used', width=16))

        # Fourth line
        # free
A
Alessio Sergi 已提交
208 209
        ret.append(self.curse_new_line())
        # Free memory usage
N
nicolargo 已提交
210
        ret.extend(self.curse_add_stat('free', width=16))
A
Alessio Sergi 已提交
211 212

        return ret