glances_network.py 11.5 KB
Newer Older
A
Alessio Sergi 已提交
1 2
# -*- coding: utf-8 -*-
#
3
# This file is part of Glances.
A
Alessio Sergi 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#
# Copyright (C) 2014 Nicolargo <nicolas@nicolargo.com>
#
# 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 23
import base64

A
Alessio Sergi 已提交
24
from glances.core.glances_timer import getTimeSinceLastUpdate
A
Alessio Sergi 已提交
25
from glances.plugins.glances_plugin import GlancesPlugin
A
Alessio Sergi 已提交
26

A
Alessio Sergi 已提交
27 28
import psutil

29
# SNMP OID
30
# http://www.net-snmp.org/docs/mibs/interfaces.html
31
# Dict key = interface_name
N
Nicolargo 已提交
32 33 34
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'}}
35

A
Alessio Sergi 已提交
36 37

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

    """Glances' network Plugin.
A
Alessio Sergi 已提交
40 41 42 43

    stats is a list
    """

44
    def __init__(self, args=None):
A
PEP 257  
Alessio Sergi 已提交
45
        """Init the plugin."""
46
        GlancesPlugin.__init__(self, args=args)
A
Alessio Sergi 已提交
47 48 49 50

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

51 52
        # Init the stats
        self.reset()
N
Nicolas Hennion 已提交
53

54
    def reset(self):
A
PEP 257  
Alessio Sergi 已提交
55
        """Reset/init the stats."""
56 57
        self.stats = []

58
    def update(self):
A
PEP 257  
Alessio Sergi 已提交
59 60
        """Update network stats using the input method.

N
Nicolas Hennion 已提交
61
        Stats is a list of dict (one dict per interface)
A
Alessio Sergi 已提交
62
        """
63 64
        # Reset stats
        self.reset()
A
Alessio Sergi 已提交
65

66
        if self.get_input() == 'local':
67 68 69
            # Update stats using the standard system lib

            # Grab network interface stat using the PsUtil net_io_counter method
A
Alessio Sergi 已提交
70
            try:
71 72 73
                netiocounters = psutil.net_io_counters(pernic=True)
            except UnicodeDecodeError:
                return self.stats
A
Alessio Sergi 已提交
74

75
            # Previous network interface stats are stored in the network_old variable
76
            if not hasattr(self, 'network_old'):
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
                # 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:
                        # Try necessary to manage dynamic network interface
N
Nicolargo 已提交
93
                        netstat = {} 
94
                        netstat['interface_name'] = net
95
                        netstat['time_since_update'] = time_since_update
96 97 98 99 100 101 102 103 104 105 106 107 108
                        netstat['cumulative_rx'] = network_new[net].bytes_recv
                        netstat['rx'] = (network_new[net].bytes_recv -
                                         self.network_old[net].bytes_recv)
                        netstat['cumulative_tx'] = network_new[net].bytes_sent
                        netstat['tx'] = (network_new[net].bytes_sent -
                                         self.network_old[net].bytes_sent)
                        netstat['cumulative_cx'] = (netstat['cumulative_rx'] +
                                                    netstat['cumulative_tx'])
                        netstat['cx'] = netstat['rx'] + netstat['tx']
                    except KeyError:
                        continue
                    else:
                        self.stats.append(netstat)
A
Alessio Sergi 已提交
109

110
                # Save stats to compute next bitrate
111
                self.network_old = network_new
N
Nicolargo 已提交
112

113
        elif self.get_input() == 'snmp':
114
            # Update stats using SNMP
115

116
            # SNMP bulk command to get all network interface in one shot
N
Nicolargo 已提交
117 118 119 120 121 122
            try:
                netiocounters = self.set_stats_snmp(snmp_oid=snmp_oid[self.get_short_system_name()], 
                                                    bulk=True)
            except KeyError:
                netiocounters = self.set_stats_snmp(snmp_oid=snmp_oid['default'], 
                                                    bulk=True)
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141

            # 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:
                        # Try necessary to manage dynamic network interface
                        netstat = {}
N
Nicolargo 已提交
142 143 144 145 146 147 148 149 150
                        # 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
                        if self.get_short_system_name() == 'windows':
                            try:
                                netstat['interface_name'] = str(base64.b16decode(net[2:-2].upper()))
                            except TypeError:
                                netstat['interface_name'] = net
                        else:
                            netstat['interface_name'] = net
151 152 153 154 155 156 157 158 159 160 161 162 163 164
                        netstat['time_since_update'] = time_since_update
                        netstat['cumulative_rx'] = float(network_new[net]['cumulative_rx'])
                        netstat['rx'] = (float(network_new[net]['cumulative_rx']) -
                                         float(self.network_old[net]['cumulative_rx']))
                        netstat['cumulative_tx'] = float(network_new[net]['cumulative_tx'])
                        netstat['tx'] = (float(network_new[net]['cumulative_tx']) -
                                         float(self.network_old[net]['cumulative_tx']))
                        netstat['cumulative_cx'] = (netstat['cumulative_rx'] +
                                                    netstat['cumulative_tx'])
                        netstat['cx'] = netstat['rx'] + netstat['tx']
                    except KeyError:
                        continue
                    else:
                        self.stats.append(netstat)
A
Alessio Sergi 已提交
165

166 167
                # Save stats to compute next bitrate
                self.network_old = network_new
168

N
Nicolas Hennion 已提交
169 170
        return self.stats

