diff options
author | angelopoerio <angelo.poerio@gmail.com> | 2016-08-19 19:26:23 +0200 |
---|---|---|
committer | angelopoerio <angelo.poerio@gmail.com> | 2016-08-20 16:26:00 +0200 |
commit | e7fafdbdeec19b7766869d5cbdf75ca57ff2329f (patch) | |
tree | 0973abc0ec4b06543cb7ffe920f295010903450f | |
parent | aa94ccb91a843526e6da2776e4af61f95aa86db6 (diff) |
Added irq monitoring plugin
-rw-r--r-- | glances/main.py | 2 | ||||
-rw-r--r-- | glances/outputs/glances_curses.py | 11 | ||||
-rw-r--r-- | glances/outputs/static/html/help.html | 2 | ||||
-rw-r--r-- | glances/outputs/static/html/index.html | 1 | ||||
-rw-r--r-- | glances/outputs/static/html/plugins/irq.html | 8 | ||||
-rw-r--r-- | glances/outputs/static/html/stats.html | 3 | ||||
-rw-r--r-- | glances/outputs/static/js/services/core/glances_stats.js | 1 | ||||
-rw-r--r-- | glances/outputs/static/js/services/plugins/glances_irq.js | 21 | ||||
-rw-r--r-- | glances/outputs/static/js/stats_controller.js | 5 | ||||
-rw-r--r-- | glances/outputs/static/js/variables.js | 3 | ||||
-rw-r--r-- | glances/plugins/glances_help.py | 4 | ||||
-rw-r--r-- | glances/plugins/glances_irq.py | 127 | ||||
-rwxr-xr-x | unitest-restful.py | 2 | ||||
-rwxr-xr-x | unitest-xmlrpc.py | 8 | ||||
-rwxr-xr-x | unitest.py | 10 |
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') @@ -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') |