Merge branch 'develop' into fix-autodiscovery-failure

This commit is contained in:
Nicolas Hennion 2021-12-05 09:06:50 +01:00 committed by GitHub
commit 6a1311c42c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
91 changed files with 2048 additions and 2086 deletions

View File

@ -29,6 +29,10 @@ test: venv
./venv/bin/python ./unitest.py
./venv/bin/python ./unitest-restful.py
./venv/bin/python ./unitest-xmlrpc.py
./venv/bin/python -m black ./glances --check
format: venv
./venv/bin/python -m black ./glances
docs: venv-dev
./venv/bin/python -m glances -C ./conf/glances.conf --api-doc > ./docs/api.rst

View File

@ -1 +1,2 @@
py-spy
py-spy
black

View File

@ -45,6 +45,7 @@ except ImportError:
from glances.logger import logger
from glances.main import GlancesMain
from glances.timer import Counter
# Check locale
try:
locale.setlocale(locale.LC_ALL, '')
@ -132,11 +133,11 @@ def main():
# Log Glances and psutil version
logger.info('Start Glances {}'.format(__version__))
logger.info('{} {} ({}) and psutil {} detected'.format(
platform.python_implementation(),
platform.python_version(),
sys.executable,
psutil_version))
logger.info(
'{} {} ({}) and psutil {} detected'.format(
platform.python_implementation(), platform.python_version(), sys.executable, psutil_version
)
)
# Share global var
global core

View File

@ -73,14 +73,15 @@ class GlancesActions(object):
:return: True if the commands have been ran.
"""
if (self.get(stat_name) == criticality and not repeat) or \
not self.start_timer.finished():
if (self.get(stat_name) == criticality and not repeat) or not self.start_timer.finished():
# Action already executed => Exit
return False
logger.debug("{} action {} for {} ({}) with stats {}".format(
"Repeat" if repeat else "Run",
commands, stat_name, criticality, mustache_dict))
logger.debug(
"{} action {} for {} ({}) with stats {}".format(
"Repeat" if repeat else "Run", commands, stat_name, criticality, mustache_dict
)
)
# Run all actions in background
for cmd in commands:
@ -90,19 +91,13 @@ class GlancesActions(object):
else:
cmd_full = cmd
# Execute the action
logger.info("Action triggered for {} ({}): {}".format(stat_name,
criticality,
cmd_full))
logger.info("Action triggered for {} ({}): {}".format(stat_name, criticality, cmd_full))
try:
ret = secure_popen(cmd_full)
except OSError as e:
logger.error("Action error for {} ({}): {}".format(stat_name,
criticality,
e))
logger.error("Action error for {} ({}): {}".format(stat_name, criticality, e))
else:
logger.debug("Action result for {} ({}): {}".format(stat_name,
criticality,
ret))
logger.debug("Action result for {} ({}): {}".format(stat_name, criticality, ret))
self.set(stat_name, criticality)

View File

@ -52,7 +52,7 @@ class GlancesAmp(object):
# AMP name (= module name without glances_)
if name is None:
self.amp_name = self.__class__.__module__[len('glances_'):]
self.amp_name = self.__class__.__module__[len('glances_') :]
else:
self.amp_name = name
@ -83,8 +83,7 @@ class GlancesAmp(object):
# option1=opt1
amp_section = 'amp_' + self.amp_name
if (hasattr(config, 'has_section') and
config.has_section(amp_section)):
if hasattr(config, 'has_section') and config.has_section(amp_section):
logger.debug("AMP - {}: Load configuration".format(self.NAME))
for param, _ in config.items(amp_section):
try:
@ -102,7 +101,11 @@ class GlancesAmp(object):
# Refresh option is mandatory
for k in ['refresh']:
if k not in self.configs:
logger.warning("AMP - {}: Can not find configuration key {} in section {} (the AMP will be disabled)".format(self.NAME, k, self.amp_name))
logger.warning(
"AMP - {}: Can not find configuration key {} in section {} (the AMP will be disabled)".format(
self.NAME, k, self.amp_name
)
)
self.configs['enable'] = 'false'
else:
logger.debug("AMP - {} is disabled".format(self.NAME))
@ -187,7 +190,7 @@ class GlancesAmp(object):
self.configs['result'] = u(result)
def result(self):
""" Return the result of the AMP (as a string)"""
"""Return the result of the AMP (as a string)"""
ret = self.get('result')
if ret is not None:
ret = u(ret)

View File

@ -70,9 +70,11 @@ class Amp(GlancesAmp):
if res is None:
# Set the default message if command return None
# Default sum of CPU and MEM for the matching regex
self.set_result('CPU: {:.1f}% | MEM: {:.1f}%'.format(
sum([p['cpu_percent'] for p in process_list]),
sum([p['memory_percent'] for p in process_list])))
self.set_result(
'CPU: {:.1f}% | MEM: {:.1f}%'.format(
sum([p['cpu_percent'] for p in process_list]), sum([p['memory_percent'] for p in process_list])
)
)
return self.result()
# Run command(s)
# Comma separated commands can be executed

View File

@ -58,7 +58,9 @@ class AmpsList(object):
# Display a warning (deprecated) message if the monitor section exist
if "monitor" in self.config.sections():
logger.warning("A deprecated [monitor] section exists in the Glances configuration file. You should use the new Applications Monitoring Process module instead (http://glances.readthedocs.io/en/develop/aoa/amps.html).")
logger.warning(
"A deprecated [monitor] section exists in the Glances configuration file. You should use the new Applications Monitoring Process module instead (http://glances.readthedocs.io/en/develop/aoa/amps.html)."
)
header = "glances_"
# For each AMP scrip, call the load_config method
@ -125,9 +127,7 @@ class AmpsList(object):
if len(amps_list) > 0:
# At least one process is matching the regex
logger.debug("AMPS: {} processes {} detected ({})".format(len(amps_list),
k,
amps_list))
logger.debug("AMPS: {} processes {} detected ({})".format(len(amps_list), k, amps_list))
# Call the AMP update method
thread = threading.Thread(target=v.update_wrapper, args=[amps_list])
thread.start()
@ -150,20 +150,20 @@ class AmpsList(object):
# Search in both cmdline and name (for kernel thread, see #1261)
for p in processlist:
add_it = False
if (re.search(amp_value.regex(), p['name']) is not None):
if re.search(amp_value.regex(), p['name']) is not None:
add_it = True
else:
if p['cmdline'] is None:
# See issue #1689 (thanks to @darylkell)
continue
for c in p['cmdline']:
if (re.search(amp_value.regex(), c) is not None):
if re.search(amp_value.regex(), c) is not None:
add_it = True
break
if add_it:
ret.append({'pid': p['pid'],
'cpu_percent': p['cpu_percent'],
'memory_percent': p['memory_percent']})
ret.append(
{'pid': p['pid'], 'cpu_percent': p['cpu_percent'], 'memory_percent': p['memory_percent']}
)
except (TypeError, KeyError) as e:
logger.debug("Can not build AMPS list ({})".format(e))

View File

@ -21,8 +21,8 @@
from datetime import datetime
class GlancesAttribute(object):
class GlancesAttribute(object):
def __init__(self, name, description='', history_max_size=None):
"""Init the attribute
@ -47,6 +47,7 @@ class GlancesAttribute(object):
"""
Properties for the attribute name
"""
@property
def name(self):
return self._name
@ -58,6 +59,7 @@ class GlancesAttribute(object):
"""
Properties for the attribute description
"""
@property
def description(self):
return self._description
@ -69,6 +71,7 @@ class GlancesAttribute(object):
"""
Properties for the attribute value
"""
@property
def value(self):
if self.history_len() > 0:
@ -88,6 +91,7 @@ class GlancesAttribute(object):
"""
Properties for the attribute history
"""
@property
def history(self):
return self._history

View File

@ -26,12 +26,8 @@ from glances.globals import BSD
from glances.logger import logger
try:
from zeroconf import (
__version__ as __zeroconf_version,
ServiceBrowser,
ServiceInfo,
Zeroconf
)
from zeroconf import __version__ as __zeroconf_version, ServiceBrowser, ServiceInfo, Zeroconf
zeroconf_tag = True
except ImportError:
zeroconf_tag = False
@ -78,10 +74,10 @@ class AutoDiscovered(object):
'username': 'glances', # Default username
'password': '', # Default password
'status': 'UNKNOWN', # Server status: 'UNKNOWN', 'OFFLINE', 'ONLINE', 'PROTECTED'
'type': 'DYNAMIC'} # Server type: 'STATIC' or 'DYNAMIC'
'type': 'DYNAMIC',
} # Server type: 'STATIC' or 'DYNAMIC'
self._server_list.append(new_server)
logger.debug("Updated servers list (%s servers): %s" %
(len(self._server_list), self._server_list))
logger.debug("Updated servers list (%s servers): %s" % (len(self._server_list), self._server_list))
def remove_server(self, name):
"""Remove a server from the dict."""
@ -90,11 +86,9 @@ class AutoDiscovered(object):
try:
self._server_list.remove(i)
logger.debug("Remove server %s from the list" % name)
logger.debug("Updated servers list (%s servers): %s" % (
len(self._server_list), self._server_list))
logger.debug("Updated servers list (%s servers): %s" % (len(self._server_list), self._server_list))
except ValueError:
logger.error(
"Cannot remove server %s from the list" % name)
logger.error("Cannot remove server %s from the list" % name)
class GlancesAutoDiscoverListener(object):
@ -122,8 +116,7 @@ class GlancesAutoDiscoverListener(object):
"""
if srv_type != zeroconf_type:
return False
logger.debug("Check new Zeroconf server: %s / %s" %
(srv_type, srv_name))
logger.debug("Check new Zeroconf server: %s / %s" % (srv_type, srv_name))
info = zeroconf.get_service_info(srv_type, srv_name)
if info and (info.addresses or info.parsed_addresses):
address = info.addresses[0] if info.addresses else info.parsed_addresses[0]
@ -132,8 +125,7 @@ class GlancesAutoDiscoverListener(object):
# Add server to the global dict
self.servers.add_server(srv_name, new_server_ip, new_server_port)
logger.info("New Glances server detected (%s from %s:%s)" %
(srv_name, new_server_ip, new_server_port))
logger.info("New Glances server detected (%s from %s:%s)" % (srv_name, new_server_ip, new_server_port))
else:
logger.warning("New Glances server detected, but failed to be get Zeroconf ServiceInfo ")
return True
@ -141,8 +133,7 @@ class GlancesAutoDiscoverListener(object):
def remove_service(self, zeroconf, srv_type, srv_name):
"""Remove the server from the list."""
self.servers.remove_server(srv_name)
logger.info(
"Glances server %s removed from the autodetect list" % srv_name)
logger.info("Glances server %s removed from the autodetect list" % srv_name)
class GlancesAutoDiscoverServer(object):
@ -159,8 +150,7 @@ class GlancesAutoDiscoverServer(object):
self.zeroconf_enable_tag = False
else:
self.listener = GlancesAutoDiscoverListener()
self.browser = ServiceBrowser(
self.zeroconf, zeroconf_type, self.listener)
self.browser = ServiceBrowser(self.zeroconf, zeroconf_type, self.listener)
self.zeroconf_enable_tag = True
else:
logger.error("Cannot start autodiscover mode (Zeroconf lib is not installed)")
@ -213,29 +203,27 @@ class GlancesAutoDiscoverClient(object):
try:
self.info = ServiceInfo(
zeroconf_type,
'{}:{}.{}'.format(hostname,
args.port,
zeroconf_type),
'{}:{}.{}'.format(hostname, args.port, zeroconf_type),
address=socket.inet_pton(address_family, zeroconf_bind_address),
port=args.port,
weight=0,
priority=0,
properties={},
server=hostname)
server=hostname,
)
except TypeError:
# Manage issue 1663 with breaking change on ServiceInfo method
# address (only one address) is replaced by addresses (list of addresses)
self.info = ServiceInfo(
zeroconf_type,
name='{}:{}.{}'.format(hostname,
args.port,
zeroconf_type),
name='{}:{}.{}'.format(hostname, args.port, zeroconf_type),
addresses=[socket.inet_pton(address_family, zeroconf_bind_address)],
port=args.port,
weight=0,
priority=0,
properties={},
server=hostname)
server=hostname,
)
try:
self.zeroconf.register_service(self.info)
except Exception as e:
@ -249,6 +237,7 @@ class GlancesAutoDiscoverClient(object):
def find_active_ip_address():
"""Try to find the active IP addresses."""
import netifaces
# Interface of the default gateway
gateway_itf = netifaces.gateways()['default'][netifaces.AF_INET][1]
# IP address for the interface

View File

@ -57,8 +57,7 @@ class GlancesClient(object):
# Build the URI
if args.password != "":
self.uri = 'http://{}:{}@{}:{}'.format(args.username, args.password,
args.client, args.port)
self.uri = 'http://{}:{}@{}:{}'.format(args.username, args.password, args.client, args.port)
else:
self.uri = 'http://{}:{}'.format(args.client, args.port)
logger.debug("Try to connect to {}".format(self.uri))
@ -130,8 +129,12 @@ class GlancesClient(object):
self.stats.set_plugins(json.loads(self.client.getAllPlugins()))
logger.debug("Client version: {} / Server version: {}".format(__version__, client_version))
else:
self.log_and_exit(('Client and server not compatible: '
'Client version: {} / Server version: {}'.format(__version__, client_version)))
self.log_and_exit(
(
'Client and server not compatible: '
'Client version: {} / Server version: {}'.format(__version__, client_version)
)
)
return False
return True
@ -248,9 +251,9 @@ class GlancesClient(object):
# Update the screen
if not self.quiet:
exit_key = self.screen.update(self.stats,
cs_status=cs_status,
return_to_browser=self.return_to_browser)
exit_key = self.screen.update(
self.stats, cs_status=cs_status, return_to_browser=self.return_to_browser
)
# Export stats using export modules
self.stats.export(self.stats)

View File

@ -86,8 +86,7 @@ class GlancesClientBrowser(object):
clear_password = self.password.get_password(server['name'])
if clear_password is not None:
server['password'] = self.password.sha256_hash(clear_password)
return 'http://{}:{}@{}:{}'.format(server['username'], server['password'],
server['ip'], server['port'])
return 'http://{}:{}@{}:{}'.format(server['username'], server['password'], server['ip'], server['port'])
else:
return 'http://{}:{}'.format(server['ip'], server['port'])
@ -104,8 +103,7 @@ class GlancesClientBrowser(object):
try:
s = ServerProxy(uri, transport=t)
except Exception as e:
logger.warning(
"Client browser couldn't create socket {}: {}".format(uri, e))
logger.warning("Client browser couldn't create socket {}: {}".format(uri, e))
else:
# Mandatory stats
try:
@ -117,8 +115,7 @@ class GlancesClientBrowser(object):
# OS (Human Readable name)
server['hr_name'] = json.loads(s.getSystem())['hr_name']
except (socket.error, Fault, KeyError) as e:
logger.debug(
"Error while grabbing stats form {}: {}".format(uri, e))
logger.debug("Error while grabbing stats form {}: {}".format(uri, e))
server['status'] = 'OFFLINE'
except ProtocolError as e:
if e.errcode == 401:
@ -139,8 +136,7 @@ class GlancesClientBrowser(object):
load_min5 = json.loads(s.getLoad())['min5']
server['load_min5'] = '{:.2f}'.format(load_min5)
except Exception as e:
logger.warning(
"Error while grabbing stats form {}: {}".format(uri, e))
logger.warning("Error while grabbing stats form {}: {}".format(uri, e))
return server
@ -151,19 +147,18 @@ class GlancesClientBrowser(object):
# Connection can take time
# Display a popup
self.screen.display_popup(
'Connect to {}:{}'.format(server['name'], server['port']), duration=1)
self.screen.display_popup('Connect to {}:{}'.format(server['name'], server['port']), duration=1)
# A password is needed to access to the server's stats
if server['password'] is None:
# First of all, check if a password is available in the [passwords] section
clear_password = self.password.get_password(server['name'])
if (clear_password is None or self.get_servers_list()
[self.screen.active_server]['status'] == 'PROTECTED'):
if clear_password is None or self.get_servers_list()[self.screen.active_server]['status'] == 'PROTECTED':
# Else, the password should be enter by the user
# Display a popup to enter password
clear_password = self.screen.display_popup(
'Password needed for {}: '.format(server['name']), is_input=True)
'Password needed for {}: '.format(server['name']), is_input=True
)
# Store the password for the selected server
if clear_password is not None:
self.set_in_selected('password', self.password.sha256_hash(clear_password))
@ -184,8 +179,8 @@ class GlancesClientBrowser(object):
# Test if client and server are in the same major version
if not client.login():
self.screen.display_popup(
"Sorry, cannot connect to '{}'\n"
"See '{}' for more details".format(server['name'], LOG_FILENAME))
"Sorry, cannot connect to '{}'\n" "See '{}' for more details".format(server['name'], LOG_FILENAME)
)
# Set the ONLINE status for the selected server
self.set_in_selected('status', 'OFFLINE')
@ -254,8 +249,8 @@ class GlancesClientBrowser(object):
# Static list then dynamic one
if self.screen.active_server >= len(self.static_server.get_servers_list()):
self.autodiscover_server.set_server(
self.screen.active_server - len(self.static_server.get_servers_list()),
key, value)
self.screen.active_server - len(self.static_server.get_servers_list()), key, value
)
else:
self.static_server.set_server(self.screen.active_server, key, value)

View File

@ -46,6 +46,7 @@ if PY3:
# Correct issue #1025 by monkey path the xmlrpc lib
from defusedxml.xmlrpc import monkey_patch
monkey_patch()
input = input
@ -121,13 +122,13 @@ if PY3:
def system_exec(command):
"""Execute a system command and return the result as a str"""
try:
res = subprocess.run(command.split(' '),
stdout=subprocess.PIPE).stdout.decode('utf-8')
res = subprocess.run(command.split(' '), stdout=subprocess.PIPE).stdout.decode('utf-8')
except Exception as e:
logger.debug('Can not evaluate command {} ({})'.format(command, e))
res = ''
return res.rstrip()
else:
from future.utils import bytes_to_native_str as n
import Queue as queue
@ -140,6 +141,7 @@ else:
# Correct issue #1025 by monkey path the xmlrpc lib
from defusedxml.xmlrpc import monkey_patch
monkey_patch()
input = raw_input
@ -233,7 +235,7 @@ def subsample(data, sampling):
if len(data) <= sampling:
return data
sampling_length = int(round(len(data) / float(sampling)))
return [mean(data[s * sampling_length:(s + 1) * sampling_length]) for s in range(0, sampling)]
return [mean(data[s * sampling_length : (s + 1) * sampling_length]) for s in range(0, sampling)]
def time_serie_subsample(data, sampling):
@ -248,8 +250,8 @@ def time_serie_subsample(data, sampling):
t = [t[0] for t in data]
v = [t[1] for t in data]
sampling_length = int(round(len(data) / float(sampling)))
t_sub_sampled = [t[s * sampling_length:(s + 1) * sampling_length][0] for s in range(0, sampling)]
v_sub_sampled = [mean(v[s * sampling_length:(s + 1) * sampling_length]) for s in range(0, sampling)]
t_sub_sampled = [t[s * sampling_length : (s + 1) * sampling_length][0] for s in range(0, sampling)]
v_sub_sampled = [mean(v[s * sampling_length : (s + 1) * sampling_length]) for s in range(0, sampling)]
return list(zip(t_sub_sampled, v_sub_sampled))
@ -272,6 +274,7 @@ def is_admin():
if os.name == 'nt':
import ctypes
import traceback
# WARNING: requires Windows XP SP2 or higher!
try:
return ctypes.windll.shell32.IsUserAnAdmin()

View File

@ -59,13 +59,11 @@ def user_cache_dir():
- Windows: {%LOCALAPPDATA%,%APPDATA%}\glances\cache
"""
if WINDOWS:
path = os.path.join(os.environ.get('LOCALAPPDATA') or os.environ.get('APPDATA'),
'glances', 'cache')
path = os.path.join(os.environ.get('LOCALAPPDATA') or os.environ.get('APPDATA'), 'glances', 'cache')
elif MACOS:
path = os.path.expanduser('~/Library/Caches/glances')
else:
path = os.path.join(os.environ.get('XDG_CACHE_HOME') or os.path.expanduser('~/.cache'),
'glances')
path = os.path.join(os.environ.get('XDG_CACHE_HOME') or os.path.expanduser('~/.cache'), 'glances')
return path
@ -206,16 +204,26 @@ class Config(object):
self.set_default_cwc('cpu', 'steal')
# By default I/O wait should be lower than 1/number of CPU cores
iowait_bottleneck = (1.0 / multiprocessing.cpu_count()) * 100.0
self.set_default_cwc('cpu', 'iowait',
[str(iowait_bottleneck - (iowait_bottleneck * 0.20)),
str(iowait_bottleneck - (iowait_bottleneck * 0.10)),
str(iowait_bottleneck)])
self.set_default_cwc(
'cpu',
'iowait',
[
str(iowait_bottleneck - (iowait_bottleneck * 0.20)),
str(iowait_bottleneck - (iowait_bottleneck * 0.10)),
str(iowait_bottleneck),
],
)
# Context switches bottleneck identification #1212
ctx_switches_bottleneck = (500000 * 0.10) * multiprocessing.cpu_count()
self.set_default_cwc('cpu', 'ctx_switches',
[str(ctx_switches_bottleneck - (ctx_switches_bottleneck * 0.20)),
str(ctx_switches_bottleneck - (ctx_switches_bottleneck * 0.10)),
str(ctx_switches_bottleneck)])
self.set_default_cwc(
'cpu',
'ctx_switches',
[
str(ctx_switches_bottleneck - (ctx_switches_bottleneck * 0.20)),
str(ctx_switches_bottleneck - (ctx_switches_bottleneck * 0.10)),
str(ctx_switches_bottleneck),
],
)
# Per-CPU
if not self.parser.has_section('percpu'):
@ -288,9 +296,7 @@ class Config(object):
"""Return info about the existence of a section."""
return self.parser.has_section(section)
def set_default_cwc(self, section,
option_header=None,
cwc=['50', '70', '90']):
def set_default_cwc(self, section, option_header=None, cwc=['50', '70', '90']):
"""Set default values for careful, warning and critical."""
if option_header is None:
header = ''
@ -300,14 +306,12 @@ class Config(object):
self.set_default(section, header + 'warning', cwc[1])
self.set_default(section, header + 'critical', cwc[2])
def set_default(self, section, option,
default):
def set_default(self, section, option, default):
"""If the option did not exist, create a default value."""
if not self.parser.has_option(section, option):
self.parser.set(section, option, default)
def get_value(self, section, option,
default=None):
def get_value(self, section, option, default=None):
"""Get the value of an option, if it exists.
If it did not exist, then return the default value.

View File

@ -30,11 +30,7 @@ class CpuPercent(object):
"""Get and store the CPU percent."""
def __init__(self, cached_timer_cpu=3):
self.cpu_info = {
'cpu_name': None,
'cpu_hz_current': None,
'cpu_hz': None
}
self.cpu_info = {'cpu_name': None, 'cpu_hz_current': None, 'cpu_hz': None}
self.cpu_percent = 0
self.percpu_percent = []
@ -109,14 +105,15 @@ class CpuPercent(object):
# Never update more than 1 time per cached_timer_cpu
if self.timer_percpu.finished():
self.percpu_percent = []
for cpu_number, cputimes in enumerate(psutil.cpu_times_percent(interval=0.0,
percpu=True)):
cpu = {'key': self.get_key(),
'cpu_number': cpu_number,
'total': round(100 - cputimes.idle, 1),
'user': cputimes.user,
'system': cputimes.system,
'idle': cputimes.idle}
for cpu_number, cputimes in enumerate(psutil.cpu_times_percent(interval=0.0, percpu=True)):
cpu = {
'key': self.get_key(),
'cpu_number': cpu_number,
'total': round(100 - cputimes.idle, 1),
'user': cputimes.user,
'system': cputimes.system,
'idle': cputimes.idle,
}
# The following stats are for API purposes only
if hasattr(cputimes, 'nice'):
cpu['nice'] = cputimes.nice

View File

@ -99,8 +99,7 @@ class GlancesEvents(object):
if glances_processes.auto_sort:
glances_processes.set_sort_key('auto')
def add(self, event_state, event_type, event_value,
proc_list=None, proc_desc="", peak_time=6):
def add(self, event_state, event_type, event_value, proc_list=None, proc_desc="", peak_time=6):
"""Add a new item to the logs list.
If 'event' is a 'new one', add it at the beginning of the list.
@ -113,17 +112,14 @@ class GlancesEvents(object):
event_index = self.__event_exist(event_type)
if event_index < 0:
# Event did not exist, add it
self._create_event(event_state, event_type, event_value,
proc_list, proc_desc, peak_time)
self._create_event(event_state, event_type, event_value, proc_list, proc_desc, peak_time)
else:
# Event exist, update it
self._update_event(event_index, event_state, event_type, event_value,
proc_list, proc_desc, peak_time)
self._update_event(event_index, event_state, event_type, event_value, proc_list, proc_desc, peak_time)
return self.len()
def _create_event(self, event_state, event_type, event_value,
proc_list, proc_desc, peak_time):
def _create_event(self, event_state, event_type, event_value, proc_list, proc_desc, peak_time):
"""Add a new item in the log list.
Item is added only if the criticality (event_state) is WARNING or CRITICAL.
@ -147,7 +143,8 @@ class GlancesEvents(object):
1, # COUNT
[], # TOP 3 PROCESS LIST
proc_desc, # MONITORED PROCESSES DESC
glances_processes.sort_key] # TOP PROCESS SORT KEY
glances_processes.sort_key,
] # TOP PROCESS SORT KEY
# Add the item to the list
self.events_list.insert(0, item)
@ -160,8 +157,7 @@ class GlancesEvents(object):
else:
return False
def _update_event(self, event_index, event_state, event_type, event_value,
proc_list, proc_desc, peak_time):
def _update_event(self, event_index, event_state, event_type, event_value, proc_list, proc_desc, peak_time):
"""Update an event in the list"""
if event_state == "OK" or event_state == "CAREFUL":
# Reset the automatic process sort key
@ -183,23 +179,19 @@ class GlancesEvents(object):
if event_state == "CRITICAL":
self.events_list[event_index][2] = event_state
# Min value
self.events_list[event_index][6] = min(self.events_list[event_index][6],
event_value)
self.events_list[event_index][6] = min(self.events_list[event_index][6], event_value)
# Max value
self.events_list[event_index][4] = max(self.events_list[event_index][4],
event_value)
self.events_list[event_index][4] = max(self.events_list[event_index][4], event_value)
# Average value
self.events_list[event_index][7] += event_value
self.events_list[event_index][8] += 1
self.events_list[event_index][5] = (self.events_list[event_index][7] /
self.events_list[event_index][8])
self.events_list[event_index][5] = self.events_list[event_index][7] / self.events_list[event_index][8]
# TOP PROCESS LIST (only for CRITICAL ALERT)
if event_state == "CRITICAL":
events_sort_key = self.get_event_sort_key(event_type)
# Sort the current process list to retrieve the TOP 3 processes
self.events_list[event_index][9] = sort_stats(proc_list,
events_sort_key)[0:3]
self.events_list[event_index][9] = sort_stats(proc_list, events_sort_key)[0:3]
self.events_list[event_index][11] = events_sort_key
# MONITORED PROCESSES DESC

View File

@ -52,13 +52,11 @@ class Export(GlancesExport):
self.password = None
# Load the Cassandra configuration file section
self.export_enable = self.load_conf('cassandra',
mandatories=['host', 'port', 'keyspace'],
options=['protocol_version',
'replication_factor',
'table',
'username',
'password'])
self.export_enable = self.load_conf(
'cassandra',
mandatories=['host', 'port', 'keyspace'],
options=['protocol_version', 'replication_factor', 'table', 'username', 'password'],
)
if not self.export_enable:
sys.exit(2)
@ -71,15 +69,16 @@ class Export(GlancesExport):
return None
# if username and/or password are not set the connection will try to connect with no auth
auth_provider = PlainTextAuthProvider(
username=self.username, password=self.password)
auth_provider = PlainTextAuthProvider(username=self.username, password=self.password)
# Cluster
try:
cluster = Cluster([self.host],
port=int(self.port),
protocol_version=int(self.protocol_version),
auth_provider=auth_provider)
cluster = Cluster(
[self.host],
port=int(self.port),
protocol_version=int(self.protocol_version),
auth_provider=auth_provider,
)
session = cluster.connect()
except Exception as e:
logger.critical("Cannot connect to Cassandra cluster '%s:%s' (%s)" % (self.host, self.port, e))
@ -90,17 +89,25 @@ class Export(GlancesExport):
session.set_keyspace(self.keyspace)
except InvalidRequest as e:
logger.info("Create keyspace {} on the Cassandra cluster".format(self.keyspace))
c = "CREATE KEYSPACE %s WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': '%s' }" % (self.keyspace, self.replication_factor)
c = "CREATE KEYSPACE %s WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': '%s' }" % (
self.keyspace,
self.replication_factor,
)
session.execute(c)
session.set_keyspace(self.keyspace)
logger.info(
"Stats will be exported to Cassandra cluster {} ({}) in keyspace {}".format(
cluster.metadata.cluster_name, cluster.metadata.all_hosts(), self.keyspace))
cluster.metadata.cluster_name, cluster.metadata.all_hosts(), self.keyspace
)
)
# Table
try:
session.execute("CREATE TABLE %s (plugin text, time timeuuid, stat map<text,float>, PRIMARY KEY (plugin, time)) WITH CLUSTERING ORDER BY (time DESC)" % self.table)
session.execute(
"CREATE TABLE %s (plugin text, time timeuuid, stat map<text,float>, PRIMARY KEY (plugin, time)) WITH CLUSTERING ORDER BY (time DESC)"
% self.table
)
except Exception:
logger.debug("Cassandra table %s already exist" % self.table)
@ -118,10 +125,7 @@ class Export(GlancesExport):
stmt = "INSERT INTO {} (plugin, time, stat) VALUES (?, ?, ?)".format(self.table)
query = self.session.prepare(stmt)
self.session.execute(
query,
(name, uuid_from_time(datetime.now()), data)
)
self.session.execute(query, (name, uuid_from_time(datetime.now()), data))
except Exception as e:
logger.error("Cannot export {} stats to Cassandra ({})".format(name, e))

View File

@ -45,9 +45,7 @@ class Export(GlancesExport):
self.password = None
# Load the Cassandra configuration file section
self.export_enable = self.load_conf('couchdb',
mandatories=['host', 'port', 'db'],
options=['user', 'password'])
self.export_enable = self.load_conf('couchdb', mandatories=['host', 'port', 'db'], options=['user', 'password'])
if not self.export_enable:
sys.exit(2)
@ -60,13 +58,9 @@ class Export(GlancesExport):
return None
if self.user is None:
server_uri = 'http://{}:{}/'.format(self.host,
self.port)
server_uri = 'http://{}:{}/'.format(self.host, self.port)
else:
server_uri = 'http://{}:{}@{}:{}/'.format(self.user,
self.password,
self.host,
self.port)
server_uri = 'http://{}:{}@{}:{}/'.format(self.user, self.password, self.host, self.port)
try:
s = couchdb.Server(server_uri)

View File

@ -94,16 +94,14 @@ class Export(GlancesExport):
for stat in all_stats[plugin]:
# First line: header
if self.first_line:
csv_header += ('{}_{}_{}'.format(
plugin, self.get_item_key(stat), item) for item in stat)
csv_header += ('{}_{}_{}'.format(plugin, self.get_item_key(stat), item) for item in stat)
# Others lines: stats
csv_data += itervalues(stat)
elif isinstance(all_stats[plugin], dict):
# First line: header
if self.first_line:
fieldnames = iterkeys(all_stats[plugin])
csv_header += ('{}_{}'.format(plugin, fieldname)
for fieldname in fieldnames)
csv_header += ('{}_{}'.format(plugin, fieldname) for fieldname in fieldnames)
# Others lines: stats
csv_data += itervalues(all_stats[plugin])

View File

@ -41,9 +41,7 @@ class Export(GlancesExport):
self.index = None
# Load the ES configuration file
self.export_enable = self.load_conf('elasticsearch',
mandatories=['host', 'port', 'index'],
options=[])
self.export_enable = self.load_conf('elasticsearch', mandatories=['host', 'port', 'index'], options=[])
if not self.export_enable:
sys.exit(2)
@ -70,8 +68,7 @@ class Export(GlancesExport):
logger.debug("Export {} stats to ElasticSearch".format(name))
# Generate index name with the index field + current day
index = '{}-{}'.format(self.index,
datetime.utcnow().strftime("%Y.%m.%d"))
index = '{}-{}'.format(self.index, datetime.utcnow().strftime("%Y.%m.%d"))
# Create DB input
# https://elasticsearch-py.readthedocs.io/en/master/helpers.html
@ -81,16 +78,12 @@ class Export(GlancesExport):
"_index": index,
"_id": '{}.{}'.format(name, dt_now),
"_type": 'glances-{}'.format(name),
"_source": {
"plugin": name,
"timestamp": dt_now
}
"_source": {"plugin": name, "timestamp": dt_now},
}
action['_source'].update(zip(columns, [str(p) for p in points]))
actions.append(action)
logger.debug(
"Exporting the following object to elasticsearch: {}".format(action))
logger.debug("Exporting the following object to elasticsearch: {}".format(action))
# Write input to the ES index
try:

View File

@ -36,26 +36,28 @@ class GlancesExport(object):
# For the moment, only the below plugins can be exported
# @TODO: remove this part and make all plugins exportable (see issue #1556)
# @TODO: also make this list configurable by the user (see issue #1443)
exportable_plugins = ['cpu',
'percpu',
'load',
'mem',
'memswap',
'network',
'diskio',
'fs',
'processcount',
'ip',
'system',
'uptime',
'sensors',
'docker',
'gpu']
exportable_plugins = [
'cpu',
'percpu',
'load',
'mem',
'memswap',
'network',
'diskio',
'fs',
'processcount',
'ip',
'system',
'uptime',
'sensors',
'docker',
'gpu',
]
def __init__(self, config=None, args=None):
"""Init the export class."""
# Export name (= module name without glances_)
self.export_name = self.__class__.__module__[len('glances_'):]
self.export_name = self.__class__.__module__[len('glances_') :]
logger.debug("Init export module %s" % self.export_name)
# Init the config & args

View File

@ -41,21 +41,14 @@ class Export(GlancesExport):
super(Export, self).__init__(config=config, args=args)
# Load the Graph configuration file section (is exists)
self.export_enable = self.load_conf('graph',
options=['path',
'generate_every',
'width',
'height',
'style'])
self.export_enable = self.load_conf('graph', options=['path', 'generate_every', 'width', 'height', 'style'])
# Manage options (command line arguments overwrite configuration file)
self.path = args.export_graph_path or self.path
self.generate_every = int(getattr(self, 'generate_every', 0))
self.width = int(getattr(self, 'width', 800))
self.height = int(getattr(self, 'height', 600))
self.style = getattr(pygal.style,
getattr(self, 'style', 'DarkStyle'),
pygal.style.DarkStyle)
self.style = getattr(pygal.style, getattr(self, 'style', 'DarkStyle'), pygal.style.DarkStyle)
# Create export folder
try:
@ -125,16 +118,17 @@ class Export(GlancesExport):
if data == {}:
return False
chart = DateTimeLine(title=title.capitalize(),
width=self.width,
height=self.height,
style=self.style,
show_dots=False,
legend_at_bottom=True,
x_label_rotation=20,
x_value_formatter=lambda dt: dt.strftime('%Y/%m/%d %H:%M:%S'))
chart = DateTimeLine(
title=title.capitalize(),
width=self.width,
height=self.height,
style=self.style,
show_dots=False,
legend_at_bottom=True,
x_label_rotation=20,
x_value_formatter=lambda dt: dt.strftime('%Y/%m/%d %H:%M:%S'),
)
for k, v in iteritems(time_serie_subsample(data, self.width)):
chart.add(k, v)
chart.render_to_file(os.path.join(self.path,
title + '.svg'))
chart.render_to_file(os.path.join(self.path, title + '.svg'))
return True

View File

@ -46,11 +46,7 @@ class Export(GlancesExport):
self.system_name = None
# Load the configuration file
self.export_enable = self.load_conf('graphite',
mandatories=['host',
'port'],
options=['prefix',
'system_name'])
self.export_enable = self.load_conf('graphite', mandatories=['host', 'port'], options=['prefix', 'system_name'])
if not self.export_enable:
sys.exit(2)
@ -73,27 +69,27 @@ class Export(GlancesExport):
try:
if self.system_name is None:
client = GraphiteClient(graphite_server=self.host,
graphite_port=self.port,
prefix=self.prefix,
lowercase_metric_names=True,
debug=self.debug)
client = GraphiteClient(
graphite_server=self.host,
graphite_port=self.port,
prefix=self.prefix,
lowercase_metric_names=True,
debug=self.debug,
)
else:
client = GraphiteClient(graphite_server=self.host,
graphite_port=self.port,
prefix=self.prefix,
system_name=self.system_name,
lowercase_metric_names=True,
debug=self.debug)
client = GraphiteClient(
graphite_server=self.host,
graphite_port=self.port,
prefix=self.prefix,
system_name=self.system_name,
lowercase_metric_names=True,
debug=self.debug,
)
except Exception as e:
logger.error("Can not write data to Graphite server: {}:{} ({})".format(self.host,
self.port,
e))
logger.error("Can not write data to Graphite server: {}:{} ({})".format(self.host, self.port, e))
client = None
else:
logger.info(
"Stats will be exported to Graphite server: {}:{}".format(self.host,
self.port))
logger.info("Stats will be exported to Graphite server: {}:{}".format(self.host, self.port))
return client
@ -101,12 +97,8 @@ class Export(GlancesExport):
"""Export the stats to the Graphite server."""
if self.client is None:
return False
before_filtering_dict = dict(zip(
[normalize('{}.{}'.format(name, i)) for i in columns],
points))
after_filtering_dict = dict(
filter(lambda i: isinstance(i[1], Number),
before_filtering_dict.items()))
before_filtering_dict = dict(zip([normalize('{}.{}'.format(name, i)) for i in columns], points))
after_filtering_dict = dict(filter(lambda i: isinstance(i[1], Number), before_filtering_dict.items()))
try:
self.client.send_dict(after_filtering_dict)
except Exception as e:

View File

@ -48,13 +48,9 @@ class Export(GlancesExport):
self.hostname = None
# Load the InfluxDB configuration file
self.export_enable = self.load_conf('influxdb',
mandatories=['host', 'port',
'user', 'password',
'db'],
options=['protocol',
'prefix',
'tags'])
self.export_enable = self.load_conf(
'influxdb', mandatories=['host', 'port', 'user', 'password', 'db'], options=['protocol', 'prefix', 'tags']
)
if not self.export_enable:
sys.exit(2)
@ -76,21 +72,22 @@ class Export(GlancesExport):
ssl = False
try:
db = InfluxDBClient(host=self.host,
port=self.port,
ssl=ssl,
verify_ssl=False,
username=self.user,
password=self.password,
database=self.db)
db = InfluxDBClient(
host=self.host,
port=self.port,
ssl=ssl,
verify_ssl=False,
username=self.user,
password=self.password,
database=self.db,
)
get_all_db = [i['name'] for i in db.get_list_database()]
except InfluxDBClientError as e:
logger.critical("Cannot connect to InfluxDB database '%s' (%s)" % (self.db, e))
sys.exit(2)
if self.db in get_all_db:
logger.info(
"Stats will be exported to InfluxDB server: {}".format(db._baseurl))
logger.info("Stats will be exported to InfluxDB server: {}".format(db._baseurl))
else:
logger.critical("InfluxDB database '%s' did not exist. Please create it" % self.db)
sys.exit(2)
@ -116,9 +113,11 @@ class Export(GlancesExport):
for measurement in keys_list:
# Manage field
if measurement is not None:
fields = {k.replace('{}.'.format(measurement), ''): data_dict[k]
for k in data_dict
if k.startswith('{}.'.format(measurement))}
fields = {
k.replace('{}.'.format(measurement), ''): data_dict[k]
for k in data_dict
if k.startswith('{}.'.format(measurement))
}
else:
fields = data_dict
# Transform to InfluxDB data model
@ -147,9 +146,7 @@ class Export(GlancesExport):
# Add the hostname as a tag
tags['hostname'] = self.hostname
# Add the measurement to the list
ret.append({'measurement': name,
'tags': tags,
'fields': fields})
ret.append({'measurement': name, 'tags': tags, 'fields': fields})
return ret
def export(self, name, columns, points):
@ -162,8 +159,7 @@ class Export(GlancesExport):
logger.debug("Cannot export empty {} stats to InfluxDB".format(name))
else:
try:
self.client.write_points(self._normalize(name, columns, points),
time_precision="s")
self.client.write_points(self._normalize(name, columns, points), time_precision="s")
except Exception as e:
# Log level set to debug instead of error (see: issue #1561)
logger.debug("Cannot export {} stats to InfluxDB ({})".format(name, e))

View File

@ -47,13 +47,11 @@ class Export(GlancesExport):
self.hostname = None
# Load the InfluxDB configuration file
self.export_enable = self.load_conf('influxdb2',
mandatories=['host', 'port',
'user', 'password',
'org', 'bucket', 'token'],
options=['protocol',
'prefix',
'tags'])
self.export_enable = self.load_conf(
'influxdb2',
mandatories=['host', 'port', 'user', 'password', 'org', 'bucket', 'token'],
options=['protocol', 'prefix', 'tags'],
)
if not self.export_enable:
sys.exit(2)
@ -71,26 +69,27 @@ class Export(GlancesExport):
url = '{}://{}:{}'.format(self.protocol, self.host, self.port)
try:
# See docs: https://influxdb-client.readthedocs.io/en/stable/api.html#influxdbclient
client = InfluxDBClient(url=url,
enable_gzip=False,
verify_ssl=False,
org=self.org,
token=self.token)
client = InfluxDBClient(url=url, enable_gzip=False, verify_ssl=False, org=self.org, token=self.token)
except Exception as e:
logger.critical("Cannot connect to InfluxDB server '%s' (%s)" % (url, e))
sys.exit(2)
else:
logger.info("Connected to InfluxDB server version {} ({})".format(client.health().version,
client.health().message))
logger.info(
"Connected to InfluxDB server version {} ({})".format(client.health().version, client.health().message)
)
# Create the write client
write_client = client.write_api(write_options=WriteOptions(batch_size=500,
flush_interval=10000,
jitter_interval=2000,
retry_interval=5000,
max_retries=5,
max_retry_delay=30000,
exponential_base=2))
write_client = client.write_api(
write_options=WriteOptions(
batch_size=500,
flush_interval=10000,
jitter_interval=2000,
retry_interval=5000,
max_retries=5,
max_retry_delay=30000,
exponential_base=2,
)
)
return write_client
def _normalize(self, name, columns, points):
@ -112,9 +111,11 @@ class Export(GlancesExport):
for measurement in keys_list:
# Manage field
if measurement is not None:
fields = {k.replace('{}.'.format(measurement), ''): data_dict[k]
for k in data_dict
if k.startswith('{}.'.format(measurement))}
fields = {
k.replace('{}.'.format(measurement), ''): data_dict[k]
for k in data_dict
if k.startswith('{}.'.format(measurement))
}
else:
fields = data_dict
# Transform to InfluxDB datamodel
@ -143,9 +144,7 @@ class Export(GlancesExport):
# Add the hostname as a tag
tags['hostname'] = self.hostname
# Add the measurement to the list
ret.append({'measurement': name,
'tags': tags,
'fields': fields})
ret.append({'measurement': name, 'tags': tags, 'fields': fields})
return ret
def export(self, name, columns, points):
@ -158,10 +157,7 @@ class Export(GlancesExport):
logger.debug("Cannot export empty {} stats to InfluxDB".format(name))
else:
try:
self.client.write(self.bucket,
self.org,
self._normalize(name, columns, points),
time_precision="s")
self.client.write(self.bucket, self.org, self._normalize(name, columns, points), time_precision="s")
except Exception as e:
# Log level set to debug instead of error (see: issue #1561)
logger.debug("Cannot export {} stats to InfluxDB ({})".format(name, e))

View File

@ -50,10 +50,7 @@ class Export(GlancesExport):
if name == self.plugins_to_export()[0] and self.buffer != {}:
# One whole loop has been completed
# Flush stats to file
logger.debug("Exporting stats ({}) to JSON file ({})".format(
listkeys(self.buffer),
self.json_filename)
)
logger.debug("Exporting stats ({}) to JSON file ({})".format(listkeys(self.buffer), self.json_filename))
# Export stats to JSON file
if PY3:

View File

@ -46,11 +46,9 @@ class Export(GlancesExport):
self.tags = None
# Load the Kafka configuration file section
self.export_enable = self.load_conf('kafka',
mandatories=['host', 'port',
'topic'],
options=['compression',
'tags'])
self.export_enable = self.load_conf(
'kafka', mandatories=['host', 'port', 'topic'], options=['compression', 'tags']
)
if not self.export_enable:
sys.exit(2)
@ -66,9 +64,11 @@ class Export(GlancesExport):
server_uri = '{}:{}'.format(self.host, self.port)
try:
s = KafkaProducer(bootstrap_servers=server_uri,
value_serializer=lambda v: json.dumps(v).encode('utf-8'),
compression_type=self.compression)
s = KafkaProducer(
bootstrap_servers=server_uri,
value_serializer=lambda v: json.dumps(v).encode('utf-8'),
compression_type=self.compression,
)
except Exception as e:
logger.critical("Cannot connect to Kafka server %s (%s)" % (server_uri, e))
sys.exit(2)
@ -90,10 +90,12 @@ class Export(GlancesExport):
# key=<plugin name>
# value=JSON dict
try:
self.client.send(self.topic,
# Kafka key name needs to be bytes #1593
key=name.encode('utf-8'),
value=data)
self.client.send(
self.topic,
# Kafka key name needs to be bytes #1593
key=name.encode('utf-8'),
value=data,
)
except Exception as e:
logger.error("Cannot export {} stats to Kafka ({})".format(name, e))

View File

@ -46,9 +46,9 @@ class Export(GlancesExport):
self.tls = 'true'
# Load the MQTT configuration file
self.export_enable = self.load_conf('mqtt',
mandatories=['host', 'password'],
options=['port', 'user', 'topic', 'tls', 'topic_structure'])
self.export_enable = self.load_conf(
'mqtt', mandatories=['host', 'password'], options=['port', 'user', 'topic', 'tls', 'topic_structure']
)
if not self.export_enable:
exit('Missing MQTT config')
@ -58,7 +58,7 @@ class Export(GlancesExport):
self.port = int(self.port) or 8883
self.topic = self.topic or 'glances'
self.user = self.user or 'glances'
self.tls = (self.tls and self.tls.lower() == 'true')
self.tls = self.tls and self.tls.lower() == 'true'
self.topic_structure = (self.topic_structure or 'per-metric').lower()
if self.topic_structure not in ['per-metric', 'per-plugin']:
@ -73,14 +73,11 @@ class Export(GlancesExport):
if not self.export_enable:
return None
try:
client = paho.Client(client_id='glances_' + self.hostname,
clean_session=False)
client.username_pw_set(username=self.user,
password=self.password)
client = paho.Client(client_id='glances_' + self.hostname, clean_session=False)
client.username_pw_set(username=self.user, password=self.password)
if self.tls:
client.tls_set(certs.where())
client.connect(host=self.host,
port=self.port)
client.connect(host=self.host, port=self.port)
client.loop_start()
return client
except Exception as e:
@ -93,9 +90,7 @@ class Export(GlancesExport):
WHITELIST = '_-' + string.ascii_letters + string.digits
SUBSTITUTE = '_'
def whitelisted(s,
whitelist=WHITELIST,
substitute=SUBSTITUTE):
def whitelisted(s, whitelist=WHITELIST, substitute=SUBSTITUTE):
return ''.join(c if c in whitelist else substitute for c in s)
if self.topic_structure == 'per-metric':
@ -133,4 +128,3 @@ class Export(GlancesExport):
self.client.publish(topic, json_value)
except Exception as e:
logger.error("Can not export stats to MQTT server (%s)" % e)

View File

@ -45,9 +45,7 @@ class Export(GlancesExport):
self.tags = None
# Load the configuration file
self.export_enable = self.load_conf('opentsdb',
mandatories=['host', 'port'],
options=['prefix', 'tags'])
self.export_enable = self.load_conf('opentsdb', mandatories=['host', 'port'], options=['prefix', 'tags'])
if not self.export_enable:
sys.exit(2)
@ -64,9 +62,7 @@ class Export(GlancesExport):
return None
try:
db = potsdb.Client(self.host,
port=int(self.port),
check_host=True)
db = potsdb.Client(self.host, port=int(self.port), check_host=True)
except Exception as e:
logger.critical("Cannot connect to OpenTSDB server %s:%s (%s)" % (self.host, self.port, e))
sys.exit(2)

View File

@ -40,9 +40,7 @@ class Export(GlancesExport):
super(Export, self).__init__(config=config, args=args)
# Load the Prometheus configuration file section
self.export_enable = self.load_conf('prometheus',
mandatories=['host', 'port', 'labels'],
options=['prefix'])
self.export_enable = self.load_conf('prometheus', mandatories=['host', 'port', 'labels'], options=['prefix'])
if not self.export_enable:
sys.exit(2)
@ -89,8 +87,7 @@ class Export(GlancesExport):
labels = self.parse_tags(self.labels)
# Manage an internal dict between metric name and Gauge
if metric_name not in self._metric_dict:
self._metric_dict[metric_name] = Gauge(metric_name, k,
labelnames=listkeys(labels))
self._metric_dict[metric_name] = Gauge(metric_name, k, labelnames=listkeys(labels))
# Write the value
if hasattr(self._metric_dict[metric_name], 'labels'):
# Add the labels (see issue #1255)

View File

@ -50,11 +50,9 @@ class Export(GlancesExport):
# N/A
# Load the rabbitMQ configuration file
self.export_enable = self.load_conf('rabbitmq',
mandatories=['host', 'port',
'user', 'password',
'queue'],
options=['protocol'])
self.export_enable = self.load_conf(
'rabbitmq', mandatories=['host', 'port', 'user', 'password', 'queue'], options=['protocol']
)
if not self.export_enable:
sys.exit(2)
@ -78,11 +76,8 @@ class Export(GlancesExport):
try:
parameters = pika.URLParameters(
self.protocol +
'://' + self.user +
':' + self.password +
'@' + self.host +
':' + self.port + '/')
self.protocol + '://' + self.user + ':' + self.password + '@' + self.host + ':' + self.port + '/'
)
connection = pika.BlockingConnection(parameters)
channel = connection.channel()
return channel
@ -92,8 +87,7 @@ class Export(GlancesExport):
def export(self, name, columns, points):
"""Write the points in RabbitMQ."""
data = ('hostname=' + self.hostname + ', name=' + name +
', dateinfo=' + datetime.datetime.utcnow().isoformat())
data = 'hostname=' + self.hostname + ', name=' + name + ', dateinfo=' + datetime.datetime.utcnow().isoformat()
for i in range(len(columns)):
if not isinstance(points[i], Number):
continue

View File

@ -42,8 +42,7 @@ class Export(GlancesExport):
self.path = None
# Load the RESTful section in the configuration file
self.export_enable = self.load_conf('restful',
mandatories=['host', 'port', 'protocol', 'path'])
self.export_enable = self.load_conf('restful', mandatories=['host', 'port', 'protocol', 'path'])
if not self.export_enable:
sys.exit(2)
@ -59,20 +58,15 @@ class Export(GlancesExport):
if not self.export_enable:
return None
# Build the RESTful URL where the stats will be posted
url = '{}://{}:{}{}'.format(self.protocol,
self.host,
self.port,
self.path)
logger.info(
"Stats will be exported to the RESTful endpoint {}".format(url))
url = '{}://{}:{}{}'.format(self.protocol, self.host, self.port, self.path)
logger.info("Stats will be exported to the RESTful endpoint {}".format(url))
return url
def export(self, name, columns, points):
"""Export the stats to the Statsd server."""
if name == self.plugins_to_export()[0] and self.buffer != {}:
# One complete loop have been done
logger.debug("Export stats ({}) to RESTful endpoint ({})".format(listkeys(self.buffer),
self.client))
logger.debug("Export stats ({}) to RESTful endpoint ({})".format(listkeys(self.buffer), self.client))
# Export stats
post(self.client, json=self.buffer, allow_redirects=True)
# Reset buffer

View File

@ -46,9 +46,7 @@ class Export(GlancesExport):
# N/A
# Load the Riemann configuration
self.export_enable = self.load_conf('riemann',
mandatories=['host', 'port'],
options=[])
self.export_enable = self.load_conf('riemann', mandatories=['host', 'port'], options=[])
if not self.export_enable:
sys.exit(2)

View File

@ -44,9 +44,7 @@ class Export(GlancesExport):
self.prefix = None
# Load the configuration file
self.export_enable = self.load_conf('statsd',
mandatories=['host', 'port'],
options=['prefix'])
self.export_enable = self.load_conf('statsd', mandatories=['host', 'port'], options=['prefix'])
if not self.export_enable:
sys.exit(2)
@ -61,12 +59,8 @@ class Export(GlancesExport):
"""Init the connection to the Statsd server."""
if not self.export_enable:
return None
logger.info(
"Stats will be exported to StatsD server: {}:{}".format(self.host,
self.port))
return StatsClient(self.host,
int(self.port),
prefix=self.prefix)
logger.info("Stats will be exported to StatsD server: {}:{}".format(self.host, self.port))
return StatsClient(self.host, int(self.port), prefix=self.prefix)
def export(self, name, columns, points):
"""Export the stats to the Statsd server."""
@ -76,8 +70,7 @@ class Export(GlancesExport):
stat_name = '{}.{}'.format(name, columns[i])
stat_value = points[i]
try:
self.client.gauge(normalize(stat_name),
stat_value)
self.client.gauge(normalize(stat_name), stat_value)
except Exception as e:
logger.error("Can not export stats to Statsd (%s)" % e)
logger.debug("Export {} stats to Statsd".format(name))

View File

@ -45,9 +45,7 @@ class Export(GlancesExport):
# N/A
# Load the ZeroMQ configuration file section ([export_zeromq])
self.export_enable = self.load_conf('zeromq',
mandatories=['host', 'port', 'prefix'],
options=[])
self.export_enable = self.load_conf('zeromq', mandatories=['host', 'port', 'prefix'], options=[])
if not self.export_enable:
sys.exit(2)
@ -96,9 +94,7 @@ class Export(GlancesExport):
# - First frame containing the following prefix (STRING)
# - Second frame with the Glances plugin name (STRING)
# - Third frame with the Glances plugin stats (JSON)
message = [b(self.prefix),
b(name),
asbytes(json.dumps(data))]
message = [b(self.prefix), b(name), asbytes(json.dumps(data))]
# Write data to the ZeroMQ bus
# Result can be view: tcp://host:port

View File

@ -121,8 +121,7 @@ class GlancesFilter(object):
if self.filter_key is None:
# Apply filter on command line and process name
return self._is_process_filtered(process, key='name') or \
self._is_process_filtered(process, key='cmdline')
return self._is_process_filtered(process, key='name') or self._is_process_filtered(process, key='cmdline')
else:
# Apply filter on <key>
return self._is_process_filtered(process)

View File

@ -100,9 +100,7 @@ class FolderList(object):
# Optional conf keys
# Refresh time
value['refresh'] = int(self.config.get_value(section,
key + 'refresh',
default=self.__default_refresh))
value['refresh'] = int(self.config.get_value(section, key + 'refresh', default=self.__default_refresh))
self.timer_folders.append(Timer(value['refresh']))
# Thresholds
for i in ['careful', 'warning', 'critical']:

View File

@ -34,14 +34,10 @@ class GlancesHistory(object):
"""
self.stats_history = {}
def add(self, key, value,
description='',
history_max_size=None):
def add(self, key, value, description='', history_max_size=None):
"""Add an new item (key, value) to the current history."""
if key not in self.stats_history:
self.stats_history[key] = GlancesAttribute(key,
description=description,
history_max_size=history_max_size)
self.stats_history[key] = GlancesAttribute(key, description=description, history_max_size=history_max_size)
self.stats_history[key].value = value
def reset(self):

