mirror of
https://github.com/nicolargo/glances.git
synced 2025-01-03 15:15:02 +03:00
Merge branch 'develop' into fix-autodiscovery-failure
This commit is contained in:
commit
6a1311c42c
4
Makefile
4
Makefile
@ -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
|
||||
|
@ -1 +1,2 @@
|
||||
py-spy
|
||||
py-spy
|
||||
black
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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])
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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))
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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']:
|
||||
|
@ -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):
|
||||
|
@ -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"},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
551
glances/main.py
551
glances/main.py
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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...
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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]])
|
||||
|
@ -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
|
||||
|
@ -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."""
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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=' '))
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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']))
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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=' '))
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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."""
|
||||
|
@ -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):
|
||||
|
@ -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']))
|
||||
|
@ -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
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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."""
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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") != {}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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
4
pyproject.toml
Normal file
@ -0,0 +1,4 @@
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
skip-string-normalization = true
|
||||
exclude = '\./glances/outputs/static/*'
|
Loading…
Reference in New Issue
Block a user