glances_network.py 12.8 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

"""Network plugin."""
A
Alessio Sergi 已提交
21

N
Nicolargo 已提交
22
import base64
23
import operator
N
Nicolargo 已提交
24

25 26
import psutil

A
Alessio Sergi 已提交
27
from glances.core.glances_timer import getTimeSinceLastUpdate
A
Alessio Sergi 已提交
28
from glances.plugins.glances_plugin import GlancesPlugin
A
Alessio Sergi 已提交
29

30
# SNMP OID
31
# http://www.net-snmp.org/docs/mibs/interfaces.html
32
# Dict key = interface_name
N
Nicolargo 已提交
33 34 35
snmp_oid = {'default': {'interface_name': '1.3.6.1.2.1.2.2.1.2',
                        'cumulative_rx': '1.3.6.1.2.1.2.2.1.10',
                        'cumulative_tx': '1.3.6.1.2.1.2.2.1.16'}}
36

37 38 39
# 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 已提交
40 41
items_history_list = [{'name': 'rx', 'color': '#00FF00', 'y_unit': 'bit/s'},
                      {'name': 'tx', 'color': '#FF0000', 'y_unit': 'bit/s'}]
42

A
Alessio Sergi 已提交
43 44

class Plugin(GlancesPlugin):
A
PEP 257  
Alessio Sergi 已提交
45

A
PEP 257  
Alessio Sergi 已提交
46
    """Glances network plugin.
A
Alessio Sergi 已提交
47 48 49 50

    stats is a list
    """

51
    def __init__(self, args=None):
A
PEP 257  
Alessio Sergi 已提交
52
        """Init the plugin."""
53
        GlancesPlugin.__init__(self, args=args, items_history_list=items_history_list)
A
Alessio Sergi 已提交
54 55 56 57

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

58 59
        # Init the stats
        self.reset()
N
Nicolas Hennion 已提交
60

61
    def get_key(self):
A
PEP 257  
Alessio Sergi 已提交
62
        """Return the key of the list."""
63 64
        return 'interface_name'

65
    def reset(self):
A
PEP 257  
Alessio Sergi 已提交
66
        """Reset/init the stats."""
67 68
        self.stats = []

69
    @GlancesPlugin._log_result_decorator
70
    def update(self):
A
PEP 257  
Alessio Sergi 已提交
71 72
        """Update network stats using the input method.

N
Nicolas Hennion 已提交
73
        Stats is a list of dict (one dict per interface)
A
Alessio Sergi 已提交
74
        """
75 76
        # Reset stats
        self.reset()
A
Alessio Sergi 已提交
77

78
        if self.input_method == 'local':
79 80 81
            # Update stats using the standard system lib

            # Grab network interface stat using the PsUtil net_io_counter method
A
Alessio Sergi 已提交
82
            try:
83 84 85
                netiocounters = psutil.net_io_counters(pernic=True)
            except UnicodeDecodeError:
                return self.stats
A
Alessio Sergi 已提交
86

87
            # Previous network interface stats are stored in the network_old variable
88
            if not hasattr(self, 'network_old'):
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
                # First call, we init the network_old var
                try:
                    self.network_old = netiocounters
                except (IOError, UnboundLocalError):
                    pass
            else:
                # By storing time data we enable Rx/s and Tx/s calculations in the
                # XML/RPC API, which would otherwise be overly difficult work
                # for users of the API
                time_since_update = getTimeSinceLastUpdate('net')

                # Loop over interfaces
                network_new = netiocounters
                for net in network_new:
                    try:
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
                        cumulative_rx = network_new[net].bytes_recv
                        cumulative_tx = network_new[net].bytes_sent
                        cumulative_cx = cumulative_rx + cumulative_tx
                        rx = cumulative_rx - self.network_old[net].bytes_recv
                        tx = cumulative_tx - self.network_old[net].bytes_sent
                        cx = rx + tx
                        netstat = {
                            'interface_name': net,
                            'time_since_update': time_since_update,
                            'cumulative_rx': cumulative_rx,
                            'rx': rx,
                            'cumulative_tx': cumulative_tx,
                            'tx': tx,
                            'cumulative_cx': cumulative_cx,
                            'cx': cx}
119 120 121
                    except KeyError:
                        continue
                    else:
122
                        netstat['key'] = self.get_key()
123
                        self.stats.append(netstat)
A
Alessio Sergi 已提交
124

125
                # Save stats to compute next bitrate
126
                self.network_old = network_new
N
Nicolargo 已提交
127

128
        elif self.input_method == 'snmp':
129
            # Update stats using SNMP
130

131
            # SNMP bulk command to get all network interface in one shot
