glances_network.py 10.2 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/>.
N
Nicolas Hennion 已提交
19 20 21
"""
Glances Network interface plugin
"""
A
Alessio Sergi 已提交
22 23

# Import system libs
A
Alessio Sergi 已提交
24
import psutil
A
Alessio Sergi 已提交
25 26 27

# Import Glances lib
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 33 34 35 36
ifNumber_oid = { 'ifNumber': '1.3.6.1.2.1.2.1.0' }
ifIndex_oid = { 'ifIndex': '1.3.6.1.2.1.2.2.1.1.' }
snmp_oid = { '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.' }
37

A
Alessio Sergi 已提交
38 39 40 41 42 43 44 45

class Plugin(GlancesPlugin):
    """
    Glances's network Plugin

    stats is a list
    """

46 47
    def __init__(self, args=None):
        GlancesPlugin.__init__(self, args=args)
A
Alessio Sergi 已提交
48 49 50 51 52 53 54 55 56 57

        # We want to display the stat in the curse interface
        self.display_curse = True
        # Set the message position
        # It is NOT the curse position but the Glances column/line
        # Enter -1 to right align
        self.column_curse = 0
        # Enter -1 to diplay bottom
        self.line_curse = 2

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

61
    def reset(self):
A
Alessio Sergi 已提交
62
        """
63 64 65 66 67 68 69 70
        Reset/init the stats
        """
        self.stats = []

    def update(self, input='local'):
        """
        Update network stats using the input method
        Input method could be: local (mandatory) or snmp (optionnal)
N
Nicolas Hennion 已提交
71
        Stats is a list of dict (one dict per interface)
A
Alessio Sergi 已提交
72 73
        """

74 75
        # Reset stats
        self.reset()
A
Alessio Sergi 已提交
76

77 78 79 80
        if input == 'local':
            # Update stats using the standard system lib

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

86
            # Previous network interface stats are stored in the network_old variable
87
            if not hasattr(self, 'network_old'):
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
                # 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
                        netstat = {}
                        netstat['interface_name'] = net
106
                        netstat['time_since_update'] = time_since_update
107 108 109 110 111 112 113 114 115 116 117 118 119
                        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)
120 121
                
                # Save stats to compute next bitrate
122
                self.network_old = network_new
N
Nicolargo 已提交
123

124 125
        elif input == 'snmp':
            # Update stats using SNMP
126
            # !!! High CPU consumption on the client side
127
            # !!! To be optimized with getbulk
128

129
            time_since_update = getTimeSinceLastUpdate('net')
130 131 132

            # Get number of network interfaces
            try:
133 134
                # IfNumber is available directly
                ifNumber = int(self.set_stats_snmp(snmp_oid=ifNumber_oid)['ifNumber']) + 1
135
            except:
136 137 138 139 140
                # Or not...
                # Walk through ifIndex
                ifNumber = 1
                while self.set_stats_snmp(snmp_oid={'ifIndex': ifIndex_oid['ifIndex'] + str(ifNumber)})['ifIndex'] != '':
                    ifNumber += 1
141 142 143 144 145

            # Loop over network interfaces
            network_new = {}
            ifIndex = 1
            ifCpt = 1
N
Nicolargo 已提交
146
            while (ifCpt < ifNumber) and (ifIndex < 1024):
147
                # Add interface index to netif OID
148
                net_oid = dict((k, v + str(ifIndex)) for (k, v) in snmp_oid.items())
N
Nicolargo 已提交
149 150
                net_stat = self.set_stats_snmp(snmp_oid=net_oid)
                if str(net_stat['interface_name']) == '':
151 152 153 154
                    ifIndex += 1
                    continue 
                else:
                    ifCpt += 1
N
Nicolargo 已提交
155
                network_new[ifIndex] = net_stat
156
                if hasattr(self, 'network_old'):
N
Nicolargo 已提交
157 158
                    net_stat['time_since_update'] = time_since_update
                    net_stat['rx'] = (float(network_new[ifIndex]['cumulative_rx']) -
159
                                     float(self.network_old[ifIndex]['cumulative_rx']))
N
Nicolargo 已提交
160
                    net_stat['tx'] = (float(network_new[ifIndex]['cumulative_tx']) -
161
                                     float(self.network_old[ifIndex]['cumulative_tx']))
N
Nicolargo 已提交
162 163 164 165
                    net_stat['cumulative_cx'] = (float(net_stat['cumulative_rx']) +
                                                float(net_stat['cumulative_tx']))
                    net_stat['cx'] = net_stat['rx'] + net_stat['tx']  
                    self.stats.append(net_stat)