View File

@ -37,39 +37,29 @@ if 'HOME' in os.environ:
else:
_XDG_CACHE_HOME = ''
# Define the glances log file
if 'XDG_CACHE_HOME' in os.environ \
and os.path.isdir(os.environ['XDG_CACHE_HOME']) \
and os.access(os.environ['XDG_CACHE_HOME'], os.W_OK):
if (
'XDG_CACHE_HOME' in os.environ
and os.path.isdir(os.environ['XDG_CACHE_HOME'])
and os.access(os.environ['XDG_CACHE_HOME'], os.W_OK)
):
safe_makedirs(os.path.join(os.environ['XDG_CACHE_HOME'], 'glances'))
LOG_FILENAME = os.path.join(os.environ['XDG_CACHE_HOME'], 'glances', 'glances.log')
elif os.path.isdir(_XDG_CACHE_HOME) and os.access(_XDG_CACHE_HOME, os.W_OK):
safe_makedirs(os.path.join(_XDG_CACHE_HOME, 'glances'))
LOG_FILENAME = os.path.join(_XDG_CACHE_HOME, 'glances', 'glances.log')
else:
LOG_FILENAME = os.path.join(tempfile.gettempdir(),
'glances-{}.log'.format(getpass.getuser()))
LOG_FILENAME = os.path.join(tempfile.gettempdir(), 'glances-{}.log'.format(getpass.getuser()))
# Define the logging configuration
LOGGING_CFG = {
"version": 1,
"disable_existing_loggers": "False",
"root": {
"level": "INFO",
"handlers": ["file", "console"]
},
"root": {"level": "INFO", "handlers": ["file", "console"]},
"formatters": {
"standard": {
"format": "%(asctime)s -- %(levelname)s -- %(message)s"
},
"short": {
"format": "%(levelname)s -- %(message)s"
},
"long": {
"format": "%(asctime)s -- %(levelname)s -- %(message)s (%(funcName)s in %(filename)s)"
},
"free": {
"format": "%(message)s"
}
"standard": {"format": "%(asctime)s -- %(levelname)s -- %(message)s"},
"short": {"format": "%(levelname)s -- %(message)s"},
"long": {"format": "%(asctime)s -- %(levelname)s -- %(message)s (%(funcName)s in %(filename)s)"},
"free": {"format": "%(message)s"},
},
"handlers": {
"file": {
@ -78,40 +68,18 @@ LOGGING_CFG = {
"maxBytes": 1000000,
"backupCount": 3,
"formatter": "standard",
"filename": LOG_FILENAME
"filename": LOG_FILENAME,
},
"console": {
"level": "CRITICAL",
"class": "logging.StreamHandler",
"formatter": "free"
}
"console": {"level": "CRITICAL", "class": "logging.StreamHandler", "formatter": "free"},
},
"loggers": {
"debug": {
"handlers": ["file", "console"],
"level": "DEBUG"
},
"verbose": {
"handlers": ["file", "console"],
"level": "INFO"
},
"standard": {
"handlers": ["file"],
"level": "INFO"
},
"requests": {
"handlers": ["file", "console"],
"level": "ERROR"
},
"elasticsearch": {
"handlers": ["file", "console"],
"level": "ERROR"
},
"elasticsearch.trace": {
"handlers": ["file", "console"],
"level": "ERROR"
}
}
"debug": {"handlers": ["file", "console"], "level": "DEBUG"},
"verbose": {"handlers": ["file", "console"], "level": "INFO"},
"standard": {"handlers": ["file"], "level": "INFO"},
"requests": {"handlers": ["file", "console"], "level": "ERROR"},
"elasticsearch": {"handlers": ["file", "console"], "level": "ERROR"},
"elasticsearch.trace": {"handlers": ["file", "console"], "level": "ERROR"},
},
}

View File