N
Nicolargo 已提交
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."""
N
Nicolas Hennion 已提交
173

A
Alessio Sergi 已提交
174 175 176
        # Init the return message
        ret = []

177
        # Only process if stats exist and display plugin enable...
178
        if self.stats == [] or args.disable_network:
179 180
            return ret

N
Nicolargo 已提交
181 182 183 184 185 186 187
        # 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 已提交
188 189
        # Build the string message
        # Header
N
Nicolargo 已提交
190
        msg = '{0:{width}}'.format(_("NETWORK"), width=ifname_max_width)
A
Alessio Sergi 已提交
191
        ret.append(self.curse_add_line(msg, "TITLE"))
192
        if args.network_cumul:
N
Nicolas Hennion 已提交
193
            # Cumulative stats
194
            if args.network_sum:
195
                # Sum stats
A
Alessio Sergi 已提交
196
                msg = '{0:>14}'.format(_("Rx+Tx"))
197 198 199
                ret.append(self.curse_add_line(msg))
            else:
                # Rx/Tx stats
A
Alessio Sergi 已提交
200
                msg = '{0:>7}'.format(_("Rx"))
201
                ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
202
                msg = '{0:>7}'.format(_("Tx"))
203
                ret.append(self.curse_add_line(msg))
N
Nicolas Hennion 已提交
204 205
        else:
            # Bitrate stats
206
            if args.network_sum:
207
                # Sum stats
A
Alessio Sergi 已提交
208
                msg = '{0:>14}'.format(_("Rx+Tx/s"))
209 210
                ret.append(self.curse_add_line(msg))
            else:
A
Alessio Sergi 已提交
211
                msg = '{0:>7}'.format(_("Rx/s"))
212
                ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
213
                msg = '{0:>7}'.format(_("Tx/s"))
A
Alessio Sergi 已提交
214
                ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
215 216
        # Interface list (sorted by name)
        for i in sorted(self.stats, key=lambda network: network['interface_name']):
217
            # Do not display hidden interfaces
218
            if self.is_hide(i['interface_name']):
219
                continue
A
Alessio Sergi 已提交
220
            # Format stats
221 222 223 224
            # Is there an alias for the interface name ?
            ifname = self.has_alias(i['interface_name'])
            if ifname is None:
                ifname = i['interface_name'].split(':')[0]
N
Nicolargo 已提交
225
            if len(ifname) > ifname_max_width:
226
                # Cut interface name if it is too long
N
Nicolargo 已提交
227
                ifname = '_' + ifname[-ifname_max_width+1:]
228
            if args.byte:
229
                # Bytes per second (for dummy)
230
                if args.network_cumul:
231 232
                    rx = self.auto_unit(int(i['cumulative_rx']))
                    tx = self.auto_unit(int(i['cumulative_tx']))
A
Alessio Sergi 已提交
233
                    sx = self.auto_unit(int(i['cumulative_tx'])
234
                                        + int(i['cumulative_tx']))
N
Nicolas Hennion 已提交
235
                else:
236 237
                    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 已提交
238
                    sx = self.auto_unit(int(i['rx'] // i['time_since_update'])
239
                                        + int(i['tx'] // i['time_since_update']))
A
Alessio Sergi 已提交
240
            else:
A
Alessio Sergi 已提交
241
                # Bits per second (for real network administrator | Default)
242
                if args.network_cumul:
243 244
                    rx = self.auto_unit(int(i['cumulative_rx'] * 8)) + "b"
                    tx = self.auto_unit(int(i['cumulative_tx'] * 8)) + "b"
A
Alessio Sergi 已提交
245
                    sx = self.auto_unit(int(i['cumulative_rx'] * 8)
246
                                        + int(i['cumulative_tx'] * 8)) + "b"
N
Nicolas Hennion 已提交
247
                else:
248
                    rx = self.auto_unit(int(i['rx'] // i['time_since_update'] * 8)) + "b"
A
Alessio Sergi 已提交
249
                    tx = self.auto_unit(int(i['tx'] // i['time_since_update'] * 8)) + "b"
250 251
                    sx = self.auto_unit(int(i['rx'] // i['time_since_update'] * 8) +
                                        int(i['tx'] // i['time_since_update'] * 8)) + "b"
A
Alessio Sergi 已提交
252 253
            # New line
            ret.append(self.curse_new_line())
N
Nicolargo 已提交
254
            msg = '{0:{width}}'.format(ifname, width=ifname_max_width)
A
Alessio Sergi 已提交
255
            ret.append(self.curse_add_line(msg))
256
            if args.network_sum:
A
Alessio Sergi 已提交
257
                msg = '{0:>14}'.format(sx)
258 259
                ret.append(self.curse_add_line(msg))
            else:
A
Alessio Sergi 已提交
260
                msg = '{0:>7}'.format(rx)
A
Alessio Sergi 已提交
261 262 263
                ret.append(self.curse_add_line(
                    msg, self.get_alert(int(i['rx'] // i['time_since_update'] * 8),
                                        header=ifname + '_rx')))
A
Alessio Sergi 已提交
264
                msg = '{0:>7}'.format(tx)
A
Alessio Sergi 已提交
265 266 267
                ret.append(self.curse_add_line(
                    msg, self.get_alert(int(i['tx'] // i['time_since_update'] * 8),
                                        header=ifname + '_tx')))
A
Alessio Sergi 已提交
268 269

        return ret