166 167 168 169
                ifIndex += 1
            
            # Save stats to compute next bitrate
            self.network_old = network_new
170

N
Nicolas Hennion 已提交
171 172
        return self.stats

A
Alessio Sergi 已提交
173 174 175 176
    def msg_curse(self, args=None):
        """
        Return the dict to display in the curse interface
        """
N
Nicolas Hennion 已提交
177 178 179

        #!!! TODO: Add alert on network interface bitrate

A
Alessio Sergi 已提交
180 181 182
        # Init the return message
        ret = []

183
        # Only process if stats exist and display plugin enable...
184
        if self.stats == [] or args.disable_network:
185 186
            return ret

A
Alessio Sergi 已提交
187 188
        # Build the string message
        # Header
189
        msg = "{0:9}".format(_("NETWORK"))
A
Alessio Sergi 已提交
190
        ret.append(self.curse_add_line(msg, "TITLE"))
191
        if args.network_cumul:
N
Nicolas Hennion 已提交
192
            # Cumulative stats
193
            if args.network_sum:
194
                # Sum stats
195
                msg = "{0:>14}".format(_("Rx+Tx"))
196 197 198
                ret.append(self.curse_add_line(msg))
            else:
                # Rx/Tx stats
199
                msg = "{0:>7}".format(_("Rx"))
200
                ret.append(self.curse_add_line(msg))
201
                msg = "{0:>7}".format(_("Tx"))
202
                ret.append(self.curse_add_line(msg))
N
Nicolas Hennion 已提交
203 204
        else:
            # Bitrate stats
205
            if args.network_sum:
206
                # Sum stats
207
                msg = "{0:>14}".format(_("Rx+Tx/s"))
208 209
                ret.append(self.curse_add_line(msg))
            else:
210
                msg = "{0:>7}".format(_("Rx/s"))
211
                ret.append(self.curse_add_line(msg))
212
                msg = "{0:>7}".format(_("Tx/s"))
A
Alessio Sergi 已提交
213
                ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
214 215
        # Interface list (sorted by name)
        for i in sorted(self.stats, key=lambda network: network['interface_name']):
216
            # Do not display hidden interfaces
217
            if self.is_hide(i['interface_name']):
218
                continue
A
Alessio Sergi 已提交
219 220
            # Format stats
            ifname = i['interface_name'].split(':')[0]
221 222
            if args.byte:
                if args.network_cumul:
223 224
                    rx = self.auto_unit(int(i['cumulative_rx']))
                    tx = self.auto_unit(int(i['cumulative_tx']))
A
Alessio Sergi 已提交
225
                    sx = self.auto_unit(int(i['cumulative_tx'])
226
                                        + int(i['cumulative_tx']))
N
Nicolas Hennion 已提交
227
                else:
228 229
                    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 已提交
230
                    sx = self.auto_unit(int(i['rx'] // i['time_since_update'])
231
                                        + int(i['tx'] // i['time_since_update']))
A
Alessio Sergi 已提交
232
            else:
233
                if args.network_cumul:
234 235
                    rx = self.auto_unit(int(i['cumulative_rx'] * 8)) + "b"
                    tx = self.auto_unit(int(i['cumulative_tx'] * 8)) + "b"
A
Alessio Sergi 已提交
236
                    sx = self.auto_unit(int(i['cumulative_rx'] * 8)
237
                                        + int(i['cumulative_tx'] * 8)) + "b"
N
Nicolas Hennion 已提交
238
                else:
239
                    rx = self.auto_unit(int(i['rx'] // i['time_since_update'] * 8)) + "b"
A
Alessio Sergi 已提交
240
                    tx = self.auto_unit(int(i['tx'] // i['time_since_update'] * 8)) + "b"
241 242
                    sx = self.auto_unit(int(i['rx'] // i['time_since_update'] * 8) +
                                        int(i['tx'] // i['time_since_update'] * 8)) + "b"
A
Alessio Sergi 已提交
243 244
            # New line
            ret.append(self.curse_new_line())
245
            msg = "{0:9}".format(ifname)
A
Alessio Sergi 已提交
246
            ret.append(self.curse_add_line(msg))
247
            if args.network_sum:
248
                msg = "{0:>14}".format(sx)
249 250
                ret.append(self.curse_add_line(msg))
            else:
251
                msg = "{0:>7}".format(rx)
252
                ret.append(self.curse_add_line(msg))
253
                msg = "{0:>7}".format(tx)
254
                ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
255 256

        return ret