@ -108,157 +108,370 @@ Examples of use:
prog='glances',
conflict_handler='resolve',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=self.example_of_use)
parser.add_argument(
'-V', '--version', action='version', version=version)
parser.add_argument('-d', '--debug', action='store_true', default=False,
dest='debug', help='enable debug mode')
parser.add_argument('-C', '--config', dest='conf_file',
help='path to the configuration file')
epilog=self.example_of_use,
)
parser.add_argument('-V', '--version', action='version', version=version)
parser.add_argument('-d', '--debug', action='store_true', default=False, dest='debug', help='enable debug mode')
parser.add_argument('-C', '--config', dest='conf_file', help='path to the configuration file')
# Disable plugin
parser.add_argument('--modules-list', '--module-list',
action='store_true', default=False,
dest='modules_list',
help='display modules (plugins & exports) list and exit')
parser.add_argument('--disable-plugin', '--disable-plugins', dest='disable_plugin',
help='disable plugin (comma separed list)')
parser.add_argument('--enable-plugin', '--enable-plugins', dest='enable_plugin',
help='enable plugin (comma separed list)')
parser.add_argument('--disable-process', action='store_true', default=False,
dest='disable_process', help='disable process module')
parser.add_argument(
'--modules-list',
'--module-list',
action='store_true',
default=False,
dest='modules_list',
help='display modules (plugins & exports) list and exit',
)
parser.add_argument(
'--disable-plugin', '--disable-plugins', dest='disable_plugin', help='disable plugin (comma separed list)'
)
parser.add_argument(
'--enable-plugin', '--enable-plugins', dest='enable_plugin', help='enable plugin (comma separed list)'
)
parser.add_argument(
'--disable-process',
action='store_true',
default=False,
dest='disable_process',
help='disable process module',
)
# Enable or disable option
parser.add_argument('--disable-webui', action='store_true', default=False,
dest='disable_webui', help='disable the Web Interface')
parser.add_argument('--light', '--enable-light', action='store_true',
default=False, dest='enable_light',
help='light mode for Curses UI (disable all but top menu)')
parser.add_argument('-0', '--disable-irix', action='store_true', default=False,
dest='disable_irix', help='task\'s cpu usage will be divided by the total number of CPUs')
parser.add_argument('-1', '--percpu', action='store_true', default=False,
dest='percpu', help='start Glances in per CPU mode')
parser.add_argument('-2', '--disable-left-sidebar', action='store_true',
default=False, dest='disable_left_sidebar',
help='disable network, disk I/O, FS and sensors modules')
parser.add_argument('-3', '--disable-quicklook', action='store_true', default=False,
dest='disable_quicklook', help='disable quick look module')
parser.add_argument('-4', '--full-quicklook', action='store_true', default=False,
dest='full_quicklook', help='disable all but quick look and load')
parser.add_argument('-5', '--disable-top', action='store_true',
default=False, dest='disable_top',
help='disable top menu (QL, CPU, MEM, SWAP and LOAD)')
parser.add_argument('-6', '--meangpu', action='store_true', default=False,
dest='meangpu', help='start Glances in mean GPU mode')
parser.add_argument('--disable-history', action='store_true', default=False,
dest='disable_history', help='disable stats history')
parser.add_argument('--disable-bold', action='store_true', default=False,
dest='disable_bold', help='disable bold mode in the terminal')
parser.add_argument('--disable-bg', action='store_true', default=False,
dest='disable_bg', help='disable background colors in the terminal')
parser.add_argument('--enable-irq', action='store_true', default=False,
dest='enable_irq', help='enable IRQ module'),
parser.add_argument('--enable-process-extended', action='store_true', default=False,
dest='enable_process_extended', help='enable extended stats on top process')
parser.add_argument(
'--disable-webui',
action='store_true',
default=False,
dest='disable_webui',
help='disable the Web Interface',
)
parser.add_argument(
'--light',
'--enable-light',
action='store_true',
default=False,
dest='enable_light',
help='light mode for Curses UI (disable all but top menu)',
)
parser.add_argument(
'-0',
'--disable-irix',
action='store_true',
default=False,
dest='disable_irix',
help='task\'s cpu usage will be divided by the total number of CPUs',
)
parser.add_argument(
'-1', '--percpu', action='store_true', default=False, dest='percpu', help='start Glances in per CPU mode'
)
parser.add_argument(
'-2',
'--disable-left-sidebar',
action='store_true',
default=False,
dest='disable_left_sidebar',
help='disable network, disk I/O, FS and sensors modules',
)
parser.add_argument(
'-3',
'--disable-quicklook',
action='store_true',
default=False,
dest='disable_quicklook',
help='disable quick look module',
)
parser.add_argument(
'-4',
'--full-quicklook',
action='store_true',
default=False,
dest='full_quicklook',
help='disable all but quick look and load',
)
parser.add_argument(
'-5',
'--disable-top',
action='store_true',
default=False,
dest='disable_top',
help='disable top menu (QL, CPU, MEM, SWAP and LOAD)',
)
parser.add_argument(
'-6', '--meangpu', action='store_true', default=False, dest='meangpu', help='start Glances in mean GPU mode'
)
parser.add_argument(
'--disable-history',
action='store_true',
default=False,
dest='disable_history',
help='disable stats history',
)
parser.add_argument(
'--disable-bold',
action='store_true',
default=False,
dest='disable_bold',
help='disable bold mode in the terminal',
)
parser.add_argument(
'--disable-bg',
action='store_true',
default=False,
dest='disable_bg',
help='disable background colors in the terminal',
)
parser.add_argument(
'--enable-irq', action='store_true', default=False, dest='enable_irq', help='enable IRQ module'
),
parser.add_argument(
'--enable-process-extended',
action='store_true',
default=False,
dest='enable_process_extended',
help='enable extended stats on top process',
)
# Sort processes list
parser.add_argument('--sort-processes', dest='sort_processes_key',
choices=sort_processes_key_list,
help='Sort processes by: {}'.format(', '.join(sort_processes_key_list)))
parser.add_argument(
'--sort-processes',
dest='sort_processes_key',
choices=sort_processes_key_list,
help='Sort processes by: {}'.format(', '.join(sort_processes_key_list)),
)
# Export modules feature
parser.add_argument('--export', dest='export',
help='enable export module (comma separed list)')
parser.add_argument('--export-csv-file',
default='./glances.csv',
dest='export_csv_file',
help='file path for CSV exporter')
parser.add_argument('--export-csv-overwrite', action='store_true', default=False,
dest='export_csv_overwrite', help='overwrite existing CSV file')
parser.add_argument('--export-json-file',
default='./glances.json',
dest='export_json_file',
help='file path for JSON exporter')
parser.add_argument('--export-graph-path',
default=tempfile.gettempdir(),
dest='export_graph_path',
help='Folder for Graph exporter')
parser.add_argument('--export', dest='export', help='enable export module (comma separed list)')
parser.add_argument(
'--export-csv-file', default='./glances.csv', dest='export_csv_file', help='file path for CSV exporter'
)
parser.add_argument(
'--export-csv-overwrite',
action='store_true',
default=False,
dest='export_csv_overwrite',
help='overwrite existing CSV file',
)
parser.add_argument(
'--export-json-file', default='./glances.json', dest='export_json_file', help='file path for JSON exporter'
)
parser.add_argument(
'--export-graph-path',
default=tempfile.gettempdir(),
dest='export_graph_path',
help='Folder for Graph exporter',
)
# Client/Server option
parser.add_argument('-c', '--client', dest='client',
help='connect to a Glances server by IPv4/IPv6 address or hostname')
parser.add_argument('-s', '--server', action='store_true', default=False,
dest='server', help='run Glances in server mode')
parser.add_argument('--browser', action='store_true', default=False,
dest='browser', help='start the client browser (list of servers)')
parser.add_argument('--disable-autodiscover', action='store_true', default=False,
dest='disable_autodiscover', help='disable autodiscover feature')
parser.add_argument('-p', '--port', default=None, type=int, dest='port',
help='define the client/server TCP port [default: {}]'.format(self.server_port))
parser.add_argument('-B', '--bind', default='0.0.0.0', dest='bind_address',
help='bind server to the given IPv4/IPv6 address or hostname')
parser.add_argument('--username', action='store_true', default=False, dest='username_prompt',
help='define a client/server username')
parser.add_argument('--password', action='store_true', default=False, dest='password_prompt',
help='define a client/server password')
parser.add_argument('-u', dest='username_used',
help='use the given client/server username')
parser.add_argument('--snmp-community', default='public', dest='snmp_community',
help='SNMP community')
parser.add_argument('--snmp-port', default=161, type=int,
dest='snmp_port', help='SNMP port')
parser.add_argument('--snmp-version', default='2c', dest='snmp_version',
help='SNMP version (1, 2c or 3)')
parser.add_argument('--snmp-user', default='private', dest='snmp_user',
help='SNMP username (only for SNMPv3)')
parser.add_argument('--snmp-auth', default='password', dest='snmp_auth',
help='SNMP authentication key (only for SNMPv3)')
parser.add_argument('--snmp-force', action='store_true', default=False,
dest='snmp_force', help='force SNMP mode')
parser.add_argument('-t', '--time', default=self.DEFAULT_REFRESH_TIME, type=float,
dest='time', help='set minumum refresh rate in seconds [default: {} sec]'.format(
self.DEFAULT_REFRESH_TIME))
parser.add_argument('-w', '--webserver', action='store_true', default=False,
dest='webserver', help='run Glances in web server mode (bottle needed)')
parser.add_argument('--cached-time', default=self.cached_time, type=int,
dest='cached_time', help='set the server cache time [default: {} sec]'.format(
self.cached_time))
parser.add_argument('--open-web-browser', action='store_true', default=False,
dest='open_web_browser', help='try to open the Web UI in the default Web browser')
parser.add_argument(
'-c', '--client', dest='client', help='connect to a Glances server by IPv4/IPv6 address or hostname'
)
parser.add_argument(
'-s', '--server', action='store_true', default=False, dest='server', help='run Glances in server mode'
)
parser.add_argument(
'--browser',
action='store_true',
default=False,
dest='browser',
help='start the client browser (list of servers)',
)
parser.add_argument(
'--disable-autodiscover',
action='store_true',
default=False,
dest='disable_autodiscover',
help='disable autodiscover feature',
)
parser.add_argument(
'-p',
'--port',
default=None,
type=int,
dest='port',
help='define the client/server TCP port [default: {}]'.format(self.server_port),
)
parser.add_argument(
'-B',
'--bind',
default='0.0.0.0',
dest='bind_address',
help='bind server to the given IPv4/IPv6 address or hostname',
)
parser.add_argument(
'--username',
action='store_true',
default=False,
dest='username_prompt',
help='define a client/server username',
)
parser.add_argument(
'--password',
action='store_true',
default=False,
dest='password_prompt',
help='define a client/server password',
)
parser.add_argument('-u', dest='username_used', help='use the given client/server username')
parser.add_argument('--snmp-community', default='public', dest='snmp_community', help='SNMP community')
parser.add_argument('--snmp-port', default=161, type=int, dest='snmp_port', help='SNMP port')
parser.add_argument('--snmp-version', default='2c', dest='snmp_version', help='SNMP version (1, 2c or 3)')
parser.add_argument('--snmp-user', default='private', dest='snmp_user', help='SNMP username (only for SNMPv3)')
parser.add_argument(
'--snmp-auth', default='password', dest='snmp_auth', help='SNMP authentication key (only for SNMPv3)'
)
parser.add_argument(
'--snmp-force', action='store_true', default=False, dest='snmp_force', help='force SNMP mode'
)
parser.add_argument(
'-t',
'--time',
default=self.DEFAULT_REFRESH_TIME,
type=float,
dest='time',
help='set minumum refresh rate in seconds [default: {} sec]'.format(self.DEFAULT_REFRESH_TIME),
)
parser.add_argument(
'-w',
'--webserver',
action='store_true',
default=False,
dest='webserver',
help='run Glances in web server mode (bottle needed)',
)
parser.add_argument(
'--cached-time',
default=self.cached_time,
type=int,
dest='cached_time',
help='set the server cache time [default: {} sec]'.format(self.cached_time),
)
parser.add_argument(
'--open-web-browser',
action='store_true',
default=False,
dest='open_web_browser',
help='try to open the Web UI in the default Web browser',
)
# Display options
parser.add_argument('-q', '--quiet', default=False, action='store_true',
dest='quiet', help='do not display the curses interface')
parser.add_argument('-f', '--process-filter', default=None, type=str,
dest='process_filter', help='set the process filter pattern (regular expression)')
parser.add_argument('--process-short-name', action='store_true', default=True,
dest='process_short_name', help='force short name for processes name')
parser.add_argument('--process-long-name', action='store_false', default=False,
dest='process_short_name', help='force long name for processes name')
parser.add_argument('--stdout', default=None,
dest='stdout', help='display stats to stdout, one stat per line (comma separated list of plugins/plugins.attribute)')
parser.add_argument('--stdout-csv', default=None,
dest='stdout_csv', help='display stats to stdout, csv format (comma separated list of plugins/plugins.attribute)')
parser.add_argument('--issue', default=None, action='store_true',
dest='stdout_issue', help='test all plugins and exit (please copy/paste the output if you open an issue)')
parser.add_argument('--api-doc', default=None, action='store_true',
dest='stdout_apidoc', help='display fields descriptions')
parser.add_argument(
'-q',
'--quiet',
default=False,
action='store_true',
dest='quiet',
help='do not display the curses interface',
)
parser.add_argument(
'-f',
'--process-filter',
default=None,
type=str,
dest='process_filter',
help='set the process filter pattern (regular expression)',
)
parser.add_argument(
'--process-short-name',
action='store_true',
default=True,
dest='process_short_name',
help='force short name for processes name',
)
parser.add_argument(
'--process-long-name',
action='store_false',
default=False,
dest='process_short_name',
help='force long name for processes name',
)
parser.add_argument(
'--stdout',
default=None,
dest='stdout',
help='display stats to stdout, one stat per line (comma separated list of plugins/plugins.attribute)',
)
parser.add_argument(
'--stdout-csv',
default=None,
dest='stdout_csv',
help='display stats to stdout, csv format (comma separated list of plugins/plugins.attribute)',
)
parser.add_argument(
'--issue',
default=None,
action='store_true',
dest='stdout_issue',
help='test all plugins and exit (please copy/paste the output if you open an issue)',
)
parser.add_argument(
'--api-doc', default=None, action='store_true', dest='stdout_apidoc', help='display fields descriptions'
)
if not WINDOWS:
parser.add_argument('--hide-kernel-threads', action='store_true', default=False,
dest='no_kernel_threads', help='hide kernel threads in process list (not available on Windows)')
parser.add_argument('-b', '--byte', action='store_true', default=False,
dest='byte', help='display network rate in byte per second')
parser.add_argument('--diskio-show-ramfs', action='store_true', default=False,
dest='diskio_show_ramfs', help='show RAM Fs in the DiskIO plugin')
parser.add_argument('--diskio-iops', action='store_true', default=False,
dest='diskio_iops', help='show IO per second in the DiskIO plugin')
parser.add_argument('--fahrenheit', action='store_true', default=False,
dest='fahrenheit', help='display temperature in Fahrenheit (default is Celsius)')
parser.add_argument('--fs-free-space', action='store_true', default=False,
dest='fs_free_space', help='display FS free space instead of used')
parser.add_argument('--sparkline', action='store_true', default=False,
dest='sparkline', help='display sparklines instead of bar in the curses interface')
parser.add_argument('--theme-white', action='store_true', default=False,
dest='theme_white', help='optimize display colors for white background')
parser.add_argument(
'--hide-kernel-threads',
action='store_true',
default=False,
dest='no_kernel_threads',
help='hide kernel threads in process list (not available on Windows)',
)
parser.add_argument(
'-b',
'--byte',
action='store_true',
default=False,
dest='byte',
help='display network rate in byte per second',
)
parser.add_argument(
'--diskio-show-ramfs',
action='store_true',
default=False,
dest='diskio_show_ramfs',
help='show RAM Fs in the DiskIO plugin',
)
parser.add_argument(
'--diskio-iops',
action='store_true',
default=False,
dest='diskio_iops',
help='show IO per second in the DiskIO plugin',
)
parser.add_argument(
'--fahrenheit',
action='store_true',
default=False,
dest='fahrenheit',
help='display temperature in Fahrenheit (default is Celsius)',
)
parser.add_argument(
'--fs-free-space',
action='store_true',
default=False,
dest='fs_free_space',
help='display FS free space instead of used',
)
parser.add_argument(
'--sparkline',
action='store_true',
default=False,
dest='sparkline',
help='display sparklines instead of bar in the curses interface',
)
parser.add_argument(
'--theme-white',
action='store_true',
default=False,
dest='theme_white',
help='optimize display colors for white background',
)
# Globals options
parser.add_argument('--disable-check-update', action='store_true', default=False,
dest='disable_check_update', help='disable online Glances version ckeck')
parser.add_argument('--strftime', dest='strftime_format', default='',
help='strftime format string for displaying current date in standalone mode')
parser.add_argument(
'--disable-check-update',
action='store_true',
default=False,
dest='disable_check_update',
help='disable online Glances version ckeck',
)
parser.add_argument(
'--strftime',
dest='strftime_format',
default='',
help='strftime format string for displaying current date in standalone mode',
)
return parser
@ -274,16 +487,16 @@ Examples of use:
# Debug mode
if args.debug:
from logging import DEBUG
logger.setLevel(DEBUG)
else:
from warnings import simplefilter
simplefilter("ignore")
# Plugins refresh rate
if self.config.has_section('global'):
global_refresh = self.config.get_float_value('global',
'refresh',
default=self.DEFAULT_REFRESH_TIME)
global_refresh = self.config.get_float_value('global', 'refresh', default=self.DEFAULT_REFRESH_TIME)
if args.time == self.DEFAULT_REFRESH_TIME:
args.time = global_refresh
logger.debug('Global refresh rate is set to {} seconds'.format(args.time))
@ -291,8 +504,7 @@ Examples of use:
# Plugins disable/enable
# Allow users to disable plugins from the glances.conf (issue #1378)
for s in self.config.sections():
if self.config.has_section(s) \
and (self.config.get_bool_value(s, 'disable', False)):
if self.config.has_section(s) and (self.config.get_bool_value(s, 'disable', False)):
disable(args, s)
logger.debug('{} disabled by the configuration file'.format(s))
# The configuration key can be overwrite from the command line
@ -316,7 +528,9 @@ Examples of use:
args.port = self.server_port
# Port in the -c URI #996
if args.client is not None:
args.client, args.port = (x if x else y for (x, y) in zip(args.client.partition(':')[::2], (args.client, args.port)))
args.client, args.port = (
x if x else y for (x, y) in zip(args.client.partition(':')[::2], (args.client, args.port))
)
# Autodiscover
if args.disable_autodiscover:
@ -332,14 +546,11 @@ Examples of use:
args.password_prompt = True
# Prompt username
if args.server:
args.username = self.__get_username(
description='Define the Glances server username: ')
args.username = self.__get_username(description='Define the Glances server username: ')
elif args.webserver:
args.username = self.__get_username(
description='Define the Glances webserver username: ')
args.username = self.__get_username(description='Define the Glances webserver username: ')
elif args.client:
args.username = self.__get_username(
description='Enter the Glances server username: ')
args.username = self.__get_username(description='Enter the Glances server username: ')
else:
if args.username_used:
# A username has been set using the -u option ?
@ -352,22 +563,22 @@ Examples of use:
# Interactive or file password
if args.server:
args.password = self.__get_password(
description='Define the Glances server password ({} username): '.format(
args.username),
description='Define the Glances server password ({} username): '.format(args.username),
confirm=True,
username=args.username)
username=args.username,
)
elif args.webserver:
args.password = self.__get_password(
description='Define the Glances webserver password ({} username): '.format(
args.username),
description='Define the Glances webserver password ({} username): '.format(args.username),
confirm=True,
username=args.username)
username=args.username,
)
elif args.client:
args.password = self.__get_password(
description='Enter the Glances server password ({} username): '.format(
args.username),
description='Enter the Glances server password ({} username): '.format(args.username),
clear=True,
username=args.username)
username=args.username,
)
else:
# Default is no password
args.password = self.password
@ -427,8 +638,7 @@ Examples of use:
# Filter is only available in standalone mode
if args.process_filter is not None and not self.is_standalone():
logger.critical(
"Process filter is only available in standalone mode")
logger.critical("Process filter is only available in standalone mode")
sys.exit(2)
# Disable HDDTemp if sensors are disabled
@ -447,10 +657,7 @@ Examples of use:
def is_standalone(self):
"""Return True if Glances is running in standalone mode."""
return (not self.args.client and
not self.args.browser and
not self.args.server and
not self.args.webserver)
return not self.args.client and not self.args.browser and not self.args.server and not self.args.webserver
def is_client(self):
"""Return True if Glances is running in client mode."""
@ -484,13 +691,13 @@ Examples of use:
"""Read an username from the command line."""
return input(description)
def __get_password(self, description='',
confirm=False, clear=False, username='glances'):
def __get_password(self, description='', confirm=False, clear=False, username='glances'):
"""Read a password from the command line.
- if confirm = True, with confirmation
- if clear = True, plain (clear password)
"""
from glances.password import GlancesPassword
password = GlancesPassword(username=username)
return password.get_password(description, confirm, clear)

View File

@ -51,11 +51,7 @@ class Outdated(object):
self.cache_file = os.path.join(self.cache_dir, 'glances-version.db')
# Set default value...
self.data = {
u'installed_version': __version__,
u'latest_version': '0.0',
u'refresh_date': datetime.now()
}
self.data = {u'installed_version': __version__, u'latest_version': '0.0', u'refresh_date': datetime.now()}
# Read the configuration file
self.load_config(config)
logger.debug("Check Glances version up-to-date: {}".format(not self.args.disable_check_update))
@ -67,8 +63,7 @@ class Outdated(object):
"""Load outdated parameter in the global section of the configuration file."""
global_section = 'global'
if (hasattr(config, 'has_section') and
config.has_section(global_section)):
if hasattr(config, 'has_section') and config.has_section(global_section):
self.args.disable_check_update = config.get_value(global_section, 'check_update').lower() == 'false'
else:
logger.debug("Cannot find section {} in the configuration file".format(global_section))
@ -113,7 +108,9 @@ class Outdated(object):
# Check is disabled by configuration
return False
logger.debug("Check Glances version (installed: {} / latest: {})".format(self.installed_version(), self.latest_version()))
logger.debug(
"Check Glances version (installed: {} / latest: {})".format(self.installed_version(), self.latest_version())
)
return Version(self.latest_version()) > Version(self.installed_version())
def _load_cache(self):
@ -128,8 +125,10 @@ class Outdated(object):
logger.debug("Cannot read version from cache file: {} ({})".format(self.cache_file, e))
else:
logger.debug("Read version from cache file")
if (cached_data['installed_version'] != self.installed_version() or
datetime.now() - cached_data['refresh_date'] > max_refresh_date):
if (
cached_data['installed_version'] != self.installed_version()
or datetime.now() - cached_data['refresh_date'] > max_refresh_date
):
# Reset the cache if:
# - the installed version is different
# - the refresh_date is > max_refresh_date

View File

@ -38,10 +38,7 @@ class Bar(object):
sys.stdout.flush()
"""
def __init__(self, size,
percentage_char='|', empty_char=' ',
pre_char='[', post_char=']',
with_text=True):
def __init__(self, size, percentage_char='|', empty_char=' ', pre_char='[', post_char=']', with_text=True):
# Build curses_bars
self.__curses_bars = [empty_char] * 5 + [percentage_char] * 5
# Bar size

View File

@ -41,14 +41,17 @@ except ImportError:
def compress(func):
"""Compress result with deflate algorithm if the client ask for it."""
def wrapper(*args, **kwargs):
"""Wrapper that take one function and return the compressed result."""
ret = func(*args, **kwargs)
logger.debug('Receive {} {} request with header: {}'.format(
request.method,
request.url,
['{}: {}'.format(h, request.headers.get(h)) for h in request.headers.keys()]
))
logger.debug(
'Receive {} {} request with header: {}'.format(
request.method,
request.url,
['{}: {}'.format(h, request.headers.get(h)) for h in request.headers.keys()],
)
)
if 'deflate' in request.headers.get('Accept-Encoding', ''):
response.headers['Content-Encoding'] = 'deflate'
ret = deflate_compress(ret)
@ -59,11 +62,9 @@ def compress(func):
def deflate_compress(data, compress_level=6):
"""Compress given data using the DEFLATE algorithm"""
# Init compression
zobj = zlib.compressobj(compress_level,
zlib.DEFLATED,
zlib.MAX_WBITS,
zlib.DEF_MEM_LEVEL,
zlib.Z_DEFAULT_STRATEGY)
zobj = zlib.compressobj(
compress_level, zlib.DEFLATED, zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, zlib.Z_DEFAULT_STRATEGY
)
# Return compressed object
return zobj.compress(b(data)) + zobj.flush()
@ -96,8 +97,7 @@ class GlancesBottle(object):
self.load_config(config)
# Set the bind URL
self.bind_url = 'http://{}:{}/'.format(self.args.bind_address,
self.args.port)
self.bind_url = 'http://{}:{}/'.format(self.args.bind_address, self.args.port)
# Init Bottle
self._app = Bottle()
@ -136,6 +136,7 @@ class GlancesBottle(object):
"""Check if a username/password combination is valid."""
if username == self.args.username:
from glances.password import GlancesPassword
pwd = GlancesPassword()
return pwd.check_password(self.args.password, pwd.sha256_hash(password))
else:
@ -144,44 +145,31 @@ class GlancesBottle(object):
def _route(self):
"""Define route."""
# REST API
self._app.route('/api/%s/config' % self.API_VERSION, method="GET",
callback=self._api_config)
self._app.route('/api/%s/config/<item>' % self.API_VERSION, method="GET",
callback=self._api_config_item)
self._app.route('/api/%s/args' % self.API_VERSION, method="GET",
callback=self._api_args)
self._app.route('/api/%s/args/<item>' % self.API_VERSION, method="GET",
callback=self._api_args_item)
self._app.route('/api/%s/help' % self.API_VERSION, method="GET",
callback=self._api_help)
self._app.route('/api/%s/pluginslist' % self.API_VERSION, method="GET",
callback=self._api_plugins)
self._app.route('/api/%s/all' % self.API_VERSION, method="GET",
callback=self._api_all)
self._app.route('/api/%s/all/limits' % self.API_VERSION, method="GET",
callback=self._api_all_limits)
self._app.route('/api/%s/all/views' % self.API_VERSION, method="GET",
callback=self._api_all_views)
self._app.route('/api/%s/<plugin>' % self.API_VERSION, method="GET",
callback=self._api)
self._app.route('/api/%s/<plugin>/history' % self.API_VERSION, method="GET",
callback=self._api_history)
self._app.route('/api/%s/<plugin>/history/<nb:int>' % self.API_VERSION, method="GET",
callback=self._api_history)
self._app.route('/api/%s/<plugin>/limits' % self.API_VERSION, method="GET",
callback=self._api_limits)
self._app.route('/api/%s/<plugin>/views' % self.API_VERSION, method="GET",
callback=self._api_views)
self._app.route('/api/%s/<plugin>/<item>' % self.API_VERSION, method="GET",
callback=self._api_item)
self._app.route('/api/%s/<plugin>/<item>/history' % self.API_VERSION, method="GET",
callback=self._api_item_history)
self._app.route('/api/%s/<plugin>/<item>/history/<nb:int>' % self.API_VERSION, method="GET",
callback=self._api_item_history)
self._app.route('/api/%s/<plugin>/<item>/<value>' % self.API_VERSION, method="GET",
callback=self._api_value)
bindmsg = 'Glances RESTful API Server started on {}api/{}/'.format(self.bind_url,
self.API_VERSION)
self._app.route('/api/%s/config' % self.API_VERSION, method="GET", callback=self._api_config)
self._app.route('/api/%s/config/<item>' % self.API_VERSION, method="GET", callback=self._api_config_item)
self._app.route('/api/%s/args' % self.API_VERSION, method="GET", callback=self._api_args)
self._app.route('/api/%s/args/<item>' % self.API_VERSION, method="GET", callback=self._api_args_item)
self._app.route('/api/%s/help' % self.API_VERSION, method="GET", callback=self._api_help)
self._app.route('/api/%s/pluginslist' % self.API_VERSION, method="GET", callback=self._api_plugins)
self._app.route('/api/%s/all' % self.API_VERSION, method="GET", callback=self._api_all)
self._app.route('/api/%s/all/limits' % self.API_VERSION, method="GET", callback=self._api_all_limits)
self._app.route('/api/%s/all/views' % self.API_VERSION, method="GET", callback=self._api_all_views)
self._app.route('/api/%s/<plugin>' % self.API_VERSION, method="GET", callback=self._api)
self._app.route('/api/%s/<plugin>/history' % self.API_VERSION, method="GET", callback=self._api_history)
self._app.route(
'/api/%s/<plugin>/history/<nb:int>' % self.API_VERSION, method="GET", callback=self._api_history
)
self._app.route('/api/%s/<plugin>/limits' % self.API_VERSION, method="GET", callback=self._api_limits)
self._app.route('/api/%s/<plugin>/views' % self.API_VERSION, method="GET", callback=self._api_views)
self._app.route('/api/%s/<plugin>/<item>' % self.API_VERSION, method="GET", callback=self._api_item)
self._app.route(
'/api/%s/<plugin>/<item>/history' % self.API_VERSION, method="GET", callback=self._api_item_history
)
self._app.route(
'/api/%s/<plugin>/<item>/history/<nb:int>' % self.API_VERSION, method="GET", callback=self._api_item_history
)
self._app.route('/api/%s/<plugin>/<item>/<value>' % self.API_VERSION, method="GET", callback=self._api_value)
bindmsg = 'Glances RESTful API Server started on {}api/{}/'.format(self.bind_url, self.API_VERSION)
logger.info(bindmsg)
# WEB UI
@ -210,14 +198,10 @@ class GlancesBottle(object):
# Try to open the Glances Web UI in the default Web browser if:
# 1) --open-web-browser option is used
# 2) Glances standalone mode is running on Windows OS
webbrowser.open(self.bind_url,
new=2,
autoraise=1)
webbrowser.open(self.bind_url, new=2, autoraise=1)
try:
self._app.run(host=self.args.bind_address,
port=self.args.port,
quiet=not self.args.debug)
self._app.run(host=self.args.bind_address, port=self.args.port, quiet=not self.args.debug)
except socket.error as e:
logger.critical('Error: Can not ran Glances Web server ({})'.format(e))
@ -488,7 +472,9 @@ class GlancesBottle(object):
ret = self.stats.get_plugin(plugin).get_stats_value(item, value)
if ret is None:
abort(404, "Cannot get item %s(%s=%s) in plugin %s" % ('history ' if history else '', item, value, plugin))
abort(
404, "Cannot get item %s(%s=%s) in plugin %s" % ('history ' if history else '', item, value, plugin)
)
return ret
@ -618,7 +604,9 @@ class EnableCors(object):
# set CORS headers
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
response.headers[
'Access-Control-Allow-Headers'
] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
if request.method != 'OPTIONS':
# actual request; reply with the actual response

View File

@ -35,11 +35,9 @@ try:
import curses.panel
from curses.textpad import Textbox
except ImportError:
logger.critical(
"Curses module not found. Glances cannot start in standalone mode.")
logger.critical("Curses module not found. Glances cannot start in standalone mode.")
if WINDOWS:
logger.critical(
"For Windows you can try installing windows-curses with pip install.")
logger.critical("For Windows you can try installing windows-curses with pip install.")
sys.exit(1)
@ -114,8 +112,20 @@ class _GlancesCurses(object):
_quicklook_max_width = 68
# Define left sidebar
_left_sidebar = ['network', 'connections', 'wifi', 'ports', 'diskio', 'fs',
'irq', 'folders', 'raid', 'smart', 'sensors', 'now']
_left_sidebar = [
'network',
'connections',
'wifi',
'ports',
'diskio',
'fs',
'irq',
'folders',
'raid',
'smart',
'sensors',
'now',
]
_left_sidebar_min_width = 23
_left_sidebar_max_width = 34
@ -210,8 +220,7 @@ class _GlancesCurses(object):
try:
if hasattr(curses, 'start_color'):
curses.start_color()
logger.debug(
'Curses interface compatible with {} colors'.format(curses.COLORS))
logger.debug('Curses interface compatible with {} colors'.format(curses.COLORS))
if hasattr(curses, 'use_default_colors'):
curses.use_default_colors()
except Exception as e:
@ -320,7 +329,7 @@ class _GlancesCurses(object):
'WARNING_LOG': self.ifWARNING_color,
'CRITICAL_LOG': self.ifCRITICAL_color,
'PASSWORD': curses.A_PROTECT,
'SELECTED': self.selected_color
'SELECTED': self.selected_color,
}
def set_cursor(self, value):
@ -357,27 +366,27 @@ class _GlancesCurses(object):
option = '_'.join(self._hotkeys[hotkey]['switch'].split('_')[1:])
if self._hotkeys[hotkey]['switch'].startswith('disable_'):
# disable_ switch
if getattr(self.args,
self._hotkeys[hotkey]['switch']):
if getattr(self.args, self._hotkeys[hotkey]['switch']):
enable(self.args, option)
else:
disable(self.args, option)
elif self._hotkeys[hotkey]['switch'].startswith('enable_'):
# enable_ switch
if getattr(self.args,
self._hotkeys[hotkey]['switch']):
if getattr(self.args, self._hotkeys[hotkey]['switch']):
disable(self.args, option)
else:
enable(self.args, option)
else:
# Others switchs options (with no enable_ or disable_)
setattr(self.args,
self._hotkeys[hotkey]['switch'],
not getattr(self.args,
self._hotkeys[hotkey]['switch']))
setattr(
self.args,
self._hotkeys[hotkey]['switch'],
not getattr(self.args, self._hotkeys[hotkey]['switch']),
)
if self.pressedkey == ord(hotkey) and 'sort_key' in self._hotkeys[hotkey]:
glances_processes.set_sort_key(self._hotkeys[hotkey]['sort_key'],
self._hotkeys[hotkey]['sort_key'] == 'auto')
glances_processes.set_sort_key(
self._hotkeys[hotkey]['sort_key'], self._hotkeys[hotkey]['sort_key'] == 'auto'
)
# Other actions...
if self.pressedkey == ord('\n'):
@ -452,8 +461,7 @@ class _GlancesCurses(object):
if return_to_browser:
logger.info("Stop Glances client and return to the browser")
else:
logger.info(
"Stop Glances (keypressed: {})".format(self.pressedkey))
logger.info("Stop Glances (keypressed: {})".format(self.pressedkey))
elif self.pressedkey == curses.KEY_F5:
# "F5" manual refresh requested
pass
@ -551,14 +559,11 @@ class _GlancesCurses(object):
# Compute the plugin max size
plugin_max_width = None
if p in self._left_sidebar:
plugin_max_width = max(self._left_sidebar_min_width,
self.term_window.getmaxyx()[1] - 105)
plugin_max_width = min(self._left_sidebar_max_width,
plugin_max_width)
plugin_max_width = max(self._left_sidebar_min_width, self.term_window.getmaxyx()[1] - 105)
plugin_max_width = min(self._left_sidebar_max_width, plugin_max_width)
# Get the view
ret[p] = stats.get_plugin(p).get_stats_display(args=self.args,
max_width=plugin_max_width)
ret[p] = stats.get_plugin(p).get_stats_display(args=self.args, max_width=plugin_max_width)
return ret
@ -586,15 +591,17 @@ class _GlancesCurses(object):
# Adapt number of processes to the available space
max_processes_displayed = (
self.term_window.getmaxyx()[0] - 11 -
(0 if 'docker' not in __stat_display else
self.get_stats_display_height(__stat_display["docker"])) -
(0 if 'processcount' not in __stat_display else
self.get_stats_display_height(__stat_display["processcount"])) -
(0 if 'amps' not in __stat_display else
self.get_stats_display_height(__stat_display["amps"])) -
(0 if 'alert' not in __stat_display else
self.get_stats_display_height(__stat_display["alert"])))
self.term_window.getmaxyx()[0]
- 11
- (0 if 'docker' not in __stat_display else self.get_stats_display_height(__stat_display["docker"]))
- (
0
if 'processcount' not in __stat_display
else self.get_stats_display_height(__stat_display["processcount"])
)
- (0 if 'amps' not in __stat_display else self.get_stats_display_height(__stat_display["amps"]))
- (0 if 'alert' not in __stat_display else self.get_stats_display_height(__stat_display["alert"]))
)
try:
if self.args.enable_process_extended:
@ -603,14 +610,12 @@ class _GlancesCurses(object):
pass
if max_processes_displayed < 0:
max_processes_displayed = 0
if (glances_processes.max_processes is None or
glances_processes.max_processes != max_processes_displayed):
if glances_processes.max_processes is None or glances_processes.max_processes != max_processes_displayed:
logger.debug("Set number of displayed processes to {}".format(max_processes_displayed))
glances_processes.max_processes = max_processes_displayed
# Get the processlist
__stat_display["processlist"] = stats.get_plugin(
'processlist').get_stats_display(args=self.args)
__stat_display["processlist"] = stats.get_plugin('processlist').get_stats_display(args=self.args)
# Display the stats on the curses interface
###########################################
@ -618,8 +623,7 @@ class _GlancesCurses(object):
# Help screen (on top of the other stats)
if self.args.help_tag:
# Display the stats...
self.display_plugin(
stats.get_plugin('help').get_stats_display(args=self.args))
self.display_plugin(stats.get_plugin('help').get_stats_display(args=self.args))
# ... and exit
return False
@ -652,17 +656,18 @@ class _GlancesCurses(object):
# Only in standalone mode (cs_status is None)
if self.edit_filter and cs_status is None:
new_filter = self.display_popup(
'Process filter pattern: \n\n' +
'Examples:\n' +
'- python\n' +
'- .*python.*\n' +
'- /usr/lib.*\n' +
'- name:.*nautilus.*\n' +
'- cmdline:.*glances.*\n' +
'- username:nicolargo\n' +
'- username:^root ',
'Process filter pattern: \n\n'
+ 'Examples:\n'
+ '- python\n'
+ '- .*python.*\n'
+ '- /usr/lib.*\n'
+ '- name:.*nautilus.*\n'
+ '- cmdline:.*glances.*\n'
+ '- username:nicolargo\n'
+ '- username:^root ',
popup_type='input',
input_value=glances_processes.process_filter_input)
input_value=glances_processes.process_filter_input,
)
glances_processes.process_filter = new_filter
elif self.edit_filter and cs_status is not None:
self.display_popup('Process filter only available in standalone mode')
@ -671,26 +676,27 @@ class _GlancesCurses(object):
# Display kill process confirmation popup
# Only in standalone mode (cs_status is None)
if self.kill_process and cs_status is None:
selected_process_raw = stats.get_plugin('processlist').get_raw()[
self.args.cursor_position]
selected_process_raw = stats.get_plugin('processlist').get_raw()[self.args.cursor_position]
confirm = self.display_popup(
'Kill process: {} (pid: {}) ?\n\nConfirm ([y]es/[n]o): '.format(
selected_process_raw['name'],
selected_process_raw['pid']),
popup_type='yesno')
selected_process_raw['name'], selected_process_raw['pid']
),
popup_type='yesno',
)
if confirm.lower().startswith('y'):
try:
ret_kill = glances_processes.kill(selected_process_raw['pid'])
except Exception as e:
logger.error('Can not kill process {} ({})'.format(
selected_process_raw['name'], e))
logger.error('Can not kill process {} ({})'.format(selected_process_raw['name'], e))
else:
logger.info('Kill signal has been sent to process {} (return code: {})'.format(
selected_process_raw['name'], ret_kill))
logger.info(
'Kill signal has been sent to process {} (return code: {})'.format(
selected_process_raw['name'], ret_kill
)
)
elif self.kill_process and cs_status is not None:
self.display_popup(
'Kill process only available in standalone mode')
self.display_popup('Kill process only available in standalone mode')
self.kill_process = False
# Display graph generation popup
@ -712,17 +718,15 @@ class _GlancesCurses(object):
for i in ['system', 'ip', 'uptime']:
if i in stat_display:
l_uptime += self.get_stats_display_width(stat_display[i])
self.display_plugin(
stat_display["system"],
display_optional=(self.term_window.getmaxyx()[1] >= l_uptime))
self.display_plugin(stat_display["system"], display_optional=(self.term_window.getmaxyx()[1] >= l_uptime))
self.space_between_column = 3
if 'ip' in stat_display:
self.new_column()
self.display_plugin(stat_display["ip"])
self.new_column()
self.display_plugin(
stat_display["uptime"],
add_space=-(self.get_stats_display_width(stat_display["cloud"]) != 0))
stat_display["uptime"], add_space=-(self.get_stats_display_width(stat_display["cloud"]) != 0)
)
# Second line (optional)
self.init_column()
self.new_line()
@ -742,24 +746,33 @@ class _GlancesCurses(object):
# Dict for plugins width
plugin_widths = {}
for p in self._top:
plugin_widths[p] = self.get_stats_display_width(stat_display.get(p, 0)) if hasattr(self.args, 'disable_' + p) else 0
plugin_widths[p] = (
self.get_stats_display_width(stat_display.get(p, 0)) if hasattr(self.args, 'disable_' + p) else 0
)
# Width of all plugins
stats_width = sum(itervalues(plugin_widths))
# Number of plugin but quicklook
stats_number = sum([int(stat_display[p]['msgdict'] != []) for p in self._top if not getattr(self.args, 'disable_' + p)])
stats_number = sum(
[int(stat_display[p]['msgdict'] != []) for p in self._top if not getattr(self.args, 'disable_' + p)]
)
if not self.args.disable_quicklook:
# Quick look is in the place !
if self.args.full_quicklook:
quicklook_width = self.term_window.getmaxyx()[1] - (stats_width + 8 + stats_number * self.space_between_column)
quicklook_width = self.term_window.getmaxyx()[1] - (
stats_width + 8 + stats_number * self.space_between_column
)
else:
quicklook_width = min(self.term_window.getmaxyx()[1] - (stats_width + 8 + stats_number * self.space_between_column),
self._quicklook_max_width - 5)
quicklook_width = min(
self.term_window.getmaxyx()[1] - (stats_width + 8 + stats_number * self.space_between_column),
self._quicklook_max_width - 5,
)
try:
stat_display["quicklook"] = stats.get_plugin(
'quicklook').get_stats_display(max_width=quicklook_width, args=self.args)
stat_display["quicklook"] = stats.get_plugin('quicklook').get_stats_display(
max_width=quicklook_width, args=self.args
)
except AttributeError as e:
logger.debug("Quicklook plugin not available (%s)" % e)
else:
@ -780,9 +793,15 @@ class _GlancesCurses(object):
# No space ? Remove optional stats
if self.space_between_column < 3:
plugin_display_optional[p] = False
plugin_widths[p] = self.get_stats_display_width(stat_display[p], without_option=True) if hasattr(self.args, 'disable_' + p) else 0
plugin_widths[p] = (
self.get_stats_display_width(stat_display[p], without_option=True)
if hasattr(self.args, 'disable_' + p)
else 0
)
stats_width = sum(itervalues(plugin_widths)) + 1
self.space_between_column = max(1, int((self.term_window.getmaxyx()[1] - stats_width) / (stats_number - 1)))
self.space_between_column = max(
1, int((self.term_window.getmaxyx()[1] - stats_width) / (stats_number - 1))
)
else:
self.space_between_column = 0
@ -791,8 +810,7 @@ class _GlancesCurses(object):
if p == 'quicklook':
continue
if p in stat_display:
self.display_plugin(stat_display[p],
display_optional=plugin_display_optional[p])
self.display_plugin(stat_display[p], display_optional=plugin_display_optional[p])
if p != 'load':
# Skip last column
self.new_column()
@ -811,8 +829,7 @@ class _GlancesCurses(object):
return
for p in self._left_sidebar:
if ((hasattr(self.args, 'enable_' + p) or
hasattr(self.args, 'disable_' + p)) and p in stat_display):
if (hasattr(self.args, 'enable_' + p) or hasattr(self.args, 'disable_' + p)) and p in stat_display:
self.new_line()
self.display_plugin(stat_display[p])
@ -831,26 +848,26 @@ class _GlancesCurses(object):
# Display right sidebar
self.new_column()
for p in self._right_sidebar:
if ((hasattr(self.args, 'enable_' + p) or
hasattr(self.args, 'disable_' + p)) and p in stat_display):
if (hasattr(self.args, 'enable_' + p) or hasattr(self.args, 'disable_' + p)) and p in stat_display:
if p not in p:
# Catch for issue #1470
continue
self.new_line()
if p == 'processlist':
self.display_plugin(stat_display['processlist'],
display_optional=(self.term_window.getmaxyx()[1] > 102),
display_additional=(not MACOS),
max_y=(self.term_window.getmaxyx()[0] - self.get_stats_display_height(stat_display['alert']) - 2))
self.display_plugin(
stat_display['processlist'],
display_optional=(self.term_window.getmaxyx()[1] > 102),
display_additional=(not MACOS),
max_y=(
self.term_window.getmaxyx()[0] - self.get_stats_display_height(stat_display['alert']) - 2
),
)
else:
self.display_plugin(stat_display[p])
def display_popup(self, message,
size_x=None, size_y=None,
duration=3,
popup_type='info',
input_size=30,
input_value=None):
def display_popup(
self, message, size_x=None, size_y=None, duration=3, popup_type='info', input_size=30, input_value=None
):
"""
Display a centered popup.
@ -923,8 +940,7 @@ class _GlancesCurses(object):
self.set_cursor(0)
# self.term_window.keypad(0)
if textbox.gather() != '':
logger.debug(
"User enters the following string: %s" % textbox.gather())
logger.debug("User enters the following string: %s" % textbox.gather())
return textbox.gather()[:-1]
else:
logger.debug("User centers an empty string")
@ -947,11 +963,7 @@ class _GlancesCurses(object):
# self.term_window.keypad(0)
return textbox.gather()
def display_plugin(self, plugin_stats,
display_optional=True,
display_additional=True,
max_y=65535,
add_space=0):
def display_plugin(self, plugin_stats, display_optional=True, display_additional=True, max_y=65535, add_space=0):
"""Display the plugin_stats on the screen.
:param plugin_stats:
@ -1015,11 +1027,14 @@ class _GlancesCurses(object):
# Is it possible to display the stat with the current screen size
# !!! Crash if not try/except... Why ???
try:
self.term_window.addnstr(y, x,
m['msg'],
# Do not display outside the screen
screen_x - x,
self.colors_list[m['decoration']])
self.term_window.addnstr(
y,
x,
m['msg'],
# Do not display outside the screen
screen_x - x,
self.colors_list[m['decoration']],
)
except Exception:
pass
else:
@ -1037,8 +1052,7 @@ class _GlancesCurses(object):
x_max = x
# Compute the next Glances column/line position
self.next_column = max(
self.next_column, x_max + self.space_between_column)
self.next_column = max(self.next_column, x_max + self.space_between_column)
self.next_line = max(self.next_line, y + self.space_between_line)
# Have empty lines after the plugins
@ -1060,11 +1074,7 @@ class _GlancesCurses(object):
self.erase()
self.display(stats, cs_status=cs_status)
def update(self,
stats,
duration=3,
cs_status=None,
return_to_browser=False):
def update(self, stats, duration=3, cs_status=None, return_to_browser=False):
"""Update the screen.
:param stats: Stats database to display
@ -1096,7 +1106,7 @@ class _GlancesCurses(object):
while not countdown.finished() and not isexitkey:
# Getkey
pressedkey = self.__catch_key(return_to_browser=return_to_browser)
isexitkey = (pressedkey == ord('\x1b') or pressedkey == ord('q'))
isexitkey = pressedkey == ord('\x1b') or pressedkey == ord('q')
if pressedkey == curses.KEY_F5:
# Were asked to refresh
@ -1125,12 +1135,27 @@ class _GlancesCurses(object):
try:
if without_option:
# Size without options
c = len(max(''.join([(u(u(nativestr(i['msg'])).encode('ascii', 'replace')) if not i['optional'] else "")
for i in curse_msg['msgdict']]).split('\n'), key=len))
c = len(
max(
''.join(
[
(u(u(nativestr(i['msg'])).encode('ascii', 'replace')) if not i['optional'] else "")
for i in curse_msg['msgdict']
]
).split('\n'),
key=len,
)
)
else:
# Size with all options
c = len(max(''.join([u(u(nativestr(i['msg'])).encode('ascii', 'replace'))
for i in curse_msg['msgdict']]).split('\n'), key=len))
c = len(
max(
''.join(
[u(u(nativestr(i['msg'])).encode('ascii', 'replace')) for i in curse_msg['msgdict']]
).split('\n'),
key=len,
)
)
except Exception as e:
logger.debug('ERROR: Can not compute plugin width ({})'.format(e))
return 0
@ -1166,7 +1191,6 @@ class GlancesCursesClient(_GlancesCurses):
class GlancesTextbox(Textbox, object):
def __init__(self, *args, **kwargs):
super(GlancesTextbox, self).__init__(*args, **kwargs)
@ -1179,7 +1203,6 @@ class GlancesTextbox(Textbox, object):
class GlancesTextboxYesNo(Textbox, object):
def __init__(self, *args, **kwargs):
super(GlancesTextboxYesNo, self).__init__(*args, **kwargs)

