Completed
Push — master ( 2b80fa...6ea077 )
by Nicolas
01:22
created

glances/plugins/glances_network.py (1 issue)

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