N
Nicolargo 已提交
132
            try:
133
                netiocounters = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
N
Nicolargo 已提交
134 135
                                                    bulk=True)
            except KeyError:
136
                netiocounters = self.get_stats_snmp(snmp_oid=snmp_oid['default'],
N
Nicolargo 已提交
137
                                                    bulk=True)
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154

            # Previous network interface stats are stored in the network_old variable
            if not hasattr(self, 'network_old'):
                # First call, we init the network_old var
                try:
                    self.network_old = netiocounters
                except (IOError, UnboundLocalError):
                    pass
            else:
                # See description in the 'local' block
                time_since_update = getTimeSinceLastUpdate('net')

                # Loop over interfaces
                network_new = netiocounters

                for net in network_new:
                    try:
N
Nicolargo 已提交
155 156
                        # Windows: a tips is needed to convert HEX to TXT
                        # http://blogs.technet.com/b/networking/archive/2009/12/18/how-to-query-the-list-of-network-interfaces-using-snmp-via-the-ifdescr-counter.aspx
157
                        if self.short_system_name == 'windows':
N
Nicolargo 已提交
158
                            try:
159
                                interface_name = str(base64.b16decode(net[2:-2].upper()))
N
Nicolargo 已提交
160
                            except TypeError:
161
                                interface_name = net
N
Nicolargo 已提交
162
                        else:
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
                            interface_name = net
                        cumulative_rx = float(network_new[net]['cumulative_rx'])
                        cumulative_tx = float(network_new[net]['cumulative_tx'])
                        cumulative_cx = cumulative_rx + cumulative_tx
                        rx = cumulative_rx - float(self.network_old[net]['cumulative_rx'])
                        tx = cumulative_tx - float(self.network_old[net]['cumulative_tx'])
                        cx = rx + tx
                        netstat = {
                            'interface_name': interface_name,
                            'time_since_update': time_since_update,
                            'cumulative_rx': cumulative_rx,
                            'rx': rx,
                            'cumulative_tx': cumulative_tx,
                            'tx': tx,
                            'cumulative_cx': cumulative_cx,
                            'cx': cx}
179 180 181
                    except KeyError:
                        continue
                    else:
182
                        netstat['key'] = self.get_key()
183
                        self.stats.append(netstat)
A
Alessio Sergi 已提交
184

185 186
                # Save stats to compute next bitrate
                self.network_old = network_new
187

188
        # Update the history list
189
        self.update_stats_history(self.get_key())
190

191 192 193
        # Update the view
        self.update_views()

N
Nicolas Hennion 已提交
194 195
        return self.stats

196
    def update_views(self):
A
PEP 257  
Alessio Sergi 已提交
197
        """Update stats views."""
198 199 200 201 202
        # Call the father's method
        GlancesPlugin.update_views(self)

        # Add specifics informations
        # Alert
203
        for i in self.stats:
