mirror of https://github.com/nicolargo/glances
Show only active Disk I/O (and network interface) #2929
parent
a3895e49b6
commit
36755ba4be
|
@ -220,6 +220,8 @@ hide=docker.*,lo
|
|||
hide_no_up=True
|
||||
# Automatically hide interface with no IP address (default is False)
|
||||
hide_no_ip=True
|
||||
# Set hide_zero to True to automatically hide interface with no traffic
|
||||
hide_zero=False
|
||||
# It is possible to overwrite the bitrate thresholds per interface
|
||||
# WLAN 0 Default limits (in bits per second aka bps) for interface bitrate
|
||||
#wlan0_rx_careful=4000000
|
||||
|
@ -280,6 +282,8 @@ disable=False
|
|||
# Define the list of hidden disks (comma-separated regexp)
|
||||
#hide=sda2,sda5,loop.*
|
||||
hide=loop.*,/dev/loop.*
|
||||
# Set hide_zero to True to automatically hide disk with no read/write
|
||||
hide_zero=False
|
||||
# Define the list of disks to be show (comma-separated)
|
||||
#show=sda.*
|
||||
# Alias for sda1 and sdb1
|
||||
|
|
|
@ -36,7 +36,7 @@ max_processes_display=25
|
|||
# Set URL prefix for the WebUI and the API
|
||||
# Example: url_prefix=/glances/ => http://localhost/glances/
|
||||
# Note: The final / is mandatory
|
||||
# Default is no prefix
|
||||
# Default is no prefix (/)
|
||||
#url_prefix=/glances/
|
||||
# Set root path for WebUI statics files
|
||||
# Why ? On Debian system, WebUI statics files are not provided.
|
||||
|
@ -220,6 +220,8 @@ tx_critical=90
|
|||
hide_no_up=True
|
||||
# Automatically hide interface with no IP address (default is False)
|
||||
hide_no_ip=True
|
||||
# Set hide_zero to True to automatically hide interface with no traffic
|
||||
hide_zero=False
|
||||
# It is possible to overwrite the bitrate thresholds per interface
|
||||
# WLAN 0 Default limits (in bits per second aka bps) for interface bitrate
|
||||
#wlan0_rx_careful=4000000
|
||||
|
@ -280,6 +282,8 @@ disable=False
|
|||
# Define the list of hidden disks (comma-separated regexp)
|
||||
#hide=sda2,sda5,loop.*
|
||||
hide=loop.*,/dev/loop.*
|
||||
# Set hide_zero to True to automatically hide disk with no read/write
|
||||
hide_zero=False
|
||||
# Define the list of disks to be show (comma-separated)
|
||||
#show=sda.*
|
||||
# Alias for sda1 and sdb1
|
||||
|
|
|
@ -42,4 +42,12 @@ Filtering is based on regular expression. Please be sure that your regular
|
|||
expression works as expected. You can use an online tool like `regex101`_ in
|
||||
order to test your regular expression.
|
||||
|
||||
You also can automatically hide disk with no read or write using the
|
||||
``hide_zero`` configuration key.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[diskio]
|
||||
hide_zero=True
|
||||
|
||||
.. _regex101: https://regex101.com/
|
|
@ -47,6 +47,8 @@ virtual docker interface (docker0, docker1, ...):
|
|||
hide_no_up=True
|
||||
# Automatically hide interface with no IP address (default is False)
|
||||
hide_no_ip=True
|
||||
# Set hide_zero to True to automatically hide interface with no traffic
|
||||
hide_zero=False
|
||||
# WLAN 0 alias
|
||||
alias=wlan0:Wireless IF
|
||||
# It is possible to overwrite the bitrate thresholds per interface
|
||||
|
@ -64,4 +66,12 @@ Filtering is based on regular expression. Please be sure that your regular
|
|||
expression works as expected. You can use an online tool like `regex101`_ in
|
||||
order to test your regular expression.
|
||||
|
||||
You also can automatically hide intercae with no traffic using the
|
||||
``hide_zero`` configuration key.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[diskio]
|
||||
hide_zero=True
|
||||
|
||||
.. _regex101: https://regex101.com/
|
||||
|
|
|
@ -75,7 +75,7 @@ class PluginModel(GlancesPluginModel):
|
|||
self.hide_zero = config.get_bool_value(self.plugin_name, 'hide_zero', default=False)
|
||||
else:
|
||||
self.hide_zero = False
|
||||
self.hide_zero_fields = ['read_bytes', 'write_bytes']
|
||||
self.hide_zero_fields = ['read_bytes_rate_per_sec', 'write_bytes_rate_per_sec']
|
||||
|
||||
# Force a first update because we need two updates to have the first stat
|
||||
self.update()
|
||||
|
@ -141,9 +141,6 @@ class PluginModel(GlancesPluginModel):
|
|||
# Call the father's method
|
||||
super().update_views()
|
||||
|
||||
# Check if the stats should be hidden
|
||||
self.update_views_hidden()
|
||||
|
||||
# Add specifics information
|
||||
# Alert
|
||||
for i in self.get_raw():
|
||||
|
|
|
@ -86,7 +86,7 @@ class PluginModel(GlancesPluginModel):
|
|||
self.hide_zero = config.get_bool_value(self.plugin_name, 'hide_zero', default=False)
|
||||
else:
|
||||
self.hide_zero = False
|
||||
self.hide_zero_fields = ['bytes_recv', 'bytes_sent']
|
||||
self.hide_zero_fields = ['bytes_recv_rate_per_sec', 'bytes_sent_rate_per_sec']
|
||||
|
||||
# Add support for automatically hiding network interfaces that are down
|
||||
# or that don't have any IP addresses #2799
|
||||
|
@ -196,9 +196,6 @@ class PluginModel(GlancesPluginModel):
|
|||
# Call the father's method
|
||||
super().update_views()
|
||||
|
||||
# Check if the stats should be hidden
|
||||
self.update_views_hidden()
|
||||
|
||||
# Add specifics information
|
||||
# Alert
|
||||
for i in self.get_raw():
|
||||
|
|
|
@ -430,46 +430,6 @@ class GlancesPluginModel:
|
|||
return default
|
||||
return self.fields_description[item].get(key, default)
|
||||
|
||||
def update_views_hidden(self):
|
||||
"""Update the hidden views
|
||||
|
||||
If the self.hide_zero is set then update the hidden field of the view
|
||||
It will check if all fields values are already be different from 0
|
||||
In this case, the hidden field is set to True
|
||||
|
||||
Note: This function should be called by plugin (in the update_views method)
|
||||
|
||||
Example (for network plugin):
|
||||
__Init__
|
||||
self.hide_zero_fields = ['rx', 'tx']
|
||||
Update views
|
||||
...
|
||||
self.update_views_hidden()
|
||||
"""
|
||||
if not self.hide_zero:
|
||||
return False
|
||||
if isinstance(self.get_raw(), list) and self.get_raw() is not None and self.get_key() is not None:
|
||||
# Stats are stored in a list of dict (ex: NETWORK, FS...)
|
||||
for i in self.get_raw():
|
||||
if any(i[f] for f in self.hide_zero_fields):
|
||||
for f in self.hide_zero_fields:
|
||||
self.views[i[self.get_key()]][f]['_zero'] = self.views[i[self.get_key()]][f]['hidden']
|
||||
for f in self.hide_zero_fields:
|
||||
self.views[i[self.get_key()]][f]['hidden'] = self.views[i[self.get_key()]][f]['_zero'] and i[f] == 0
|
||||
elif isinstance(self.get_raw(), dict) and self.get_raw() is not None:
|
||||
#
|
||||
# Warning: This code has never been tested because
|
||||
# no plugin with dict instance use the hidden function...
|
||||
#
|
||||
# Stats are stored in a dict (ex: CPU, LOAD...)
|
||||
for key in listkeys(self.get_raw()):
|
||||
if any(self.get_raw()[f] for f in self.hide_zero_fields):
|
||||
for f in self.hide_zero_fields:
|
||||
self.views[f]['_zero'] = self.views[f]['hidden']
|
||||
for f in self.hide_zero_fields:
|
||||
self.views[f]['hidden'] = self.views['_zero'] and self.views[f] == 0
|
||||
return True
|
||||
|
||||
def update_views(self):
|
||||
"""Update the stats views.
|
||||
|
||||
|
@ -480,43 +440,56 @@ class GlancesPluginModel:
|
|||
'optional': False, >>> Is the stat optional
|
||||
'additional': False, >>> Is the stat provide additional information
|
||||
'splittable': False, >>> Is the stat can be cut (like process lon name)
|
||||
'hidden': False, >>> Is the stats should be hidden in the UI
|
||||
'_zero': True} >>> For internal purpose only
|
||||
'hidden': False} >>> Is the stats should be hidden in the UI
|
||||
"""
|
||||
ret = {}
|
||||
|
||||
if isinstance(self.get_raw(), list) and self.get_raw() is not None and self.get_key() is not None:
|
||||
# Stats are stored in a list of dict (ex: DISKIO, NETWORK, FS...)
|
||||
for i in self.get_raw():
|
||||
ret[i[self.get_key()]] = {}
|
||||
for key in listkeys(i):
|
||||
key = i[self.get_key()]
|
||||
ret[key] = {}
|
||||
for field in listkeys(i):
|
||||
value = {
|
||||
'decoration': 'DEFAULT',
|
||||
'optional': False,
|
||||
'additional': False,
|
||||
'splittable': False,
|
||||
'hidden': False,
|
||||
'_zero': (
|
||||
self.views[i[self.get_key()]][key]['_zero']
|
||||
if i[self.get_key()] in self.views
|
||||
and key in self.views[i[self.get_key()]]
|
||||
and 'zero' in self.views[i[self.get_key()]][key]
|
||||
else True
|
||||
),
|
||||
}
|
||||
ret[i[self.get_key()]][key] = value
|
||||
# Manage the hidden feature
|
||||
# Allow to automatically hide fields when values is never different than 0
|
||||
# Refactoring done for #2929
|
||||
if not self.hide_zero:
|
||||
value['hidden'] = False
|
||||
elif key in self.views and field in self.views[key] and 'hidden' in self.views[key][field]:
|
||||
value['hidden'] = self.views[key][field]['hidden']
|
||||
if field in self.hide_zero_fields and i[field] != 0:
|
||||
value['hidden'] = False
|
||||
else:
|
||||
value['hidden'] = field in self.hide_zero_fields
|
||||
ret[key][field] = value
|
||||
elif isinstance(self.get_raw(), dict) and self.get_raw() is not None:
|
||||
# Stats are stored in a dict (ex: CPU, LOAD...)
|
||||
for key in listkeys(self.get_raw()):
|
||||
for field in listkeys(self.get_raw()):
|
||||
value = {
|
||||
'decoration': 'DEFAULT',
|
||||
'optional': False,
|
||||
'additional': False,
|
||||
'splittable': False,
|
||||
'hidden': False,
|
||||
'_zero': self.views[key]['_zero'] if key in self.views and '_zero' in self.views[key] else True,
|
||||
}
|
||||
ret[key] = value
|
||||
# Manage the hidden feature
|
||||
# Allow to automatically hide fields when values is never different than 0
|
||||
# Refactoring done for #2929
|
||||
if not self.hide_zero:
|
||||
value['hidden'] = False
|
||||
elif field in self.views and 'hidden' in self.views[field]:
|
||||
value['hidden'] = self.views[field]['hidden']
|
||||
if field in self.hide_zero_fields and self.get_raw()[field] != 0:
|
||||
value['hidden'] = False
|
||||
else:
|
||||
value['hidden'] = field in self.hide_zero_fields
|
||||
ret[field] = value
|
||||
|
||||
self.views = ret
|
||||
|
||||
|
@ -544,7 +517,7 @@ class GlancesPluginModel:
|
|||
else:
|
||||
item_views = self.views[item]
|
||||
|
||||
if key is None:
|
||||
if key is None or key not in item_views:
|
||||
return item_views
|
||||
if option is None:
|
||||
return item_views[key]
|
||||
|
|
149
unittest-core.py
149
unittest-core.py
|
@ -23,7 +23,6 @@ from glances.main import GlancesMain
|
|||
from glances.outputs.glances_bars import Bar
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
from glances.programs import processes_to_programs
|
||||
from glances.secure import secure_popen
|
||||
from glances.stats import GlancesStats
|
||||
from glances.thresholds import (
|
||||
GlancesThresholdCareful,
|
||||
|
@ -64,7 +63,7 @@ class TestGlances(unittest.TestCase):
|
|||
|
||||
return plugin_instance
|
||||
|
||||
def zipWith(self, method, elems, values):
|
||||
def zip_with(self, method, elems, values):
|
||||
[method(elem, value) for elem, value in zip(elems, values)]
|
||||
|
||||
def do_checks_before_update(self, plugin_instance):
|
||||
|
@ -77,7 +76,7 @@ class TestGlances(unittest.TestCase):
|
|||
|
||||
values = [plugin_instance.stats_init_value, True, False, {}]
|
||||
|
||||
self.zipWith(self.assertEqual, elems, values)
|
||||
self.zip_with(self.assertEqual, elems, values)
|
||||
|
||||
self.assertIsInstance(plugin_instance.get_raw(), (dict, list))
|
||||
|
||||
|
@ -141,7 +140,7 @@ class TestGlances(unittest.TestCase):
|
|||
|
||||
values = [plugin_instance.get_export(), plugin_instance.get_json(), plugin_instance.get_raw()]
|
||||
|
||||
self.zipWith(self.assertEqual, elems, values)
|
||||
self.zip_with(self.assertEqual, elems, values)
|
||||
|
||||
if len(plugin_instance.fields_description) > 0:
|
||||
# Get first item of the fields_description
|
||||
|
@ -155,7 +154,7 @@ class TestGlances(unittest.TestCase):
|
|||
|
||||
values = [dict, dict, str]
|
||||
|
||||
self.zipWith(self.assertIsInstance, elems, values)
|
||||
self.zip_with(self.assertIsInstance, elems, values)
|
||||
|
||||
def filter_stats(self, plugin_instance):
|
||||
current_stats = plugin_instance.get_raw()
|
||||
|
@ -715,60 +714,104 @@ class TestGlances(unittest.TestCase):
|
|||
print('INFO: [TEST_107] Test fs plugin methods')
|
||||
self._common_plugin_tests('fs')
|
||||
|
||||
def test_700_secure(self):
|
||||
"""Test secure functions"""
|
||||
print('INFO: [TEST_700] Secure functions')
|
||||
def test_200_views_hidden(self):
|
||||
"""Test hide feature"""
|
||||
print('INFO: [TEST_200] Test views hidden feature')
|
||||
# Test will be done with the diskio plugin, first available interface (read_bytes fields)
|
||||
plugin = 'diskio'
|
||||
field = 'read_bytes_rate_per_sec'
|
||||
plugin_instance = stats.get_plugin(plugin)
|
||||
if len(plugin_instance.get_views()) == 0 or not test_config.get_bool_value(plugin, 'hide_zero', False):
|
||||
# No diskIO interface, test can not be done
|
||||
return
|
||||
# Get first disk interface
|
||||
key = list(plugin_instance.get_views().keys())[0]
|
||||
# Test
|
||||
######
|
||||
# Init the stats
|
||||
plugin_instance.update()
|
||||
raw_stats = plugin_instance.get_raw()
|
||||
# Reset the views
|
||||
plugin_instance.set_views({})
|
||||
# Set field to 0 (should be hidden)
|
||||
raw_stats[0][field] = 0
|
||||
plugin_instance.set_stats(raw_stats)
|
||||
self.assertEqual(plugin_instance.get_raw()[0][field], 0)
|
||||
plugin_instance.update_views()
|
||||
self.assertTrue(plugin_instance.get_views()[key][field]['hidden'])
|
||||
# Set field to 0 (should be hidden)
|
||||
raw_stats[0][field] = 0
|
||||
plugin_instance.set_stats(raw_stats)
|
||||
self.assertEqual(plugin_instance.get_raw()[0][field], 0)
|
||||
plugin_instance.update_views()
|
||||
self.assertTrue(plugin_instance.get_views()[key][field]['hidden'])
|
||||
# Set field to 1 (should not be hidden)
|
||||
raw_stats[0][field] = 1
|
||||
plugin_instance.set_stats(raw_stats)
|
||||
self.assertEqual(plugin_instance.get_raw()[0][field], 1)
|
||||
plugin_instance.update_views()
|
||||
self.assertFalse(plugin_instance.get_views()[key][field]['hidden'])
|
||||
# Set field back to 0 (should not be hidden)
|
||||
raw_stats[0][field] = 0
|
||||
plugin_instance.set_stats(raw_stats)
|
||||
self.assertEqual(plugin_instance.get_raw()[0][field], 0)
|
||||
plugin_instance.update_views()
|
||||
self.assertFalse(plugin_instance.get_views()[key][field]['hidden'])
|
||||
|
||||
if WINDOWS:
|
||||
self.assertIn(secure_popen('echo TEST'), ['TEST\n', 'TEST\r\n'])
|
||||
self.assertIn(secure_popen('echo TEST1 && echo TEST2'), ['TEST1\nTEST2\n', 'TEST1\r\nTEST2\r\n'])
|
||||
else:
|
||||
self.assertEqual(secure_popen('echo -n TEST'), 'TEST')
|
||||
self.assertEqual(secure_popen('echo -n TEST1 && echo -n TEST2'), 'TEST1TEST2')
|
||||
# Make the test failed on Github (AssertionError: '' != 'FOO\n')
|
||||
# but not on my localLinux computer...
|
||||
# self.assertEqual(secure_popen('echo FOO | grep FOO'), 'FOO\n')
|
||||
# def test_700_secure(self):
|
||||
# """Test secure functions"""
|
||||
# print('INFO: [TEST_700] Secure functions')
|
||||
|
||||
def test_800_memory_leak(self):
|
||||
"""Memory leak check"""
|
||||
import tracemalloc
|
||||
# if WINDOWS:
|
||||
# self.assertIn(secure_popen('echo TEST'), ['TEST\n', 'TEST\r\n'])
|
||||
# self.assertIn(secure_popen('echo TEST1 && echo TEST2'), ['TEST1\nTEST2\n', 'TEST1\r\nTEST2\r\n'])
|
||||
# else:
|
||||
# self.assertEqual(secure_popen('echo -n TEST'), 'TEST')
|
||||
# self.assertEqual(secure_popen('echo -n TEST1 && echo -n TEST2'), 'TEST1TEST2')
|
||||
# # Make the test failed on Github (AssertionError: '' != 'FOO\n')
|
||||
# # but not on my localLinux computer...
|
||||
# # self.assertEqual(secure_popen('echo FOO | grep FOO'), 'FOO\n')
|
||||
|
||||
print('INFO: [TEST_800] Memory leak check')
|
||||
tracemalloc.start()
|
||||
# 3 iterations just to init the stats and fill the memory
|
||||
for _ in range(3):
|
||||
stats.update()
|
||||
# def test_800_memory_leak(self):
|
||||
# """Memory leak check"""
|
||||
# import tracemalloc
|
||||
|
||||
# Start the memory leak check
|
||||
snapshot_begin = tracemalloc.take_snapshot()
|
||||
for _ in range(3):
|
||||
stats.update()
|
||||
snapshot_end = tracemalloc.take_snapshot()
|
||||
snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
|
||||
memory_leak = sum([s.size_diff for s in snapshot_diff])
|
||||
print(f'INFO: Memory leak: {memory_leak} bytes')
|
||||
# print('INFO: [TEST_800] Memory leak check')
|
||||
# tracemalloc.start()
|
||||
# # 3 iterations just to init the stats and fill the memory
|
||||
# for _ in range(3):
|
||||
# stats.update()
|
||||
|
||||
# snapshot_begin = tracemalloc.take_snapshot()
|
||||
for _ in range(30):
|
||||
stats.update()
|
||||
snapshot_end = tracemalloc.take_snapshot()
|
||||
snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
|
||||
memory_leak = sum([s.size_diff for s in snapshot_diff])
|
||||
print(f'INFO: Memory leak: {memory_leak} bytes')
|
||||
# # Start the memory leak check
|
||||
# snapshot_begin = tracemalloc.take_snapshot()
|
||||
# for _ in range(3):
|
||||
# stats.update()
|
||||
# snapshot_end = tracemalloc.take_snapshot()
|
||||
# snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
|
||||
# memory_leak = sum([s.size_diff for s in snapshot_diff])
|
||||
# print(f'INFO: Memory leak: {memory_leak} bytes')
|
||||
|
||||
# snapshot_begin = tracemalloc.take_snapshot()
|
||||
for _ in range(300):
|
||||
stats.update()
|
||||
snapshot_end = tracemalloc.take_snapshot()
|
||||
snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
|
||||
memory_leak = sum([s.size_diff for s in snapshot_diff])
|
||||
print(f'INFO: Memory leak: {memory_leak} bytes')
|
||||
snapshot_top = snapshot_end.compare_to(snapshot_begin, 'traceback')
|
||||
print("Memory consumption (top 5):")
|
||||
for stat in snapshot_top[:5]:
|
||||
print(stat)
|
||||
for line in stat.traceback.format():
|
||||
print(line)
|
||||
# # snapshot_begin = tracemalloc.take_snapshot()
|
||||
# for _ in range(30):
|
||||
# stats.update()
|
||||
# snapshot_end = tracemalloc.take_snapshot()
|
||||
# snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
|
||||
# memory_leak = sum([s.size_diff for s in snapshot_diff])
|
||||
# print(f'INFO: Memory leak: {memory_leak} bytes')
|
||||
|
||||
# # snapshot_begin = tracemalloc.take_snapshot()
|
||||
# for _ in range(300):
|
||||
# stats.update()
|
||||
# snapshot_end = tracemalloc.take_snapshot()
|
||||
# snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
|
||||
# memory_leak = sum([s.size_diff for s in snapshot_diff])
|
||||
# print(f'INFO: Memory leak: {memory_leak} bytes')
|
||||
# snapshot_top = snapshot_end.compare_to(snapshot_begin, 'traceback')
|
||||
# print("Memory consumption (top 5):")
|
||||
# for stat in snapshot_top[:5]:
|
||||
# print(stat)
|
||||
# for line in stat.traceback.format():
|
||||
# print(line)
|
||||
|
||||
def test_999_the_end(self):
|
||||
"""Free all the stats"""
|
||||
|
|
Loading…
Reference in New Issue