Completed
Push — master ( 3dcd25...20576f )
by Nicolas
01:26
created

glances/plugins/glances_network.py (2 issues)

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 2015 Nicolargo <[email protected]>
6
#
7
# Glances is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU Lesser General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Glances is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Lesser General Public License for more details.
16
#
17
# You should have received a copy of the GNU Lesser General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20
"""Network plugin."""
21
22
import base64
23
import operator
24
25
from glances.timer import getTimeSinceLastUpdate
26
from glances.plugins.glances_plugin import GlancesPlugin
27
28
import psutil
29
30
# SNMP OID
31
# http://www.net-snmp.org/docs/mibs/interfaces.html
32
# Dict key = interface_name
33
snmp_oid = {'default': {'interface_name': '1.3.6.1.2.1.2.2.1.2',
34
                        'cumulative_rx': '1.3.6.1.2.1.2.2.1.10',
35
                        'cumulative_tx': '1.3.6.1.2.1.2.2.1.16'}}
36
37
# Define the history items list
38
# All items in this list will be historised if the --enable-history tag is set
39
# 'color' define the graph color in #RGB format
40
items_history_list = [{'name': 'rx', 'color': '#00FF00', 'y_unit': 'bit/s'},
41
                      {'name': 'tx', 'color': '#FF0000', 'y_unit': 'bit/s'}]
42
43
44
class Plugin(GlancesPlugin):
45
46
    """Glances network plugin.
47
48
    stats is a list
49
    """
50
51
    def __init__(self, args=None):
52
        """Init the plugin."""
53
        super(Plugin, self).__init__(args=args, items_history_list=items_history_list)
54
55
        # We want to display the stat in the curse interface
56
        self.display_curse = True
57
58
        # Init the stats
59
        self.reset()
60
61
    def get_key(self):
62
        """Return the key of the list."""
63
        return 'interface_name'
64
65
    def reset(self):
66
        """Reset/init the stats."""
67
        self.stats = []
68
69
    @GlancesPlugin._log_result_decorator
70
    def update(self):
71
        """Update network stats using the input method.
72
73
        Stats is a list of dict (one dict per interface)
74
        """
75
        # Reset stats
76
        self.reset()
77
78
        if self.input_method == 'local':
79
            # Update stats using the standard system lib
80
81
            # Grab network interface stat using the PsUtil net_io_counter method
82
            try:
83
                netiocounters = psutil.net_io_counters(pernic=True)
84
            except UnicodeDecodeError:
85
                return self.stats
86
87
            # New in PsUtil 3.0: optionaly import the interface's status (issue #765)
88
            netstatus = {}
89
            try:
90
                netstatus = psutil.net_if_stats()
91
            except AttributeError:
92
                pass
93
94
            # Previous network interface stats are stored in the network_old variable
95
            if not hasattr(self, 'network_old'):
96
                # First call, we init the network_old var
97
                try:
98
                    self.network_old = netiocounters
99
                except (IOError, UnboundLocalError):
100
                    pass
101
            else:
102
                # By storing time data we enable Rx/s and Tx/s calculations in the
103
                # XML/RPC API, which would otherwise be overly difficult work
104
                # for users of the API
105
                time_since_update = getTimeSinceLastUpdate('net')
106
107
                # Loop over interfaces
108
                network_new = netiocounters
109
                for net in network_new:
110
                    # Do not take hidden interface into account
111
                    if self.is_hide(net):
112
                        continue
113
                    try:
114
                        cumulative_rx = network_new[net].bytes_recv
115
                        cumulative_tx = network_new[net].bytes_sent
116
                        cumulative_cx = cumulative_rx + cumulative_tx
117
                        rx = cumulative_rx - self.network_old[net].bytes_recv
118
                        tx = cumulative_tx - self.network_old[net].bytes_sent
119
                        cx = rx + tx
120
                        netstat = {
121
                            'interface_name': net,
122
                            'time_since_update': time_since_update,
123
                            'cumulative_rx': cumulative_rx,
124
                            'rx': rx,
125
                            'cumulative_tx': cumulative_tx,
126
                            'tx': tx,
127
                            'cumulative_cx': cumulative_cx,
128
                            'cx': cx}
129
                    except KeyError:
130
                        continue
131
                    else:
132
                        # Optional stats (only compliant with PsUtil 3.0+)
133
                        try:
134
                            netstat['is_up'] = netstatus[net].isup
135
                        except (KeyError, AttributeError):
136
                            pass
137
                        # Set the key
138
                        netstat['key'] = self.get_key()
139
                        self.stats.append(netstat)
140
141
                # Save stats to compute next bitrate
142
                self.network_old = network_new
143
144
        elif self.input_method == 'snmp':
145
            # Update stats using SNMP
146
147
            # SNMP bulk command to get all network interface in one shot
148
            try:
149
                netiocounters = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
150
                                                    bulk=True)
151
            except KeyError:
152
                netiocounters = self.get_stats_snmp(snmp_oid=snmp_oid['default'],
153
                                                    bulk=True)
154
155
            # Previous network interface stats are stored in the network_old variable
156
            if not hasattr(self, 'network_old'):
157
                # First call, we init the network_old var
158
                try:
159
                    self.network_old = netiocounters
160
                except (IOError, UnboundLocalError):
161
                    pass
162
            else:
163
                # See description in the 'local' block
164
                time_since_update = getTimeSinceLastUpdate('net')
165
166
                # Loop over interfaces
167
                network_new = netiocounters
168
169
                for net in network_new:
170
                    # Do not take hidden interface into account
171
                    if self.is_hide(net):
172
                        continue
173
174
                    try:
175
                        # Windows: a tips is needed to convert HEX to TXT
176
                        # 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
177
                        if self.short_system_name == 'windows':
178
                            try:
179
                                interface_name = str(base64.b16decode(net[2:-2].upper()))
180
                            except TypeError:
181
                                interface_name = net
182
                        else:
183
                            interface_name = net
184
185
                        cumulative_rx = float(network_new[net]['cumulative_rx'])
186
                        cumulative_tx = float(network_new[net]['cumulative_tx'])
187
                        cumulative_cx = cumulative_rx + cumulative_tx
188
                        rx = cumulative_rx - float(self.network_old[net]['cumulative_rx'])
189
                        tx = cumulative_tx - float(self.network_old[net]['cumulative_tx'])
190
                        cx = rx + tx
191
                        netstat = {
192
                            'interface_name': interface_name,
193
                            'time_since_update': time_since_update,
194
                            'cumulative_rx': cumulative_rx,
195
                            'rx': rx,
196
                            'cumulative_tx': cumulative_tx,
197
                            'tx': tx,
198
                            'cumulative_cx': cumulative_cx,
199
                            'cx': cx}
200
                    except KeyError:
201
                        continue
202
                    else:
203
                        netstat['key'] = self.get_key()
204
                        self.stats.append(netstat)
205
206
                # Save stats to compute next bitrate
207
                self.network_old = network_new
208
209
        # Update the history list
210
        self.update_stats_history(self.get_key())
211
212
        # Update the view
213
        self.update_views()
214
215
        return self.stats
216
217 View Code Duplication
    def update_views(self):
218
        """Update stats views."""
219
        # Call the father's method
220
        super(Plugin, self).update_views()
221
222
        # Add specifics informations
223
        # Alert
224
        for i in self.stats:
225
            ifrealname = i['interface_name'].split(':')[0]