View File

@ -108,12 +108,10 @@ class GlancesCursesBrowser(_GlancesCurses):
stats_list = None
if self._stats_list is not None:
stats_list = self._stats_list
stats_list.sort(reverse = self._revesed_sorting,
key = lambda x: { 'UNKNOWN' : 0,
'OFFLINE' : 1,
'PROTECTED' : 2,
'SNMP' : 3,
'ONLINE': 4 }.get(x['status'], 99))
stats_list.sort(
reverse=self._revesed_sorting,
key=lambda x: {'UNKNOWN': 0, 'OFFLINE': 1, 'PROTECTED': 2, 'SNMP': 3, 'ONLINE': 4}.get(x['status'], 99),
)
else:
stats_list = stats
@ -124,7 +122,7 @@ class GlancesCursesBrowser(_GlancesCurses):
if 0 <= self.cursor_position - 1:
self.cursor_position -= 1
else:
if self._current_page - 1 < 0 :
if self._current_page - 1 < 0:
self._current_page = self._page_max - 1
self.cursor_position = (len(stats) - 1) % self._page_max_lines
else:
@ -175,7 +173,7 @@ class GlancesCursesBrowser(_GlancesCurses):
self.is_end = True
elif self.pressedkey == 10:
# 'ENTER' > Run Glances on the selected server
self.active_server = self._current_page * self._page_max_lines + self.cursor_position
self.active_server = self._current_page * self._page_max_lines + self.cursor_position
logger.debug("Server {}/{} selected".format(self.active_server, len(stats)))
elif self.pressedkey == curses.KEY_UP or self.pressedkey == 65:
# 'UP' > Up in the server list
@ -213,11 +211,7 @@ class GlancesCursesBrowser(_GlancesCurses):
# Return the key code
return self.pressedkey
def update(self,
stats,
duration=3,
cs_status=None,
return_to_browser=False):
def update(self, stats, duration=3, cs_status=None, return_to_browser=False):
"""Update the servers' list screen.
Wait for __refresh_time sec / catch key every 100 ms.
@ -238,8 +232,7 @@ class GlancesCursesBrowser(_GlancesCurses):
# Getkey
pressedkey = self.__catch_key(stats)
# Is it an exit or select server key ?
exitkey = (
pressedkey == ord('\x1b') or pressedkey == ord('q') or pressedkey == 10)
exitkey = pressedkey == ord('\x1b') or pressedkey == ord('q') or pressedkey == 10
if not exitkey and pressedkey > -1:
# Redraw display
self.flush(stats)
@ -290,24 +283,16 @@ class GlancesCursesBrowser(_GlancesCurses):
if self.args.disable_autodiscover:
msg += ' (auto discover is disabled)'
if screen_y > 1:
self.term_window.addnstr(y, x,
msg,
screen_x - x,
self.colors_list['TITLE'])
self.term_window.addnstr(y, x, msg, screen_x - x, self.colors_list['TITLE'])
msg = '{}'.format(self._get_status_count(stats))
self.term_window.addnstr(y + 1, x,
msg,
screen_x - x)
self.term_window.addnstr(y + 1, x, msg, screen_x - x)
if stats_len > stats_max and screen_y > 2:
msg = '{} servers displayed.({}/{}) {}'.format(self.get_pagelines(stats),
self._current_page + 1,
self._page_max,
self._get_status_count(stats))
self.term_window.addnstr(y + 1, x,
msg,
screen_x - x)
msg = '{} servers displayed.({}/{}) {}'.format(
self.get_pagelines(stats), self._current_page + 1, self._page_max, self._get_status_count(stats)
)
self.term_window.addnstr(y + 1, x, msg, screen_x - x)
if stats_len == 0:
return False
@ -334,10 +319,7 @@ class GlancesCursesBrowser(_GlancesCurses):
xc = x + 2
for cpt, c in enumerate(column_def):
if xc < screen_x and y < screen_y and c[1] is not None:
self.term_window.addnstr(y, xc,
c[1],
screen_x - x,
self.colors_list['BOLD'])
self.term_window.addnstr(y, xc, c[1], screen_x - x, self.colors_list['BOLD'])
xc += c[2] + self.space_between_column
y += 1
@ -364,8 +346,7 @@ class GlancesCursesBrowser(_GlancesCurses):
try:
server_stat[c[0]] = v[c[0]]
except KeyError as e:
logger.debug(
"Cannot grab stats {} from server (KeyError: {})".format(c[0], e))
logger.debug("Cannot grab stats {} from server (KeyError: {})".format(c[0], e))
server_stat[c[0]] = '?'
# Display alias instead of name
try:
@ -381,16 +362,14 @@ class GlancesCursesBrowser(_GlancesCurses):
# Is the line selected ?
if line == self.cursor:
# Display cursor
self.term_window.addnstr(
y, xc, ">", screen_x - xc, self.colors_list['BOLD'])
self.term_window.addnstr(y, xc, ">", screen_x - xc, self.colors_list['BOLD'])
# Display the line
xc += 2
for c in column_def:
if xc < screen_x and y < screen_y and c[1] is not None:
# Display server stats
self.term_window.addnstr(
y, xc, format(server_stat[c[0]]), c[2], self.colors_list[v['status']])
self.term_window.addnstr(y, xc, format(server_stat[c[0]]), c[2], self.colors_list[v['status']])
xc += c[2] + self.space_between_column
cpt += 1
# Next line, next server...

View File

@ -54,17 +54,14 @@ class GlancesStdout(object):
def end(self):
pass
def update(self,
stats,
duration=3):
def update(self, stats, duration=3):
"""Display stats to stdout.
Refresh every duration second.
"""
for plugin, attribute in self.plugins_list:
# Check if the plugin exist and is enable
if plugin in stats.getPluginsList() and \
stats.get_plugin(plugin).is_enabled():
if plugin in stats.getPluginsList() and stats.get_plugin(plugin).is_enabled():
stat = stats.get_plugin(plugin).get_export()
else:
continue
@ -72,8 +69,7 @@ class GlancesStdout(object):
if attribute is not None:
# With attribute
try:
printandflush("{}.{}: {}".format(plugin, attribute,
stat[attribute]))
printandflush("{}.{}: {}".format(plugin, attribute, stat[attribute]))
except KeyError as err:
logger.error("Can not display stat {}.{} ({})".format(plugin, attribute, err))
else:

View File

@ -84,9 +84,15 @@ def print_plugin_description(plugin, stat):
print('Fields descriptions:')
print('')
for field, description in iteritems(stat.fields_description):
print('* **{}**: {} (unit is *{}*)'.format(field,
description['description'][:-1] if description['description'].endswith('.') else description['description'],
description['unit']))
print(
'* **{}**: {} (unit is *{}*)'.format(
field,
description['description'][:-1]
if description['description'].endswith('.')
else description['description'],
description['unit'],
)
)
print('')
else:
logger.error('No fields_description variable defined for plugin {}'.format(plugin))
@ -194,9 +200,7 @@ class GlancesStdoutApiDoc(object):
def end(self):
pass
def update(self,
stats,
duration=1):
def update(self, stats, duration=1):
"""Display issue"""
# Display header

View File

@ -69,17 +69,12 @@ class GlancesStdoutCsv(object):
else:
if isinstance(stat, dict):
for k in stat.keys():
line += '{}.{}{}'.format(plugin,
str(k),
self.separator)
line += '{}.{}{}'.format(plugin, str(k), self.separator)
elif isinstance(stat, list):
for i in stat:
if isinstance(i, dict) and 'key' in i:
for k in i.keys():
line += '{}.{}.{}{}'.format(plugin,
str(i[i['key']]),
str(k),
self.separator)
line += '{}.{}.{}{}'.format(plugin, str(i[i['key']]), str(k), self.separator)
else:
line += '{}{}'.format(plugin, self.separator)
@ -90,8 +85,7 @@ class GlancesStdoutCsv(object):
line = ''
if attribute is not None:
line += '{}{}'.format(str(stat.get(attribute, self.na)),
self.separator)
line += '{}{}'.format(str(stat.get(attribute, self.na)), self.separator)
else:
if isinstance(stat, dict):
for v in stat.values():
@ -106,9 +100,7 @@ class GlancesStdoutCsv(object):
return line
def update(self,
stats,
duration=3):
def update(self, stats, duration=3):
"""Display stats to stdout.
Refresh every duration second.
@ -117,8 +109,7 @@ class GlancesStdoutCsv(object):
line = ''
for plugin, attribute in self.plugins_list:
# Check if the plugin exist and is enable
if plugin in stats.getPluginsList() and \
stats.get_plugin(plugin).is_enabled():
if plugin in stats.getPluginsList() and stats.get_plugin(plugin).is_enabled():
stat = stats.get_plugin(plugin).get_export()
else:
continue

View File

@ -66,27 +66,22 @@ class GlancesStdoutIssue(object):
def print_version(self):
sys.stdout.write('=' * TERMINAL_WIDTH + '\n')
sys.stdout.write('Glances {} ({})\n'.format(
colors.BLUE + __version__ + colors.NO,
os.path.realpath(glances.__file__)))
sys.stdout.write('Python {} ({})\n'.format(
colors.BLUE + platform.python_version() + colors.NO,
sys.executable))
sys.stdout.write('PsUtil {} ({})\n'.format(
colors.BLUE + psutil_version + colors.NO,
os.path.realpath(psutil.__file__)))
sys.stdout.write(
'Glances {} ({})\n'.format(colors.BLUE + __version__ + colors.NO, os.path.realpath(glances.__file__))
)
sys.stdout.write('Python {} ({})\n'.format(colors.BLUE + platform.python_version() + colors.NO, sys.executable))
sys.stdout.write(
'PsUtil {} ({})\n'.format(colors.BLUE + psutil_version + colors.NO, os.path.realpath(psutil.__file__))
)
sys.stdout.write('=' * TERMINAL_WIDTH + '\n')
sys.stdout.flush()
def print_issue(self, plugin, result, message):
sys.stdout.write('{}{}{}'.format(
colors.BLUE + plugin, result, message))
sys.stdout.write('{}{}{}'.format(colors.BLUE + plugin, result, message))
sys.stdout.write(colors.NO + '\n')
sys.stdout.flush()
def update(self,
stats,
duration=3):
def update(self, stats, duration=3):
"""Display issue"""
self.print_version()
@ -122,21 +117,19 @@ class GlancesStdoutIssue(object):
except Exception as e:
stat_error = e
if stat_error is None:
result = (colors.GREEN +
'[OK] ' +
colors.BLUE +
' {:.5f}s '.format(counter.get())).rjust(41 - len(plugin))
result = (colors.GREEN + '[OK] ' + colors.BLUE + ' {:.5f}s '.format(counter.get())).rjust(
41 - len(plugin)
)
if isinstance(stat, list) and len(stat) > 0 and 'key' in stat[0]:
key = 'key={} '.format(stat[0]['key'])
message = colors.ORANGE + key + colors.NO + str(stat)[0:TERMINAL_WIDTH-41-len(key)]
message = colors.ORANGE + key + colors.NO + str(stat)[0 : TERMINAL_WIDTH - 41 - len(key)]
else:
message = colors.NO + str(stat)[0:TERMINAL_WIDTH-41]
message = colors.NO + str(stat)[0 : TERMINAL_WIDTH - 41]
else:
result = (colors.RED +
'[ERROR]' +
colors.BLUE +
' {:.5f}s '.format(counter.get())).rjust(41 - len(plugin))
message = colors.NO + str(stat_error)[0:TERMINAL_WIDTH-41]
result = (colors.RED + '[ERROR]' + colors.BLUE + ' {:.5f}s '.format(counter.get())).rjust(
41 - len(plugin)
)
message = colors.NO + str(stat_error)[0 : TERMINAL_WIDTH - 41]
self.print_issue(plugin, result, message)
# Return True to exit directly (no refresh)

View File

@ -24,6 +24,7 @@ from datetime import datetime
from glances.logger import logger
from glances.events import glances_events
from glances.thresholds import glances_thresholds
# from glances.logger import logger
from glances.plugins.glances_plugin import GlancesPlugin
@ -35,34 +36,21 @@ from glances.plugins.glances_plugin import GlancesPlugin
# - 1: CAREFUL
# - 2: WARNING
# - 3: CRITICAL
tree = [{'msg': 'No warning or critical alert detected',
'thresholds': [],
'thresholds_min': 0},
{'msg': 'High CPU user mode',
'thresholds': ['cpu_user'],
'thresholds_min': 2},
{'msg': 'High CPU kernel usage',
'thresholds': ['cpu_system'],
'thresholds_min': 2},
{'msg': 'High CPU I/O waiting',
'thresholds': ['cpu_iowait'],
'thresholds_min': 2},
{'msg': 'Large CPU stolen time. System running the hypervisor is too busy.',
'thresholds': ['cpu_steal'],
'thresholds_min': 2},
{'msg': 'High CPU niced value',
'thresholds': ['cpu_niced'],
'thresholds_min': 2},
{'msg': 'System overloaded in the last 5 minutes',
'thresholds': ['load'],
'thresholds_min': 2},
{'msg': 'High swap (paging) usage',
'thresholds': ['memswap'],
'thresholds_min': 2},
{'msg': 'High memory consumption',
'thresholds': ['mem'],
'thresholds_min': 2},
]
tree = [
{'msg': 'No warning or critical alert detected', 'thresholds': [], 'thresholds_min': 0},
{'msg': 'High CPU user mode', 'thresholds': ['cpu_user'], 'thresholds_min': 2},
{'msg': 'High CPU kernel usage', 'thresholds': ['cpu_system'], 'thresholds_min': 2},
{'msg': 'High CPU I/O waiting', 'thresholds': ['cpu_iowait'], 'thresholds_min': 2},
{
'msg': 'Large CPU stolen time. System running the hypervisor is too busy.',
'thresholds': ['cpu_steal'],
'thresholds_min': 2,
},
{'msg': 'High CPU niced value', 'thresholds': ['cpu_niced'], 'thresholds_min': 2},
{'msg': 'System overloaded in the last 5 minutes', 'thresholds': ['load'], 'thresholds_min': 2},
{'msg': 'High swap (paging) usage', 'thresholds': ['memswap'], 'thresholds_min': 2},
{'msg': 'High memory consumption', 'thresholds': ['mem'], 'thresholds_min': 2},
]
# @TODO: change the algo to use the following decision tree
# Source: Inspire by https://scoutapm.com/blog/slow_server_flow_chart
@ -74,7 +62,7 @@ tree = [{'msg': 'No warning or critical alert detected',
# - 2: WARNING
# - 3: CRITICAL
tree_new = {
'cpu_iowait': {
'cpu_iowait': {
'_yes': {
'memswap': {
'_yes': {
@ -84,13 +72,13 @@ tree_new = {
# business-as-usual or not. For example, a memory leak can be satisfactorily addressed by a one-time or periodic
# restart of the process.
# - if memory usage seems anomalous: kill the offending processes.
# - if memory usage seems business-as-usual: add RAM to the server, or split high-memory using services to other servers.
# - if memory usage seems business-as-usual: add RAM to the server, or split high-memory using services to other servers.
'_msg': "Memory issue"
},
'_no': {
# ???
'_msg': "Swap issue"
}
},
}
},
'_no': {
@ -100,7 +88,7 @@ tree_new = {
# Recommendation: install it before you need it - - it's no fun trying to install a troubleshooting
# tool on an overloaded machine (iotop requires a Linux of 2.62 or above)
'_msg': "I/O issue"
}
},
}
},
'_no': {
@ -128,11 +116,9 @@ tree_new = {
'_yes': {
'_msg': "CPU issue with stolen time. System running the hypervisor may be too busy."
},
'_no': {
'_msg': "CPU issue with system process(es)"
}
'_no': {'_msg': "CPU issue with system process(es)"},
}
}
},
}
},
'_no': {
@ -152,10 +138,10 @@ tree_new = {
# to(or being attempted to be read from) and lsof can give you a mapping of those file descriptors to
# network connections.
'_msg': "External issue"
}
}
},
},
}
}
},
}
}
@ -185,9 +171,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
stats_init_value=[])
super(Plugin, self).__init__(args=args, config=config, stats_init_value=[])
# We want to display the stat in the curse interface
self.display_curse = True
@ -226,8 +210,7 @@ class Plugin(GlancesPlugin):
# Duration
if alert[1] > 0:
# If finished display duration
msg = ' ({})'.format(datetime.fromtimestamp(alert[1]) -
datetime.fromtimestamp(alert[0]))
msg = ' ({})'.format(datetime.fromtimestamp(alert[1]) - datetime.fromtimestamp(alert[0]))
else:
msg = ' (ongoing)'
ret.append(self.curse_add_line(msg))
@ -244,8 +227,7 @@ class Plugin(GlancesPlugin):
if self.approx_equal(alert[6], alert[4], tolerance=0.1):
msg = ' ({:.1f})'.format(alert[5])
else:
msg = ' (Min:{:.1f} Mean:{:.1f} Max:{:.1f})'.format(
alert[6], alert[5], alert[4])
msg = ' (Min:{:.1f} Mean:{:.1f} Max:{:.1f})'.format(alert[6], alert[5], alert[4])
ret.append(self.curse_add_line(msg))
# Top processes
top_process = ', '.join([p['name'] for p in alert[9]])

View File

@ -29,9 +29,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
stats_init_value=[])
super(Plugin, self).__init__(args=args, config=config, stats_init_value=[])
self.args = args
self.config = config
@ -54,16 +52,19 @@ class Plugin(GlancesPlugin):
if self.input_method == 'local':
for k, v in iteritems(self.glances_amps.update()):
stats.append({'key': self.get_key(),
'name': v.NAME,
'result': v.result(),
'refresh': v.refresh(),
'timer': v.time_until_refresh(),
'count': v.count(),
'countmin': v.count_min(),
'countmax': v.count_max(),
'regex': v.regex() is not None},
)
stats.append(
{
'key': self.get_key(),
'name': v.NAME,
'result': v.result(),
'refresh': v.refresh(),
'timer': v.time_until_refresh(),
'count': v.count(),
'countmin': v.count_min(),
'countmax': v.count_max(),
'regex': v.regex() is not None,
},
)
else:
# Not available in SNMP mode
pass

View File

@ -111,14 +111,12 @@ class Plugin(GlancesPlugin):
return ret
# Generate the output
if 'instance-type' in self.stats \
and 'instance-id' in self.stats \
and 'region' in self.stats:
if 'instance-type' in self.stats and 'instance-id' in self.stats and 'region' in self.stats:
msg = 'Cloud '
ret.append(self.curse_add_line(msg, "TITLE"))
msg = '{} instance {} ({})'.format(self.stats['instance-type'],
self.stats['instance-id'],
self.stats['region'])
msg = '{} instance {} ({})'.format(
self.stats['instance-type'], self.stats['instance-id'], self.stats['region']
)
ret.append(self.curse_add_line(msg))
# Return the message with decoration
@ -135,10 +133,12 @@ class ThreadOpenStack(threading.Thread):
# https://docs.openstack.org/nova/latest/user/metadata-service.html
OPENSTACK_API_URL = 'http://169.254.169.254/latest/meta-data'
OPENSTACK_API_METADATA = {'ami-id': 'ami-id',
'instance-id': 'instance-id',
'instance-type': 'instance-type',
'region': 'placement/availability-zone'}
OPENSTACK_API_METADATA = {
'ami-id': 'ami-id',
'instance-id': 'instance-id',
'instance-type': 'instance-type',
'region': 'placement/availability-zone',
}
def __init__(self):
"""Init the class."""

View File

@ -41,25 +41,29 @@ class Plugin(GlancesPlugin):
stats is a dict
"""
status_list = [psutil.CONN_LISTEN,
psutil.CONN_ESTABLISHED]
initiated_states = [psutil.CONN_SYN_SENT,
psutil.CONN_SYN_RECV]
terminated_states = [psutil.CONN_FIN_WAIT1,
psutil.CONN_FIN_WAIT2,
psutil.CONN_TIME_WAIT,
psutil.CONN_CLOSE,
psutil.CONN_CLOSE_WAIT,
psutil.CONN_LAST_ACK]
conntrack = {'nf_conntrack_count': '/proc/sys/net/netfilter/nf_conntrack_count',
'nf_conntrack_max': '/proc/sys/net/netfilter/nf_conntrack_max'}
status_list = [psutil.CONN_LISTEN, psutil.CONN_ESTABLISHED]
initiated_states = [psutil.CONN_SYN_SENT, psutil.CONN_SYN_RECV]
terminated_states = [
psutil.CONN_FIN_WAIT1,
psutil.CONN_FIN_WAIT2,
psutil.CONN_TIME_WAIT,
psutil.CONN_CLOSE,
psutil.CONN_CLOSE_WAIT,
psutil.CONN_LAST_ACK,
]
conntrack = {
'nf_conntrack_count': '/proc/sys/net/netfilter/nf_conntrack_count',
'nf_conntrack_max': '/proc/sys/net/netfilter/nf_conntrack_max',
}
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
# items_history_list=items_history_list,
stats_init_value={})
super(Plugin, self).__init__(
args=args,
config=config,
# items_history_list=items_history_list,
stats_init_value={},
)
# We want to display the stat in the curse interface
self.display_curse = True
@ -167,11 +171,10 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_new_line())
msg = '{:{width}}'.format(nativestr(s).capitalize(), width=len(s))
ret.append(self.curse_add_line(msg))
msg = '{:>{width}}'.format('{:0.0f}/{:0.0f}'.format(self.stats['nf_conntrack_count'],
self.stats['nf_conntrack_max']),
width=max_width - len(s) + 2)
ret.append(self.curse_add_line(msg,
self.get_views(key='nf_conntrack_percent',
option='decoration')))
msg = '{:>{width}}'.format(
'{:0.0f}/{:0.0f}'.format(self.stats['nf_conntrack_count'], self.stats['nf_conntrack_max']),
width=max_width - len(s) + 2,
)
ret.append(self.curse_add_line(msg, self.get_views(key='nf_conntrack_percent', option='decoration')))
return ret

View File

@ -25,14 +25,15 @@ import psutil
# Fields description
# - phys: physical cores only (hyper thread CPUs are excluded)
# - log: logical CPUs in the system
# - phys: physical cores only (hyper thread CPUs are excluded)
# - log: logical CPUs in the system
fields_description = {
'phys': {'description': 'Number of physical cores (hyper thread CPUs are excluded).',
'unit': 'number'},
'log': {'description': 'Number of logical CPUs. A logical CPU is the number of \
'phys': {'description': 'Number of physical cores (hyper thread CPUs are excluded).', 'unit': 'number'},
'log': {
'description': 'Number of logical CPUs. A logical CPU is the number of \
physical cores multiplied by the number of threads that can run on each core.',
'unit': 'number'},
'unit': 'number',
},
}
@ -46,9 +47,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
fields_description=fields_description)
super(Plugin, self).__init__(args=args, config=config, fields_description=fields_description)
# We dot not want to display the stat in the curse interface
# The core number is displayed by the load plugin

View File

@ -36,84 +36,107 @@ import psutil
# rate: is it a rate ? If yes, // by time_since_update when displayed,
# min_symbol: Auto unit should be used if value > than 1 'X' (K, M, G)...
fields_description = {
'total': {'description': 'Sum of all CPU percentages (except idle).',
'unit': 'percent'},
'system': {'description': 'percent time spent in kernel space. System CPU time is the \
'total': {'description': 'Sum of all CPU percentages (except idle).', 'unit': 'percent'},
'system': {
'description': 'percent time spent in kernel space. System CPU time is the \
time spent running code in the Operating System kernel.',
'unit': 'percent'},
'user': {'description': 'CPU percent time spent in user space. \
'unit': 'percent',
},
'user': {
'description': 'CPU percent time spent in user space. \
User CPU time is the time spent on the processor running your program\'s code (or code in libraries).',
'unit': 'percent'},
'iowait': {'description': '*(Linux)*: percent time spent by the CPU waiting for I/O \
'unit': 'percent',
},
'iowait': {
'description': '*(Linux)*: percent time spent by the CPU waiting for I/O \
operations to complete.',
'unit': 'percent'},
'idle': {'description': 'percent of CPU used by any program. Every program or task \
'unit': 'percent',
},
'idle': {
'description': 'percent of CPU used by any program. Every program or task \
that runs on a computer system occupies a certain amount of processing \
time on the CPU. If the CPU has completed all tasks it is idle.',
'unit': 'percent'},
'irq': {'description': '*(Linux and BSD)*: percent time spent servicing/handling \
'unit': 'percent',
},
'irq': {
'description': '*(Linux and BSD)*: percent time spent servicing/handling \
hardware/software interrupts. Time servicing interrupts (hardware + \
software).',
'unit': 'percent'},
'nice': {'description': '*(Unix)*: percent time occupied by user level processes with \
'unit': 'percent',
},
'nice': {
'description': '*(Unix)*: percent time occupied by user level processes with \
a positive nice value. The time the CPU has spent running users\' \
processes that have been *niced*.',
'unit': 'percent'},
'steal': {'description': '*(Linux)*: percentage of time a virtual CPU waits for a real \
'unit': 'percent',
},
'steal': {
'description': '*(Linux)*: percentage of time a virtual CPU waits for a real \
CPU while the hypervisor is servicing another virtual processor.',
'unit': 'percent'},
'ctx_switches': {'description': 'number of context switches (voluntary + involuntary) per \
'unit': 'percent',
},
'ctx_switches': {
'description': 'number of context switches (voluntary + involuntary) per \
second. A context switch is a procedure that a computer\'s CPU (central \
processing unit) follows to change from one task (or process) to \
another while ensuring that the tasks do not conflict.',
'unit': 'number',
'rate': True,
'min_symbol': 'K',
'short_name': 'ctx_sw'},
'interrupts': {'description': 'number of interrupts per second.',
'unit': 'number',
'rate': True,
'min_symbol': 'K',
'short_name': 'inter'},
'soft_interrupts': {'description': 'number of software interrupts per second. Always set to \
'unit': 'number',
'rate': True,
'min_symbol': 'K',
'short_name': 'ctx_sw',
},
'interrupts': {
'description': 'number of interrupts per second.',
'unit': 'number',
'rate': True,
'min_symbol': 'K',
'short_name': 'inter',
},
'soft_interrupts': {
'description': 'number of software interrupts per second. Always set to \
0 on Windows and SunOS.',
'unit': 'number',
'rate': True,
'min_symbol': 'K',
'short_name': 'sw_int'},
'syscalls': {'description': 'number of system calls per second. Always 0 on Linux OS.',
'unit': 'number',
'rate': True,
'min_symbol': 'K',
'short_name': 'sw_int'},
'cpucore': {'description': 'Total number of CPU core.',
'unit': 'number'},
'time_since_update': {'description': 'Number of seconds since last update.',
'unit': 'seconds'},
'unit': 'number',
'rate': True,
'min_symbol': 'K',
'short_name': 'sw_int',
},
'syscalls': {
'description': 'number of system calls per second. Always 0 on Linux OS.',
'unit': 'number',
'rate': True,
'min_symbol': 'K',
'short_name': 'sw_int',
},
'cpucore': {'description': 'Total number of CPU core.', 'unit': 'number'},
'time_since_update': {'description': 'Number of seconds since last update.', 'unit': 'seconds'},
}
# SNMP OID
# percentage of user CPU time: .1.3.6.1.4.1.2021.11.9.0
# percentages of system CPU time: .1.3.6.1.4.1.2021.11.10.0
# percentages of idle CPU time: .1.3.6.1.4.1.2021.11.11.0
snmp_oid = {'default': {'user': '1.3.6.1.4.1.2021.11.9.0',
'system': '1.3.6.1.4.1.2021.11.10.0',
'idle': '1.3.6.1.4.1.2021.11.11.0'},
'windows': {'percent': '1.3.6.1.2.1.25.3.3.1.2'},
'esxi': {'percent': '1.3.6.1.2.1.25.3.3.1.2'},
'netapp': {'system': '1.3.6.1.4.1.789.1.2.1.3.0',
'idle': '1.3.6.1.4.1.789.1.2.1.5.0',
'cpucore': '1.3.6.1.4.1.789.1.2.1.6.0'}}
snmp_oid = {
'default': {
'user': '1.3.6.1.4.1.2021.11.9.0',
'system': '1.3.6.1.4.1.2021.11.10.0',
'idle': '1.3.6.1.4.1.2021.11.11.0',
},
'windows': {'percent': '1.3.6.1.2.1.25.3.3.1.2'},
'esxi': {'percent': '1.3.6.1.2.1.25.3.3.1.2'},
'netapp': {
'system': '1.3.6.1.4.1.789.1.2.1.3.0',
'idle': '1.3.6.1.4.1.789.1.2.1.5.0',
'cpucore': '1.3.6.1.4.1.789.1.2.1.6.0',
},
}
# Define the history items list
# - 'name' define the stat identifier
# - 'y_unit' define the Y label
items_history_list = [{'name': 'user',
'description': 'User CPU usage',
'y_unit': '%'},
{'name': 'system',
'description': 'System CPU usage',
'y_unit': '%'}]
items_history_list = [
{'name': 'user', 'description': 'User CPU usage', 'y_unit': '%'},
{'name': 'system', 'description': 'System CPU usage', 'y_unit': '%'},
]
class Plugin(GlancesPlugin):
@ -125,10 +148,9 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the CPU plugin."""
super(Plugin, self).__init__(args=args,
config=config,
items_history_list=items_history_list,
fields_description=fields_description)
super(Plugin, self).__init__(
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
)
# We want to display the stat in the curse interface
self.display_curse = True
@ -218,8 +240,7 @@ class Plugin(GlancesPlugin):
# You can find the CPU utilization of windows system by querying the oid
# Give also the number of core (number of element in the table)
try:
cpu_stats = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
bulk=True)
cpu_stats = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name], bulk=True)
except KeyError:
self.reset()
@ -238,11 +259,9 @@ class Plugin(GlancesPlugin):
else:
# Default behavior
try:
stats = self.get_stats_snmp(
snmp_oid=snmp_oid[self.short_system_name])
stats = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name])
except KeyError:
stats = self.get_stats_snmp(
snmp_oid=snmp_oid['default'])
stats = self.get_stats_snmp(snmp_oid=snmp_oid['default'])
if stats['idle'] == '':
self.reset()
@ -272,7 +291,9 @@ class Plugin(GlancesPlugin):
# Alert only but depend on Core number
for key in ['ctx_switches']:
if key in self.stats:
self.views[key]['decoration'] = self.get_alert(self.stats[key], maximum=100 * self.stats['cpucore'], header=key)
self.views[key]['decoration'] = self.get_alert(
self.stats[key], maximum=100 * self.stats['cpucore'], header=key
)
# Optional
for key in ['nice', 'irq', 'idle', 'steal', 'ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls']:
if key in self.stats:
@ -305,16 +326,13 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg))
# Total CPU usage
msg = '{:5.1f}%'.format(self.stats['total'])
ret.append(self.curse_add_line(
msg, self.get_views(key='total', option='decoration')))
ret.append(self.curse_add_line(msg, self.get_views(key='total', option='decoration')))
# Idle CPU
if 'idle' in self.stats and not idle_tag:
msg = ' {:8}'.format('idle:')
ret.append(self.curse_add_line(msg, optional=self.get_views(key='idle',
option='optional')))
ret.append(self.curse_add_line(msg, optional=self.get_views(key='idle', option='optional')))
msg = '{:5.1f}%'.format(self.stats['idle'])
ret.append(self.curse_add_line(msg, optional=self.get_views(key='idle',
option='optional')))
ret.append(self.curse_add_line(msg, optional=self.get_views(key='idle', option='optional')))
# ctx_switches
ret.extend(self.curse_add_stat('ctx_switches', width=15, header=' '))

