提交 5c0540ee 编写于 作者: N nicolargo

Merge branch 'fieldsdescription' into develop

......@@ -30,7 +30,8 @@ test: venv
./venv/bin/python ./unitest-xmlrpc.py
docs: venv-dev
cd docs && ./build.sh
./venv/bin/python -m glances -C ./conf/glances.conf --api-doc > ./docs/api.rst
cd docs && ./build.sh && cd ..
docs-server: docs
(sleep 2 && sensible-browser "http://localhost:$(PORT)") &
......@@ -48,6 +49,9 @@ run-debug: venv
run-webserver: venv
./venv/bin/python -m glances -C ./conf/glances.conf -w
run-restapiserver: venv
./venv/bin/python -m glances -C ./conf/glances.conf -w --disable-webui
run-server: venv
./venv/bin/python -m glances -C ./conf/glances.conf -s
......
此差异已折叠。
.\" Man page generated from reStructuredText.
.
.TH "GLANCES" "1" "Jul 11, 2021" "3.2.2_beta0" "Glances"
.TH "GLANCES" "1" "Jul 17, 2021" "3.2.2_beta0" "Glances"
.SH NAME
glances \- An eye on your system
.
......
......@@ -110,7 +110,7 @@ def start(config, args):
# Start the main loop
logger.debug("Glances started in {} seconds".format(start_duration.get()))
if args.stdout_issue:
if args.stdout_issue or args.stdout_apidoc:
# Serve once for issue/test mode
mode.serve_issue()
else:
......
......@@ -230,6 +230,8 @@ Examples of use:
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)')
......
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
# Copyright (C) 2021 Nicolargo <nicolas@nicolargo.com>
#
# Glances is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Glances is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Fields description interface class."""
from pprint import pformat
import json
import time
from glances.logger import logger
from glances.compat import iteritems
API_URL = "http://localhost:61208/api/3"
APIDOC_HEADER = """\
.. _api:
API (Restfull/JSON) documentation
=================================
The Glances Restfull/API server could be ran using the following command line:
.. code-block:: bash
# glances -w --disable-webui
Note: Change request URL api/3 by api/2 if you use Glances 2.x.
"""
def indent_stat(stat, indent=' '):
# Indent stats to pretty print it
if isinstance(stat, list) and len(stat) > 1 and isinstance(stat[0], dict):
# Only display two first items
return indent + pformat(stat[0:2]).replace('\n', '\n' + indent)
else:
return indent + pformat(stat).replace('\n', '\n' + indent)
def print_plugins_list(stat):
sub_title = 'GET plugins list'
print(sub_title)
print('-' * len(sub_title))
print('')
print('Get the plugins list::')
print('')
print(' # curl {}/pluginslist'.format(API_URL))
print(indent_stat(stat))
print('')
def print_plugin_export(plugin, stat_export):
sub_title = 'GET {}'.format(plugin)
print(sub_title)
print('-' * len(sub_title))
print('')
print('Get plugin stats::')
print('')
print(' # curl {}/{}'.format(API_URL, plugin))
print(indent_stat(stat_export))
print('')
def print_plugin_description(plugin, stat):
if stat.fields_description:
# For each plugins with a description
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('')
else:
logger.error('No fields_description variable defined for plugin {}'.format(plugin))
def print_plugin_item_value(plugin, stat, stat_export):
item = None
value = None
if isinstance(stat_export, dict):
item = list(stat_export.keys())[0]
value = None
elif isinstance(stat_export, list) and len(stat_export) > 0 and isinstance(stat_export[0], dict):
if 'key' in stat_export[0]:
item = stat_export[0]['key']
else:
item = list(stat_export[0].keys())[0]
if item and stat.get_stats_item(item):
stat_item = json.loads(stat.get_stats_item(item))
if isinstance(stat_item[item], list):
value = stat_item[item][0]
else:
value = stat_item[item]
print('Get a specific field::')
print('')
print(' # curl {}/{}/{}'.format(API_URL, plugin, item))
print(indent_stat(stat_item))
print('')
if item and value and stat.get_stats_value(item, value):
print('Get a specific item when field matchs the given value::')
print('')
print(' # curl {}/{}/{}/{}'.format(API_URL, plugin, item, value))
print(indent_stat(json.loads(stat.get_stats_value(item, value))))
print('')
def print_all():
sub_title = 'GET all stats'
print(sub_title)
print('-' * len(sub_title))
print('')
print('Get all Glances stats::')
print('')
print(' # curl {}/all'.format(API_URL))
print(' Return a very big dictionnary (avoid using this request, performances will be poor)...')
print('')
def print_history(stats):
time.sleep(1)
stats.update()
time.sleep(1)
stats.update()
sub_title = 'GET stats history'
print(sub_title)
print('-' * len(sub_title))
print('')
print('History of a plugin::')
print('')
print(' # curl {}/cpu/history'.format(API_URL))
print(indent_stat(json.loads(stats.get_plugin('cpu').get_stats_history(nb=3))))
print('')
print('Limit history to last 2 values::')
print('')
print(' # curl {}/cpu/history/2'.format(API_URL))
print(indent_stat(json.loads(stats.get_plugin('cpu').get_stats_history(nb=2))))
print('')
print('History for a specific field::')
print('')
print(' # curl {}/cpu/system/history'.format(API_URL))
print(indent_stat(json.loads(stats.get_plugin('cpu').get_stats_history('system'))))
print('')
print('Limit history for a specific field to last 2 values::')
print('')
print(' # curl {}/cpu/system/history'.format(API_URL))
print(indent_stat(json.loads(stats.get_plugin('cpu').get_stats_history('system', nb=2))))
print('')
def print_limits(stats):
sub_title = 'GET limits (used for thresholds)'
print(sub_title)
print('-' * len(sub_title))
print('')
print('All limits/thresholds::')
print('')
print(' # curl {}/all/limits'.format(API_URL))
print(indent_stat(stats.getAllLimitsAsDict()))
print('')
print('Limits/thresholds for the cpu plugin::')
print('')
print(' # curl {}/cpu/limits'.format(API_URL))
print(indent_stat(stats.get_plugin('cpu').limits))
print('')
class GlancesStdoutApiDoc(object):
"""
This class manages the fields description display.
"""
def __init__(self, config=None, args=None):
# Init
self.config = config
self.args = args
def end(self):
pass
def update(self,
stats,
duration=1):
"""Display issue
"""
# Display header
print(APIDOC_HEADER)
# Display plugins list
print_plugins_list(sorted(stats._plugins))
# Loop over plugins
for plugin in sorted(stats._plugins):
stat = stats.get_plugin(plugin)
stat_export = stat.get_export()
if stat_export is None or stat_export == [] or stat_export == {}:
continue
print_plugin_export(plugin, stat_export)
print_plugin_description(plugin, stat)
print_plugin_item_value(plugin, stat, stat_export)
# Get all stats
print_all()
# History
print_history(stats)
# Limits
print_limits(stats)
# Return True to exit directly (no refresh)
return True
......@@ -23,6 +23,17 @@ from glances.plugins.glances_plugin import GlancesPlugin
import psutil
# Fields description
# - 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 \
physical cores multiplied by the number of threads that can run on each core.',
'unit': 'number'},
}
class Plugin(GlancesPlugin):
"""Glances CPU core plugin.
......@@ -34,7 +45,9 @@ class Plugin(GlancesPlugin):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(Plugin, self).__init__(args=args, config=config)
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
......
......@@ -29,6 +29,50 @@ from glances.plugins.glances_plugin import GlancesPlugin
import psutil
# Fields description
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 \
time spent running code in the Operating System kernel.',
'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 \
operations to complete.',
'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 \
hardware/software interrupts. Time servicing interrupts (hardware + \
software).',
'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 \
CPU while the hypervisor is servicing another virtual processor.',
'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': 'percent'},
'interrupts': {'description': 'number of interrupts per second.',
'unit': 'percent'},
'soft_interrupts': {'description': 'number of software interrupts per second. Always set to \
0 on Windows and SunOS.',
'unit': 'percent'},
'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
......@@ -40,7 +84,7 @@ snmp_oid = {'default': {'user': '1.3.6.1.4.1.2021.11.9.0',
'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',
'nb_log_core': '1.3.6.1.4.1.789.1.2.1.6.0'}}
'cpucore': '1.3.6.1.4.1.789.1.2.1.6.0'}}
# Define the history items list
# - 'name' define the stat identifier
......@@ -64,7 +108,8 @@ class Plugin(GlancesPlugin):
"""Init the CPU plugin."""
super(Plugin, self).__init__(args=args,
config=config,
items_history_list=items_history_list)
items_history_list=items_history_list,
fields_description=fields_description)
# We want to display the stat in the curse interface
self.display_curse = True
......
......@@ -27,6 +27,24 @@ from glances.plugins.glances_core import Plugin as CorePlugin
from glances.plugins.glances_plugin import GlancesPlugin
from glances.logger import logger
# Fields description
fields_description = {
'min1': {'description': 'Average sum of the number of processes \
waiting in the run-queue plus the number currently executing \
over 1 minute.',
'unit': 'number'},
'min5': {'description': 'Average sum of the number of processes \
waiting in the run-queue plus the number currently executing \
over 5 minutes.',
'unit': 'number'},
'min15': {'description': 'Average sum of the number of processes \
waiting in the run-queue plus the number currently executing \
over 15 minutes.',
'unit': 'number'},
'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
......@@ -55,7 +73,8 @@ class Plugin(GlancesPlugin):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
items_history_list=items_history_list)
items_history_list=items_history_list,
fields_description=fields_description)
# We want to display the stat in the curse interface
self.display_curse = True
......
......@@ -25,6 +25,37 @@ from glances.plugins.glances_plugin import GlancesPlugin
import psutil
# Fields description
fields_description = {
'total': {'description': 'Total physical memory available.',
'unit': 'bytes'},
'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'},
'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'},
'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'},
'active': {'description': '*(UNIX)*: memory currently in use or very recently used, and so it is in RAM.',
'unit': 'bytes'},
'inactive': {'description': '*(UNIX)*: memory that is marked as not used.',
'unit': 'bytes'},
'buffers': {'description': '*(Linux, BSD)*: cache for things like file system metadata.',
'unit': 'bytes'},
'cached': {'description': '*(Linux, BSD)*: cache for various things.',
'unit': 'bytes'},
'wired': {'description': '*(BSD, macOS)*: memory that is marked to always stay in RAM. It is never moved to disk.',
'unit': 'bytes'},
'shared': {'description': '*(BSD)*: memory that may be simultaneously accessed by multiple processes.',
'unit': 'bytes'},
}
# SNMP OID
# Total RAM in machine: .1.3.6.1.4.1.2021.4.5.0
# Total RAM used: .1.3.6.1.4.1.2021.4.6.0
......@@ -64,7 +95,8 @@ class Plugin(GlancesPlugin):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
items_history_list=items_history_list)
items_history_list=items_history_list,
fields_description=fields_description)
# We want to display the stat in the curse interface
self.display_curse = True
......
......@@ -20,10 +20,29 @@
"""Swap memory plugin."""
from glances.compat import iterkeys
from glances.timer import getTimeSinceLastUpdate
from glances.plugins.glances_plugin import GlancesPlugin
import psutil
# Fields description
fields_description = {
'total': {'description': 'Total swap memory.',
'unit': 'bytes'},
'used': {'description': 'Used swap memory.',
'unit': 'bytes'},
'free': {'description': 'Free swap memory.',
'unit': 'bytes'},
'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'},
'sout': {'description': 'The number of bytes the system has swapped out from disk (cumulative).',
'unit': 'bytes'},
'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
......@@ -51,7 +70,8 @@ class Plugin(GlancesPlugin):
"""Init the plugin."""
super(Plugin, self).__init__(args=args,
config=config,
items_history_list=items_history_list)
items_history_list=items_history_list,
fields_description=fields_description)
# We want to display the stat in the curse interface
self.display_curse = True
......@@ -78,12 +98,16 @@ class Plugin(GlancesPlugin):
# free: free swap memory in bytes
# percent: the percentage usage
# sin: the number of bytes the system has swapped in from disk (cumulative)
# sout: the number of bytes the system has swapped out from disk
# (cumulative)
# sout: the number of bytes the system has swapped out from disk (cumulative)
for swap in ['total', 'used', 'free', 'percent',
'sin', 'sout']:
if hasattr(sm_stats, swap):
stats[swap] = getattr(sm_stats, swap)
# By storing time data we enable sin/s and sout/s calculations in the
# XML/RPC API, which would otherwise be overly difficult work
# for users of the API
stats['time_since_update'] = getTimeSinceLastUpdate('memswap')
elif self.input_method == 'snmp':
# Update stats using SNMP
if self.short_system_name == 'windows':
......
......@@ -29,6 +29,36 @@ from glances.logger import logger
import psutil
# {'interface_name': 'mpqemubr0-dummy',
# 'alias': None,
# 'time_since_update': 2.081636428833008,
# 'cumulative_rx': 0,
# 'rx': 0, 'cumulative_tx': 0, 'tx': 0, 'cumulative_cx': 0, 'cx': 0,
# 'is_up': False,
# 'speed': 0,
# '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'},
}
# SNMP OID
# http://www.net-snmp.org/docs/mibs/interfaces.html
# Dict key = interface_name
......@@ -56,6 +86,7 @@ class Plugin(GlancesPlugin):
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
......
......@@ -44,7 +44,8 @@ class GlancesPlugin(object):
args=None,
config=None,
items_history_list=None,
stats_init_value={}):
stats_init_value={},
fields_description=None):
"""Init the plugin of plugins class.
All Glances' plugins should inherit from this class. Most of the
......@@ -107,6 +108,9 @@ class GlancesPlugin(object):
# Set the initial refresh time to display stats the first time
self.refresh_timer = Timer(0)
# Init stats description
self.fields_description = fields_description
# Init the stats
self.stats_init_value = stats_init_value
self.stats = None
......@@ -430,7 +434,7 @@ class GlancesPlugin(object):
if not isinstance(self.stats, list):
return None
else:
if value.isdigit():
if not isinstance(value, int) and value.isdigit():
value = int(value)
try:
return self._json_dumps({value: [i for i in self.stats if i[item] == value]})
......
......@@ -363,6 +363,9 @@ class GlancesProcesses(object):
first = False
# /End of extended stats
# PID is the key
proc['key'] = 'pid'
# Time since last update (for disk_io rate computation)
proc['time_since_update'] = time_since_update
......
......@@ -30,6 +30,7 @@ from glances.outputs.glances_curses import GlancesCursesStandalone
from glances.outputs.glances_stdout import GlancesStdout
from glances.outputs.glances_stdout_csv import GlancesStdoutCsv
from glances.outputs.glances_stdout_issue import GlancesStdoutIssue
from glances.outputs.glances_stdout_apidoc import GlancesStdoutApiDoc
from glances.outdated import Outdated
from glances.timer import Counter
......@@ -87,6 +88,10 @@ class GlancesStandalone(object):
logger.info("Issue mode is ON")
# Init screen
self.screen = GlancesStdoutIssue(config=config, args=args)
elif args.stdout_apidoc:
logger.info("Fields descriptions mode is ON")
# Init screen
self.screen = GlancesStdoutApiDoc(config=config, args=args)
elif args.stdout:
logger.info("Stdout mode is ON, following stats will be displayed: {}".format(args.stdout))
# Init screen
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册