Test Failed
Push — develop ( 66c9ff...e21229 )
by Nicolas
05:06
created

glances/plugins/glances_irq.py (5 issues)

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 2018 Angelo Poerio <[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
"""IRQ plugin."""
21
22
import os
23
import operator
0 ignored issues
show
import missing from __future__ import absolute_import
Loading history...
24
25
from glances.globals import LINUX
0 ignored issues
show
import missing from __future__ import absolute_import
Loading history...
26
from glances.timer import getTimeSinceLastUpdate
0 ignored issues
show
import missing from __future__ import absolute_import
Loading history...
27
from glances.plugins.glances_plugin import GlancesPlugin
0 ignored issues
show
import missing from __future__ import absolute_import
Loading history...
28
29
30
class Plugin(GlancesPlugin):
31
    """Glances IRQ plugin.
32
33
    stats is a list
34
    """
35
36
    def __init__(self, args=None, config=None):
37
        """Init the plugin."""
38
        super(Plugin, self).__init__(args=args,
39
                                     config=config,
40
                                     stats_init_value=[])
41
42
        # We want to display the stat in the curse interface
43
        self.display_curse = True
44
45
        # Init the stats
46
        self.irq = GlancesIRQ()
47
48
    def get_key(self):
49
        """Return the key of the list."""
50
        return self.irq.get_key()
51
52
    @GlancesPlugin._check_decorator
53
    @GlancesPlugin._log_result_decorator
54
    def update(self):
55
        """Update the IRQ stats."""
56
        # Init new stats
57
        stats = self.get_init_value()
58
59
        # IRQ plugin only available on GNU/Linux
60
        if not LINUX:
61
            return self.stats
62
63
        if self.input_method == 'local':
64
            # Grab the stats
65
            stats = self.irq.get()
66
67
        elif self.input_method == 'snmp':
68
            # not available
69
            pass
70
71
        # Get the TOP 5 (by rate/s)
72
        stats = sorted(stats,
73
                       key=operator.itemgetter('irq_rate'),
74
                       reverse=True)[:5]
75
76
        # Update the stats
77
        self.stats = stats
78
79
        return self.stats
80
81
    def update_views(self):
0 ignored issues
show
Useless super delegation in method 'update_views'
Loading history...
82
        """Update stats views."""
83
        # Call the father's method
84
        super(Plugin, self).update_views()
85
86
    def msg_curse(self, args=None, max_width=None):
87
        """Return the dict to display in the curse interface."""
88
        # Init the return message
89
        ret = []
90
91
        # Only available on GNU/Linux
92
        # Only process if stats exist and display plugin enable...
93
        if not LINUX or not self.stats or not self.args.enable_irq:
94
            return ret
95
96
        # Max size for the interface name
97
        name_max_width = max_width - 7
98
99
        # Build the string message
100
        # Header
101
        msg = '{:{width}}'.format('IRQ', width=name_max_width)
102
        ret.append(self.curse_add_line(msg, "TITLE"))
103
        msg = '{:>9}'.format('Rate/s')
104
        ret.append(self.curse_add_line(msg))
105
106
        for i in self.stats:
107
            ret.append(self.curse_new_line())
108
            msg = '{:{width}}'.format(i['irq_line'][:name_max_width],
109
                                      width=name_max_width)
110
            ret.append(self.curse_add_line(msg))
111
            msg = '{:>9}'.format(str(i['irq_rate']))
112
            ret.append(self.curse_add_line(msg))
113
114
        return ret
115
116
117
class GlancesIRQ(object):
118
    """This class manages the IRQ file."""
119
120
    IRQ_FILE = '/proc/interrupts'
121
122
    def __init__(self):
123
        """Init the class.
124
125
        The stat are stored in a internal list of dict
126
        """
127
        self.lasts = {}
128
        self.reset()
129
130
    def reset(self):
131
        """Reset the stats."""
132
        self.stats = []
133
        self.cpu_number = 0
134
135
    def get(self):
136
        """Return the current IRQ stats."""
137
        return self.__update()
138
139
    def get_key(self):
140
        """Return the key of the dict."""
141
        return 'irq_line'
142
143
    def __header(self, line):
144
        """Build the header (contain the number of CPU).
145
146
        CPU0       CPU1       CPU2       CPU3
147
        0:         21          0          0          0   IO-APIC   2-edge      timer
148
        """
149
        self.cpu_number = len(line.split())
150
        return self.cpu_number
151
152
    def __humanname(self, line):
153
        """Return the IRQ name, alias or number (choose the best for human).
154
155
        IRQ line samples:
156
        1:      44487        341         44         72   IO-APIC   1-edge      i8042
157
        LOC:   33549868   22394684   32474570   21855077   Local timer interrupts
158
        """
159
        splitted_line = line.split()
160
        irq_line = splitted_line[0].replace(':', '')
161
        if irq_line.isdigit():
162
            # If the first column is a digit, use the alias (last column)
163
            irq_line += '_{}'.format(splitted_line[-1])
164
        return irq_line
165
166
    def __sum(self, line):
167
        """Return the IRQ sum number.
168
169
        IRQ line samples:
170
        1:     44487        341         44         72   IO-APIC   1-edge      i8042
171
        LOC:   33549868   22394684   32474570   21855077   Local timer interrupts
172
        FIQ:   usb_fiq
173
        """
174
        splitted_line = line.split()
175
        try:
176
            ret = sum(map(int, splitted_line[1:(self.cpu_number + 1)]))
177
        except ValueError:
178
            # Correct issue #1007 on some conf (Raspberry Pi with Raspbian)
179
            ret = 0
180
        return ret
181
182
    def __update(self):
183
        """Load the IRQ file and update the internal dict."""
184
        self.reset()
185
186
        if not os.path.exists(self.IRQ_FILE):
187
            # Correct issue #947: IRQ file do not exist on OpenVZ container
188
            return self.stats
189
190
        try:
191
            with open(self.IRQ_FILE) as irq_proc:
192
                time_since_update = getTimeSinceLastUpdate('irq')
193
                # Read the header
194
                self.__header(irq_proc.readline())
195
                # Read the rest of the lines (one line per IRQ)
196
                for line in irq_proc.readlines():
197
                    irq_line = self.__humanname(line)
198
                    current_irqs = self.__sum(line)
199
                    irq_rate = int(
200
                        current_irqs - self.lasts.get(irq_line)
201
                        if self.lasts.get(irq_line)
202
                        else 0 // time_since_update)
203
                    irq_current = {
204
                        'irq_line': irq_line,
205
                        'irq_rate': irq_rate,
206
                        'key': self.get_key(),
207
                        'time_since_update': time_since_update
208
                    }
209
                    self.stats.append(irq_current)
210
                    self.lasts[irq_line] = current_irqs
211
        except (OSError, IOError):
212
            pass
213
214
        return self.stats
215