View File

@ -29,12 +29,10 @@ import psutil
# Define the history items list
items_history_list = [{'name': 'read_bytes',
'description': 'Bytes read per second',
'y_unit': 'B/s'},
{'name': 'write_bytes',
'description': 'Bytes write per second',
'y_unit': 'B/s'}]
items_history_list = [
{'name': 'read_bytes', 'description': 'Bytes read per second', 'y_unit': 'B/s'},
{'name': 'write_bytes', 'description': 'Bytes write per second', 'y_unit': 'B/s'},
]
class Plugin(GlancesPlugin):
@ -45,18 +43,16 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
items_history_list=items_history_list,
stats_init_value=[])
super(Plugin, self).__init__(
args=args, config=config, items_history_list=items_history_list, stats_init_value=[]
)
# We want to display the stat in the curse interface
self.display_curse = True
# Hide stats if it has never been != 0
if config is not None:
self.hide_zero = config.get_bool_value(
self.plugin_name, 'hide_zero', default=False)
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']
@ -111,14 +107,10 @@ class Plugin(GlancesPlugin):
diskstat = {
'time_since_update': time_since_update,
'disk_name': n(disk),
'read_count': diskio[disk].read_count - \
self.diskio_old[disk].read_count,
'write_count': diskio[disk].write_count - \
self.diskio_old[disk].write_count,
'read_bytes': diskio[disk].read_bytes - \
self.diskio_old[disk].read_bytes,
'write_bytes': diskio[disk].write_bytes - \
self.diskio_old[disk].write_bytes
'read_count': diskio[disk].read_count - self.diskio_old[disk].read_count,
'write_count': diskio[disk].write_count - self.diskio_old[disk].write_count,
'read_bytes': diskio[disk].read_bytes - self.diskio_old[disk].read_bytes,
'write_bytes': diskio[disk].write_bytes - self.diskio_old[disk].write_bytes,
}
except (KeyError, AttributeError):
diskstat = {
@ -127,7 +119,8 @@ class Plugin(GlancesPlugin):
'read_count': 0,
'write_count': 0,
'read_bytes': 0,
'write_bytes': 0}
'write_bytes': 0,
}
# Add alias if exist (define in the configuration file)
if self.has_alias(disk) is not None:
@ -166,10 +159,12 @@ class Plugin(GlancesPlugin):
# Alert
for i in self.get_raw():
disk_real_name = i['disk_name']
self.views[i[self.get_key()]]['read_bytes']['decoration'] = self.get_alert(int(i['read_bytes'] // i['time_since_update']),
header=disk_real_name + '_rx')
self.views[i[self.get_key()]]['write_bytes']['decoration'] = self.get_alert(int(i['write_bytes'] // i['time_since_update']),
header=disk_real_name + '_tx')
self.views[i[self.get_key()]]['read_bytes']['decoration'] = self.get_alert(
int(i['read_bytes'] // i['time_since_update']), header=disk_real_name + '_rx'
)
self.views[i[self.get_key()]]['write_bytes']['decoration'] = self.get_alert(
int(i['write_bytes'] // i['time_since_update']), header=disk_real_name + '_tx'
)
def msg_curse(self, args=None, max_width=None):
"""Return the dict to display in the curse interface."""
@ -210,41 +205,40 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_new_line())
if len(disk_name) > name_max_width:
# Cut disk name if it is too long
disk_name = '_' + disk_name[-name_max_width+1:]
msg = '{:{width}}'.format(nativestr(disk_name),
width=name_max_width+1)
disk_name = '_' + disk_name[-name_max_width + 1 :]
msg = '{:{width}}'.format(nativestr(disk_name), width=name_max_width + 1)
ret.append(self.curse_add_line(msg))
if args.diskio_iops:
# count
txps = self.auto_unit(
int(i['read_count'] // i['time_since_update']))
rxps = self.auto_unit(
int(i['write_count'] // i['time_since_update']))
txps = self.auto_unit(int(i['read_count'] // i['time_since_update']))
rxps = self.auto_unit(int(i['write_count'] // i['time_since_update']))
msg = '{:>7}'.format(txps)
ret.append(self.curse_add_line(msg,
self.get_views(item=i[self.get_key()],
key='read_count',
option='decoration')))
ret.append(
self.curse_add_line(
msg, self.get_views(item=i[self.get_key()], key='read_count', option='decoration')
)
)
msg = '{:>7}'.format(rxps)
ret.append(self.curse_add_line(msg,
self.get_views(item=i[self.get_key()],
key='write_count',
option='decoration')))
ret.append(
self.curse_add_line(
msg, self.get_views(item=i[self.get_key()], key='write_count', option='decoration')
)
)
else:
# Bitrate
txps = self.auto_unit(
int(i['read_bytes'] // i['time_since_update']))
rxps = self.auto_unit(
int(i['write_bytes'] // i['time_since_update']))
txps = self.auto_unit(int(i['read_bytes'] // i['time_since_update']))
rxps = self.auto_unit(int(i['write_bytes'] // i['time_since_update']))
msg = '{:>7}'.format(txps)
ret.append(self.curse_add_line(msg,
self.get_views(item=i[self.get_key()],
key='read_bytes',
option='decoration')))
ret.append(
self.curse_add_line(
msg, self.get_views(item=i[self.get_key()], key='read_bytes', option='decoration')
)
)
msg = '{:>7}'.format(rxps)
ret.append(self.curse_add_line(msg,
self.get_views(item=i[self.get_key()],
key='write_bytes',
option='decoration')))
ret.append(
self.curse_add_line(
msg, self.get_views(item=i[self.get_key()], key='write_bytes', option='decoration')
)
)
return ret

View File

@ -62,9 +62,7 @@ else:
# {'name': 'io_w',
# 'description': 'Container IO bytes write per second',
# 'y_unit': 'Bps'}]
items_history_list = [{'name': 'cpu_percent',
'description': 'Container CPU consumption in %',
'y_unit': '%'}]
items_history_list = [{'name': 'cpu_percent', 'description': 'Container CPU consumption in %', 'y_unit': '%'}]
# List of key to remove before export
export_exclude_list = ['cpu', 'io', 'memory', 'network']
@ -78,9 +76,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
items_history_list=items_history_list)
super(Plugin, self).__init__(args=args, config=config, items_history_list=items_history_list)
# The plugin can be disabled using: args.disable_docker
self.args = args
@ -220,7 +216,9 @@ class Plugin(GlancesPlugin):
if container.id not in self.thread_list:
# Thread did not exist in the internal dict
# Create it and add it to the internal dict
logger.debug("{} plugin - Create thread for container {}".format(self.plugin_name, container.id[:12]))
logger.debug(
"{} plugin - Create thread for container {}".format(self.plugin_name, container.id[:12])
)
t = ThreadDockerGrabber(container)
self.thread_list[container.id] = t
t.start()
@ -276,8 +274,9 @@ class Plugin(GlancesPlugin):
container_stats['cpu'] = self.get_docker_cpu(container.id, self.thread_list[container.id].stats)
container_stats['cpu_percent'] = container_stats['cpu'].get('total', None)
# MEM
container_stats['memory'] = self.get_docker_memory(container.id,
self.thread_list[container.id].stats)
container_stats['memory'] = self.get_docker_memory(
container.id, self.thread_list[container.id].stats
)
container_stats['memory_usage'] = container_stats['memory'].get('usage', None)
if container_stats['memory'].get('cache', None) is not None:
container_stats['memory_usage'] -= container_stats['memory']['cache']
@ -286,8 +285,9 @@ class Plugin(GlancesPlugin):
container_stats['io_r'] = container_stats['io'].get('ior', None)
container_stats['io_w'] = container_stats['io'].get('iow', None)
# NET
container_stats['network'] = self.get_docker_network(container.id,
self.thread_list[container.id].stats)
container_stats['network'] = self.get_docker_network(
container.id, self.thread_list[container.id].stats
)
container_stats['network_rx'] = container_stats['network'].get('rx', None)
container_stats['network_tx'] = container_stats['network'].get('tx', None)
else:
@ -326,11 +326,11 @@ class Plugin(GlancesPlugin):
try:
cpu = {
'system': all_stats['cpu_stats']['system_cpu_usage'],
'total': all_stats['cpu_stats']['cpu_usage']['total_usage']
'total': all_stats['cpu_stats']['cpu_usage']['total_usage'],
}
precpu = {
'system': all_stats['precpu_stats']['system_cpu_usage'],
'total': all_stats['precpu_stats']['cpu_usage']['total_usage']
'total': all_stats['precpu_stats']['cpu_usage']['total_usage'],
}
# Issue #1857
# If either precpu_stats.online_cpus or cpu_stats.online_cpus is nil
@ -340,8 +340,7 @@ class Plugin(GlancesPlugin):
if cpu['nb_core'] is None:
cpu['nb_core'] = len(all_stats['cpu_stats']['cpu_usage']['percpu_usage'] or [])
except KeyError as e:
logger.debug(
"docker plugin - Cannot grab CPU usage for container {} ({})".format(container_id, e))
logger.debug("docker plugin - Cannot grab CPU usage for container {} ({})".format(container_id, e))
logger.debug(all_stats)
else:
try:
@ -350,8 +349,7 @@ class Plugin(GlancesPlugin):
# CPU usage % = (cpu_delta / system_cpu_delta) * number_cpus * 100.0
cpu_stats['total'] = (cpu_delta / system_cpu_delta) * cpu['nb_core'] * 100.0
except TypeError as e:
logger.debug(
"docker plugin - Cannot compute CPU usage for container {} ({})".format(container_id, e))
logger.debug("docker plugin - Cannot compute CPU usage for container {} ({})".format(container_id, e))
logger.debug(all_stats)
# Return the stats
@ -418,7 +416,9 @@ class Plugin(GlancesPlugin):
network_new['cumulative_tx'] = net_stats["eth0"]["tx_bytes"]
except KeyError as e:
# all_stats do not have INTERFACE information
logger.debug("docker plugin - Cannot grab network interface usage for container {} ({})".format(container_id, e))
logger.debug(
"docker plugin - Cannot grab network interface usage for container {} ({})".format(container_id, e)
)
logger.debug(all_stats)
else:
network_new['time_since_update'] = getTimeSinceLastUpdate('docker_net_{}'.format(container_id))
@ -507,9 +507,7 @@ class Plugin(GlancesPlugin):
# CPU alert
if 'cpu' in i and 'total' in i['cpu']:
# Looking for specific CPU container threshold in the conf file
alert = self.get_alert(i['cpu']['total'],
header=i['name'] + '_cpu',
action_key=i['name'])
alert = self.get_alert(i['cpu']['total'], header=i['name'] + '_cpu', action_key=i['name'])
if alert == 'DEFAULT':
# Not found ? Get back to default CPU threshold value
alert = self.get_alert(i['cpu']['total'], header='cpu')
@ -517,15 +515,12 @@ class Plugin(GlancesPlugin):
# MEM alert
if 'memory' in i and 'usage' in i['memory']:
# Looking for specific MEM container threshold in the conf file
alert = self.get_alert(i['memory']['usage'],
maximum=i['memory']['limit'],
header=i['name'] + '_mem',
action_key=i['name'])
alert = self.get_alert(
i['memory']['usage'], maximum=i['memory']['limit'], header=i['name'] + '_mem', action_key=i['name']
)
if alert == 'DEFAULT':
# Not found ? Get back to default MEM threshold value
alert = self.get_alert(i['memory']['usage'],
maximum=i['memory']['limit'],
header='mem')
alert = self.get_alert(i['memory']['usage'], maximum=i['memory']['limit'], header='mem')
self.views[i[self.get_key()]]['mem']['decoration'] = alert
return True
@ -536,9 +531,7 @@ class Plugin(GlancesPlugin):
ret = []
# Only process if stats exist (and non null) and display plugin enable...
if not self.stats \
or 'containers' not in self.stats or len(self.stats['containers']) == 0 \
or self.is_disabled():
if not self.stats or 'containers' not in self.stats or len(self.stats['containers']) == 0 or self.is_disabled():
return ret
# Build the string message
@ -554,12 +547,10 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_new_line())
# Get the maximum containers name
# Max size is configurable. See feature request #1723.
name_max_width = min(self.config.get_int_value('docker',
'max_name_size',
default=20)
if self.config is not None else 20,
len(max(self.stats['containers'],
key=lambda x: len(x['name']))['name']))
name_max_width = min(
self.config.get_int_value('docker', 'max_name_size', default=20) if self.config is not None else 20,
len(max(self.stats['containers'], key=lambda x: len(x['name']))['name']),
)
msg = ' {:{width}}'.format('Name', width=name_max_width)
ret.append(self.curse_add_line(msg))
msg = '{:>10}'.format('Status')
@ -584,8 +575,7 @@ class Plugin(GlancesPlugin):
for container in self.stats['containers']:
ret.append(self.curse_new_line())
# Name
ret.append(self.curse_add_line(self._msg_name(container=container,
max_width=name_max_width)))
ret.append(self.curse_add_line(self._msg_name(container=container, max_width=name_max_width)))
# Status
status = self.container_alert(container['Status'])
msg = '{:>10}'.format(container['Status'][0:10])
@ -595,17 +585,13 @@ class Plugin(GlancesPlugin):
msg = '{:>6.1f}'.format(container['cpu']['total'])
except KeyError:
msg = '{:>6}'.format('_')
ret.append(self.curse_add_line(msg, self.get_views(item=container['name'],
key='cpu',
option='decoration')))
ret.append(self.curse_add_line(msg, self.get_views(item=container['name'], key='cpu', option='decoration')))
# MEM
try:
msg = '{:>7}'.format(self.auto_unit(container['memory']['usage']))
except KeyError:
msg = '{:>7}'.format('_')
ret.append(self.curse_add_line(msg, self.get_views(item=container['name'],
key='mem',
option='decoration')))
ret.append(self.curse_add_line(msg, self.get_views(item=container['name'], key='mem', option='decoration')))
try:
msg = '{:>7}'.format(self.auto_unit(container['memory']['limit']))
except KeyError:
@ -631,8 +617,12 @@ class Plugin(GlancesPlugin):
unit = 'b'
for r in ['rx', 'tx']:
try:
value = self.auto_unit(
int(container['network'][r] // container['network']['time_since_update'] * to_bit)) + unit
value = (
self.auto_unit(
int(container['network'][r] // container['network']['time_since_update'] * to_bit)
)
+ unit
)
msg = '{:>7}'.format(value)
except KeyError:
msg = '{:>7}'.format('_')
@ -650,7 +640,7 @@ class Plugin(GlancesPlugin):
"""Build the container name."""
name = container['name']
if len(name) > max_width:
name = '_' + name[-max_width + 1:]
name = '_' + name[-max_width + 1 :]
else:
name = name[:max_width]
return ' {:{width}}'.format(name, width=max_width)
@ -731,7 +721,5 @@ def sort_stats(stats):
if glances_processes.sort_key.startswith('memory'):
sort_by = 'memory_usage'
sort_by_secondary = 'cpu_percent'
sort_stats_processes(stats['containers'],
sorted_by=sort_by,
sorted_by_secondary=sort_by_secondary)
sort_stats_processes(stats['containers'], sorted_by=sort_by, sorted_by_secondary=sort_by_secondary)
return stats

View File

@ -33,9 +33,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
stats_init_value=[])
super(Plugin, self).__init__(args=args, config=config, stats_init_value=[])
self.args = args
self.config = config
@ -82,14 +80,11 @@ class Plugin(GlancesPlugin):
else:
ret = 'OK'
if stat['critical'] is not None and \
stat['size'] > int(stat['critical']) * 1000000:
if stat['critical'] is not None and stat['size'] > int(stat['critical']) * 1000000:
ret = 'CRITICAL'
elif stat['warning'] is not None and \
stat['size'] > int(stat['warning']) * 1000000:
elif stat['warning'] is not None and stat['size'] > int(stat['warning']) * 1000000:
ret = 'WARNING'
elif stat['careful'] is not None and \
stat['size'] > int(stat['careful']) * 1000000:
elif stat['careful'] is not None and stat['size'] > int(stat['careful']) * 1000000:
ret = 'CAREFUL'
# Get stat name
@ -99,10 +94,7 @@ class Plugin(GlancesPlugin):
self.manage_threshold(stat_name, ret)
# Manage action
self.manage_action(stat_name,
ret.lower(),
header,
stat[self.get_key()])
self.manage_action(stat_name, ret.lower(), header, stat[self.get_key()])
return ret
@ -119,8 +111,7 @@ class Plugin(GlancesPlugin):
name_max_width = max_width - 7
# Header
msg = '{:{width}}'.format('FOLDERS',
width=name_max_width)
msg = '{:{width}}'.format('FOLDERS', width=name_max_width)
ret.append(self.curse_add_line(msg, "TITLE"))
# Data
@ -128,11 +119,10 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_new_line())
if len(i['path']) > name_max_width:
# Cut path if it is too long
path = '_' + i['path'][-name_max_width + 1:]
path = '_' + i['path'][-name_max_width + 1 :]
else:
path = i['path']
msg = '{:{width}}'.format(nativestr(path),
width=name_max_width)
msg = '{:{width}}'.format(nativestr(path), width=name_max_width)
ret.append(self.curse_add_line(msg))
try:
msg = '{:>9}'.format(self.auto_unit(i['size']))

View File

@ -41,27 +41,33 @@ import psutil
# Used space on the disk: .1.3.6.1.4.1.2021.9.1.8.1
# Percentage of space used on disk: .1.3.6.1.4.1.2021.9.1.9.1
# Percentage of inodes used on disk: .1.3.6.1.4.1.2021.9.1.10.1
snmp_oid = {'default': {'mnt_point': '1.3.6.1.4.1.2021.9.1.2',
'device_name': '1.3.6.1.4.1.2021.9.1.3',
'size': '1.3.6.1.4.1.2021.9.1.6',
'used': '1.3.6.1.4.1.2021.9.1.8',
'percent': '1.3.6.1.4.1.2021.9.1.9'},
'windows': {'mnt_point': '1.3.6.1.2.1.25.2.3.1.3',
'alloc_unit': '1.3.6.1.2.1.25.2.3.1.4',
'size': '1.3.6.1.2.1.25.2.3.1.5',
'used': '1.3.6.1.2.1.25.2.3.1.6'},
'netapp': {'mnt_point': '1.3.6.1.4.1.789.1.5.4.1.2',
'device_name': '1.3.6.1.4.1.789.1.5.4.1.10',
'size': '1.3.6.1.4.1.789.1.5.4.1.3',
'used': '1.3.6.1.4.1.789.1.5.4.1.4',
'percent': '1.3.6.1.4.1.789.1.5.4.1.6'}}
snmp_oid = {
'default': {
'mnt_point': '1.3.6.1.4.1.2021.9.1.2',
'device_name': '1.3.6.1.4.1.2021.9.1.3',
'size': '1.3.6.1.4.1.2021.9.1.6',
'used': '1.3.6.1.4.1.2021.9.1.8',
'percent': '1.3.6.1.4.1.2021.9.1.9',
},
'windows': {
'mnt_point': '1.3.6.1.2.1.25.2.3.1.3',
'alloc_unit': '1.3.6.1.2.1.25.2.3.1.4',
'size': '1.3.6.1.2.1.25.2.3.1.5',
'used': '1.3.6.1.2.1.25.2.3.1.6',
},
'netapp': {
'mnt_point': '1.3.6.1.4.1.789.1.5.4.1.2',
'device_name': '1.3.6.1.4.1.789.1.5.4.1.10',
'size': '1.3.6.1.4.1.789.1.5.4.1.3',
'used': '1.3.6.1.4.1.789.1.5.4.1.4',
'percent': '1.3.6.1.4.1.789.1.5.4.1.6',
},
}
snmp_oid['esxi'] = snmp_oid['windows']
# Define the history items list
# All items in this list will be historised if the --enable-history tag is set
items_history_list = [{'name': 'percent',
'description': 'File system usage in percent',
'y_unit': '%'}]
items_history_list = [{'name': 'percent', 'description': 'File system usage in percent', 'y_unit': '%'}]
class Plugin(GlancesPlugin):
@ -72,10 +78,9 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
items_history_list=items_history_list,
stats_init_value=[])
super(Plugin, self).__init__(
args=args, config=config, items_history_list=items_history_list, stats_init_value=[]
)
# We want to display the stat in the curse interface
self.display_curse = True
@ -131,7 +136,8 @@ class Plugin(GlancesPlugin):
'used': fs_usage.used,
'free': fs_usage.free,
'percent': fs_usage.percent,
'key': self.get_key()}
'key': self.get_key(),
}
stats.append(fs_current)
elif self.input_method == 'snmp':
@ -139,11 +145,9 @@ class Plugin(GlancesPlugin):
# SNMP bulk command to get all file system in one shot
try:
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
bulk=True)
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name], bulk=True)
except KeyError:
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid['default'],
bulk=True)
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid['default'], bulk=True)
# Loop over fs
if self.short_system_name in ('windows', 'esxi'):
@ -161,7 +165,8 @@ class Plugin(GlancesPlugin):
'size': size,
'used': used,
'percent': percent,
'key': self.get_key()}
'key': self.get_key(),
}
# Do not take hidden file system into account
if self.is_hide(fs_current['mnt_point']):
continue
@ -176,7 +181,8 @@ class Plugin(GlancesPlugin):
'size': int(fs_stat[fs]['size']) * 1024,
'used': int(fs_stat[fs]['used']) * 1024,
'percent': float(fs_stat[fs]['percent']),
'key': self.get_key()}
'key': self.get_key(),
}
# Do not take hidden file system into account
if self.is_hide(fs_current['mnt_point']) or self.is_hide(fs_current['device_name']):
continue
@ -197,7 +203,8 @@ class Plugin(GlancesPlugin):
# Alert
for i in self.stats:
self.views[i[self.get_key()]]['used']['decoration'] = self.get_alert(
current=i['size'] - i['free'], maximum=i['size'], header=i['mnt_point'])
current=i['size'] - i['free'], maximum=i['size'], header=i['mnt_point']
)
def msg_curse(self, args=None, max_width=None):
"""Return the dict to display in the curse interface."""
@ -228,25 +235,24 @@ class Plugin(GlancesPlugin):
# New line
ret.append(self.curse_new_line())
if i['device_name'] == '' or i['device_name'] == 'none':
mnt_point = i['mnt_point'][-name_max_width + 1:]
mnt_point = i['mnt_point'][-name_max_width + 1 :]
elif len(i['mnt_point']) + len(i['device_name'].split('/')[-1]) <= name_max_width - 3:
# If possible concatenate mode info... Glances touch inside :)
mnt_point = i['mnt_point'] + ' (' + i['device_name'].split('/')[-1] + ')'
elif len(i['mnt_point']) > name_max_width:
# Cut mount point name if it is too long
mnt_point = '_' + i['mnt_point'][-name_max_width + 1:]
mnt_point = '_' + i['mnt_point'][-name_max_width + 1 :]
else:
mnt_point = i['mnt_point']
msg = '{:{width}}'.format(nativestr(mnt_point),
width=name_max_width)
msg = '{:{width}}'.format(nativestr(mnt_point), width=name_max_width)
ret.append(self.curse_add_line(msg))
if args.fs_free_space:
msg = '{:>7}'.format(self.auto_unit(i['free']))
else:
msg = '{:>7}'.format(self.auto_unit(i['used']))
ret.append(self.curse_add_line(msg, self.get_views(item=i[self.get_key()],
key='used',
option='decoration')))
ret.append(
self.curse_add_line(msg, self.get_views(item=i[self.get_key()], key='used', option='decoration'))
)
msg = '{:>7}'.format(self.auto_unit(i['size']))
ret.append(self.curse_add_line(msg))

View File

@ -35,12 +35,10 @@ else:
# Define the history items list
# All items in this list will be historised if the --enable-history tag is set
items_history_list = [{'name': 'proc',
'description': 'GPU processor',
'y_unit': '%'},
{'name': 'mem',
'description': 'Memory consumption',
'y_unit': '%'}]
items_history_list = [
{'name': 'proc', 'description': 'GPU processor', 'y_unit': '%'},
{'name': 'mem', 'description': 'Memory consumption', 'y_unit': '%'},
]
class Plugin(GlancesPlugin):
@ -51,9 +49,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
stats_init_value=[])
super(Plugin, self).__init__(args=args, config=config, stats_init_value=[])
# Init the Nvidia API
self.init_nvidia()
@ -142,9 +138,7 @@ class Plugin(GlancesPlugin):
# Alert
for i in self.stats:
# Init the views for the current GPU
self.views[i[self.get_key()]] = {'proc': {},
'mem': {},
'temperature': {}}
self.views[i[self.get_key()]] = {'proc': {}, 'mem': {}, 'temperature': {}}
# Processor alert
if 'proc' in i:
alert = self.get_alert(i['proc'], header='proc')
@ -203,10 +197,11 @@ class Plugin(GlancesPlugin):
else:
msg = '{:13}'.format('proc:')
ret.append(self.curse_add_line(msg))
ret.append(self.curse_add_line(
mean_proc_msg, self.get_views(item=gpu_stats[self.get_key()],
key='proc',
option='decoration')))
ret.append(
self.curse_add_line(
mean_proc_msg, self.get_views(item=gpu_stats[self.get_key()], key='proc', option='decoration')
)
)
# New line
ret.append(self.curse_new_line())
# GPU MEM
@ -221,10 +216,11 @@ class Plugin(GlancesPlugin):
else:
msg = '{:13}'.format('mem:')
ret.append(self.curse_add_line(msg))
ret.append(self.curse_add_line(
mean_mem_msg, self.get_views(item=gpu_stats[self.get_key()],
key='mem',
option='decoration')))
ret.append(
self.curse_add_line(
mean_mem_msg, self.get_views(item=gpu_stats[self.get_key()], key='mem', option='decoration')
)
)
# New line
ret.append(self.curse_new_line())
# GPU TEMPERATURE
@ -237,17 +233,18 @@ class Plugin(GlancesPlugin):
if args.fahrenheit:
mean_temperature = to_fahrenheit(mean_temperature)
unit = 'F'
mean_temperature_msg = '{:>3.0f}{}'.format(mean_temperature,
unit)
mean_temperature_msg = '{:>3.0f}{}'.format(mean_temperature, unit)
if len(self.stats) > 1:
msg = '{:13}'.format('temp mean:')
else:
msg = '{:13}'.format('temperature:')
ret.append(self.curse_add_line(msg))
ret.append(self.curse_add_line(
mean_temperature_msg, self.get_views(item=gpu_stats[self.get_key()],
key='temperature',
option='decoration')))
ret.append(
self.curse_add_line(
mean_temperature_msg,
self.get_views(item=gpu_stats[self.get_key()], key='temperature', option='decoration'),
)
)
else:
# Multi GPU
# Temperature is not displayed in this mode...
@ -264,9 +261,7 @@ class Plugin(GlancesPlugin):
mem_msg = '{:>3.0f}%'.format(gpu_stats['mem'])
except (ValueError, TypeError):
mem_msg = '{:>4}'.format('N/A')
msg = '{}: {} mem: {}'.format(id_msg,
proc_msg,
mem_msg)
msg = '{}: {} mem: {}'.format(id_msg, proc_msg, mem_msg)
ret.append(self.curse_add_line(msg))
return ret
@ -341,7 +336,6 @@ def get_proc(device_handle):
def get_temperature(device_handle):
"""Get GPU device CPU consumption in percent."""
try:
return pynvml.nvmlDeviceGetTemperature(device_handle,
pynvml.NVML_TEMPERATURE_GPU)
return pynvml.nvmlDeviceGetTemperature(device_handle, pynvml.NVML_TEMPERATURE_GPU)
except pynvml.NVMLError:
return None

View File

@ -41,10 +41,12 @@ else:
# - url: URL of the Web site
# - json: service return a JSON (True) or string (False)
# - key: key of the IP address in the JSON structure
urls = [('https://ip.42.pl/raw', False, None),
('https://httpbin.org/ip', True, 'origin'),
('https://jsonip.com', True, 'ip'),
('https://api.ipify.org/?format=json', True, 'ip')]
urls = [
('https://ip.42.pl/raw', False, None),
('https://httpbin.org/ip', True, 'origin'),
('https://jsonip.com', True, 'ip'),
('https://api.ipify.org/?format=json', True, 'ip'),
]
class Plugin(GlancesPlugin):

View File

@ -35,9 +35,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
stats_init_value=[])
super(Plugin, self).__init__(args=args, config=config, stats_init_value=[])
# We want to display the stat in the curse interface
self.display_curse = True
@ -69,9 +67,7 @@ class Plugin(GlancesPlugin):
pass
# Get the TOP 5 (by rate/s)
stats = sorted(stats,
key=operator.itemgetter('irq_rate'),
reverse=True)[:5]
stats = sorted(stats, key=operator.itemgetter('irq_rate'), reverse=True)[:5]
# Update the stats
self.stats = stats
@ -105,8 +101,7 @@ class Plugin(GlancesPlugin):
for i in self.stats:
ret.append(self.curse_new_line())
msg = '{:{width}}'.format(i['irq_line'][:name_max_width],
width=name_max_width)
msg = '{:{width}}'.format(i['irq_line'][:name_max_width], width=name_max_width)
ret.append(self.curse_add_line(msg))
msg = '{:>9}'.format(str(i['irq_rate']))
ret.append(self.curse_add_line(msg))
@ -173,7 +168,7 @@ class GlancesIRQ(object):
"""
splitted_line = line.split()
try:
ret = sum(map(int, splitted_line[1:(self.cpu_number + 1)]))
ret = sum(map(int, splitted_line[1 : (self.cpu_number + 1)]))
except ValueError:
# Correct issue #1007 on some conf (Raspberry Pi with Raspbian)
ret = 0
@ -197,14 +192,13 @@ class GlancesIRQ(object):
irq_line = self.__humanname(line)
current_irqs = self.__sum(line)
irq_rate = int(
current_irqs - self.lasts.get(irq_line)
if self.lasts.get(irq_line)
else 0 // time_since_update)
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
'time_since_update': time_since_update,
}
self.stats.append(irq_current)
self.lasts[irq_line] = current_irqs

View File

@ -29,38 +29,44 @@ from glances.logger import logger
# Fields description
fields_description = {
'min1': {'description': 'Average sum of the number of processes \
'min1': {
'description': 'Average sum of the number of processes \
waiting in the run-queue plus the number currently executing \
over 1 minute.',
'unit': 'float'},
'min5': {'description': 'Average sum of the number of processes \
'unit': 'float',
},
'min5': {
'description': 'Average sum of the number of processes \
waiting in the run-queue plus the number currently executing \
over 5 minutes.',
'unit': 'float'},
'min15': {'description': 'Average sum of the number of processes \
'unit': 'float',
},
'min15': {
'description': 'Average sum of the number of processes \
waiting in the run-queue plus the number currently executing \
over 15 minutes.',
'unit': 'float'},
'cpucore': {'description': 'Total number of CPU core.',
'unit': 'number'},
'unit': 'float',
},
'cpucore': {'description': 'Total number of CPU core.', 'unit': 'number'},
}
# SNMP OID
# 1 minute Load: .1.3.6.1.4.1.2021.10.1.3.1
# 5 minute Load: .1.3.6.1.4.1.2021.10.1.3.2
# 15 minute Load: .1.3.6.1.4.1.2021.10.1.3.3
snmp_oid = {'min1': '1.3.6.1.4.1.2021.10.1.3.1',
'min5': '1.3.6.1.4.1.2021.10.1.3.2',
'min15': '1.3.6.1.4.1.2021.10.1.3.3'}
snmp_oid = {
'min1': '1.3.6.1.4.1.2021.10.1.3.1',
'min5': '1.3.6.1.4.1.2021.10.1.3.2',
'min15': '1.3.6.1.4.1.2021.10.1.3.3',
}
# Define the history items list
# All items in this list will be historised if the --enable-history tag is set
items_history_list = [{'name': 'min1',
'description': '1 minute load'},
{'name': 'min5',
'description': '5 minutes load'},
{'name': 'min15',
'description': '15 minutes load'}]
items_history_list = [
{'name': 'min1', 'description': '1 minute load'},
{'name': 'min5', 'description': '5 minutes load'},
{'name': 'min15', 'description': '15 minutes load'},
]
class Plugin(GlancesPlugin):
@ -71,10 +77,9 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
items_history_list=items_history_list,
fields_description=fields_description)
super(Plugin, self).__init__(
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
)
# We want to display the stat in the curse interface
self.display_curse = True
@ -112,10 +117,7 @@ class Plugin(GlancesPlugin):
if load is None:
stats = self.get_init_value()
else:
stats = {'min1': load[0],
'min5': load[1],
'min15': load[2],
'cpucore': self.nb_log_core}
stats = {'min1': load[0], 'min5': load[1], 'min15': load[2], 'cpucore': self.nb_log_core}
elif self.input_method == 'snmp':
# Update stats using SNMP
@ -145,11 +147,11 @@ class Plugin(GlancesPlugin):
# Add specifics informations
try:
# Alert and log
self.views['min15']['decoration'] = self.get_alert_log(self.stats['min15'],
maximum=100 * self.stats['cpucore'])
self.views['min15']['decoration'] = self.get_alert_log(
self.stats['min15'], maximum=100 * self.stats['cpucore']
)
# Alert only
self.views['min5']['decoration'] = self.get_alert(self.stats['min5'],
maximum=100 * self.stats['cpucore'])
self.views['min5']['decoration'] = self.get_alert(self.stats['min5'], maximum=100 * self.stats['cpucore'])
except KeyError:
# try/except mandatory for Windows compatibility (no load stats)
pass
@ -186,8 +188,6 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg))
else:
# Alert is only for 5 and 15 min
ret.append(self.curse_add_line(
msg, self.get_views(key='min{}'.format(load_time),
option='decoration')))
ret.append(self.curse_add_line(msg, self.get_views(key='min{}'.format(load_time), option='decoration')))
return ret

View File

@ -27,43 +27,53 @@ import psutil
# Fields description
fields_description = {
'total': {'description': 'Total physical memory available.',
'unit': 'bytes',
'min_symbol': 'K'},
'available': {'description': 'The actual amount of available memory that can be given instantly \
'total': {'description': 'Total physical memory available.', 'unit': 'bytes', 'min_symbol': 'K'},
'available': {
'description': 'The actual amount of available memory that can be given instantly \
to processes that request more memory in bytes; this is calculated by summing \
different memory values depending on the platform (e.g. free + buffers + cached on Linux) \
and it is supposed to be used to monitor actual memory usage in a cross platform fashion.',
'unit': 'bytes',
'min_symbol': 'K'},
'percent': {'description': 'The percentage usage calculated as (total - available) / total * 100.',
'unit': 'percent'},
'used': {'description': 'Memory used, calculated differently depending on the platform and \
'unit': 'bytes',
'min_symbol': 'K',
},
'percent': {
'description': 'The percentage usage calculated as (total - available) / total * 100.',
'unit': 'percent',
},
'used': {
'description': 'Memory used, calculated differently depending on the platform and \
designed for informational purposes only.',
'unit': 'bytes',
'min_symbol': 'K'},
'free': {'description': 'Memory not being used at all (zeroed) that is readily available; \
'unit': 'bytes',
'min_symbol': 'K',
},
'free': {
'description': 'Memory not being used at all (zeroed) that is readily available; \
note that this doesn\'t reflect the actual memory available (use \'available\' instead).',
'unit': 'bytes',
'min_symbol': 'K'},
'active': {'description': '*(UNIX)*: memory currently in use or very recently used, and so it is in RAM.',
'unit': 'bytes',
'min_symbol': 'K'},
'inactive': {'description': '*(UNIX)*: memory that is marked as not used.',
'unit': 'bytes',
'min_symbol': 'K'},
'buffers': {'description': '*(Linux, BSD)*: cache for things like file system metadata.',
'unit': 'bytes',
'min_symbol': 'K'},
'cached': {'description': '*(Linux, BSD)*: cache for various things.',
'unit': 'bytes',
'min_symbol': 'K'},
'wired': {'description': '*(BSD, macOS)*: memory that is marked to always stay in RAM. It is never moved to disk.',
'unit': 'bytes',
'min_symbol': 'K'},
'shared': {'description': '*(BSD)*: memory that may be simultaneously accessed by multiple processes.',
'unit': 'bytes',
'min_symbol': 'K'},
'unit': 'bytes',
'min_symbol': 'K',
},
'active': {
'description': '*(UNIX)*: memory currently in use or very recently used, and so it is in RAM.',
'unit': 'bytes',
'min_symbol': 'K',
},
'inactive': {'description': '*(UNIX)*: memory that is marked as not used.', 'unit': 'bytes', 'min_symbol': 'K'},
'buffers': {
'description': '*(Linux, BSD)*: cache for things like file system metadata.',
'unit': 'bytes',
'min_symbol': 'K',
},
'cached': {'description': '*(Linux, BSD)*: cache for various things.', 'unit': 'bytes', 'min_symbol': 'K'},
'wired': {
'description': '*(BSD, macOS)*: memory that is marked to always stay in RAM. It is never moved to disk.',
'unit': 'bytes',
'min_symbol': 'K',
},
'shared': {
'description': '*(BSD)*: memory that may be simultaneously accessed by multiple processes.',
'unit': 'bytes',
'min_symbol': 'K',
},
}
# SNMP OID
@ -74,25 +84,31 @@ note that this doesn\'t reflect the actual memory available (use \'available\' i
# Total RAM Buffered: .1.3.6.1.4.1.2021.4.14.0
# Total Cached Memory: .1.3.6.1.4.1.2021.4.15.0
# Note: For Windows, stats are in the FS table
snmp_oid = {'default': {'total': '1.3.6.1.4.1.2021.4.5.0',
'free': '1.3.6.1.4.1.2021.4.11.0',
'shared': '1.3.6.1.4.1.2021.4.13.0',
'buffers': '1.3.6.1.4.1.2021.4.14.0',
'cached': '1.3.6.1.4.1.2021.4.15.0'},
'windows': {'mnt_point': '1.3.6.1.2.1.25.2.3.1.3',
'alloc_unit': '1.3.6.1.2.1.25.2.3.1.4',
'size': '1.3.6.1.2.1.25.2.3.1.5',
'used': '1.3.6.1.2.1.25.2.3.1.6'},
'esxi': {'mnt_point': '1.3.6.1.2.1.25.2.3.1.3',
'alloc_unit': '1.3.6.1.2.1.25.2.3.1.4',
'size': '1.3.6.1.2.1.25.2.3.1.5',
'used': '1.3.6.1.2.1.25.2.3.1.6'}}
snmp_oid = {
'default': {
'total': '1.3.6.1.4.1.2021.4.5.0',
'free': '1.3.6.1.4.1.2021.4.11.0',
'shared': '1.3.6.1.4.1.2021.4.13.0',
'buffers': '1.3.6.1.4.1.2021.4.14.0',
'cached': '1.3.6.1.4.1.2021.4.15.0',
},
'windows': {
'mnt_point': '1.3.6.1.2.1.25.2.3.1.3',
'alloc_unit': '1.3.6.1.2.1.25.2.3.1.4',
'size': '1.3.6.1.2.1.25.2.3.1.5',
'used': '1.3.6.1.2.1.25.2.3.1.6',
},
'esxi': {
'mnt_point': '1.3.6.1.2.1.25.2.3.1.3',
'alloc_unit': '1.3.6.1.2.1.25.2.3.1.4',
'size': '1.3.6.1.2.1.25.2.3.1.5',
'used': '1.3.6.1.2.1.25.2.3.1.6',
},
}
# Define the history items list
# All items in this list will be historised if the --enable-history tag is set
items_history_list = [{'name': 'percent',
'description': 'RAM memory usage',
'y_unit': '%'}]
items_history_list = [{'name': 'percent', 'description': 'RAM memory usage', 'y_unit': '%'}]
class Plugin(GlancesPlugin):
@ -103,10 +119,9 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
items_history_list=items_history_list,
fields_description=fields_description)
super(Plugin, self).__init__(
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
)
# We want to display the stat in the curse interface
self.display_curse = True
@ -142,9 +157,19 @@ class Plugin(GlancesPlugin):
# wired: (BSD, macOS): memory that is marked to always stay in RAM. It is never moved to disk.
# shared: (BSD): memory that may be simultaneously accessed by multiple processes.
self.reset()
for mem in ['total', 'available', 'percent', 'used', 'free',
'active', 'inactive', 'buffers', 'cached',
'wired', 'shared']:
for mem in [
'total',
'available',
'percent',
'used',
'free',
'active',
'inactive',
'buffers',
'cached',
'wired',
'shared',
]:
if hasattr(vm_stats, mem):
stats[mem] = getattr(vm_stats, mem)
@ -162,8 +187,7 @@ class Plugin(GlancesPlugin):
if self.short_system_name in ('windows', 'esxi'):
# Mem stats for Windows|Vmware Esxi are stored in the FS table
try:
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
bulk=True)
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name], bulk=True)
except KeyError:
self.reset()
else:
@ -232,8 +256,7 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg))
# Percent memory usage
msg = '{:>7.1%}'.format(self.stats['percent'] / 100)
ret.append(self.curse_add_line(
msg, self.get_views(key='percent', option='decoration')))
ret.append(self.curse_add_line(msg, self.get_views(key='percent', option='decoration')))
# Active memory usage
ret.extend(self.curse_add_stat('active', width=18, header=' '))

View File

@ -27,42 +27,39 @@ import psutil
# Fields description
fields_description = {
'total': {'description': 'Total swap memory.',
'unit': 'bytes',
'min_symbol': 'K'},
'used': {'description': 'Used swap memory.',
'unit': 'bytes',
'min_symbol': 'K'},
'free': {'description': 'Free swap memory.',
'unit': 'bytes',
'min_symbol': 'K'},
'percent': {'description': 'Used swap memory in percentage.',
'unit': 'percent'},
'sin': {'description': 'The number of bytes the system has swapped in from disk (cumulative).',
'unit': 'bytes',
'min_symbol': 'K'},
'sout': {'description': 'The number of bytes the system has swapped out from disk (cumulative).',
'unit': 'bytes',
'min_symbol': 'K'},
'time_since_update': {'description': 'Number of seconds since last update.',
'unit': 'seconds'},
'total': {'description': 'Total swap memory.', 'unit': 'bytes', 'min_symbol': 'K'},
'used': {'description': 'Used swap memory.', 'unit': 'bytes', 'min_symbol': 'K'},
'free': {'description': 'Free swap memory.', 'unit': 'bytes', 'min_symbol': 'K'},
'percent': {'description': 'Used swap memory in percentage.', 'unit': 'percent'},
'sin': {
'description': 'The number of bytes the system has swapped in from disk (cumulative).',
'unit': 'bytes',
'min_symbol': 'K',
},
'sout': {
'description': 'The number of bytes the system has swapped out from disk (cumulative).',
'unit': 'bytes',
'min_symbol': 'K',
},
'time_since_update': {'description': 'Number of seconds since last update.', 'unit': 'seconds'},
}
# SNMP OID
# Total Swap Size: .1.3.6.1.4.1.2021.4.3.0
# Available Swap Space: .1.3.6.1.4.1.2021.4.4.0
snmp_oid = {'default': {'total': '1.3.6.1.4.1.2021.4.3.0',
'free': '1.3.6.1.4.1.2021.4.4.0'},
'windows': {'mnt_point': '1.3.6.1.2.1.25.2.3.1.3',
'alloc_unit': '1.3.6.1.2.1.25.2.3.1.4',
'size': '1.3.6.1.2.1.25.2.3.1.5',
'used': '1.3.6.1.2.1.25.2.3.1.6'}}
snmp_oid = {
'default': {'total': '1.3.6.1.4.1.2021.4.3.0', 'free': '1.3.6.1.4.1.2021.4.4.0'},
'windows': {
'mnt_point': '1.3.6.1.2.1.25.2.3.1.3',
'alloc_unit': '1.3.6.1.2.1.25.2.3.1.4',
'size': '1.3.6.1.2.1.25.2.3.1.5',
'used': '1.3.6.1.2.1.25.2.3.1.6',
},
}
# Define the history items list
# All items in this list will be historised if the --enable-history tag is set
items_history_list = [{'name': 'percent',
'description': 'Swap memory usage',
'y_unit': '%'}]
items_history_list = [{'name': 'percent', 'description': 'Swap memory usage', 'y_unit': '%'}]
class Plugin(GlancesPlugin):
@ -73,10 +70,9 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
items_history_list=items_history_list,
fields_description=fields_description)
super(Plugin, self).__init__(
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
)
# We want to display the stat in the curse interface
self.display_curse = True
@ -117,8 +113,7 @@ class Plugin(GlancesPlugin):
if self.short_system_name == 'windows':
# Mem stats for Windows OS are stored in the FS table
try:
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name],
bulk=True)
fs_stat = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name], bulk=True)
except KeyError:
self.reset()
else:
@ -149,8 +144,7 @@ class Plugin(GlancesPlugin):
# percent: the percentage usage calculated as (total -
# available) / total * 100.
stats['percent'] = float(
(stats['total'] - stats['free']) / stats['total'] * 100)
stats['percent'] = float((stats['total'] - stats['free']) / stats['total'] * 100)
# Update the stats
self.stats = stats
@ -184,8 +178,7 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg))
# Percent memory usage
msg = '{:>6.1%}'.format(self.stats['percent'] / 100)
ret.append(self.curse_add_line(
msg, self.get_views(key='percent', option='decoration')))
ret.append(self.curse_add_line(msg, self.get_views(key='percent', option='decoration')))
# Second line
# total

View File

@ -39,40 +39,39 @@ import psutil
# 'key': 'interface_name'}
# Fields description
fields_description = {
'interface_name': {'description': 'Interface name.',
'unit': 'string'},
'alias': {'description': 'Interface alias name (optional).',
'unit': 'string'},
'rx': {'description': 'The received/input rate (in bit per second).',
'unit': 'bps'},
'tx': {'description': 'The sent/output rate (in bit per second).',
'unit': 'bps'},
'cumulative_rx': {'description': 'The number of bytes received through the interface (cumulative).',
'unit': 'bytes'},
'cumulative_tx': {'description': 'The number of bytes sent through the interface (cumulative).',
'unit': 'bytes'},
'speed': {'description': 'Maximum interface speed (in bit per second). Can return 0 on some operating-system.',
'unit': 'bps'},
'is_up': {'description': 'Is the interface up ?',
'unit': 'bool'},
'time_since_update': {'description': 'Number of seconds since last update.',
'unit': 'seconds'},
'interface_name': {'description': 'Interface name.', 'unit': 'string'},
'alias': {'description': 'Interface alias name (optional).', 'unit': 'string'},
'rx': {'description': 'The received/input rate (in bit per second).', 'unit': 'bps'},
'tx': {'description': 'The sent/output rate (in bit per second).', 'unit': 'bps'},
'cumulative_rx': {
'description': 'The number of bytes received through the interface (cumulative).',
'unit': 'bytes',
},
'cumulative_tx': {'description': 'The number of bytes sent through the interface (cumulative).', 'unit': 'bytes'},
'speed': {
'description': 'Maximum interface speed (in bit per second). Can return 0 on some operating-system.',
'unit': 'bps',
},
'is_up': {'description': 'Is the interface up ?', 'unit': 'bool'},
'time_since_update': {'description': 'Number of seconds since last update.', 'unit': 'seconds'},
}
# SNMP OID
# http://www.net-snmp.org/docs/mibs/interfaces.html
# Dict key = interface_name
snmp_oid = {'default': {'interface_name': '1.3.6.1.2.1.2.2.1.2',
'cumulative_rx': '1.3.6.1.2.1.2.2.1.10',
'cumulative_tx': '1.3.6.1.2.1.2.2.1.16'}}
snmp_oid = {
'default': {
'interface_name': '1.3.6.1.2.1.2.2.1.2',
'cumulative_rx': '1.3.6.1.2.1.2.2.1.10',
'cumulative_tx': '1.3.6.1.2.1.2.2.1.16',
}
}
# Define the history items list
items_history_list = [{'name': 'rx',
'description': 'Download rate per second',
'y_unit': 'bit/s'},
{'name': 'tx',
'description': 'Upload rate per second',
'y_unit': 'bit/s'}]
items_history_list = [
{'name': 'rx', 'description': 'Download rate per second', 'y_unit': 'bit/s'},
{'name': 'tx', 'description': 'Upload rate per second', 'y_unit': 'bit/s'},
]
class Plugin(GlancesPlugin):
@ -83,19 +82,20 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
items_history_list=items_history_list,
fields_description=fields_description,
stats_init_value=[])
super(Plugin, self).__init__(
args=args,
config=config,
items_history_list=items_history_list,
fields_description=fields_description,
stats_init_value=[],
)
# We want to display the stat in the curse interface
self.display_curse = True
# Hide stats if it has never been != 0
if config is not None:
self.hide_zero = config.get_bool_value(
self.plugin_name, 'hide_zero', default=False)
self.hide_zero = config.get_bool_value(self.plugin_name, 'hide_zero', default=False)
else:
self.hide_zero = False
self.hide_zero_fields = ['rx', 'tx']
@ -165,23 +165,24 @@ class Plugin(GlancesPlugin):
rx = cumulative_rx - self.network_old[net].bytes_recv
tx = cumulative_tx - self.network_old[net].bytes_sent
cx = rx + tx
netstat = {'interface_name': n(net),
'alias': self.has_alias(n(net)),
'time_since_update': time_since_update,
'cumulative_rx': cumulative_rx,
'rx': rx,
'cumulative_tx': cumulative_tx,
'tx': tx,
'cumulative_cx': cumulative_cx,
'cx': cx,
# Interface status
'is_up': net_status[net].isup,
# Interface speed in Mbps, convert it to bps
# Can be always 0 on some OSes
'speed': net_status[net].speed * 1048576,
# Set the key for the dict
'key': self.get_key()
}
netstat = {
'interface_name': n(net),
'alias': self.has_alias(n(net)),
'time_since_update': time_since_update,
'cumulative_rx': cumulative_rx,
'rx': rx,
'cumulative_tx': cumulative_tx,
'tx': tx,
'cumulative_cx': cumulative_cx,
'cx': cx,
# Interface status
'is_up': net_status[net].isup,
# Interface speed in Mbps, convert it to bps
# Can be always 0 on some OSes
'speed': net_status[net].speed * 1048576,
# Set the key for the dict
'key': self.get_key(),
}
except KeyError:
continue
else:
@ -245,7 +246,8 @@ class Plugin(GlancesPlugin):
'cumulative_tx': cumulative_tx,
'tx': tx,
'cumulative_cx': cumulative_cx,
'cx': cx}
'cx': cx,
}
except KeyError:
continue
else:
@ -282,13 +284,9 @@ class Plugin(GlancesPlugin):
# If nothing is define in the configuration file...
# ... then use the interface speed (not available on all systems)
if alert_rx == 'DEFAULT' and 'speed' in i and i['speed'] != 0:
alert_rx = self.get_alert(current=bps_rx,
maximum=i['speed'],
header='rx')
alert_rx = self.get_alert(current=bps_rx, maximum=i['speed'], header='rx')
if alert_tx == 'DEFAULT' and 'speed' in i and i['speed'] != 0:
alert_tx = self.get_alert(current=bps_tx,
maximum=i['speed'],
header='tx')
alert_tx = self.get_alert(current=bps_tx, maximum=i['speed'], header='tx')
# then decorates
self.views[i[self.get_key()]]['rx']['decoration'] = alert_rx
self.views[i[self.get_key()]]['tx']['decoration'] = alert_tx
@ -347,7 +345,7 @@ class Plugin(GlancesPlugin):
if_name = i['alias']
if len(if_name) > name_max_width:
# Cut interface name if it is too long
if_name = '_' + if_name[-name_max_width + 1:]
if_name = '_' + if_name[-name_max_width + 1 :]
if args.byte:
# Bytes per second (for dummy)
@ -361,13 +359,17 @@ class Plugin(GlancesPlugin):
if args.network_cumul:
rx = self.auto_unit(int(i['cumulative_rx'] * to_bit)) + unit
tx = self.auto_unit(int(i['cumulative_tx'] * to_bit)) + unit
sx = self.auto_unit(int(i['cumulative_rx'] * to_bit) +
int(i['cumulative_tx'] * to_bit)) + unit
sx = self.auto_unit(int(i['cumulative_rx'] * to_bit) + int(i['cumulative_tx'] * to_bit)) + unit
else:
rx = self.auto_unit(int(i['rx'] // i['time_since_update'] * to_bit)) + unit
tx = self.auto_unit(int(i['tx'] // i['time_since_update'] * to_bit)) + unit
sx = self.auto_unit(int(i['rx'] // i['time_since_update'] * to_bit) +
int(i['tx'] // i['time_since_update'] * to_bit)) + unit
sx = (
self.auto_unit(
int(i['rx'] // i['time_since_update'] * to_bit)
+ int(i['tx'] // i['time_since_update'] * to_bit)
)
+ unit
)
# New line
ret.append(self.curse_new_line())
@ -378,10 +380,12 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg))
else:
msg = '{:>7}'.format(rx)
ret.append(self.curse_add_line(
msg, self.get_views(item=i[self.get_key()], key='rx', option='decoration')))
ret.append(
self.curse_add_line(msg, self.get_views(item=i[self.get_key()], key='rx', option='decoration'))
)
msg = '{:>7}'.format(tx)
ret.append(self.curse_add_line(
msg, self.get_views(item=i[self.get_key()], key='tx', option='decoration')))
ret.append(
self.curse_add_line(msg, self.get_views(item=i[self.get_key()], key='tx', option='decoration'))
)
return ret

View File

@ -23,12 +23,10 @@ from glances.cpu_percent import cpu_percent
from glances.plugins.glances_plugin import GlancesPlugin
# Define the history items list
items_history_list = [{'name': 'user',
'description': 'User CPU usage',
'y_unit': '%'},
{'name': 'system',
'description': 'System CPU usage',
'y_unit': '%'}]
items_history_list = [
{'name': 'user', 'description': 'User CPU usage', 'y_unit': '%'},
{'name': 'system', 'description': 'System CPU usage', 'y_unit': '%'},
]
class Plugin(GlancesPlugin):
@ -40,10 +38,9 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
items_history_list=items_history_list,
stats_init_value=[])
super(Plugin, self).__init__(
args=args, config=config, items_history_list=items_history_list, stats_init_value=[]
)
# We want to display the stat in the curse interface
self.display_curse = True
@ -110,8 +107,6 @@ class Plugin(GlancesPlugin):
msg = '{:6.1f}%'.format(cpu[stat])
except TypeError:
msg = '{:>6}%'.format('?')
ret.append(self.curse_add_line(msg,
self.get_alert(cpu[stat],
header=stat)))
ret.append(self.curse_add_line(msg, self.get_alert(cpu[stat], header=stat)))
return ret

View File

@ -37,9 +37,7 @@ from glances.thresholds import glances_thresholds
from glances.timer import Counter, Timer
fields_unit_short = {
'percent': '%'
}
fields_unit_short = {'percent': '%'}
fields_unit_type = {
'percent': 'float',
@ -53,19 +51,14 @@ fields_unit_type = {
'second': 'int',
'seconds': 'int',
'byte': 'int',
'bytes': 'int'
'bytes': 'int',
}
class GlancesPlugin(object):
"""Main class for Glances plugin."""
def __init__(self,
args=None,
config=None,
items_history_list=None,
stats_init_value={},
fields_description=None):
def __init__(self, args=None, config=None, items_history_list=None, stats_init_value={}, fields_description=None):
"""Init the plugin of plugins class.
All Glances' plugins should inherit from this class. Most of the
@ -110,8 +103,7 @@ class GlancesPlugin(object):
# Init the limits (configuration keys) dictionary
self._limits = dict()
if config is not None:
logger.debug('Load section {} in {}'.format(self.plugin_name,
config.config_file_paths()))
logger.debug('Load section {} in {}'.format(self.plugin_name, config.config_file_paths()))
self.load_limits(config=config)
# Init the actions
@ -188,9 +180,7 @@ class GlancesPlugin(object):
return json.dumps(d, ensure_ascii=False)
def history_enable(self):
return self.args is not None and \
not self.args.disable_history and \
self.get_items_history_list() is not None
return self.args is not None and not self.args.disable_history and self.get_items_history_list() is not None
def init_stats_history(self):
"""Init the stats history (dict of GlancesAttribute)."""
@ -224,14 +214,17 @@ class GlancesPlugin(object):
nativestr(l_export[item_name]) + '_' + nativestr(i['name']),
l_export[i['name']],
description=i['description'],
history_max_size=self._limits['history_size'])
history_max_size=self._limits['history_size'],
)
else:
# Stats is not a list
# Add the item to the history directly
self.stats_history.add(nativestr(i['name']),
self.get_export()[i['name']],
description=i['description'],
history_max_size=self._limits['history_size'])
self.stats_history.add(
nativestr(i['name']),
self.get_export()[i['name']],
description=i['description'],
history_max_size=self._limits['history_size'],
)
def get_items_history_list(self):
"""Return the items history list."""
@ -334,16 +327,23 @@ class GlancesPlugin(object):
"""Get the stats sorted by an alias (if present) or key."""
key = self.get_key()
try:
return sorted(self.stats, key=lambda stat: tuple(map(
lambda part: int(part) if part.isdigit() else part.lower(),
re.split(r"(\d+|\D+)", self.has_alias(stat[key]) or stat[key])
)))
return sorted(
self.stats,
key=lambda stat: tuple(
map(
lambda part: int(part) if part.isdigit() else part.lower(),
re.split(r"(\d+|\D+)", self.has_alias(stat[key]) or stat[key]),
)
),
)
except TypeError:
# Correct "Starting an alias with a number causes a crash #1885"
return sorted(self.stats, key=lambda stat: tuple(map(
lambda part: part.lower(),
re.split(r"(\d+|\D+)", self.has_alias(stat[key]) or stat[key])
)))
return sorted(
self.stats,
key=lambda stat: tuple(
map(lambda part: part.lower(), re.split(r"(\d+|\D+)", self.has_alias(stat[key]) or stat[key]))
),
)
@short_system_name.setter
def short_system_name(self, short_name):
@ -364,10 +364,12 @@ class GlancesPlugin(object):
from glances.snmp import GlancesSNMPClient
# Init the SNMP request
snmp_client = GlancesSNMPClient(host=self.args.client,
port=self.args.snmp_port,
version=self.args.snmp_version,
community=self.args.snmp_community)
snmp_client = GlancesSNMPClient(
host=self.args.client,
port=self.args.snmp_port,
version=self.args.snmp_version,
community=self.args.snmp_community,
)
# Process the SNMP request
ret = {}
@ -380,8 +382,9 @@ class GlancesPlugin(object):
# Note: key is the item indexed but the OID result
for item in snmp_result:
if iterkeys(item)[0].startswith(itervalues(snmp_oid)[0]):
ret[iterkeys(snmp_oid)[0] + iterkeys(item)
[0].split(itervalues(snmp_oid)[0])[1]] = itervalues(item)[0]
ret[iterkeys(snmp_oid)[0] + iterkeys(item)[0].split(itervalues(snmp_oid)[0])[1]] = itervalues(
item
)[0]
else:
# Build the internal dict with the SNMP result
# Note: key is the first item in the snmp_oid
@ -457,8 +460,7 @@ class GlancesPlugin(object):
try:
return self._json_dumps({value: [i for i in self.stats if i[item] == value]})
except (KeyError, ValueError) as e:
logger.error(
"Cannot get item({})=value({}) ({})".format(item, value, e))
logger.error("Cannot get item({})=value({}) ({})".format(item, value, e))
return None
def update_views_hidden(self):
@ -479,18 +481,14 @@ class GlancesPlugin(object):
"""
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):
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']
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
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
@ -520,30 +518,36 @@ class GlancesPlugin(object):
"""
ret = {}
if (isinstance(self.get_raw(), list) and
self.get_raw() is not None and
self.get_key() is not None):
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():
# i[self.get_key()] is the interface name (example for NETWORK)
ret[i[self.get_key()]] = {}
for key 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}
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
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()):
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}
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
self.views = ret
@ -656,21 +660,23 @@ class GlancesPlugin(object):
return self.stats
def get_stat_name(self, header=""):
""""Return the stat name with an optional header"""
""" "Return the stat name with an optional header"""
ret = self.plugin_name
if header != "":
ret += '_' + header
return ret
def get_alert(self,
current=0,
minimum=0,
maximum=100,
highlight_zero=True,
is_max=False,
header="",
action_key=None,
log=False):
def get_alert(
self,
current=0,
minimum=0,
maximum=100,
highlight_zero=True,
is_max=False,
header="",
action_key=None,
log=False,
):
"""Return the alert status relative to a current value.
Use this function for minor stats.
@ -740,9 +746,7 @@ class GlancesPlugin(object):
# Default is 'OK'
return ret + log_str
def manage_threshold(self,
stat_name,
trigger):
def manage_threshold(self, stat_name, trigger):
"""Manage the threshold for the current stat."""
glances_thresholds.add(stat_name, trigger)
@ -774,23 +778,13 @@ class GlancesPlugin(object):
# Use the stats dict
mustache_dict = self.get_stats_action()
# 2) Run the action
self.actions.run(
stat_name, trigger,
command, repeat, mustache_dict=mustache_dict)
self.actions.run(stat_name, trigger, command, repeat, mustache_dict=mustache_dict)
def get_alert_log(self,
current=0,
minimum=0,
maximum=100,
header="",
action_key=None):
def get_alert_log(self, current=0, minimum=0, maximum=100, header="", action_key=None):
"""Get the alert log."""
return self.get_alert(current=current,
minimum=minimum,
maximum=maximum,
header=header,
action_key=action_key,
log=True)
return self.get_alert(
current=current, minimum=minimum, maximum=maximum, header=header, action_key=action_key, log=True
)
def is_limit(self, criticality, stat_name=""):
"""Return true if the criticality limit exist for the given stat_name"""
@ -824,10 +818,12 @@ class GlancesPlugin(object):
# Get the action for stat + header
# Example: network_wlan0_rx_careful_action
# Action key available ?
ret = [(stat_name + '_' + criticality + '_action', False),
(stat_name + '_' + criticality + '_action_repeat', True),
(self.plugin_name + '_' + criticality + '_action', False),
(self.plugin_name + '_' + criticality + '_action_repeat', True)]
ret = [
(stat_name + '_' + criticality + '_action', False),
(stat_name + '_' + criticality + '_action_repeat', True),
(self.plugin_name + '_' + criticality + '_action', False),
(self.plugin_name + '_' + criticality + '_action_repeat', True),
]
for r in ret:
if r[0] in self._limits:
return self._limits[r[0]], r[1]
@ -928,19 +924,13 @@ class GlancesPlugin(object):
align_curse = self._align
if max_width is not None:
ret = {'display': display_curse,
'msgdict': self.msg_curse(args, max_width=max_width),
'align': align_curse}
ret = {'display': display_curse, 'msgdict': self.msg_curse(args, max_width=max_width), 'align': align_curse}
else:
ret = {'display': display_curse,
'msgdict': self.msg_curse(args),
'align': align_curse}
ret = {'display': display_curse, 'msgdict': self.msg_curse(args), 'align': align_curse}
return ret
def curse_add_line(self, msg, decoration="DEFAULT",
optional=False, additional=False,
splittable=False):
def curse_add_line(self, msg, decoration="DEFAULT", optional=False, additional=False, splittable=False):
"""Return a dict with.
Where:
@ -966,18 +956,19 @@ class GlancesPlugin(object):
additional: True if the stat is additional (display only if space is available after optional)
spittable: Line can be split to fit on the screen (default is not)
"""
return {'msg': msg,
'decoration': decoration,
'optional': optional,
'additional': additional,
'splittable': splittable}
return {
'msg': msg,
'decoration': decoration,
'optional': optional,
'additional': additional,
'splittable': splittable,
}
def curse_new_line(self):
"""Go to a new line."""
return self.curse_add_line('\n')
def curse_add_stat(self, key, width=None,
header='', separator='', trailer=''):
def curse_add_stat(self, key, width=None, header='', separator='', trailer=''):
"""Return a list of dict messages with the 'key: value' result
<=== width ===>
@ -1002,34 +993,39 @@ class GlancesPlugin(object):
return []
# Check if a shortname is defined
if key in self.fields_description and \
'short_name' in self.fields_description[key]:
if key in self.fields_description and 'short_name' in self.fields_description[key]:
key_name = self.fields_description[key]['short_name']
else:
key_name = key
# Check if unit is defined and get the short unit char in the unit_sort dict
if key in self.fields_description and \
'unit' in self.fields_description[key] and \
self.fields_description[key]['unit'] in fields_unit_short:
if (
key in self.fields_description
and 'unit' in self.fields_description[key]
and self.fields_description[key]['unit'] in fields_unit_short
):
# Get the shortname
unit_short = fields_unit_short[self.fields_description[key]['unit']]
else:
unit_short = ''
# Check if unit is defined and get the unit type unit_type dict
if key in self.fields_description and \
'unit' in self.fields_description[key] and \
self.fields_description[key]['unit'] in fields_unit_type:
if (
key in self.fields_description
and 'unit' in self.fields_description[key]
and self.fields_description[key]['unit'] in fields_unit_type
):
# Get the shortname
unit_type = fields_unit_type[self.fields_description[key]['unit']]
else:
unit_type = 'float'
# Is it a rate ? Yes, compute it thanks to the time_since_update key
if key in self.fields_description and \
'rate' in self.fields_description[key] and \
self.fields_description[key]['rate'] is True:
if (
key in self.fields_description
and 'rate' in self.fields_description[key]
and self.fields_description[key]['rate'] is True
):
value = self.stats[key] // self.stats['time_since_update']
else:
value = self.stats[key]
@ -1039,29 +1035,37 @@ class GlancesPlugin(object):
if unit_type == 'float':
msg_value = '{:.1f}{}'.format(value, unit_short) + trailer
elif 'min_symbol' in self.fields_description[key]:
msg_value = '{}{}'.format(self.auto_unit(int(value),
min_symbol=self.fields_description[key]['min_symbol']),
unit_short) + trailer
msg_value = (
'{}{}'.format(
self.auto_unit(int(value), min_symbol=self.fields_description[key]['min_symbol']), unit_short
)
+ trailer
)
else:
msg_value = '{}{}'.format(int(value), unit_short) + trailer
else:
# Define the size of the message
# item will be on the left
# value will be on the right
msg_item = header + '{:{width}}'.format(key_name, width=width-7) + separator
msg_item = header + '{:{width}}'.format(key_name, width=width - 7) + separator
if unit_type == 'float':
msg_value = '{:5.1f}{}'.format(value, unit_short) + trailer
elif 'min_symbol' in self.fields_description[key]:
msg_value = '{:>5}{}'.format(self.auto_unit(int(value),
min_symbol=self.fields_description[key]['min_symbol']),
unit_short) + trailer
msg_value = (
'{:>5}{}'.format(
self.auto_unit(int(value), min_symbol=self.fields_description[key]['min_symbol']), unit_short
)
+ trailer
)
else:
msg_value = '{:>5}{}'.format(int(value), unit_short) + trailer
decoration = self.get_views(key=key, option='decoration')
optional = self.get_views(key=key, option='optional')
return [self.curse_add_line(msg_item, optional=optional),
self.curse_add_line(msg_value, decoration=decoration, optional=optional)]
return [
self.curse_add_line(msg_item, optional=optional),
self.curse_add_line(msg_value, decoration=decoration, optional=optional),
]
@property
def align(self):
@ -1076,10 +1080,7 @@ class GlancesPlugin(object):
"""
self._align = value
def auto_unit(self, number,
low_precision=False,
min_symbol='K'
):
def auto_unit(self, number, low_precision=False, min_symbol='K'):
"""Make a nice human-readable string out of number.
Number of decimal places increases as quantity approaches 1.
@ -1097,7 +1098,7 @@ class GlancesPlugin(object):
"""
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
if min_symbol in symbols:
symbols = symbols[symbols.index(min_symbol):]
symbols = symbols[symbols.index(min_symbol) :]
prefix = {
'Y': 1208925819614629174706176,
'Z': 1180591620717411303424,
@ -1106,7 +1107,7 @@ class GlancesPlugin(object):
'T': 1099511627776,
'G': 1073741824,
'M': 1048576,
'K': 1024
'K': 1024,
}
for symbol in reversed(symbols):
@ -1124,8 +1125,7 @@ class GlancesPlugin(object):
decimal_precision = min(1, decimal_precision)
elif symbol in 'K':
decimal_precision = 0
return '{:.{decimal}f}{symbol}'.format(
value, decimal=decimal_precision, symbol=symbol)
return '{:.{decimal}f}{symbol}'.format(value, decimal=decimal_precision, symbol=symbol)
return '{!s}'.format(number)
def trend_msg(self, trend, significant=1):
@ -1162,20 +1162,28 @@ class GlancesPlugin(object):
# Return the last result available
ret = self.stats
return ret
return wrapper
def _log_result_decorator(fct):
"""Log (DEBUG) the result of the function fct."""
def wrapper(*args, **kw):
counter = Counter()
ret = fct(*args, **kw)
duration = counter.get()
logger.debug("%s %s %s return %s in %s seconds" % (
args[0].__class__.__name__,
args[0].__class__.__module__[len('glances_'):],
fct.__name__, ret,
duration))
logger.debug(
"%s %s %s return %s in %s seconds"
% (
args[0].__class__.__name__,
args[0].__class__.__module__[len('glances_') :],
fct.__name__,
ret,
duration,
)
)
return ret
return wrapper
# Mandatory to call the decorator in child classes

View File

@ -36,6 +36,7 @@ from glances.plugins.glances_plugin import GlancesPlugin
try:
import requests
requests_tag = True
except ImportError as e:
requests_tag = False
@ -47,9 +48,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
stats_init_value=[])
super(Plugin, self).__init__(args=args, config=config, stats_init_value=[])
self.args = args
self.config = config
@ -57,8 +56,10 @@ class Plugin(GlancesPlugin):
self.display_curse = True
# Init stats
self.stats = GlancesPortsList(config=config, args=args).get_ports_list() + \
GlancesWebList(config=config, args=args).get_web_list()
self.stats = (
GlancesPortsList(config=config, args=args).get_ports_list()
+ GlancesWebList(config=config, args=args).get_web_list()
)
# Global Thread running all the scans
self._thread = None
@ -108,9 +109,11 @@ class Plugin(GlancesPlugin):
ret = 'CAREFUL'
elif port['status'] == 0:
ret = 'CRITICAL'
elif (isinstance(port['status'], (float, int)) and
port['rtt_warning'] is not None and
port['status'] > port['rtt_warning']):
elif (
isinstance(port['status'], (float, int))
and port['rtt_warning'] is not None
and port['status'] > port['rtt_warning']
):
ret = 'WARNING'
# Get stat name
@ -120,10 +123,7 @@ class Plugin(GlancesPlugin):
self.manage_threshold(stat_name, ret)
# Manage action
self.manage_action(stat_name,
ret.lower(),
header,
port[self.get_key()])
self.manage_action(stat_name, ret.lower(), header, port[self.get_key()])
return ret
@ -144,10 +144,7 @@ class Plugin(GlancesPlugin):
self.manage_threshold(stat_name, ret)
# Manage action
self.manage_action(stat_name,
ret.lower(),
header,
web[self.get_key()])
self.manage_action(stat_name, ret.lower(), header, web[self.get_key()])
return ret
@ -178,17 +175,13 @@ class Plugin(GlancesPlugin):
# Convert second to ms
status = '{0:.0f}ms'.format(p['status'] * 1000.0)
msg = '{:{width}}'.format(p['description'][0:name_max_width],
width=name_max_width)
msg = '{:{width}}'.format(p['description'][0:name_max_width], width=name_max_width)
ret.append(self.curse_add_line(msg))
msg = '{:>9}'.format(status)
ret.append(self.curse_add_line(msg,
self.get_ports_alert(p,
header=p['indice'] + '_rtt')))
ret.append(self.curse_add_line(msg, self.get_ports_alert(p, header=p['indice'] + '_rtt')))
ret.append(self.curse_new_line())
elif 'url' in p:
msg = '{:{width}}'.format(p['description'][0:name_max_width],
width=name_max_width)
msg = '{:{width}}'.format(p['description'][0:name_max_width], width=name_max_width)
ret.append(self.curse_add_line(msg))
if isinstance(p['status'], numbers.Number):
status = 'Code {}'.format(p['status'])
@ -197,9 +190,7 @@ class Plugin(GlancesPlugin):
else:
status = p['status']
msg = '{:>9}'.format(status)
ret.append(self.curse_add_line(msg,
self.get_web_alert(p,
header=p['indice'] + '_rtt')))
ret.append(self.curse_add_line(msg, self.get_web_alert(p, header=p['indice'] + '_rtt')))
ret.append(self.curse_new_line())
# Delete the last empty line
@ -270,11 +261,13 @@ class ThreadScanner(threading.Thread):
def _web_scan(self, web):
"""Scan the Web/URL (dict) and update the status key."""
try:
req = requests.head(web['url'],
allow_redirects=True,
verify=web['ssl_verify'],
proxies=web['proxies'],
timeout=web['timeout'])
req = requests.head(
web['url'],
allow_redirects=True,
verify=web['ssl_verify'],
proxies=web['proxies'],
timeout=web['timeout'],
)
except Exception as e:
logger.debug(e)
web['status'] = 'Error'
@ -319,10 +312,14 @@ class ThreadScanner(threading.Thread):
count_opt = '-c'
# Build the command line
# Note: Only string are allowed
cmd = ['ping',
count_opt, '1',
timeout_opt, str(self._resolv_name(port['timeout'])),
self._resolv_name(port['host'])]
cmd = [
'ping',
count_opt,
'1',
timeout_opt,
str(self._resolv_name(port['timeout'])),
self._resolv_name(port['host']),
]
fnull = open(os.devnull, 'w')
try:

View File

@ -23,18 +23,12 @@ from glances.processes import glances_processes
from glances.plugins.glances_plugin import GlancesPlugin
# Define the history items list
items_history_list = [{'name': 'total',
'description': 'Total number of processes',
'y_unit': ''},
{'name': 'running',
'description': 'Total number of running processes',
'y_unit': ''},
{'name': 'sleeping',
'description': 'Total number of sleeping processes',
'y_unit': ''},
{'name': 'thread',
'description': 'Total number of threads',
'y_unit': ''}]
items_history_list = [
{'name': 'total', 'description': 'Total number of processes', 'y_unit': ''},
{'name': 'running', 'description': 'Total number of running processes', 'y_unit': ''},
{'name': 'sleeping', 'description': 'Total number of sleeping processes', 'y_unit': ''},
{'name': 'thread', 'description': 'Total number of threads', 'y_unit': ''},
]
class Plugin(GlancesPlugin):
@ -43,19 +37,19 @@ class Plugin(GlancesPlugin):
stats is a list
"""
sort_for_human = {'io_counters': 'disk IO',
'cpu_percent': 'CPU consumption',
'memory_percent': 'memory consumption',
'cpu_times': 'process time',
'username': 'user name',
'name': 'process name',
None: 'None'}
sort_for_human = {
'io_counters': 'disk IO',
'cpu_percent': 'CPU consumption',
'memory_percent': 'memory consumption',
'cpu_times': 'process time',
'username': 'user name',
'name': 'process name',
None: 'None',
}
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
items_history_list=items_history_list)
super(Plugin, self).__init__(args=args, config=config, items_history_list=items_history_list)
# We want to display the stat in the curse interface
self.display_curse = True