226
            self.views[i[self.get_key()]]['rx']['decoration'] = self.get_alert(int(i['rx'] // i['time_since_update'] * 8),
227
                                                                               header=ifrealname + '_rx')
228
            self.views[i[self.get_key()]]['tx']['decoration'] = self.get_alert(int(i['tx'] // i['time_since_update'] * 8),
229
                                                                               header=ifrealname + '_tx')
230
231
    def msg_curse(self, args=None, max_width=None):
232
        """Return the dict to display in the curse interface."""
233
        # Init the return message
234
        ret = []
235
236
        # Only process if stats exist and display plugin enable...
237
        if not self.stats or args.disable_network:
238
            return ret
239
240
        # Max size for the interface name
241
        if max_width is not None and max_width >= 23:
242
            # Interface size name = max_width - space for interfaces bitrate
243
            ifname_max_width = max_width - 14
244
        else:
245
            ifname_max_width = 9
246
247
        # Build the string message
248
        # Header
249
        msg = '{0:{width}}'.format('NETWORK', width=ifname_max_width)
250
        ret.append(self.curse_add_line(msg, "TITLE"))
251
        if args.network_cumul:
252
            # Cumulative stats
253
            if args.network_sum:
254
                # Sum stats
255
                msg = '{0:>14}'.format('Rx+Tx')
256
                ret.append(self.curse_add_line(msg))
257
            else:
258
                # Rx/Tx stats
259
                msg = '{0:>7}'.format('Rx')
260
                ret.append(self.curse_add_line(msg))
261
                msg = '{0:>7}'.format('Tx')
262
                ret.append(self.curse_add_line(msg))
263
        else:
264
            # Bitrate stats
265
            if args.network_sum:
266
                # Sum stats
267
                msg = '{0:>14}'.format('Rx+Tx/s')
268
                ret.append(self.curse_add_line(msg))
269
            else:
270
                msg = '{0:>7}'.format('Rx/s')
271
                ret.append(self.curse_add_line(msg))
272
                msg = '{0:>7}'.format('Tx/s')
273
                ret.append(self.curse_add_line(msg))
274
        # Interface list (sorted by name)
275
        for i in sorted(self.stats, key=operator.itemgetter(self.get_key())):
276
            # Do not display interface in down state (issue #765)
277
            if ('is_up' in i) and (i['is_up'] is False):
278
                continue
279
            # Format stats
280
            # Is there an alias for the interface name ?
281
            ifrealname = i['interface_name'].split(':')[0]
282
            ifname = self.has_alias(i['interface_name'])
283
            if ifname is None:
284
                ifname = ifrealname
285
            if len(ifname) > ifname_max_width:
286
                # Cut interface name if it is too long
287
                ifname = '_' + ifname[-ifname_max_width + 1:]
288
            if args.byte:
289
                # Bytes per second (for dummy)
290 View Code Duplication
                if args.network_cumul:
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
291
                    rx = self.auto_unit(int(i['cumulative_rx']))
292
                    tx = self.auto_unit(int(i['cumulative_tx']))
293
                    sx = self.auto_unit(int(i['cumulative_tx']) +
294
                                        int(i['cumulative_tx']))
295
                else:
296
                    rx = self.auto_unit(int(i['rx'] // i['time_since_update']))
297
                    tx = self.auto_unit(int(i['tx'] // i['time_since_update']))
298
                    sx = self.auto_unit(int(i['rx'] // i['time_since_update']) +
299
                                        int(i['tx'] // i['time_since_update']))
300
            else:
301
                # Bits per second (for real network administrator | Default)
302 View Code Duplication
                if args.network_cumul:
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
303
                    rx = self.auto_unit(int(i['cumulative_rx'] * 8)) + "b"
304
                    tx = self.auto_unit(int(i['cumulative_tx'] * 8)) + "b"
305
                    sx = self.auto_unit(int(i['cumulative_rx'] * 8) +
306
                                        int(i['cumulative_tx'] * 8)) + "b"
307
                else:
308
                    rx = self.auto_unit(int(i['rx'] // i['time_since_update'] * 8)) + "b"
309
                    tx = self.auto_unit(int(i['tx'] // i['time_since_update'] * 8)) + "b"
310
                    sx = self.auto_unit(int(i['rx'] // i['time_since_update'] * 8) +
311
                                        int(i['tx'] // i['time_since_update'] * 8)) + "b"
312
            # New line
313
            ret.append(self.curse_new_line())
314
            msg = '{0:{width}}'.format(ifname, width=ifname_max_width)
315
            ret.append(self.curse_add_line(msg))
316
            if args.network_sum:
317
                msg = '{0:>14}'.format(sx)
318
                ret.append(self.curse_add_line(msg))
319
            else:
320
                msg = '{0:>7}'.format(rx)
321
                ret.append(self.curse_add_line(
322
                    msg, self.get_views(item=i[self.get_key()], key='rx', option='decoration')))
323
                msg = '{0:>7}'.format(tx)
324
                ret.append(self.curse_add_line(
325
                    msg, self.get_views(item=i[self.get_key()], key='tx', option='decoration')))
326
327
        return ret
328