summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorangelopoerio <angelo.poerio@gmail.com>2016-08-19 19:26:23 +0200
committerangelopoerio <angelo.poerio@gmail.com>2016-08-20 16:26:00 +0200
commite7fafdbdeec19b7766869d5cbdf75ca57ff2329f (patch)
tree0973abc0ec4b06543cb7ffe920f295010903450f
parentaa94ccb91a843526e6da2776e4af61f95aa86db6 (diff)
Added irq monitoring plugin
-rw-r--r--glances/main.py2
-rw-r--r--glances/outputs/glances_curses.py11
-rw-r--r--glances/outputs/static/html/help.html2
-rw-r--r--glances/outputs/static/html/index.html1
-rw-r--r--glances/outputs/static/html/plugins/irq.html8
-rw-r--r--glances/outputs/static/html/stats.html3
-rw-r--r--glances/outputs/static/js/services/core/glances_stats.js1
-rw-r--r--glances/outputs/static/js/services/plugins/glances_irq.js21
-rw-r--r--glances/outputs/static/js/stats_controller.js5
-rw-r--r--glances/outputs/static/js/variables.js3
-rw-r--r--glances/plugins/glances_help.py4
-rw-r--r--glances/plugins/glances_irq.py127
-rwxr-xr-xunitest-restful.py2
-rwxr-xr-xunitest-xmlrpc.py8
-rwxr-xr-xunitest.py10
15 files changed, 201 insertions, 7 deletions
diff --git a/glances/main.py b/glances/main.py
index c83f2b11..b5bb2f22 100644
--- a/glances/main.py
+++ b/glances/main.py
@@ -120,6 +120,8 @@ Start the client browser (browser mode):\n\
dest='disable_ip', help='disable IP module')
parser.add_argument('--disable-diskio', action='store_true', default=False,
dest='disable_diskio', help='disable disk I/O module')
+ parser.add_argument('--disable-irq', action='store_true', default=False,
+ dest='disable_irq', help='disable IRQ module'),
parser.add_argument('--disable-fs', action='store_true', default=False,
dest='disable_fs', help='disable filesystem module')
parser.add_argument('--disable-folder', action='store_true', default=False,
diff --git a/glances/outputs/glances_curses.py b/glances/outputs/glances_curses.py
index 892125a8..c5260e28 100644
--- a/glances/outputs/glances_curses.py
+++ b/glances/outputs/glances_curses.py
@@ -361,7 +361,9 @@ class _GlancesCurses(object):
elif self.pressedkey == ord('d'):
# 'd' > Show/hide disk I/O stats
self.args.disable_diskio = not self.args.disable_diskio
- elif self.pressedkey == ord('D'):
+ elif self.pressedkey == ord('R'):
+ self.args.disable_irq = not self.args.disable_irq
+ elif self.pressedkey == ord('D'):
# 'D' > Show/hide Docker stats
self.args.disable_docker = not self.args.disable_docker
elif self.pressedkey == ord('e'):
@@ -537,7 +539,9 @@ class _GlancesCurses(object):
stats_memswap = stats.get_plugin('memswap').get_stats_display(args=self.args)
stats_network = stats.get_plugin('network').get_stats_display(
args=self.args, max_width=plugin_max_width)
- try:
+ stats_irq = stats.get_plugin('irq').get_stats_display(
+ args=self.args, max_width=plugin_max_width)
+ try:
stats_ip = stats.get_plugin('ip').get_stats_display(args=self.args)
except AttributeError:
stats_ip = None
@@ -728,6 +732,8 @@ class _GlancesCurses(object):
self.new_line()
self.display_plugin(stats_fs)
self.new_line()
+ self.display_plugin(stats_irq)
+ self.new_line()
self.display_plugin(stats_folders)
self.new_line()
self.display_plugin(stats_raid)
@@ -735,6 +741,7 @@ class _GlancesCurses(object):
self.display_plugin(stats_sensors)
self.new_line()
self.display_plugin(stats_now)
+ self.new_line()
# ====================================
# Display right stats (process and co)
diff --git a/glances/outputs/static/html/help.html b/glances/outputs/static/html/help.html
index 43308138..ecb64150 100644
--- a/glances/outputs/static/html/help.html
+++ b/glances/outputs/static/html/help.html
@@ -56,7 +56,7 @@
</div>
<div class="row">
<div class="col-sm-12 col-lg-6">{{help.enable_disable_process_stats}}</div>
- <div class="col-sm-12 col-lg-6"></div>
+ <div class="col-sm-12 col-lg-6">{{help.show_hide_irq}}</div>
</div>
<div class="row">
<div class="col-sm-12 col-lg-6">{{help.enable_disable_quick_look}}</div>
diff --git a/glances/outputs/static/html/index.html b/glances/outputs/static/html/index.html
index 31b515c2..6f8c7bf7 100644
--- a/glances/outputs/static/html/index.html
+++ b/glances/outputs/static/html/index.html
@@ -24,6 +24,7 @@
<script type="text/javascript" src="services/plugins/glances_alert.js"></script>
<script type="text/javascript" src="services/plugins/glances_cpu.js"></script>
<script type="text/javascript" src="services/plugins/glances_diskio.js"></script>
+ <script type="text/javascript" src="services/plugins/glances_irq.js"></script>
<script type="text/javascript" src="services/plugins/glances_docker.js"></script>
<script type="text/javascript" src="services/plugins/glances_fs.js"></script>
<script type="text/javascript" src="services/plugins/glances_folders.js"></script>
diff --git a/glances/outputs/static/html/plugins/irq.html b/glances/outputs/static/html/plugins/irq.html
new file mode 100644
index 00000000..50eb5406
--- /dev/null
+++ b/glances/outputs/static/html/plugins/irq.html
@@ -0,0 +1,8 @@
+<div class="table-row">
+ <div class="table-cell text-left title">IRQ</div>
+ <div class="table-cell">Rate/s</div>
+</div>
+<div class="table-row" ng-repeat="irq in statsIrq.irqs">
+ <div class="table-cell text-left">{{irq.irq_line}}</div>
+ <div class="table-cell">{{irq.irq_rate}}</div>
+</div>
diff --git a/glances/outputs/static/html/stats.html b/glances/outputs/static/html/stats.html
index 5658bc55..5aee5b02 100644
--- a/glances/outputs/static/html/stats.html
+++ b/glances/outputs/static/html/stats.html
@@ -48,6 +48,9 @@
<section id="network" class="plugin table-row-group" ng-show="!arguments.disable_network" ng-include src="'plugins/network.html'"></section>
<section id="ports" class="plugin table-row-group" ng-show="!arguments.disable_ports" ng-include src="'plugins/ports.html'"></section>
<section id="diskio" class="plugin table-row-group" ng-show="!arguments.disable_diskio && statsDiskio.disks.length > 0" ng-include src="'plugins/diskio.html'"></section>
+
+ <section id="irq" class="plugin table-row-group" ng-show="!arguments.disable_irq" ng-include src="'plugins/irq.html'"></section>
+
<section id="fs" class="plugin table-row-group" ng-show="!arguments.disable_fs" ng-include src="'plugins/fs.html'"></section>
<section id="folders" class="plugin table-row-group" ng-show="!arguments.disable_fs && statsFolders.folders.length > 0" ng-include src="'plugins/folders.html'"></section>
<section id="raid" class="plugin table-row-group" ng-show="statsRaid.hasDisks()" ng-include src="'plugins/raid.html'"></section>
diff --git a/glances/outputs/static/js/services/core/glances_stats.js b/glances/outputs/static/js/services/core/glances_stats.js
index 404baeac..520dca54 100644
--- a/glances/outputs/static/js/services/core/glances_stats.js
+++ b/glances/outputs/static/js/services/core/glances_stats.js
@@ -5,6 +5,7 @@ glancesApp.service('GlancesStats', function($http, $injector, $q, GlancesPlugin)
'alert': 'GlancesPluginAlert',
'cpu': 'GlancesPluginCpu',
'diskio': 'GlancesPluginDiskio',
+ 'irq' : 'GlancesPluginIrq',
'docker': 'GlancesPluginDocker',
'ip': 'GlancesPluginIp',
'fs': 'GlancesPluginFs',
diff --git a/glances/outputs/static/js/services/plugins/glances_irq.js b/glances/outputs/static/js/services/plugins/glances_irq.js
new file mode 100644
index 00000000..ec0f9e24
--- /dev/null
+++ b/glances/outputs/static/js/services/plugins/glances_irq.js
@@ -0,0 +1,21 @@
+glancesApp.service('GlancesPluginIrq', function($filter) {
+ var _pluginName = "irq";
+ this.irqs = [];
+
+ this.setData = function(data, views) {
+ data = data[_pluginName];
+ this.irqs = [];
+
+ for (var i = 0; i < data.length; i++) {
+ var IrqData = data[i];
+ var timeSinceUpdate = IrqData['time_since_update'];
+
+ var irq = {
+ 'irq_line': IrqData['irq_line'],
+ 'irq_rate': IrqData['irq_rate']
+ };
+
+ this.irqs.push(irq);
+ }
+ };
+});
diff --git a/glances/outputs/static/js/stats_controller.js b/glances/outputs/static/js/stats_controller.js
index 5a5320ba..446b9b11 100644
--- a/glances/outputs/static/js/stats_controller.js
+++ b/glances/outputs/static/js/stats_controller.js
@@ -24,6 +24,7 @@ glancesApp.controller('statsController', function ($scope, $rootScope, $interval
$scope.statsAlert = GlancesStats.getPlugin('alert');
$scope.statsCpu = GlancesStats.getPlugin('cpu');
$scope.statsDiskio = GlancesStats.getPlugin('diskio');
+ $scope.statsIrq = GlancesStats.getPlugin('irq');
$scope.statsDocker = GlancesStats.getPlugin('docker');
$scope.statsFs = GlancesStats.getPlugin('fs');
$scope.statsFolders = GlancesStats.getPlugin('folders');
@@ -103,6 +104,10 @@ glancesApp.controller('statsController', function ($scope, $rootScope, $interval
// d => Show/hide disk I/O stats
$scope.arguments.disable_diskio = !$scope.arguments.disable_diskio;
break;
+ case $event.shiftKey && $event.keyCode == keycodes.R:
+ // R => Show/hide IRQ
+ $scope.arguments.disable_irq = !$scope.arguments.disable_irq;
+ break;
case !$event.shiftKey && $event.keyCode == keycodes.f:
// f => Show/hide filesystem stats
$scope.arguments.disable_fs = !$scope.arguments.disable_fs;
diff --git a/glances/outputs/static/js/variables.js b/glances/outputs/static/js/variables.js
index c0400c2f..f6ebe80e 100644
--- a/glances/outputs/static/js/variables.js
+++ b/glances/outputs/static/js/variables.js
@@ -29,5 +29,6 @@ var keycodes = {
'g' : '71',
'r' : '82',
'q' : '81',
- 'A' : '65'
+ 'A' : '65',
+ 'R' : '82',
}
diff --git a/glances/plugins/glances_help.py b/glances/plugins/glances_help.py
index fb29130c..5369927c 100644
--- a/glances/plugins/glances_help.py
+++ b/glances/plugins/glances_help.py
@@ -74,7 +74,8 @@ class Plugin(GlancesPlugin):
self.view_data['sort_cpu_times'] = msg_col.format('t', 'Sort processes by TIME')
self.view_data['show_hide_help'] = msg_col2.format('h', 'Show/hide this help screen')
self.view_data['show_hide_diskio'] = msg_col.format('d', 'Show/hide disk I/O stats')
- self.view_data['view_network_io_combination'] = msg_col2.format('T', 'View network I/O as combination')
+ self.view_data['show_hide_irq'] = msg_col2.format('R', 'Show/hide IRQ stats')
+ self.view_data['view_network_io_combination'] = msg_col2.format('T', 'View network I/O as combination')
self.view_data['show_hide_filesystem'] = msg_col.format('f', 'Show/hide filesystem stats')
self.view_data['view_cumulative_network'] = msg_col2.format('U', 'View cumulative network I/O')
self.view_data['show_hide_network'] = msg_col.format('n', 'Show/hide network stats')
@@ -172,6 +173,7 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(self.view_data['quit']))
ret.append(self.curse_new_line())
ret.append(self.curse_add_line(self.view_data['enable_disable_ports']))
+ ret.append(self.curse_add_line(self.view_data['show_hide_irq']))
ret.append(self.curse_new_line())
ret.append(self.curse_new_line())
diff --git a/glances/plugins/glances_irq.py b/glances/plugins/glances_irq.py
new file mode 100644
index 00000000..84730144
--- /dev/null
+++ b/glances/plugins/glances_irq.py
@@ -0,0 +1,127 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of Glances.
+#
+# Copyright (C) 2015 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/>.
+
+"""IRQ plugin."""
+
+import operator
+from glances.timer import getTimeSinceLastUpdate
+from glances.plugins.glances_plugin import GlancesPlugin
+
+
+class Plugin(GlancesPlugin):
+
+ """Glances IRQ plugin.
+
+ stats is a list
+ """
+
+ def __init__(self, args=None):
+ """Init the plugin."""
+ super(Plugin, self).__init__(args=args)
+
+ # We want to display the stat in the curse interface
+ self.display_curse = True
+
+ self.lasts = {}
+
+ # Init the stats
+ self.reset()
+
+ def get_key(self):
+ """Return the key of the list."""
+ return 'irq_line'
+
+ def reset(self):
+ """Reset/init the stats."""
+ self.stats = []
+
+ @GlancesPlugin._log_result_decorator
+ def update(self):
+ """Update the IRQ stats"""
+
+ # Reset the list
+ self.reset()
+
+ if self.input_method == 'local':
+ with open('/proc/interrupts') as irq_proc:
+ time_since_update = getTimeSinceLastUpdate('irq')
+ irq_proc.readline() # skip header line
+ for irq_line in irq_proc.readlines():
+ splitted_line = irq_line.split()
+ irq_line = splitted_line[0].replace(':','')
+ current_irqs = sum([int(count) for count in splitted_line[1:] if count.isdigit()]) # sum interrupts on all CPUs
+ irq_rate = int(current_irqs - self.lasts.get(irq_line) if self.lasts.get(irq_line) else 0 // time_since_update)
+ irq_current = {
+ 'irq_line': irq_line,
+ 'irq_rate': irq_rate,
+ 'key': self.get_key(),
+ 'time_since_update': time_since_update
+ }
+ self.stats.append(irq_current)
+ self.lasts[irq_line] = current_irqs
+
+ elif self.input_method == 'snmp':
+ # not available
+ pass
+
+ # Update the view
+ self.update_views()
+
+ self.stats = sorted(self.stats, key=operator.itemgetter('irq_rate'), reverse=True)[:5] # top 5 IRQ by rate/s
+ return self.stats
+
+ def update_views(self):
+ """Update stats views."""
+ # Call the father's method
+ super(Plugin, self).update_views()
+
+
+ def msg_curse(self, args=None, max_width=None):
+ """Return the dict to display in the curse interface."""
+ # Init the return message
+ ret = []
+
+ # Only process if stats exist and display plugin enable...
+ if not self.stats or args.disable_irq:
+ return ret
+
+ # Max size for the fsname name
+ if max_width is not None and max_width >= 23:
+ # Interface size name = max_width - space for interfaces bitrate
+ irq_max_width = max_width - 14
+ else:
+ irq_max_width = 9
+
+ # Build the string message
+ # Header
+ msg = '{:{width}}'.format('IRQ', width=irq_max_width)
+ ret.append(self.curse_add_line(msg, "TITLE"))
+ msg = '{:>7}'.format('Rate/s')
+ ret.append(self.curse_add_line(msg))
+
+ # Filesystem list (sorted by name)
+ for i in self.stats:
+ # New line
+ ret.append(self.curse_new_line())
+ msg = '{:>3}'.format(i['irq_line'])
+ ret.append(self.curse_add_line(msg))
+ msg = '{:>12}'.format(str(i['irq_rate']))
+ ret.append(self.curse_add_line(msg))
+
+ return ret
diff --git a/unitest-restful.py b/unitest-restful.py
index d5627582..ae7bd696 100755
--- a/unitest-restful.py
+++ b/unitest-restful.py
@@ -94,7 +94,7 @@ class TestGlances(unittest.TestCase):
if p in ('uptime', 'now'):
self.assertIsInstance(req.json(), text_type)
elif p in ('fs', 'percpu', 'sensors', 'alert', 'processlist', 'diskio',
- 'hddtemp', 'batpercent', 'network', 'folders', 'amps', 'ports'):
+ 'hddtemp', 'batpercent', 'network', 'folders', 'amps', 'ports', 'irq'):
self.assertIsInstance(req.json(), list)
elif p in ('psutilversion', 'help'):
pass
diff --git a/unitest-xmlrpc.py b/unitest-xmlrpc.py
index b72d1ec8..55211790 100755
--- a/unitest-xmlrpc.py
+++ b/unitest-xmlrpc.py
@@ -175,6 +175,14 @@ class TestGlances(unittest.TestCase):
self.assertIsInstance(req, dict)
self.assertIsInstance(req['cpu'], dict)
+ def test_012_irq(self):
+ """IRQS"""
+ method = "getIrqs()"
+ print('INFO: [TEST_012] Method: %s' % method)
+ req = json.loads(client.getIrq())
+ self.assertIsInstance(req, list)
+
+
def test_999_stop_server(self):
"""Stop the Glances Web Server."""
print('INFO: [TEST_999] Stop the Glances Server')
diff --git a/unitest.py b/unitest.py
index 2872a760..bc226912 100755
--- a/unitest.py
+++ b/unitest.py
@@ -76,7 +76,7 @@ class TestGlances(unittest.TestCase):
def test_001_plugins(self):
"""Check mandatory plugins."""
- plugins_to_check = ['system', 'cpu', 'load', 'mem', 'memswap', 'network', 'diskio', 'fs']
+ plugins_to_check = ['system', 'cpu', 'load', 'mem', 'memswap', 'network', 'diskio', 'fs', 'irq']
print('INFO: [TEST_001] Check the mandatory plugins list: %s' % ', '.join(plugins_to_check))
plugins_list = stats.getAllPlugins()
for plugin in plugins_to_check:
@@ -193,6 +193,14 @@ class TestGlances(unittest.TestCase):
self.assertTrue(type(stats_grab) is dict, msg='IP stats is not a dict')
print('INFO: IP stats: %s' % stats_grab)
+ def test_013_irq(self):
+ """Check IRQ plugin."""
+ print('INFO: [TEST_013] Check IRQ stats')
+ stats_grab = stats.get_plugin('irq').get_raw()
+ self.assertTrue(type(stats_grab) is list, msg='IRQ stats is not a list')
+ print('INFO: IRQ stats: %s' % stats_grab)
+
+
def test_097_attribute(self):
"""Test GlancesAttribute classe"""
print('INFO: [TEST_097] Test attribute')