View File

@ -42,9 +42,20 @@ def seconds_to_hms(input_seconds):
return hours, minutes, seconds
def split_cmdline(cmdline):
"""Return path, cmd and arguments for a process cmdline."""
path, cmd = os.path.split(cmdline[0])
def split_cmdline(bare_process_name, cmdline):
"""Return path, cmd and arguments for a process cmdline based on bare_process_name.
If first argument of cmdline starts with the bare_process_name then
cmdline will just be considered cmd and path will be empty (see https://github.com/nicolargo/glances/issues/1795)
:param bare_process_name: Name of the process from psutil
:param cmdline: cmdline from psutil
:return: Tuple with three strings, which are path, cmd and arguments of the process
"""
if cmdline[0].startswith(bare_process_name):
path, cmd = "", cmdline[0]
else:
path, cmd = os.path.split(cmdline[0])
arguments = ' '.join(cmdline[1:])
return path, cmd, arguments
@ -88,14 +99,12 @@ class Plugin(GlancesPlugin):
'ior': '{:>4} ',
'iow': '{:<4} ',
'command': '{}',
'name': '[{}]'
'name': '[{}]',
}
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
stats_init_value=[])
super(Plugin, self).__init__(args=args, config=config, stats_init_value=[])
# We want to display the stat in the curse interface
self.display_curse = True
@ -121,7 +130,9 @@ class Plugin(GlancesPlugin):
if 'processlist' in config.as_dict() and 'sort_key' in config.as_dict()['processlist']:
logger.debug(
'Configuration overwrites processes sort key by {}'.format(
config.as_dict()['processlist']['sort_key']))
config.as_dict()['processlist']['sort_key']
)
)
glances_processes.set_sort_key(config.as_dict()['processlist']['sort_key'], False)
# The default sort key could also be overwrite by command line (see #1903)
@ -183,15 +194,15 @@ class Plugin(GlancesPlugin):
if key_exist_value_not_none_not_v('cpu_percent', p, ''):
cpu_layout = self.layout_stat['cpu'] if p['cpu_percent'] < 100 else self.layout_stat['cpu_no_digit']
if args.disable_irix and self.nb_log_core != 0:
msg = cpu_layout.format(
p['cpu_percent'] / float(self.nb_log_core))
msg = cpu_layout.format(p['cpu_percent'] / float(self.nb_log_core))
else:
msg = cpu_layout.format(p['cpu_percent'])
alert = self.get_alert(p['cpu_percent'],
highlight_zero=False,
is_max=(p['cpu_percent'] ==
self.max_values['cpu_percent']),
header="cpu")
alert = self.get_alert(
p['cpu_percent'],
highlight_zero=False,
is_max=(p['cpu_percent'] == self.max_values['cpu_percent']),
header="cpu",
)
ret = self.curse_add_line(msg, alert)
else:
msg = self.layout_header['cpu'].format('?')
@ -202,11 +213,12 @@ class Plugin(GlancesPlugin):
"""Return process MEM curses"""
if key_exist_value_not_none_not_v('memory_percent', p, ''):
msg = self.layout_stat['mem'].format(p['memory_percent'])
alert = self.get_alert(p['memory_percent'],
highlight_zero=False,
is_max=(p['memory_percent'] ==
self.max_values['memory_percent']),
header="mem")
alert = self.get_alert(
p['memory_percent'],
highlight_zero=False,
is_max=(p['memory_percent'] == self.max_values['memory_percent']),
header="mem",
)
ret = self.curse_add_line(msg, alert)
else:
msg = self.layout_header['mem'].format('?')
@ -216,8 +228,7 @@ class Plugin(GlancesPlugin):
def _get_process_curses_vms(self, p, selected, args):
"""Return process VMS curses"""
if key_exist_value_not_none_not_v('memory_info', p, ''):
msg = self.layout_stat['virt'].format(
self.auto_unit(p['memory_info'][1], low_precision=False))
msg = self.layout_stat['virt'].format(self.auto_unit(p['memory_info'][1], low_precision=False))
ret = self.curse_add_line(msg, optional=True)
else:
msg = self.layout_header['virt'].format('?')
@ -227,8 +238,7 @@ class Plugin(GlancesPlugin):
def _get_process_curses_rss(self, p, selected, args):
"""Return process RSS curses"""
if key_exist_value_not_none_not_v('memory_info', p, ''):
msg = self.layout_stat['res'].format(
self.auto_unit(p['memory_info'][0], low_precision=False))
msg = self.layout_stat['res'].format(self.auto_unit(p['memory_info'][0], low_precision=False))
ret = self.curse_add_line(msg, optional=True)
else:
msg = self.layout_header['res'].format('?')
@ -270,9 +280,7 @@ class Plugin(GlancesPlugin):
msg = '{}:{}'.format(minutes, seconds)
msg = self.layout_stat['time'].format(msg)
if hours > 0:
ret = self.curse_add_line(msg,
decoration='CPU_TIME',
optional=True)
ret = self.curse_add_line(msg, decoration='CPU_TIME', optional=True)
else:
ret = self.curse_add_line(msg, optional=True)
return ret
@ -297,8 +305,7 @@ class Plugin(GlancesPlugin):
if nice is None:
nice = '?'
msg = self.layout_stat['nice'].format(nice)
ret = self.curse_add_line(msg,
decoration=self.get_nice_alert(nice))
ret = self.curse_add_line(msg, decoration=self.get_nice_alert(nice))
else:
msg = self.layout_header['nice'].format('?')
ret = self.curse_add_line(msg)
@ -320,19 +327,17 @@ class Plugin(GlancesPlugin):
def _get_process_curses_io(self, p, selected, args, rorw='ior'):
"""Return process IO Read or Write curses"""
if 'io_counters' in p and \
p['io_counters'][4] == 1 and \
p['time_since_update'] != 0:
if 'io_counters' in p and p['io_counters'][4] == 1 and p['time_since_update'] != 0:
# Display rate if stats is available and io_tag ([4]) == 1
# IO
io = int((p['io_counters'][0 if rorw == 'ior' else 1] - p['io_counters']
[2 if rorw == 'ior' else 3]) / p['time_since_update'])
io = int(
(p['io_counters'][0 if rorw == 'ior' else 1] - p['io_counters'][2 if rorw == 'ior' else 3])
/ p['time_since_update']
)
if io == 0:
msg = self.layout_stat[rorw].format("0")
else:
msg = self.layout_stat[rorw].format(
self.auto_unit(io,
low_precision=True))
msg = self.layout_stat[rorw].format(self.auto_unit(io, low_precision=True))
ret = self.curse_add_line(msg, optional=True, additional=True)
else:
msg = self.layout_header[rorw].format("?")
@ -392,33 +397,29 @@ class Plugin(GlancesPlugin):
ret.append(self._get_process_curses_io_write(p, selected, args))
# Command line
# If no command line for the process is available, fallback to
# the bare process name instead
if 'cmdline' in p:
cmdline = p['cmdline']
else:
cmdline = '?'
# If no command line for the process is available, fallback to the bare process name instead
bare_process_name = p['name']
cmdline = p.get('cmdline', '?')
try:
process_decoration = 'PROCESS_SELECTED' if (selected and args.is_standalone) else 'PROCESS'
if cmdline:
path, cmd, arguments = split_cmdline(cmdline)
path, cmd, arguments = split_cmdline(bare_process_name, cmdline)
# Manage end of line in arguments (see #1692)
arguments.replace('\r\n', ' ')
arguments.replace('\n', ' ')
if os.path.isdir(path) and not args.process_short_name:
msg = self.layout_stat['command'].format(path) + os.sep
ret.append(self.curse_add_line(msg, splittable=True))
ret.append(self.curse_add_line(
cmd, decoration=process_decoration, splittable=True))
ret.append(self.curse_add_line(cmd, decoration=process_decoration, splittable=True))
else:
msg = self.layout_stat['command'].format(cmd)
ret.append(self.curse_add_line(
msg, decoration=process_decoration, splittable=True))
ret.append(self.curse_add_line(msg, decoration=process_decoration, splittable=True))
if arguments:
msg = ' ' + self.layout_stat['command'].format(arguments)
ret.append(self.curse_add_line(msg, splittable=True))
else:
msg = self.layout_stat['name'].format(p['name'])
msg = self.layout_stat['name'].format(bare_process_name)
ret.append(self.curse_add_line(msg, decoration=process_decoration, splittable=True))
except (TypeError, UnicodeEncodeError) as e:
# Avoid crash after running fine for several hours #1335
@ -435,8 +436,7 @@ class Plugin(GlancesPlugin):
msg = xpad + 'CPU affinity: ' + str(len(p['cpu_affinity'])) + ' cores'
ret.append(self.curse_add_line(msg, splittable=True))
# Second line is memory info
if 'memory_info' in p and \
p['memory_info'] is not None:
if 'memory_info' in p and p['memory_info'] is not None:
ret.append(self.curse_new_line())
msg = '{}Memory info: {}'.format(xpad, p['memory_info'])
if 'memory_swap' in p and p['memory_swap'] is not None:
@ -459,9 +459,7 @@ class Plugin(GlancesPlugin):
msg = xpad + 'Open: ' + msg
ret.append(self.curse_add_line(msg, splittable=True))
# Fourth line is IO nice level (only Linux and Windows OS)
if 'ionice' in p and \
p['ionice'] is not None \
and hasattr(p['ionice'], 'ioclass'):
if 'ionice' in p and p['ionice'] is not None and hasattr(p['ionice'], 'ioclass'):
ret.append(self.curse_new_line())
msg = xpad + 'IO nice: '
k = 'Class is '
@ -515,8 +513,7 @@ class Plugin(GlancesPlugin):
# Loop over processes (sorted by the sort key previously compute)
i = 0
for p in self.__sort_stats(process_sort_key):
ret.extend(self.get_process_curses_data(
p, i == args.cursor_position, args))
ret.extend(self.get_process_curses_data(p, i == args.cursor_position, args))
i += 1
# A filter is set Display the stats summaries
@ -553,9 +550,9 @@ class Plugin(GlancesPlugin):
msg = self.layout_header['user'].format('USER')
ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'username' else 'DEFAULT'))
msg = self.layout_header['time'].format('TIME+')
ret.append(self.curse_add_line(msg,
sort_style if process_sort_key == 'cpu_times' else 'DEFAULT',
optional=True))
ret.append(
self.curse_add_line(msg, sort_style if process_sort_key == 'cpu_times' else 'DEFAULT', optional=True)
)
msg = self.layout_header['thread'].format('THR')
ret.append(self.curse_add_line(msg))
msg = self.layout_header['nice'].format('NI')
@ -563,17 +560,18 @@ class Plugin(GlancesPlugin):
msg = self.layout_header['status'].format('S')
ret.append(self.curse_add_line(msg))
msg = self.layout_header['ior'].format('R/s')
ret.append(self.curse_add_line(msg,
sort_style if process_sort_key == 'io_counters' else 'DEFAULT',
optional=True,
additional=True))
ret.append(
self.curse_add_line(
msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True
)
)
msg = self.layout_header['iow'].format('W/s')
ret.append(self.curse_add_line(msg,
sort_style if process_sort_key == 'io_counters' else 'DEFAULT',
optional=True,
additional=True))
msg = self.layout_header['command'].format('Command',
"('k' to kill)" if args.is_standalone else "")
ret.append(
self.curse_add_line(
msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True
)
)
msg = self.layout_header['command'].format('Command', "('k' to kill)" if args.is_standalone else "")
ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'name' else 'DEFAULT'))
def __msg_curse_sum(self, ret, sep_char='_', mmm=None, args=None):
@ -591,27 +589,26 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_new_line())
# CPU percent sum
msg = self.layout_stat['cpu'].format(self.__sum_stats('cpu_percent', mmm=mmm))
ret.append(self.curse_add_line(msg,
decoration=self.__mmm_deco(mmm)))
ret.append(self.curse_add_line(msg, decoration=self.__mmm_deco(mmm)))
# MEM percent sum
msg = self.layout_stat['mem'].format(self.__sum_stats('memory_percent', mmm=mmm))
ret.append(self.curse_add_line(msg,
decoration=self.__mmm_deco(mmm)))
ret.append(self.curse_add_line(msg, decoration=self.__mmm_deco(mmm)))
# VIRT and RES memory sum
if 'memory_info' in self.stats[0] and \
self.stats[0]['memory_info'] is not None and self.stats[0]['memory_info'] != '':
if (
'memory_info' in self.stats[0]
and self.stats[0]['memory_info'] is not None
and self.stats[0]['memory_info'] != ''
):
# VMS
msg = self.layout_stat['virt'].format(self.auto_unit(self.__sum_stats('memory_info', indice=1, mmm=mmm),
low_precision=False))
ret.append(self.curse_add_line(msg,
decoration=self.__mmm_deco(mmm),
optional=True))
msg = self.layout_stat['virt'].format(
self.auto_unit(self.__sum_stats('memory_info', indice=1, mmm=mmm), low_precision=False)
)
ret.append(self.curse_add_line(msg, decoration=self.__mmm_deco(mmm), optional=True))
# RSS
msg = self.layout_stat['res'].format(self.auto_unit(self.__sum_stats('memory_info', indice=0, mmm=mmm),
low_precision=False))
ret.append(self.curse_add_line(msg,
decoration=self.__mmm_deco(mmm),
optional=True))
msg = self.layout_stat['res'].format(
self.auto_unit(self.__sum_stats('memory_info', indice=0, mmm=mmm), low_precision=False)
)
ret.append(self.curse_add_line(msg, decoration=self.__mmm_deco(mmm), optional=True))
else:
msg = self.layout_header['virt'].format('')
ret.append(self.curse_add_line(msg))
@ -638,25 +635,25 @@ class Plugin(GlancesPlugin):
# IO read/write
if 'io_counters' in self.stats[0] and mmm is None:
# IO read
io_rs = int((self.__sum_stats('io_counters', 0) - self.__sum_stats('io_counters',
indice=2, mmm=mmm)) / self.stats[0]['time_since_update'])
io_rs = int(
(self.__sum_stats('io_counters', 0) - self.__sum_stats('io_counters', indice=2, mmm=mmm))
/ self.stats[0]['time_since_update']
)
if io_rs == 0:
msg = self.layout_stat['ior'].format('0')
else:
msg = self.layout_stat['ior'].format(self.auto_unit(io_rs, low_precision=True))
ret.append(self.curse_add_line(msg,
decoration=self.__mmm_deco(mmm),
optional=True, additional=True))
ret.append(self.curse_add_line(msg, decoration=self.__mmm_deco(mmm), optional=True, additional=True))
# IO write
io_ws = int((self.__sum_stats('io_counters', 1) - self.__sum_stats('io_counters',
indice=3, mmm=mmm)) / self.stats[0]['time_since_update'])
io_ws = int(
(self.__sum_stats('io_counters', 1) - self.__sum_stats('io_counters', indice=3, mmm=mmm))
/ self.stats[0]['time_since_update']
)
if io_ws == 0:
msg = self.layout_stat['iow'].format('0')
else:
msg = self.layout_stat['iow'].format(self.auto_unit(io_ws, low_precision=True))
ret.append(self.curse_add_line(msg,
decoration=self.__mmm_deco(mmm),
optional=True, additional=True))
ret.append(self.curse_add_line(msg, decoration=self.__mmm_deco(mmm), optional=True, additional=True))
else:
msg = self.layout_header['ior'].format('')
ret.append(self.curse_add_line(msg, optional=True, additional=True))
@ -736,8 +733,7 @@ class Plugin(GlancesPlugin):
def __sort_stats(self, sorted_by=None):
"""Return the stats (dict) sorted by (sorted_by)."""
return sort_stats(self.stats, sorted_by,
reverse=glances_processes.sort_reverse)
return sort_stats(self.stats, sorted_by, reverse=glances_processes.sort_reverse)
def __max_pid_size(self):
"""Return the maximum PID size in number of char."""

