mirror of https://github.com/nicolargo/glances.git
Test ok tests-data/issues/issue3290.py but not in the code...
This commit is contained in:
parent
60be92c7b1
commit
9da27d843f
|
|
@ -343,7 +343,7 @@ careful=50
|
|||
warning=70
|
||||
critical=90
|
||||
# Allow additional file system types (comma-separated FS type)
|
||||
#allow=nfs
|
||||
allow=nfs
|
||||
# Alias for root file system
|
||||
#alias=/:Root,/zsfpool:ZSF
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import weakref
|
|||
from collections import OrderedDict
|
||||
from configparser import ConfigParser, NoOptionError, NoSectionError
|
||||
from datetime import datetime
|
||||
from multiprocessing import Process, Queue
|
||||
from operator import itemgetter, methodcaller
|
||||
from statistics import mean
|
||||
from typing import Any, Optional, Union
|
||||
|
|
@ -584,3 +585,27 @@ def atoi(text):
|
|||
def natural_keys(text):
|
||||
"""Return a text in a natural/human readable format."""
|
||||
return [atoi(c) for c in re.split(r'(\d+)', text)]
|
||||
|
||||
|
||||
def exit_after(seconds, default=None):
|
||||
"""Exit the function if it takes more than 'seconds' seconds to complete.
|
||||
In this case, return the value of 'default' (default: None)."""
|
||||
|
||||
def handler(q, func, args, kwargs):
|
||||
q.put(func(*args, **kwargs))
|
||||
|
||||
def decorator(func):
|
||||
def wraps(*args, **kwargs):
|
||||
q = Queue()
|
||||
p = Process(target=handler, args=(q, func, args, kwargs))
|
||||
p.start()
|
||||
p.join(timeout=seconds)
|
||||
if p.is_alive():
|
||||
p.terminate()
|
||||
p.join()
|
||||
return default
|
||||
return q.get()
|
||||
|
||||
return wraps
|
||||
|
||||
return decorator
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import operator
|
|||
|
||||
import psutil
|
||||
|
||||
from glances.globals import PermissionError, nativestr, u
|
||||
from glances.globals import PermissionError, exit_after, nativestr, u
|
||||
from glances.logger import logger
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
|
|
@ -88,6 +88,16 @@ snmp_oid['esxi'] = snmp_oid['windows']
|
|||
items_history_list = [{'name': 'percent', 'description': 'File system usage in percent', 'y_unit': '%'}]
|
||||
|
||||
|
||||
@exit_after(1, default=None)
|
||||
def get_disk_usage(fs):
|
||||
"""Return all partitions."""
|
||||
try:
|
||||
return psutil.disk_usage(fs.mountpoint)
|
||||
except OSError:
|
||||
logger.debug("Plugin - fs: PsUtil fetch failed")
|
||||
return None
|
||||
|
||||
|
||||
class FsPlugin(GlancesPluginModel):
|
||||
"""Glances file system plugin.
|
||||
|
||||
|
|
@ -126,15 +136,6 @@ class FsPlugin(GlancesPluginModel):
|
|||
|
||||
return self.stats
|
||||
|
||||
@GlancesPluginModel._exit_after(3)
|
||||
def get_all_stats_partitions(self):
|
||||
"""Return all partitions."""
|
||||
try:
|
||||
return psutil.disk_partitions(all=True)
|
||||
except (UnicodeDecodeError, PermissionError):
|
||||
logger.debug("Plugin - fs: PsUtil fetch failed")
|
||||
return []
|
||||
|
||||
def update_local(self):
|
||||
"""Update the FS stats using the input method."""
|
||||
# Init new stats
|
||||
|
|
@ -155,29 +156,30 @@ class FsPlugin(GlancesPluginModel):
|
|||
allowed_fs_types = self.get_conf_value('allow')
|
||||
if allowed_fs_types:
|
||||
# Avoid Psutil call unless mounts need to be allowed
|
||||
all_mounted_fs = self.get_all_stats_partitions()
|
||||
# Discard duplicates (#2299) and add entries matching allowed fs types
|
||||
tracked_mnt_points = {f.mountpoint for f in fs_stat}
|
||||
for f in all_mounted_fs:
|
||||
if (
|
||||
any(f.fstype.find(fs_type) >= 0 for fs_type in allowed_fs_types)
|
||||
and f.mountpoint not in tracked_mnt_points
|
||||
):
|
||||
fs_stat.append(f)
|
||||
try:
|
||||
all_mounted_fs = psutil.disk_partitions(all=True)
|
||||
except (UnicodeDecodeError, PermissionError):
|
||||
logger.debug("Plugin - fs: PsUtil fetch failed")
|
||||
else:
|
||||
# Discard duplicates (#2299) and add entries matching allowed fs types
|
||||
tracked_mnt_points = {f.mountpoint for f in fs_stat}
|
||||
for f in all_mounted_fs:
|
||||
if (
|
||||
any(f.fstype.find(fs_type) >= 0 for fs_type in allowed_fs_types)
|
||||
and f.mountpoint not in tracked_mnt_points
|
||||
):
|
||||
fs_stat.append(f)
|
||||
|
||||
# Loop over fs
|
||||
for fs in fs_stat:
|
||||
# Hide the stats if the mount point is in the exclude list
|
||||
# # It avoids unnecessary call to PsUtil disk_usage
|
||||
# It avoids unnecessary call to PsUtil disk_usage
|
||||
if not self.is_display_any(fs.mountpoint, fs.device):
|
||||
continue
|
||||
|
||||
# Grab the disk usage
|
||||
try:
|
||||
fs_usage = psutil.disk_usage(fs.mountpoint)
|
||||
except OSError:
|
||||
# Correct issue #346
|
||||
# Disk is ejected during the command
|
||||
fs_usage = get_disk_usage(fs)
|
||||
if fs_usage is None:
|
||||
continue
|
||||
fs_current = {
|
||||
'device_name': fs.device,
|
||||
|
|
|
|||
|
|
@ -14,12 +14,6 @@ I am your father...
|
|||
|
||||
import copy
|
||||
import re
|
||||
import threading
|
||||
|
||||
try:
|
||||
import thread
|
||||
except ImportError:
|
||||
import _thread as thread
|
||||
|
||||
from glances.actions import GlancesActions
|
||||
from glances.events_list import glances_events
|
||||
|
|
@ -1218,25 +1212,7 @@ class GlancesPluginModel:
|
|||
|
||||
return wrapper
|
||||
|
||||
def _exit_after(second):
|
||||
"""Exit the function if it takes more than 'second' seconds to complete."""
|
||||
|
||||
def outer(fn):
|
||||
def inner(*args, **kwargs):
|
||||
timer = threading.Timer(second, thread.interrupt_main, args=[fn.__name__])
|
||||
timer.start()
|
||||
try:
|
||||
result = fn(*args, **kwargs)
|
||||
finally:
|
||||
timer.cancel()
|
||||
return result
|
||||
|
||||
return inner
|
||||
|
||||
return outer
|
||||
|
||||
# Mandatory to call the decorator in child classes
|
||||
_check_decorator = staticmethod(_check_decorator)
|
||||
_log_result_decorator = staticmethod(_log_result_decorator)
|
||||
_manage_rate = staticmethod(_manage_rate)
|
||||
_exit_after = staticmethod(_exit_after)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
import time
|
||||
from multiprocessing import Process, Queue
|
||||
|
||||
import psutil
|
||||
|
||||
|
||||
def exit_after(seconds, default=None):
|
||||
"""Exit the function if it takes more than 'second' seconds to complete.
|
||||
In this case, return the value of 'default' (default: None)."""
|
||||
|
||||
def handler(q, func, args, kwargs):
|
||||
q.put(func(*args, **kwargs))
|
||||
|
||||
def decorator(func):
|
||||
def wraps(*args, **kwargs):
|
||||
q = Queue()
|
||||
p = Process(target=handler, args=(q, func, args, kwargs))
|
||||
p.start()
|
||||
p.join(timeout=seconds)
|
||||
if p.is_alive():
|
||||
p.terminate()
|
||||
p.join()
|
||||
return default
|
||||
return q.get()
|
||||
|
||||
return wraps
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
class Issue3290:
|
||||
@exit_after(1, default=None)
|
||||
def blocking_io_call(self, fs):
|
||||
try:
|
||||
return psutil.disk_usage(fs)
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
|
||||
issue = Issue3290()
|
||||
while True:
|
||||
print(f"{time.time()} {issue.blocking_io_call('/home/nicolargo/tmp/hang')}")
|
||||
time.sleep(1)
|
||||
Loading…
Reference in New Issue