mirror of
https://github.com/nicolargo/glances.git
synced 2024-11-24 05:15:47 +03:00
Added irq monitoring plugin
This commit is contained in:
parent
aa94ccb91a
commit
e7fafdbdee
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
8
glances/outputs/static/html/plugins/irq.html
Normal file
8
glances/outputs/static/html/plugins/irq.html
Normal file
@ -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>
|
@ -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>
|
||||
|
@ -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',
|
||||
|
21
glances/outputs/static/js/services/plugins/glances_irq.js
Normal file
21
glances/outputs/static/js/services/plugins/glances_irq.js
Normal file
@ -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);
|
||||
}
|
||||
};
|
||||
});
|
@ -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;
|
||||
|
@ -29,5 +29,6 @@ var keycodes = {
|
||||
'g' : '71',
|
||||
'r' : '82',
|
||||
'q' : '81',
|
||||
'A' : '65'
|
||||
'A' : '65',
|
||||
'R' : '82',
|
||||
}
|
||||
|
@ -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())
|
||||
|
127
glances/plugins/glances_irq.py
Normal file
127
glances/plugins/glances_irq.py
Normal file
@ -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
|
@ -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
|
||||
|
@ -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')
|
||||
|
10
unitest.py
10
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')
|
||||
|
Loading…
Reference in New Issue
Block a user