View File

@ -30,18 +30,12 @@ import psutil
# Define the history items list
# All items in this list will be historised if the --enable-history tag is set
items_history_list = [{'name': 'cpu',
'description': 'CPU percent usage',
'y_unit': '%'},
{'name': 'percpu',
'description': 'PERCPU percent usage',
'y_unit': '%'},
{'name': 'mem',
'description': 'MEM percent usage',
'y_unit': '%'},
{'name': 'swap',
'description': 'SWAP percent usage',
'y_unit': '%'}]
items_history_list = [
{'name': 'cpu', 'description': 'CPU percent usage', 'y_unit': '%'},
{'name': 'percpu', 'description': 'PERCPU percent usage', 'y_unit': '%'},
{'name': 'mem', 'description': 'MEM percent usage', 'y_unit': '%'},
{'name': 'swap', 'description': 'SWAP percent usage', 'y_unit': '%'},
]
class Plugin(GlancesPlugin):
@ -52,9 +46,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the quicklook plugin."""
super(Plugin, self).__init__(args=args,
config=config,
items_history_list=items_history_list)
super(Plugin, self).__init__(args=args, config=config, items_history_list=items_history_list)
# We want to display the stat in the curse interface
self.display_curse = True
@ -82,7 +74,9 @@ class Plugin(GlancesPlugin):
# Get additional information
cpu_info = cpu_percent.get_info()
stats['cpu_name'] = cpu_info['cpu_name']
stats['cpu_hz_current'] = self._mhz_to_hz(cpu_info['cpu_hz_current']) if cpu_info['cpu_hz_current'] is not None else None
stats['cpu_hz_current'] = (
self._mhz_to_hz(cpu_info['cpu_hz_current']) if cpu_info['cpu_hz_current'] is not None else None
)
stats['cpu_hz'] = self._mhz_to_hz(cpu_info['cpu_hz']) if cpu_info['cpu_hz'] is not None else None
elif self.input_method == 'snmp':
@ -127,8 +121,9 @@ class Plugin(GlancesPlugin):
if 'cpu_name' in self.stats and 'cpu_hz_current' in self.stats and 'cpu_hz' in self.stats:
msg_name = self.stats['cpu_name']
if self.stats['cpu_hz_current'] and self.stats['cpu_hz']:
msg_freq = ' - {:.2f}/{:.2f}GHz'.format(self._hz_to_ghz(self.stats['cpu_hz_current']),
self._hz_to_ghz(self.stats['cpu_hz']))
msg_freq = ' - {:.2f}/{:.2f}GHz'.format(
self._hz_to_ghz(self.stats['cpu_hz_current']), self._hz_to_ghz(self.stats['cpu_hz'])
)
else:
msg_freq = ''
if len(msg_name + msg_freq) - 6 <= max_width:
@ -179,7 +174,8 @@ class Plugin(GlancesPlugin):
self.curse_add_line(msg),
self.curse_add_line(data.pre_char, decoration='BOLD'),
self.curse_add_line(data.get(), self.get_views(key=key, option='decoration')),
self.curse_add_line(data.post_char, decoration='BOLD'), self.curse_add_line(' ')
self.curse_add_line(data.post_char, decoration='BOLD'),
self.curse_add_line(' '),
]
def _hz_to_ghz(self, hz):

View File

@ -90,8 +90,7 @@ class Plugin(GlancesPlugin):
name_max_width = max_width - 12
# Header
msg = '{:{width}}'.format('RAID disks',
width=name_max_width)
msg = '{:{width}}'.format('RAID disks', width=name_max_width)
ret.append(self.curse_add_line(msg, "TITLE"))
msg = '{:>7}'.format('Used')
ret.append(self.curse_add_line(msg))
@ -103,16 +102,17 @@ class Plugin(GlancesPlugin):
# New line
ret.append(self.curse_new_line())
# Display the current status
status = self.raid_alert(self.stats[array]['status'],
self.stats[array]['used'],
self.stats[array]['available'],
self.stats[array]['type'])
status = self.raid_alert(
self.stats[array]['status'],
self.stats[array]['used'],
self.stats[array]['available'],
self.stats[array]['type'],
)
# Data: RAID type name | disk used | disk available
array_type = self.stats[array]['type'].upper() if self.stats[array]['type'] is not None else 'UNKNOWN'
# Build the full name = array type + array name
full_name = '{} {}'.format(array_type, array)
msg = '{:{width}}'.format(full_name,
width=name_max_width)
msg = '{:{width}}'.format(full_name, width=name_max_width)
ret.append(self.curse_add_line(msg))
if self.stats[array]['type'] == 'raid0' and self.stats[array]['status'] == 'active':
msg = '{:>7}'.format(len(self.stats[array]['components']))

View File

@ -43,9 +43,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
stats_init_value=[])
super(Plugin, self).__init__(args=args, config=config, stats_init_value=[])
start_duration = Counter()
@ -90,8 +88,7 @@ class Plugin(GlancesPlugin):
stats = []
# Get the temperature
try:
temperature = self.__set_type(self.glances_grab_sensors.get('temperature_core'),
'temperature_core')
temperature = self.__set_type(self.glances_grab_sensors.get('temperature_core'), 'temperature_core')
except Exception as e:
logger.error("Cannot grab sensors temperatures (%s)" % e)
else:
@ -99,8 +96,7 @@ class Plugin(GlancesPlugin):
stats.extend(temperature)
# Get the FAN speed
try:
fan_speed = self.__set_type(self.glances_grab_sensors.get('fan_speed'),
'fan_speed')
fan_speed = self.__set_type(self.glances_grab_sensors.get('fan_speed'), 'fan_speed')
except Exception as e:
logger.error("Cannot grab FAN speed (%s)" % e)
else:
@ -185,11 +181,9 @@ class Plugin(GlancesPlugin):
else:
alert = 'OK'
elif i['type'] == 'battery':
alert = self.get_alert(current=100 - i['value'],
header=i['type'])
alert = self.get_alert(current=100 - i['value'], header=i['type'])
else:
alert = self.get_alert(current=i['value'],
header=i['type'])
alert = self.get_alert(current=i['value'], header=i['type'])
# Set the alert in the view
self.views[i[self.get_key()]]['value']['decoration'] = alert
@ -216,18 +210,15 @@ class Plugin(GlancesPlugin):
continue
# New line
ret.append(self.curse_new_line())
msg = '{:{width}}'.format(i["label"][:name_max_width],
width=name_max_width)
msg = '{:{width}}'.format(i["label"][:name_max_width], width=name_max_width)
ret.append(self.curse_add_line(msg))
if i['value'] in (b'ERR', b'SLP', b'UNK', b'NOS'):
msg = '{:>13}'.format(i['value'])
ret.append(self.curse_add_line(
msg, self.get_views(item=i[self.get_key()],
key='value',
option='decoration')))
ret.append(
self.curse_add_line(msg, self.get_views(item=i[self.get_key()], key='value', option='decoration'))
)
else:
if (args.fahrenheit and i['type'] != 'battery' and
i['type'] != 'fan_speed'):
if args.fahrenheit and i['type'] != 'battery' and i['type'] != 'fan_speed':
value = to_fahrenheit(i['value'])
unit = 'F'
else:
@ -235,10 +226,11 @@ class Plugin(GlancesPlugin):
unit = i['unit']
try:
msg = '{:>13.0f}{}'.format(value, unit)
ret.append(self.curse_add_line(
msg, self.get_views(item=i[self.get_key()],
key='value',
option='decoration')))
ret.append(
self.curse_add_line(
msg, self.get_views(item=i[self.get_key()], key='value', option='decoration')
)
)
except (TypeError, ValueError):
pass

View File

@ -99,16 +99,17 @@ def get_smart_data():
devlist = DeviceList()
except TypeError as e:
# Catch error (see #1806)
logger.debug(
'Smart plugin error - Can not grab device list ({})'.format(e))
logger.debug('Smart plugin error - Can not grab device list ({})'.format(e))
global import_error_tag
import_error_tag = True
return stats
for dev in devlist.devices:
stats.append({
'DeviceName': '{} {}'.format(dev.name, dev.model),
})
stats.append(
{
'DeviceName': '{} {}'.format(dev.name, dev.model),
}
)
for attribute in dev.attributes:
if attribute is None:
pass
@ -134,10 +135,7 @@ class Plugin(GlancesPlugin):
stats is a list of dicts
"""
def __init__(self,
args=None,
config=None,
stats_init_value=[]):
def __init__(self, args=None, config=None, stats_init_value=[]):
"""Init the plugin."""
# check if user is admin
if not is_admin():
@ -186,20 +184,19 @@ class Plugin(GlancesPlugin):
name_max_width = max_width - 6
# Header
msg = '{:{width}}'.format('SMART disks',
width=name_max_width)
msg = '{:{width}}'.format('SMART disks', width=name_max_width)
ret.append(self.curse_add_line(msg, "TITLE"))
# Data
for device_stat in self.stats:
# New line
ret.append(self.curse_new_line())
msg = '{:{width}}'.format(device_stat['DeviceName'][:max_width],
width=max_width)
msg = '{:{width}}'.format(device_stat['DeviceName'][:max_width], width=max_width)
ret.append(self.curse_add_line(msg))
for smart_stat in sorted([i for i in device_stat.keys() if i != 'DeviceName'], key=int):
ret.append(self.curse_new_line())
msg = ' {:{width}}'.format(device_stat[smart_stat]['name'][:name_max_width-1].replace('_', ' '),
width=name_max_width-1)
msg = ' {:{width}}'.format(
device_stat[smart_stat]['name'][: name_max_width - 1].replace('_', ' '), width=name_max_width - 1
)
ret.append(self.curse_add_line(msg))
msg = '{:>8}'.format(device_stat[smart_stat]['raw'])
ret.append(self.curse_add_line(msg))