204 205 206 207 208 209
            ifrealname = i['interface_name'].split(':')[0]
            self.views[i[self.get_key()]]['rx']['decoration'] = self.get_alert(int(i['rx'] // i['time_since_update'] * 8),
                                                                               header=ifrealname + '_rx')
            self.views[i[self.get_key()]]['tx']['decoration'] = self.get_alert(int(i['tx'] // i['time_since_update'] * 8),
                                                                               header=ifrealname + '_tx')

N
Nicolargo 已提交
210
    def msg_curse(self, args=None, max_width=None):
A
PEP 257  
Alessio Sergi 已提交
211
        """Return the dict to display in the curse interface."""
A
Alessio Sergi 已提交
212 213 214
        # Init the return message
        ret = []

215
        # Only process if stats exist and display plugin enable...
D
desbma 已提交
216
        if not self.stats or args.disable_network:
217 218
            return ret

N
Nicolargo 已提交
219 220 221 222 223 224 225
        # Max size for the interface name
        if max_width is not None and max_width >= 23:
            # Interface size name = max_width - space for interfaces bitrate
            ifname_max_width = max_width - 14
        else:
            ifname_max_width = 9

A
Alessio Sergi 已提交
226 227
        # Build the string message
        # Header
A
Alessio Sergi 已提交
228
        msg = '{0:{width}}'.format('NETWORK', width=ifname_max_width)
A
Alessio Sergi 已提交
229
        ret.append(self.curse_add_line(msg, "TITLE"))
230
        if args.network_cumul:
N
Nicolas Hennion 已提交
231
            # Cumulative stats
232
            if args.network_sum:
233
                # Sum stats
A
Alessio Sergi 已提交
234
                msg = '{0:>14}'.format('Rx+Tx')
235 236 237
                ret.append(self.curse_add_line(msg))
            else:
                # Rx/Tx stats
A
Alessio Sergi 已提交
238
                msg = '{0:>7}'.format('Rx')
239
                ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
240
                msg = '{0:>7}'.format('Tx')
241
                ret.append(self.curse_add_line(msg))
N
Nicolas Hennion 已提交
242 243
        else:
            # Bitrate stats
244
            if args.network_sum:
245
                # Sum stats
A
Alessio Sergi 已提交
246
                msg = '{0:>14}'.format('Rx+Tx/s')
247 248
                ret.append(self.curse_add_line(msg))
            else:
A
Alessio Sergi 已提交
249
                msg = '{0:>7}'.format('Rx/s')
250
                ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
251
                msg = '{0:>7}'.format('Tx/s')
A
Alessio Sergi 已提交
252
                ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
253
        # Interface list (sorted by name)
254
        for i in sorted(self.stats, key=operator.itemgetter(self.get_key())):
255
            # Do not display hidden interfaces
256
            if self.is_hide(i['interface_name']):
257
                continue
A
Alessio Sergi 已提交
258
            # Format stats
259
            # Is there an alias for the interface name ?
N
Nicolargo 已提交
260
            ifrealname = i['interface_name'].split(':')[0]
261 262
            ifname = self.has_alias(i['interface_name'])
            if ifname is None:
N
Nicolargo 已提交
263
                ifname = ifrealname
N
Nicolargo 已提交
264
            if len(ifname) > ifname_max_width:
265
                # Cut interface name if it is too long
A
Alessio Sergi 已提交
266
                ifname = '_' + ifname[-ifname_max_width + 1:]
267
            if args.byte:
268
                # Bytes per second (for dummy)
269
                if args.network_cumul:
270 271
                    rx = self.auto_unit(int(i['cumulative_rx']))
                    tx = self.auto_unit(int(i['cumulative_tx']))
A
Alessio Sergi 已提交
272
                    sx = self.auto_unit(int(i['cumulative_tx'])
273
                                        + int(i['cumulative_tx']))
N
Nicolas Hennion 已提交
274
                else:
275 276
                    rx = self.auto_unit(int(i['rx'] // i['time_since_update']))
                    tx = self.auto_unit(int(i['tx'] // i['time_since_update']))
A
Alessio Sergi 已提交
277
                    sx = self.auto_unit(int(i['rx'] // i['time_since_update'])
278
                                        + int(i['tx'] // i['time_since_update']))
A
Alessio Sergi 已提交
279
            else:
A
Alessio Sergi 已提交
280
                # Bits per second (for real network administrator | Default)
281
                if args.network_cumul:
282 283
                    rx = self.auto_unit(int(i['cumulative_rx'] * 8)) + "b"
                    tx = self.auto_unit(int(i['cumulative_tx'] * 8)) + "b"
A
Alessio Sergi 已提交
284
                    sx = self.auto_unit(int(i['cumulative_rx'] * 8)
285
                                        + int(i['cumulative_tx'] * 8)) + "b"
N
Nicolas Hennion 已提交
286
                else:
287
                    rx = self.auto_unit(int(i['rx'] // i['time_since_update'] * 8)) + "b"
A
Alessio Sergi 已提交
288
                    tx = self.auto_unit(int(i['tx'] // i['time_since_update'] * 8)) + "b"
289 290
                    sx = self.auto_unit(int(i['rx'] // i['time_since_update'] * 8) +
                                        int(i['tx'] // i['time_since_update'] * 8)) + "b"
A
Alessio Sergi 已提交
291 292
            # New line
            ret.append(self.curse_new_line())
N
Nicolargo 已提交
293
            msg = '{0:{width}}'.format(ifname, width=ifname_max_width)
A
Alessio Sergi 已提交
294
            ret.append(self.curse_add_line(msg))
295
            if args.network_sum:
A
Alessio Sergi 已提交
296
                msg = '{0:>14}'.format(sx)
297 298
                ret.append(self.curse_add_line(msg))
            else:
A
Alessio Sergi 已提交
299
                msg = '{0:>7}'.format(rx)
A
Alessio Sergi 已提交
300
                ret.append(self.curse_add_line(
301
                    msg, self.get_views(item=i[self.get_key()], key='rx', option='decoration')))
A
Alessio Sergi 已提交
302
                msg = '{0:>7}'.format(tx)
A
Alessio Sergi 已提交
303
                ret.append(self.curse_add_line(
304
                    msg, self.get_views(item=i[self.get_key()], key='tx', option='decoration')))
A
Alessio Sergi 已提交
305 306

        return ret