View File

@ -29,24 +29,31 @@ from glances.compat import iteritems
from glances.plugins.glances_plugin import GlancesPlugin
# SNMP OID
snmp_oid = {'default': {'hostname': '1.3.6.1.2.1.1.5.0',
'system_name': '1.3.6.1.2.1.1.1.0'},
'netapp': {'hostname': '1.3.6.1.2.1.1.5.0',
'system_name': '1.3.6.1.2.1.1.1.0',
'platform': '1.3.6.1.4.1.789.1.1.5.0'}}
snmp_oid = {
'default': {'hostname': '1.3.6.1.2.1.1.5.0', 'system_name': '1.3.6.1.2.1.1.1.0'},
'netapp': {
'hostname': '1.3.6.1.2.1.1.5.0',
'system_name': '1.3.6.1.2.1.1.1.0',
'platform': '1.3.6.1.4.1.789.1.1.5.0',
},
}
# SNMP to human read
# Dict (key: OS short name) of dict (reg exp OID to human)
# Windows:
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms724832%28v=vs.85%29.aspx
snmp_to_human = {'windows': {'Windows Version 10.0': 'Windows 10 or Server 2016',
'Windows Version 6.3': 'Windows 8.1 or Server 2012R2',
'Windows Version 6.2': 'Windows 8 or Server 2012',
'Windows Version 6.1': 'Windows 7 or Server 2008R2',
'Windows Version 6.0': 'Windows Vista or Server 2008',
'Windows Version 5.2': 'Windows XP 64bits or 2003 server',
'Windows Version 5.1': 'Windows XP',
'Windows Version 5.0': 'Windows 2000'}}
snmp_to_human = {
'windows': {
'Windows Version 10.0': 'Windows 10 or Server 2016',
'Windows Version 6.3': 'Windows 8.1 or Server 2012R2',
'Windows Version 6.2': 'Windows 8 or Server 2012',
'Windows Version 6.1': 'Windows 7 or Server 2008R2',
'Windows Version 6.0': 'Windows Vista or Server 2008',
'Windows Version 5.2': 'Windows XP 64bits or 2003 server',
'Windows Version 5.1': 'Windows XP',
'Windows Version 5.0': 'Windows 2000',
}
}
def _linux_os_release():
@ -121,8 +128,7 @@ class Plugin(GlancesPlugin):
else:
stats['linux_distro'] = ' '.join(linux_distro[:2])
stats['os_version'] = platform.release()
elif (stats['os_name'].endswith('BSD') or
stats['os_name'] == 'SunOS'):
elif stats['os_name'].endswith('BSD') or stats['os_name'] == 'SunOS':
stats['os_version'] = platform.release()
elif stats['os_name'] == "Darwin":
stats['os_version'] = platform.mac_ver()[0]
@ -139,15 +145,13 @@ class Plugin(GlancesPlugin):
if stats['os_name'] == "Linux":
stats['hr_name'] = stats['linux_distro']
else:
stats['hr_name'] = '{} {}'.format(
stats['os_name'], stats['os_version'])
stats['hr_name'] = '{} {}'.format(stats['os_name'], stats['os_version'])
stats['hr_name'] += ' {}'.format(stats['platform'])
elif self.input_method == 'snmp':
# Update stats using SNMP
try:
stats = self.get_stats_snmp(
snmp_oid=snmp_oid[self.short_system_name])
stats = self.get_stats_snmp(snmp_oid=snmp_oid[self.short_system_name])
except KeyError:
stats = self.get_stats_snmp(snmp_oid=snmp_oid['default'])
# Default behavior: display all the information
@ -189,15 +193,12 @@ class Plugin(GlancesPlugin):
ret.append(self.curse_add_line(msg, "TITLE"))
# System info
if self.stats['os_name'] == "Linux" and self.stats['linux_distro']:
msg = ' ({} {} / {} {})'.format(self.stats['linux_distro'],
self.stats['platform'],
self.stats['os_name'],
self.stats['os_version'])
msg = ' ({} {} / {} {})'.format(
self.stats['linux_distro'], self.stats['platform'], self.stats['os_name'], self.stats['os_version']
)
else:
try:
msg = ' ({} {} {})'.format(self.stats['os_name'],
self.stats['os_version'],
self.stats['platform'])
msg = ' ({} {} {})'.format(self.stats['os_name'], self.stats['os_version'], self.stats['platform'])
except Exception:
msg = ' ({})'.format(self.stats['os_name'])
ret.append(self.curse_add_line(msg, optional=True))

View File

@ -26,6 +26,7 @@ from glances.logger import logger
from glances.plugins.glances_plugin import GlancesPlugin
import psutil
# Use the Wifi Python lib (https://pypi.python.org/pypi/wifi)
# Linux-only
try:
@ -51,9 +52,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
stats_init_value=[])
super(Plugin, self).__init__(args=args, config=config, stats_init_value=[])
# We want to display the stat in the curse interface
self.display_curse = True
@ -114,7 +113,7 @@ class Plugin(GlancesPlugin):
'signal': wifi_cell.signal,
'quality': wifi_cell.quality,
'encrypted': wifi_cell.encrypted,
'encryption_type': wifi_cell.encryption_type if wifi_cell.encrypted else None
'encryption_type': wifi_cell.encryption_type if wifi_cell.encrypted else None,
}
# Add the hotspot to the list
stats.append(hotspot)
@ -160,7 +159,9 @@ class Plugin(GlancesPlugin):
# Alert on signal thresholds
for i in self.stats:
self.views[i[self.get_key()]]['signal']['decoration'] = self.get_alert(i['signal'])
self.views[i[self.get_key()]]['quality']['decoration'] = self.views[i[self.get_key()]]['signal']['decoration']
self.views[i[self.get_key()]]['quality']['decoration'] = self.views[i[self.get_key()]]['signal'][
'decoration'
]
def msg_curse(self, args=None, max_width=None):
"""Return the dict to display in the curse interface."""
@ -195,16 +196,13 @@ class Plugin(GlancesPlugin):
hotspot_name += ' {}'.format(i['encryption_type'])
# Cut hotspot_name if it is too long
if len(hotspot_name) > if_name_max_width:
hotspot_name = '_' + hotspot_name[-if_name_max_width + 1:]
hotspot_name = '_' + hotspot_name[-if_name_max_width + 1 :]
# Add the new hotspot to the message
msg = '{:{width}}'.format(nativestr(hotspot_name),
width=if_name_max_width)
msg = '{:{width}}'.format(nativestr(hotspot_name), width=if_name_max_width)
ret.append(self.curse_add_line(msg))
msg = '{:>7}'.format(i['signal'],
width=if_name_max_width)
ret.append(self.curse_add_line(msg,
self.get_views(item=i[self.get_key()],
key='signal',
option='decoration')))
msg = '{:>7}'.format(i['signal'], width=if_name_max_width)
ret.append(
self.curse_add_line(msg, self.get_views(item=i[self.get_key()], key='signal', option='decoration'))
)
return ret

View File

@ -51,9 +51,7 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
stats_init_value=[])
super(Plugin, self).__init__(args=args, config=config, stats_init_value=[])
# Init the sensor class
try:
@ -120,18 +118,13 @@ class GlancesGrabBat(object):
# 'value': self.battery_percent,
# 'unit': '%'}]
for b in self.bat.stat:
self.bat_list.append({
'label': 'BAT {}'.format(b.path.split('/')[-1]),
'value': b.capacity,
'unit': '%'
})
self.bat_list.append(
{'label': 'BAT {}'.format(b.path.split('/')[-1]), 'value': b.capacity, 'unit': '%'}
)
elif psutil_tag and hasattr(self.bat.sensors_battery(), 'percent'):
# Use psutil to grab the stats
# Give directly the battery percent
self.bat_list = [{
'label': 'Battery',
'value': int(self.bat.sensors_battery().percent),
'unit': '%'}]
self.bat_list = [{'label': 'Battery', 'value': int(self.bat.sensors_battery().percent), 'unit': '%'}]
def get(self):
"""Get the stats."""

View File

@ -35,18 +35,12 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
stats_init_value=[])
super(Plugin, self).__init__(args=args, config=config, stats_init_value=[])
# Init the sensor class
hddtemp_host = self.get_conf_value("host",
default=["127.0.0.1"])[0]
hddtemp_port = int(self.get_conf_value("port",
default="7634"))
self.hddtemp = GlancesGrabHDDTemp(args=args,
host=hddtemp_host,
port=hddtemp_port)
hddtemp_host = self.get_conf_value("host", default=["127.0.0.1"])[0]
hddtemp_port = int(self.get_conf_value("port", default="7634"))
self.hddtemp = GlancesGrabHDDTemp(args=args, host=hddtemp_host, port=hddtemp_port)
# We do not want to display the stat in a dedicated area
# The HDD temp is displayed within the sensors plugin

View File

@ -29,6 +29,7 @@ from glances.globals import BSD
if not BSD:
try:
import netifaces
netifaces_tag = True
except ImportError:
netifaces_tag = False
@ -96,12 +97,10 @@ class GlancesPortsList(object):
# Read optionals configuration keys
# Port is set to 0 by default. 0 mean ICMP check instead of TCP check
new_port['port'] = config.get_value(self._section,
'%s%s' % (postfix, 'port'),
0)
new_port['description'] = config.get_value(self._section,
'%sdescription' % postfix,
default="%s:%s" % (new_port['host'], new_port['port']))
new_port['port'] = config.get_value(self._section, '%s%s' % (postfix, 'port'), 0)
new_port['description'] = config.get_value(
self._section, '%sdescription' % postfix, default="%s:%s" % (new_port['host'], new_port['port'])
)
# Default status
new_port['status'] = None
@ -110,14 +109,10 @@ class GlancesPortsList(object):
new_port['refresh'] = refresh
# Timeout in second
new_port['timeout'] = int(config.get_value(self._section,
'%stimeout' % postfix,
default=timeout))
new_port['timeout'] = int(config.get_value(self._section, '%stimeout' % postfix, default=timeout))
# RTT warning
new_port['rtt_warning'] = config.get_value(self._section,
'%srtt_warning' % postfix,
default=None)
new_port['rtt_warning'] = config.get_value(self._section, '%srtt_warning' % postfix, default=None)
if new_port['rtt_warning'] is not None:
# Convert to second
new_port['rtt_warning'] = int(new_port['rtt_warning']) / 1000.0

View File

@ -28,12 +28,7 @@ from glances.logger import logger
import psutil
# This constant defines the list of available processes sort key
sort_processes_key_list = ['cpu_percent',
'memory_percent',
'username',
'cpu_times',
'io_counters',
'name']
sort_processes_key_list = ['cpu_percent', 'memory_percent', 'username', 'cpu_times', 'io_counters', 'name']
class GlancesProcesses(object):
@ -115,11 +110,7 @@ class GlancesProcesses(object):
def reset_processcount(self):
"""Reset the global process count"""
self.processcount = {'total': 0,
'running': 0,
'sleeping': 0,
'thread': 0,
'pid_max': None}
self.processcount = {'total': 0, 'running': 0, 'sleeping': 0, 'thread': 0, 'pid_max': None}
def update_processcount(self, plist):
"""Update the global process count from the current processes list"""
@ -128,11 +119,9 @@ class GlancesProcesses(object):
# For each key in the processcount dict
# count the number of processes with the same status
for k in iterkeys(self.processcount):
self.processcount[k] = len(list(filter(lambda v: v['status'] is k,
plist)))
self.processcount[k] = len(list(filter(lambda v: v['status'] is k, plist)))
# Compute thread
self.processcount['thread'] = sum(i['num_threads'] for i in plist
if i['num_threads'] is not None)
self.processcount['thread'] = sum(i['num_threads'] for i in plist if i['num_threads'] is not None)
# Compute total
self.processcount['total'] = len(plist)
@ -295,15 +284,17 @@ class GlancesProcesses(object):
is_cached = True
# Build the processes stats list (it is why we need psutil>=5.3.0)
self.processlist = [p.info
for p in psutil.process_iter(attrs=sorted_attrs,
ad_value=None)
# OS-related processes filter
if not (BSD and p.info['name'] == 'idle') and
not (WINDOWS and p.info['name'] == 'System Idle Process') and
not (MACOS and p.info['name'] == 'kernel_task') and
# Kernel threads filter
not (self.no_kernel_threads and LINUX and p.info['gids'].real == 0)]
self.processlist = [
p.info
for p in psutil.process_iter(attrs=sorted_attrs, ad_value=None)
# OS-related processes filter
if not (BSD and p.info['name'] == 'idle')
and not (WINDOWS and p.info['name'] == 'System Idle Process')
and not (MACOS and p.info['name'] == 'kernel_task')
and
# Kernel threads filter
not (self.no_kernel_threads and LINUX and p.info['gids'].real == 0)
]
# Sort the processes list by the current sort_key
self.processlist = sort_stats(self.processlist, sorted_by=self.sort_key, reverse=True)
@ -327,8 +318,7 @@ class GlancesProcesses(object):
extended = {}
try:
top_process = psutil.Process(proc['pid'])
extended_stats = ['cpu_affinity', 'ionice',
'num_ctx_switches']
extended_stats = ['cpu_affinity', 'ionice', 'num_ctx_switches']
if LINUX:
# num_fds only avalable on Unix system (see issue #1351)
extended_stats += ['num_fds']
@ -336,8 +326,7 @@ class GlancesProcesses(object):
extended_stats += ['num_handles']
# Get the extended stats
extended = top_process.as_dict(attrs=extended_stats,
ad_value=None)
extended = top_process.as_dict(attrs=extended_stats, ad_value=None)
if LINUX:
try:
@ -404,8 +393,9 @@ class GlancesProcesses(object):
# Grab cached values (in case of a new incoming process)
if proc['pid'] not in self.processlist_cache:
try:
self.processlist_cache[proc['pid']]= psutil.Process(pid=proc['pid']).as_dict(attrs=cached_attrs,
ad_value=None)
self.processlist_cache[proc['pid']] = psutil.Process(pid=proc['pid']).as_dict(
attrs=cached_attrs, ad_value=None
)
except psutil.NoSuchProcess:
pass
# Add cached value to current stat
@ -415,7 +405,7 @@ class GlancesProcesses(object):
pass
else:
# Save values to cache
self.processlist_cache[proc['pid']] = { cached: proc[cached] for cached in cached_attrs }
self.processlist_cache[proc['pid']] = {cached: proc[cached] for cached in cached_attrs}
# Apply filter
self.processlist = [p for p in self.processlist if not (self._filter.is_filtered(p))]
@ -463,9 +453,7 @@ def weighted(value):
return -float('inf') if value is None else value
def _sort_io_counters(process,
sorted_by='io_counters',
sorted_by_secondary='memory_percent'):
def _sort_io_counters(process, sorted_by='io_counters', sorted_by_secondary='memory_percent'):
"""Specific case for io_counters
:return: Sum of io_r + io_w
@ -473,10 +461,8 @@ def _sort_io_counters(process,
return process[sorted_by][0] - process[sorted_by][2] + process[sorted_by][1] - process[sorted_by][3]
def _sort_cpu_times(process,
sorted_by='cpu_times',
sorted_by_secondary='memory_percent'):
""" Specific case for cpu_times
def _sort_cpu_times(process, sorted_by='cpu_times', sorted_by_secondary='memory_percent'):
"""Specific case for cpu_times
Patch for "Sorting by process time works not as expected #1321"
By default PsUtil only takes user time into account
@ -486,8 +472,7 @@ def _sort_cpu_times(process,
return process[sorted_by][0] + process[sorted_by][1]
def _sort_lambda(sorted_by='cpu_percent',
sorted_by_secondary='memory_percent'):
def _sort_lambda(sorted_by='cpu_percent', sorted_by_secondary='memory_percent'):
"""Return a sort lambda function for the sorted_by key"""
ret = None
if sorted_by == 'io_counters':
@ -497,10 +482,7 @@ def _sort_lambda(sorted_by='cpu_percent',
return ret
def sort_stats(stats,
sorted_by='cpu_percent',
sorted_by_secondary='memory_percent',
reverse=True):
def sort_stats(stats, sorted_by='cpu_percent', sorted_by_secondary='memory_percent', reverse=True):
"""Return the stats (dict) sorted by (sorted_by).
Reverse the sort if reverse is True.
@ -510,8 +492,7 @@ def sort_stats(stats,
return stats
# Check if a specific sort should be done
sort_lambda = _sort_lambda(sorted_by=sorted_by,
sorted_by_secondary=sorted_by_secondary)
sort_lambda = _sort_lambda(sorted_by=sorted_by, sorted_by_secondary=sorted_by_secondary)
if sort_lambda is not None:
# Specific sort
@ -519,19 +500,20 @@ def sort_stats(stats,
stats.sort(key=sort_lambda, reverse=reverse)
except Exception:
# If an error is detected, fallback to cpu_percent
stats.sort(key=lambda process: (weighted(process['cpu_percent']),
weighted(process[sorted_by_secondary])),
reverse=reverse)
stats.sort(
key=lambda process: (weighted(process['cpu_percent']), weighted(process[sorted_by_secondary])),
reverse=reverse,
)
else:
# Standard sort
try:
stats.sort(key=lambda process: (weighted(process[sorted_by]),
weighted(process[sorted_by_secondary])),
reverse=reverse)
stats.sort(
key=lambda process: (weighted(process[sorted_by]), weighted(process[sorted_by_secondary])),
reverse=reverse,
)
except (KeyError, TypeError):
# Fallback to name
stats.sort(key=lambda process: process['name'] if process['name'] is not None else '~',
reverse=False)
stats.sort(key=lambda process: process['name'] if process['name'] is not None else '~', reverse=False)
return stats

View File

@ -60,11 +60,7 @@ def __secure_popen(cmd):
for sub_cmd in cmd.split('|'):
# Split by space ' '
sub_cmd_split = [i for i in sub_cmd.split(' ') if i]
p = Popen(sub_cmd_split,
shell=False,
stdin=sub_cmd_stdin,
stdout=PIPE,
stderr=PIPE)
p = Popen(sub_cmd_split, shell=False, stdin=sub_cmd_stdin, stdout=PIPE, stderr=PIPE)
if p_last is not None:
# Allow p_last to receive a SIGPIPE if p exits.
p_last.stdout.close()

View File

@ -36,7 +36,7 @@ class GlancesXMLRPCHandler(SimpleXMLRPCRequestHandler, object):
"""Main XML-RPC handler."""
rpc_paths = ('/RPC2', )
rpc_paths = ('/RPC2',)
def end_headers(self):
# Hack to add a specific header
@ -76,6 +76,7 @@ class GlancesXMLRPCHandler(SimpleXMLRPCRequestHandler, object):
# Check username and password in the dictionary
if username in self.server.user_dict:
from glances.password import GlancesPassword
pwd = GlancesPassword()
return pwd.check_password(self.server.user_dict[username], password)
else:
@ -102,8 +103,7 @@ class GlancesXMLRPCServer(SimpleXMLRPCServer, object):
finished = False
def __init__(self, bind_address, bind_port=61209,
requestHandler=GlancesXMLRPCHandler):
def __init__(self, bind_address, bind_port=61209, requestHandler=GlancesXMLRPCHandler):
self.bind_address = bind_address
self.bind_port = bind_port
@ -130,9 +130,7 @@ class GlancesInstance(object):
"""All the methods of this class are published as XML-RPC methods."""
def __init__(self,
config=None,
args=None):
def __init__(self, config=None, args=None):
# Init stats
self.stats = GlancesStatsServer(config=config, args=args)
@ -197,10 +195,7 @@ class GlancesServer(object):
"""This class creates and manages the TCP server."""
def __init__(self,
requestHandler=GlancesXMLRPCHandler,
config=None,
args=None):
def __init__(self, requestHandler=GlancesXMLRPCHandler, config=None, args=None):
# Args
self.args = args

View File

@ -33,8 +33,7 @@ class GlancesSNMPClient(object):
"""SNMP client class (based on pysnmp library)."""
def __init__(self, host='localhost', port=161, version='2c',
community='public', user='private', auth=''):
def __init__(self, host='localhost', port=161, version='2c', community='public', user='private', auth=''):
super(GlancesSNMPClient, self).__init__()
self.cmdGen = cmdgen.CommandGenerator()
@ -78,15 +77,11 @@ class GlancesSNMPClient(object):
"""
if self.version == '3':
errorIndication, errorStatus, errorIndex, varBinds = self.cmdGen.getCmd(
cmdgen.UsmUserData(self.user, self.auth),
cmdgen.UdpTransportTarget((self.host, self.port)),
*oid
cmdgen.UsmUserData(self.user, self.auth), cmdgen.UdpTransportTarget((self.host, self.port)), *oid
)
else:
errorIndication, errorStatus, errorIndex, varBinds = self.cmdGen.getCmd(
cmdgen.CommunityData(self.community),
cmdgen.UdpTransportTarget((self.host, self.port)),
*oid
cmdgen.CommunityData(self.community), cmdgen.UdpTransportTarget((self.host, self.port)), *oid
)
return self.__get_result__(errorIndication, errorStatus, errorIndex, varBinds)

View File

@ -116,10 +116,8 @@ class GlancesStandalone(object):
def display_modules_list(self):
"""Display modules list"""
print("Plugins list: {}".format(
', '.join(sorted(self.stats.getPluginsList(enable=False)))))
print("Exporters list: {}".format(
', '.join(sorted(self.stats.getExportsList(enable=False)))))
print("Plugins list: {}".format(', '.join(sorted(self.stats.getPluginsList(enable=False)))))
print("Exporters list: {}".format(', '.join(sorted(self.stats.getExportsList(enable=False)))))
def serve_issue(self):
"""Special mode for the --issue option
@ -184,6 +182,9 @@ class GlancesStandalone(object):
# Check Glances version versus PyPI one
if self.outdated.is_outdated():
print("You are using Glances version {}, however version {} is available.".format(
self.outdated.installed_version(), self.outdated.latest_version()))
print(
"You are using Glances version {}, however version {} is available.".format(
self.outdated.installed_version(), self.outdated.latest_version()
)
)
print("You should consider upgrading using: pip install --upgrade glances")

View File

@ -58,7 +58,7 @@ class GlancesStats(object):
# Check if the attribute starts with 'get'
if item.startswith('getViews'):
# Get the plugin name
plugname = item[len('getViews'):].lower()
plugname = item[len('getViews') :].lower()
# Get the plugin instance
plugin = self._plugins[plugname]
if hasattr(plugin, 'get_json_views'):
@ -69,7 +69,7 @@ class GlancesStats(object):
raise AttributeError(item)
elif item.startswith('get'):
# Get the plugin name
plugname = item[len('get'):].lower()
plugname = item[len('get') :].lower()
# Get the plugin instance
plugin = self._plugins[plugname]
if hasattr(plugin, 'get_stats'):
@ -107,7 +107,7 @@ class GlancesStats(object):
# The key is the plugin name
# for example, the file glances_xxx.py
# generate self._plugins_list["xxx"] = ...
name = plugin_script[len(self.header):-3].lower()
name = plugin_script[len(self.header) : -3].lower()
# Loaf the plugin class
try:
@ -122,29 +122,21 @@ class GlancesStats(object):
logger.error(traceback.format_exc())
# Disable the plugin
if args is not None:
setattr(args,
'disable_' + name,
False)
setattr(args, 'disable_' + name, False)
else:
# Set the disable_<name> to False by default
if args is not None:
setattr(args,
'disable_' + name,
getattr(args, 'disable_' + name, False))
setattr(args, 'disable_' + name, getattr(args, 'disable_' + name, False))
def load_plugins(self, args=None):
"""Load all plugins in the 'plugins' folder."""
start_duration = Counter()
for item in os.listdir(plugins_path):
if (item.startswith(self.header) and
item.endswith(".py") and
item != (self.header + "plugin.py")):
if item.startswith(self.header) and item.endswith(".py") and item != (self.header + "plugin.py"):
# Load the plugin
start_duration.reset()
self._load_plugin(os.path.basename(item),
args=args, config=self.config)
logger.debug("Plugin {} started in {} seconds".format(item,
start_duration.get()))
self._load_plugin(os.path.basename(item), args=args, config=self.config)
logger.debug("Plugin {} started in {} seconds".format(item, start_duration.get()))
# Log plugins list
logger.debug("Active plugins list: {}".format(self.getPluginsList()))
@ -157,16 +149,16 @@ class GlancesStats(object):
# Build the export module available list
args_var = vars(locals()['args'])
for item in os.listdir(exports_path):
export_name = os.path.basename(item)[len(header):-3].lower()
if (item.startswith(header) and
item.endswith(".py") and
item != (header + "export.py") and
item != (header + "history.py")):
export_name = os.path.basename(item)[len(header) : -3].lower()
if (
item.startswith(header)
and item.endswith(".py")
and item != (header + "export.py")
and item != (header + "history.py")
):
self._exports_all[export_name] = os.path.basename(item)[:-3]
# Set the disable_<name> to False by default
setattr(self.args,
'export_' + export_name,
getattr(self.args, 'export_' + export_name, False))
setattr(self.args, 'export_' + export_name, getattr(self.args, 'export_' + export_name, False))
# Aim is to check if the export module should be loaded
for export_name in self._exports_all:
@ -177,8 +169,7 @@ class GlancesStats(object):
# The key is the module name
# for example, the file glances_xxx.py
# generate self._exports_list["xxx"] = ...
self._exports[export_name] = export_module.Export(args=args,
config=self.config)
self._exports[export_name] = export_module.Export(args=args, config=self.config)
self._exports_all[export_name] = self._exports[export_name]
# Log plugins list
@ -247,8 +238,7 @@ class GlancesStats(object):
for e in self._exports:
logger.debug("Export stats using the %s module" % e)
thread = threading.Thread(target=self._exports[e].update,
args=(input_stats,))
thread = threading.Thread(target=self._exports[e].update, args=(input_stats,))
thread.start()
return True

View File

@ -49,7 +49,9 @@ class GlancesStatsClient(GlancesStats):
plugin = __import__(header + item)
except ImportError:
# Server plugin can not be imported from the client side
logger.error("Can not import {} plugin. Please upgrade your Glances client/server version.".format(item))
logger.error(
"Can not import {} plugin. Please upgrade your Glances client/server version.".format(item)
)
else:
# Add the plugin to the dictionary
# The key is the plugin name

View File

@ -26,13 +26,15 @@ from glances.compat import iteritems
from glances.logger import logger
# SNMP OID regexp pattern to short system name dict
oid_to_short_system_name = {'.*Linux.*': 'linux',
'.*Darwin.*': 'mac',
'.*BSD.*': 'bsd',
'.*Windows.*': 'windows',
'.*Cisco.*': 'cisco',
'.*VMware ESXi.*': 'esxi',
'.*NetApp.*': 'netapp'}
oid_to_short_system_name = {
'.*Linux.*': 'linux',
'.*Darwin.*': 'mac',
'.*BSD.*': 'bsd',
'.*Windows.*': 'windows',
'.*Cisco.*': 'cisco',
'.*VMware ESXi.*': 'esxi',
'.*NetApp.*': 'netapp',
}
class GlancesStatsClientSNMP(GlancesStats):
@ -60,12 +62,14 @@ class GlancesStatsClientSNMP(GlancesStats):
from glances.snmp import GlancesSNMPClient
# Create an instance of the SNMP client
snmp_client = GlancesSNMPClient(host=self.args.client,
port=self.args.snmp_port,
version=self.args.snmp_version,
community=self.args.snmp_community,
user=self.args.snmp_user,
auth=self.args.snmp_auth)
snmp_client = GlancesSNMPClient(
host=self.args.client,
port=self.args.snmp_port,
version=self.args.snmp_version,
community=self.args.snmp_community,
user=self.args.snmp_user,
auth=self.args.snmp_auth,
)
# If we cannot grab the hostname, then exit...
ret = snmp_client.get_by_oid("1.3.6.1.2.1.1.5.0") != {}

View File

@ -28,9 +28,7 @@ class GlancesStatsServer(GlancesStats):
"""This class stores, updates and gives stats for the server."""
def __init__(self,
config=None,
args=None):
def __init__(self, config=None, args=None):
# Init the stats
super(GlancesStatsServer, self).__init__(config=config, args=args)

View File

@ -56,8 +56,9 @@ class GlancesThresholds(object):
if threshold_description not in self.threshold_list:
return False
else:
self._thresholds[stat_name] = getattr(self.current_module,
'GlancesThreshold' + threshold_description.capitalize())()
self._thresholds[stat_name] = getattr(
self.current_module, 'GlancesThreshold' + threshold_description.capitalize()
)()
return True
@ -93,29 +94,25 @@ class GlancesThresholdOk(_GlancesThreshold):
"""Ok Threshold class"""
_threshold = {'description': 'OK',
'value': 0}
_threshold = {'description': 'OK', 'value': 0}
class GlancesThresholdCareful(_GlancesThreshold):
"""Careful Threshold class"""
_threshold = {'description': 'CAREFUL',
'value': 1}
_threshold = {'description': 'CAREFUL', 'value': 1}
class GlancesThresholdWarning(_GlancesThreshold):
"""Warning Threshold class"""
_threshold = {'description': 'WARNING',
'value': 2}
_threshold = {'description': 'WARNING', 'value': 2}
class GlancesThresholdCritical(_GlancesThreshold):
"""Warning Threshold class"""
_threshold = {'description': 'CRITICAL',
'value': 3}
_threshold = {'description': 'CRITICAL', 'value': 3}

View File

@ -65,15 +65,16 @@ class GlancesWebList(object):
continue
url_parse = urlparse(new_web['url'])
if not bool(url_parse.scheme) or not bool(url_parse.netloc):
logger.error('Bad URL (%s) in the [%s] section of configuration file.' % (new_web['url'],
self._section))
logger.error(
'Bad URL (%s) in the [%s] section of configuration file.' % (new_web['url'], self._section)
)
continue
# Read optionals configuration keys
# Default description is the URL without the http://
new_web['description'] = config.get_value(self._section,
'%sdescription' % postfix,
default="%s" % url_parse.netloc)
new_web['description'] = config.get_value(
self._section, '%sdescription' % postfix, default="%s" % url_parse.netloc
)
# Default status
new_web['status'] = None
@ -83,14 +84,10 @@ class GlancesWebList(object):
new_web['refresh'] = refresh
# Timeout in second
new_web['timeout'] = int(config.get_value(self._section,
'%stimeout' % postfix,
default=timeout))
new_web['timeout'] = int(config.get_value(self._section, '%stimeout' % postfix, default=timeout))
# RTT warning
new_web['rtt_warning'] = config.get_value(self._section,
'%srtt_warning' % postfix,
default=None)
new_web['rtt_warning'] = config.get_value(self._section, '%srtt_warning' % postfix, default=None)
if new_web['rtt_warning'] is not None:
# Convert to second
new_web['rtt_warning'] = int(new_web['rtt_warning']) / 1000.0
@ -99,23 +96,16 @@ class GlancesWebList(object):
new_web['indice'] = 'web_' + str(i)
# ssl_verify
new_web['ssl_verify'] = config.get_value(self._section,
'%sssl_verify' % postfix,
default=True)
new_web['ssl_verify'] = config.get_value(self._section, '%sssl_verify' % postfix, default=True)
# Proxy
http_proxy = config.get_value(self._section,
'%shttp_proxy' % postfix,
default=None)
http_proxy = config.get_value(self._section, '%shttp_proxy' % postfix, default=None)
https_proxy = config.get_value(self._section,
'%shttps_proxy' % postfix,
default=None)
https_proxy = config.get_value(self._section, '%shttps_proxy' % postfix, default=None)
if https_proxy is None and http_proxy is None:
new_web['proxies'] = None
else:
new_web['proxies'] = {'http' : http_proxy,
'https' : https_proxy }
new_web['proxies'] = {'http': http_proxy, 'https': https_proxy}
# Add the server to the list
logger.debug("Add Web URL %s to the static list" % new_web['url'])

4
pyproject.toml Normal file
View File

@ -0,0 +1,4 @@
[tool.black]
line-length = 120
skip-string-normalization = true
exclude = '\./glances/outputs/static/*'