mirror of
https://github.com/nicolargo/glances.git
synced 2024-08-17 08:20:25 +03:00
Merge pull request #2779 from nicolargo/ruff-cleanup
chore: ruff cleanup
This commit is contained in:
commit
a99bf33b4b
8
.flake8
8
.flake8
@ -1,8 +0,0 @@
|
||||
[flake8]
|
||||
exclude = .git,__pycache__,docs/,build,dist
|
||||
ignore =
|
||||
W504, B007, B014, B008, B902, Q000,
|
||||
N801, N802, N803, N806, N807, N811, N812, N813, N814, N815, N816, N817, N818
|
||||
|
||||
# lines should not exceed 120 characters
|
||||
max-line-length = 120
|
91
.github/workflows/test.yml
vendored
91
.github/workflows/test.yml
vendored
@ -7,8 +7,32 @@ on:
|
||||
|
||||
jobs:
|
||||
|
||||
source-code-checks:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Check formatting with Ruff
|
||||
uses: chartboost/ruff-action@v1
|
||||
with:
|
||||
args: 'format --check'
|
||||
|
||||
- name: Check linting with Ruff
|
||||
uses: chartboost/ruff-action@v1
|
||||
with:
|
||||
args: 'check'
|
||||
|
||||
- name: Static type check
|
||||
run: |
|
||||
echo "Skipping static type check for the moment, too much error...";
|
||||
# pip install pyright
|
||||
# pyright glances
|
||||
|
||||
|
||||
test-linux:
|
||||
|
||||
needs: source-code-checks
|
||||
# https://github.com/actions/runner-images?tab=readme-ov-file#available-images
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
@ -17,36 +41,22 @@ jobs:
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install flake8
|
||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||
|
||||
- name: Lint with flake8
|
||||
run: |
|
||||
# Stop the build if there are Python syntax errors or undefined names
|
||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=.git,./docs,./glances/outputs/static
|
||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --exclude=.git,./docs,./glances/outputs/static
|
||||
|
||||
- name: Static type check
|
||||
run: |
|
||||
echo "Skipping static type check for the moment, too much error...";
|
||||
# pip install pyright
|
||||
# pyright glances
|
||||
|
||||
- name: Unitary tests
|
||||
run: |
|
||||
python ./unittest-core.py
|
||||
- name: Unitary tests
|
||||
run: |
|
||||
python ./unittest-core.py
|
||||
|
||||
# Error appear with h11, not related to Glances
|
||||
# Should be tested if correction is done
|
||||
@ -84,6 +94,7 @@ jobs:
|
||||
|
||||
test-macos:
|
||||
|
||||
needs: source-code-checks
|
||||
# https://github.com/actions/runner-images?tab=readme-ov-file#available-images
|
||||
runs-on: macos-14
|
||||
strategy:
|
||||
@ -92,22 +103,22 @@ jobs:
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||
|
||||
- name: Unitary tests
|
||||
run: |
|
||||
python ./unittest-core.py
|
||||
- name: Unitary tests
|
||||
run: |
|
||||
python ./unittest-core.py
|
||||
|
||||
# Error when trying to implement #2749
|
||||
# pkg: No packages available to install matching 'py-pip' have been found in the repositories
|
||||
|
22
.pre-commit-config.yaml
Normal file
22
.pre-commit-config.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: check-ast
|
||||
- id: check-docstring-first
|
||||
- id: check-json
|
||||
- id: check-merge-conflict
|
||||
- id: check-shebang-scripts-are-executable
|
||||
- id: check-toml
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
- id: detect-private-key
|
||||
- id: mixed-line-ending
|
||||
- id: requirements-txt-fixer
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.4.4
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
- id: ruff
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
@ -10,7 +10,6 @@ the developers managing and developing this open source project. In return,
|
||||
they should reciprocate that respect in addressing your issue or assessing
|
||||
patches and features.
|
||||
|
||||
|
||||
## Using the issue tracker
|
||||
|
||||
The [issue tracker](https://github.com/nicolargo/glances/issues) is
|
||||
@ -24,7 +23,6 @@ restrictions:
|
||||
* Please **do not** derail or troll issues. Keep the discussion on topic and
|
||||
respect the opinions of others.
|
||||
|
||||
|
||||
## Bug reports
|
||||
|
||||
A bug is a _demonstrable problem_ that is caused by the code in the repository.
|
||||
@ -65,19 +63,17 @@ Example:
|
||||
> causing the bug, and potential solutions (and your opinions on their
|
||||
> merits).
|
||||
>
|
||||
> You can also run Glances in debug mode (-d) and paste/bin the glances.conf file (https://glances.readthedocs.io/en/latest/config.html).
|
||||
> You can also run Glances in debug mode (-d) and paste/bin the glances.conf file (<https://glances.readthedocs.io/en/latest/config.html>).
|
||||
>
|
||||
> Glances 3.2.0 or higher have also a --issue option to run a simple test. Please use it and copy/paste the output.
|
||||
|
||||
|
||||
## Feature requests
|
||||
|
||||
Feature requests are welcome. But take a moment to find out whether your idea
|
||||
fits with the scope and aims of the project. It's up to *you* to make a strong
|
||||
fits with the scope and aims of the project. It's up to _you* to make a strong
|
||||
case to convince the project's developers of the merits of this feature. Please
|
||||
provide as much detail and context as possible.
|
||||
|
||||
|
||||
## Pull requests
|
||||
|
||||
Good pull requests—patches, improvements, new features—are a fantastic
|
||||
@ -133,6 +129,7 @@ included in the project:
|
||||
|
||||
5. Test you code using the Makefile:
|
||||
|
||||
* make format ==> Format your code thanks to the Ruff linter
|
||||
* make run ==> Run Glances
|
||||
* make run-webserver ==> Run a Glances Web Server
|
||||
* make test ==> Run unit tests
|
||||
|
12
Makefile
12
Makefile
@ -52,6 +52,7 @@ venv-dev-python: ## Install Python 3 venv
|
||||
venv-dev: venv-python ## Install Python 3 dev dependencies
|
||||
./venv-dev/bin/pip install -r dev-requirements.txt
|
||||
./venv-dev/bin/pip install -r doc-requirements.txt
|
||||
./venv-dev/bin/pre-commit install --hook-type pre-commit
|
||||
|
||||
venv-dev-upgrade: ## Upgrade Python 3 dev dependencies
|
||||
./venv-dev/bin/pip install --upgrade pip
|
||||
@ -86,15 +87,10 @@ test-min-with-upgrade: venv-min-upgrade ## Upgrade deps and run unit tests in mi
|
||||
# ===================================================================
|
||||
|
||||
format: ## Format the code
|
||||
@git ls-files 'glances/*.py' | xargs ./venv-dev/bin/python -m autopep8 --in-place --jobs 0 --global-config=.flake8
|
||||
@git ls-files 'glances/*.py' | xargs ./venv-dev/bin/python -m autoflake --in-place --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys --exclude="compat.py,globals.py"
|
||||
./venv-dev/bin/python -m black ./glances --exclude outputs/static
|
||||
./venv-dev/bin/python -m ruff format .
|
||||
|
||||
flake8: ## Run flake8 linter.
|
||||
@git ls-files 'glances/ *.py' | xargs ./venv-dev/bin/python -m flake8 --config=.flake8
|
||||
|
||||
ruff: ## Run Ruff (fastest) linter.
|
||||
./venv-dev/bin/python -m ruff check . --config=./pyproject.toml
|
||||
lint: ## Lint the code.
|
||||
./venv-dev/bin/python -m ruff check . --fix
|
||||
|
||||
codespell: ## Run codespell to fix common misspellings in text files
|
||||
./venv-dev/bin/codespell -S .git,./docs/_build,./Glances.egg-info,./venv*,./glances/outputs,*.svg -L hart,bu,te,statics
|
||||
|
@ -1,17 +1,14 @@
|
||||
py-spy
|
||||
gprof2dot
|
||||
black
|
||||
pyright
|
||||
requirements-parser
|
||||
flake8
|
||||
autopep8
|
||||
autoflake
|
||||
ruff
|
||||
codespell
|
||||
memory-profiler
|
||||
fonttools>=4.43.0 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
gprof2dot
|
||||
matplotlib
|
||||
semgrep
|
||||
setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
memory-profiler
|
||||
numpy>=1.22.2 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
pillow>=10.0.1 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
fonttools>=4.43.0 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
pre-commit
|
||||
py-spy
|
||||
pyright
|
||||
requirements-parser
|
||||
ruff
|
||||
semgrep
|
||||
setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
|
@ -1,5 +1,5 @@
|
||||
reuse
|
||||
setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
sphinx
|
||||
sphinx_rtd_theme
|
||||
ujson
|
||||
reuse
|
||||
setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
|
@ -2,9 +2,9 @@
|
||||
-r requirements.txt
|
||||
|
||||
docker>=6.1.1; python_version >= "3.7"
|
||||
podman; python_version >= "3.6"
|
||||
packaging; python_version >= "3.7"
|
||||
podman; python_version >= "3.6"
|
||||
python-dateutil
|
||||
requests
|
||||
six
|
||||
urllib3
|
||||
requests
|
||||
|
39
docs/conf.py
39
docs/conf.py
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Glances documentation build configuration file, created by
|
||||
# sphinx-quickstart on Tue Mar 1 10:53:59 2016.
|
||||
@ -12,8 +11,8 @@
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
@ -27,7 +26,6 @@ sys.path.insert(0, os.path.abspath('..'))
|
||||
# WARNING: Do not move this import before the sys.path.insert() call.
|
||||
from glances import __version__
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
@ -125,8 +123,7 @@ html_theme = 'sphinx_rtd_theme'
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
html_theme_options = {
|
||||
}
|
||||
html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
# html_theme_path = []
|
||||
@ -166,14 +163,7 @@ html_static_path = ['_static']
|
||||
# html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {
|
||||
'**': [
|
||||
'about.html',
|
||||
'navigation.html',
|
||||
'links.html',
|
||||
'searchbox.html'
|
||||
]
|
||||
}
|
||||
html_sidebars = {'**': ['about.html', 'navigation.html', 'links.html', 'searchbox.html']}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
@ -227,13 +217,10 @@ htmlhelp_basename = 'Glancesdoc'
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
@ -242,8 +229,7 @@ latex_elements = {
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'Glances.tex', 'Glances Documentation',
|
||||
'Nicolas Hennion', 'manual'),
|
||||
(master_doc, 'Glances.tex', 'Glances Documentation', 'Nicolas Hennion', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
@ -271,10 +257,7 @@ latex_documents = [
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('glances', 'glances', 'An eye on your system',
|
||||
'', 1)
|
||||
]
|
||||
man_pages = [('glances', 'glances', 'An eye on your system', '', 1)]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# man_show_urls = False
|
||||
@ -286,9 +269,15 @@ man_pages = [
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'Glances', 'Glances Documentation',
|
||||
author, 'Glances', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
(
|
||||
master_doc,
|
||||
'Glances',
|
||||
'Glances Documentation',
|
||||
author,
|
||||
'Glances',
|
||||
'One line description of project.',
|
||||
'Miscellaneous',
|
||||
),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -11,11 +10,11 @@
|
||||
"""Init the Glances software."""
|
||||
|
||||
# Import system libs
|
||||
import tracemalloc
|
||||
import locale
|
||||
import platform
|
||||
import signal
|
||||
import sys
|
||||
import tracemalloc
|
||||
|
||||
# Global name
|
||||
# Version should start and end with a numerical char
|
||||
@ -44,11 +43,6 @@ try:
|
||||
except locale.Error:
|
||||
print("Warning: Unable to set locale. Expect encoding problems.")
|
||||
|
||||
# Check Python version
|
||||
if sys.version_info < (3, 4):
|
||||
print('Glances requires at least Python 3.4 to run.')
|
||||
sys.exit(1)
|
||||
|
||||
# Check psutil version
|
||||
psutil_min_version = (5, 3, 0)
|
||||
psutil_version_info = tuple([int(num) for num in psutil_version.split('.')])
|
||||
@ -56,11 +50,12 @@ if psutil_version_info < psutil_min_version:
|
||||
print('psutil 5.3.0 or higher is needed. Glances cannot start.')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# Trac malloc is only available on Python 3.4 or higher
|
||||
|
||||
|
||||
def __signal_handler(signal, frame):
|
||||
logger.debug("Signal {} catched".format(signal))
|
||||
logger.debug(f"Signal {signal} catched")
|
||||
end()
|
||||
|
||||
|
||||
@ -103,20 +98,16 @@ def start(config, args):
|
||||
from glances.webserver import GlancesWebServer as GlancesMode
|
||||
|
||||
# Init the mode
|
||||
logger.info("Start {} mode".format(GlancesMode.__name__))
|
||||
logger.info(f"Start {GlancesMode.__name__} mode")
|
||||
mode = GlancesMode(config=config, args=args)
|
||||
|
||||
# Start the main loop
|
||||
logger.debug("Glances started in {} seconds".format(start_duration.get()))
|
||||
logger.debug(f"Glances started in {start_duration.get()} seconds")
|
||||
if args.stop_after:
|
||||
logger.info('Glances will be stopped in ~{} seconds'.format(args.stop_after * args.time))
|
||||
logger.info(f'Glances will be stopped in ~{args.stop_after * args.time} seconds')
|
||||
|
||||
if args.memory_leak:
|
||||
print(
|
||||
'Memory leak detection, please wait ~{} seconds...'.format(
|
||||
args.stop_after * args.time * args.memory_leak * 2
|
||||
)
|
||||
)
|
||||
print(f'Memory leak detection, please wait ~{args.stop_after * args.time * args.memory_leak * 2} seconds...')
|
||||
# First run without dump to fill the memory
|
||||
mode.serve_n(args.stop_after)
|
||||
# Then start the memory-leak loop
|
||||
@ -133,7 +124,7 @@ def start(config, args):
|
||||
snapshot_end = tracemalloc.take_snapshot()
|
||||
snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
|
||||
memory_leak = sum([s.size_diff for s in snapshot_diff])
|
||||
print("Memory consumption: {0:.1f}KB (see log for details)".format(memory_leak / 1000))
|
||||
print(f"Memory consumption: {memory_leak / 1000:.1f}KB (see log for details)")
|
||||
logger.info("Memory consumption (top 5):")
|
||||
for stat in snapshot_diff[:5]:
|
||||
logger.info(stat)
|
||||
@ -165,12 +156,10 @@ def main():
|
||||
signal.signal(sig, __signal_handler)
|
||||
|
||||
# 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(f'Start Glances {__version__}')
|
||||
python_impl = platform.python_implementation()
|
||||
python_ver = platform.python_version()
|
||||
logger.info(f'{python_impl} {python_ver} ({sys.executable}) and psutil {psutil_version} detected')
|
||||
|
||||
# Share global var
|
||||
global core
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Glances - An eye on your system
|
||||
#
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -10,8 +9,8 @@
|
||||
"""Manage on alert actions."""
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.timer import Timer
|
||||
from glances.secure import secure_popen
|
||||
from glances.timer import Timer
|
||||
|
||||
try:
|
||||
import chevron
|
||||
@ -22,7 +21,7 @@ else:
|
||||
chevron_tag = True
|
||||
|
||||
|
||||
class GlancesActions(object):
|
||||
class GlancesActions:
|
||||
"""This class manage action if an alert is reached."""
|
||||
|
||||
def __init__(self, args=None):
|
||||
@ -80,13 +79,13 @@ class GlancesActions(object):
|
||||
else:
|
||||
cmd_full = cmd
|
||||
# Execute the action
|
||||
logger.info("Action triggered for {} ({}): {}".format(stat_name, criticality, cmd_full))
|
||||
logger.info(f"Action triggered for {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(f"Action error for {stat_name} ({criticality}): {e}")
|
||||
else:
|
||||
logger.debug("Action result for {} ({}): {}".format(stat_name, criticality, ret))
|
||||
logger.debug(f"Action result for {stat_name} ({criticality}): {ret}")
|
||||
|
||||
self.set(stat_name, criticality)
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -23,11 +22,11 @@ If the *one_line* var is true then the AMP will be displayed in one line.
|
||||
"""
|
||||
|
||||
from glances.globals import u
|
||||
from glances.timer import Timer
|
||||
from glances.logger import logger
|
||||
from glances.timer import Timer
|
||||
|
||||
|
||||
class GlancesAmp(object):
|
||||
class GlancesAmp:
|
||||
"""Main class for Glances AMP."""
|
||||
|
||||
NAME = '?'
|
||||
@ -38,7 +37,7 @@ class GlancesAmp(object):
|
||||
|
||||
def __init__(self, name=None, args=None):
|
||||
"""Init AMP class."""
|
||||
logger.debug("AMP - Init {} version {}".format(self.NAME, self.VERSION))
|
||||
logger.debug(f"AMP - Init {self.NAME} version {self.VERSION}")
|
||||
|
||||
# AMP name (= module name without glances_)
|
||||
if name is None:
|
||||
@ -74,7 +73,7 @@ class GlancesAmp(object):
|
||||
|
||||
amp_section = 'amp_' + self.amp_name
|
||||
if hasattr(config, 'has_section') and config.has_section(amp_section):
|
||||
logger.debug("AMP - {}: Load configuration".format(self.NAME))
|
||||
logger.debug(f"AMP - {self.NAME}: Load configuration")
|
||||
for param, _ in config.items(amp_section):
|
||||
try:
|
||||
self.configs[param] = config.get_float_value(amp_section, param)
|
||||
@ -82,9 +81,9 @@ class GlancesAmp(object):
|
||||
self.configs[param] = config.get_value(amp_section, param).split(',')
|
||||
if len(self.configs[param]) == 1:
|
||||
self.configs[param] = self.configs[param][0]
|
||||
logger.debug("AMP - {}: Load parameter: {} = {}".format(self.NAME, param, self.configs[param]))
|
||||
logger.debug(f"AMP - {self.NAME}: Load parameter: {param} = {self.configs[param]}")
|
||||
else:
|
||||
logger.debug("AMP - {}: Can not find section {} in the configuration file".format(self.NAME, self.amp_name))
|
||||
logger.debug(f"AMP - {self.NAME}: Can not find section {self.amp_name} in the configuration file")
|
||||
return False
|
||||
|
||||
if self.enable():
|
||||
@ -92,13 +91,12 @@ class GlancesAmp(object):
|
||||
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
|
||||
)
|
||||
f"AMP - {self.NAME}: Can not find configuration key {k} in section {self.amp_name} "
|
||||
f"(the AMP will be disabled)"
|
||||
)
|
||||
self.configs['enable'] = 'false'
|
||||
else:
|
||||
logger.debug("AMP - {} is disabled".format(self.NAME))
|
||||
logger.debug(f"AMP - {self.NAME} is disabled")
|
||||
|
||||
# Init the count to 0
|
||||
self.configs['count'] = 0
|
||||
@ -109,16 +107,14 @@ class GlancesAmp(object):
|
||||
"""Generic method to get the item in the AMP configuration"""
|
||||
if key in self.configs:
|
||||
return self.configs[key]
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
def enable(self):
|
||||
"""Return True|False if the AMP is enabled in the configuration file (enable=true|false)."""
|
||||
ret = self.get('enable')
|
||||
if ret is None:
|
||||
return False
|
||||
else:
|
||||
return ret.lower().startswith('true')
|
||||
return ret.lower().startswith('true')
|
||||
|
||||
def regex(self):
|
||||
"""Return regular expression used to identified the current application."""
|
||||
@ -133,8 +129,7 @@ class GlancesAmp(object):
|
||||
ret = self.get('one_line')
|
||||
if ret is None:
|
||||
return False
|
||||
else:
|
||||
return ret.lower().startswith('true')
|
||||
return ret.lower().startswith('true')
|
||||
|
||||
def time_until_refresh(self):
|
||||
"""Return time in seconds until refresh."""
|
||||
@ -193,5 +188,4 @@ class GlancesAmp(object):
|
||||
# Call the children update method
|
||||
if self.should_update():
|
||||
return self.update(process_list)
|
||||
else:
|
||||
return self.result()
|
||||
return self.result()
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -25,11 +24,11 @@ one_line=false
|
||||
command=foo status
|
||||
"""
|
||||
|
||||
from subprocess import check_output, STDOUT, CalledProcessError
|
||||
from subprocess import STDOUT, CalledProcessError, check_output
|
||||
|
||||
from glances.globals import u, to_ascii
|
||||
from glances.logger import logger
|
||||
from glances.amps.amp import GlancesAmp
|
||||
from glances.globals import to_ascii, u
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Amp(GlancesAmp):
|
||||
@ -44,7 +43,7 @@ class Amp(GlancesAmp):
|
||||
def __init__(self, name=None, args=None):
|
||||
"""Init the AMP."""
|
||||
self.NAME = name.capitalize()
|
||||
super(Amp, self).__init__(name=name, args=args)
|
||||
super().__init__(name=name, args=args)
|
||||
|
||||
def update(self, process_list):
|
||||
"""Update the AMP"""
|
||||
@ -54,7 +53,7 @@ class Amp(GlancesAmp):
|
||||
try:
|
||||
res = self.get('command')
|
||||
except OSError as e:
|
||||
logger.debug('{}: Error while executing command ({})'.format(self.NAME, e))
|
||||
logger.debug(f'{self.NAME}: Error while executing command ({e})')
|
||||
return self.result()
|
||||
# No command found, use default message
|
||||
if res is None:
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -46,8 +45,8 @@ status_url=http://localhost/nginx_status
|
||||
|
||||
import requests
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.amps.amp import GlancesAmp
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Amp(GlancesAmp):
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -35,11 +34,11 @@ one_line=true
|
||||
systemctl_cmd=/usr/bin/systemctl --plain
|
||||
"""
|
||||
|
||||
from subprocess import check_output, CalledProcessError
|
||||
from subprocess import CalledProcessError, check_output
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.globals import iteritems, to_ascii
|
||||
from glances.amps.amp import GlancesAmp
|
||||
from glances.globals import iteritems, to_ascii
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Amp(GlancesAmp):
|
||||
@ -62,7 +61,7 @@ class Amp(GlancesAmp):
|
||||
try:
|
||||
res = check_output(self.get('systemctl_cmd').split())
|
||||
except (OSError, CalledProcessError) as e:
|
||||
logger.debug('{}: Error while executing systemctl ({})'.format(self.NAME, e))
|
||||
logger.debug(f'{self.NAME}: Error while executing systemctl ({e})')
|
||||
else:
|
||||
status = {}
|
||||
# For each line
|
||||
@ -79,7 +78,7 @@ class Amp(GlancesAmp):
|
||||
# Build the output (string) message
|
||||
output = 'Services\n'
|
||||
for k, v in iteritems(status):
|
||||
output += '{}: {}\n'.format(k, v)
|
||||
output += f'{k}: {v}\n'
|
||||
self.set_result(output, separator=' ')
|
||||
|
||||
return self.result()
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -34,11 +33,11 @@ one_line=true
|
||||
service_cmd=/usr/bin/service --status-all
|
||||
"""
|
||||
|
||||
from subprocess import check_output, STDOUT
|
||||
from subprocess import STDOUT, check_output
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.globals import iteritems
|
||||
from glances.amps.amp import GlancesAmp
|
||||
from glances.globals import iteritems
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Amp(GlancesAmp):
|
||||
@ -61,7 +60,7 @@ class Amp(GlancesAmp):
|
||||
try:
|
||||
res = check_output(self.get('service_cmd').split(), stderr=STDOUT).decode('utf-8')
|
||||
except OSError as e:
|
||||
logger.debug('{}: Error while executing service ({})'.format(self.NAME, e))
|
||||
logger.debug(f'{self.NAME}: Error while executing service ({e})')
|
||||
else:
|
||||
status = {'running': 0, 'stopped': 0, 'upstart': 0}
|
||||
# For each line
|
||||
@ -79,7 +78,7 @@ class Amp(GlancesAmp):
|
||||
# Build the output (string) message
|
||||
output = 'Services\n'
|
||||
for k, v in iteritems(status):
|
||||
output += '{}: {}\n'.format(k, v)
|
||||
output += f'{k}: {v}\n'
|
||||
self.set_result(output, separator=' ')
|
||||
|
||||
return self.result()
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -13,12 +12,12 @@ import os
|
||||
import re
|
||||
import threading
|
||||
|
||||
from glances.globals import listkeys, iteritems, amps_path
|
||||
from glances.globals import amps_path, iteritems, listkeys
|
||||
from glances.logger import logger
|
||||
from glances.processes import glances_processes
|
||||
|
||||
|
||||
class AmpsList(object):
|
||||
class AmpsList:
|
||||
"""This class describes the optional application monitoring process list.
|
||||
|
||||
The AMP list is a list of processes with a specific monitoring action.
|
||||
@ -57,9 +56,9 @@ class AmpsList(object):
|
||||
try:
|
||||
amp = __import__(os.path.basename(amp_module))
|
||||
except ImportError as e:
|
||||
logger.warning("Missing Python Lib ({}), cannot load AMP {}".format(e, amp_name))
|
||||
logger.warning(f"Missing Python Lib ({e}), cannot load AMP {amp_name}")
|
||||
except Exception as e:
|
||||
logger.warning("Cannot load AMP {} ({})".format(amp_name, e))
|
||||
logger.warning(f"Cannot load AMP {amp_name} ({e})")
|
||||
else:
|
||||
# Add the AMP to the dictionary
|
||||
# The key is the AMP name
|
||||
@ -69,7 +68,7 @@ class AmpsList(object):
|
||||
# Load the AMP configuration
|
||||
self.__amps_dict[amp_name].load_config(self.config)
|
||||
# Log AMPs list
|
||||
logger.debug("AMPs list: {}".format(self.getList()))
|
||||
logger.debug(f"AMPs list: {self.getList()}")
|
||||
|
||||
return True
|
||||
|
||||
@ -108,7 +107,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(f"AMPS: {len(amps_list)} processes {k} detected ({amps_list})")
|
||||
# Call the AMP update method
|
||||
thread = threading.Thread(target=v.update_wrapper, args=[amps_list])
|
||||
thread.start()
|
||||
@ -140,7 +139,7 @@ class AmpsList(object):
|
||||
)
|
||||
|
||||
except (TypeError, KeyError) as e:
|
||||
logger.debug("Can not build AMPS list ({})".format(e))
|
||||
logger.debug(f"Can not build AMPS list ({e})")
|
||||
|
||||
return ret
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -12,7 +11,7 @@
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class GlancesAttribute(object):
|
||||
class GlancesAttribute:
|
||||
def __init__(self, name, description='', history_max_size=None):
|
||||
"""Init the attribute
|
||||
|
||||
@ -66,8 +65,7 @@ class GlancesAttribute(object):
|
||||
def value(self):
|
||||
if self.history_len() > 0:
|
||||
return (self._value[1] - self.history_value()[1]) / (self._value[0] - self.history_value()[0])
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
@value.setter
|
||||
def value(self, new_value):
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -16,7 +15,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 ServiceBrowser, ServiceInfo, Zeroconf
|
||||
from zeroconf import __version__ as __zeroconf_version
|
||||
|
||||
zeroconf_tag = True
|
||||
except ImportError:
|
||||
@ -26,7 +26,7 @@ except ImportError:
|
||||
if zeroconf_tag:
|
||||
zeroconf_min_version = (0, 17, 0)
|
||||
zeroconf_version = tuple([int(num) for num in __zeroconf_version.split('.')])
|
||||
logger.debug("Zeroconf version {} detected.".format(__zeroconf_version))
|
||||
logger.debug(f"Zeroconf version {__zeroconf_version} detected.")
|
||||
if zeroconf_version < zeroconf_min_version:
|
||||
logger.critical("Please install zeroconf 0.17 or higher.")
|
||||
sys.exit(1)
|
||||
@ -34,10 +34,10 @@ if zeroconf_tag:
|
||||
# Global var
|
||||
# Recent versions of the zeroconf python package doesn't like a zeroconf type that ends with '._tcp.'.
|
||||
# Correct issue: zeroconf problem with zeroconf_type = "_%s._tcp." % 'glances' #888
|
||||
zeroconf_type = "_%s._tcp.local." % 'glances'
|
||||
zeroconf_type = "_{}._tcp.local.".format('glances')
|
||||
|
||||
|
||||
class AutoDiscovered(object):
|
||||
class AutoDiscovered:
|
||||
"""Class to manage the auto discovered servers dict."""
|
||||
|
||||
def __init__(self):
|
||||
@ -66,7 +66,7 @@ class AutoDiscovered(object):
|
||||
'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(f"Updated servers list ({len(self._server_list)} servers): {self._server_list}")
|
||||
|
||||
def remove_server(self, name):
|
||||
"""Remove a server from the dict."""
|
||||
@ -74,13 +74,13 @@ class AutoDiscovered(object):
|
||||
if i['key'] == name:
|
||||
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(f"Remove server {name} from the list")
|
||||
logger.debug(f"Updated servers list ({len(self._server_list)} servers): {self._server_list}")
|
||||
except ValueError:
|
||||
logger.error("Cannot remove server %s from the list" % name)
|
||||
logger.error(f"Cannot remove server {name} from the list")
|
||||
|
||||
|
||||
class GlancesAutoDiscoverListener(object):
|
||||
class GlancesAutoDiscoverListener:
|
||||
"""Zeroconf listener for Glances server."""
|
||||
|
||||
def __init__(self):
|
||||
@ -104,7 +104,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(f"Check new Zeroconf server: {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]
|
||||
@ -113,7 +113,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(f"New Glances server detected ({srv_name} from {new_server_ip}:{new_server_port})")
|
||||
else:
|
||||
logger.warning("New Glances server detected, but failed to be get Zeroconf ServiceInfo ")
|
||||
return True
|
||||
@ -121,10 +121,10 @@ 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(f"Glances server {srv_name} removed from the autodetect list")
|
||||
|
||||
|
||||
class GlancesAutoDiscoverServer(object):
|
||||
class GlancesAutoDiscoverServer:
|
||||
"""Implementation of the Zeroconf protocol (server side for the Glances client)."""
|
||||
|
||||
def __init__(self, args=None):
|
||||
@ -132,8 +132,8 @@ class GlancesAutoDiscoverServer(object):
|
||||
logger.info("Init autodiscover mode (Zeroconf protocol)")
|
||||
try:
|
||||
self.zeroconf = Zeroconf()
|
||||
except socket.error as e:
|
||||
logger.error("Cannot start Zeroconf (%s)" % e)
|
||||
except OSError as e:
|
||||
logger.error(f"Cannot start Zeroconf ({e})")
|
||||
self.zeroconf_enable_tag = False
|
||||
else:
|
||||
self.listener = GlancesAutoDiscoverListener()
|
||||
@ -147,8 +147,7 @@ class GlancesAutoDiscoverServer(object):
|
||||
"""Return the current server list (dict of dict)."""
|
||||
if zeroconf_tag and self.zeroconf_enable_tag:
|
||||
return self.listener.get_servers_list()
|
||||
else:
|
||||
return []
|
||||
return []
|
||||
|
||||
def set_server(self, server_pos, key, value):
|
||||
"""Set the key to the value for the server_pos (position in the list)."""
|
||||
@ -160,7 +159,7 @@ class GlancesAutoDiscoverServer(object):
|
||||
self.zeroconf.close()
|
||||
|
||||
|
||||
class GlancesAutoDiscoverClient(object):
|
||||
class GlancesAutoDiscoverClient:
|
||||
"""Implementation of the zeroconf protocol (client side for the Glances server)."""
|
||||
|
||||
def __init__(self, hostname, args=None):
|
||||
@ -168,8 +167,8 @@ class GlancesAutoDiscoverClient(object):
|
||||
zeroconf_bind_address = args.bind_address
|
||||
try:
|
||||
self.zeroconf = Zeroconf()
|
||||
except socket.error as e:
|
||||
logger.error("Cannot start zeroconf: {}".format(e))
|
||||
except OSError as e:
|
||||
logger.error(f"Cannot start zeroconf: {e}")
|
||||
|
||||
# XXX *BSDs: Segmentation fault (core dumped)
|
||||
# -- https://bitbucket.org/al45tair/netifaces/issues/15
|
||||
@ -192,7 +191,7 @@ class GlancesAutoDiscoverClient(object):
|
||||
try:
|
||||
self.info = ServiceInfo(
|
||||
zeroconf_type,
|
||||
'{}:{}.{}'.format(hostname, args.port, zeroconf_type),
|
||||
f'{hostname}:{args.port}.{zeroconf_type}',
|
||||
address=socket.inet_pton(address_family, zeroconf_bind_address),
|
||||
port=args.port,
|
||||
weight=0,
|
||||
@ -205,7 +204,7 @@ class GlancesAutoDiscoverClient(object):
|
||||
# address (only one address) is replaced by addresses (list of addresses)
|
||||
self.info = ServiceInfo(
|
||||
zeroconf_type,
|
||||
name='{}:{}.{}'.format(hostname, args.port, zeroconf_type),
|
||||
name=f'{hostname}:{args.port}.{zeroconf_type}',
|
||||
addresses=[socket.inet_pton(address_family, zeroconf_bind_address)],
|
||||
port=args.port,
|
||||
weight=0,
|
||||
@ -216,9 +215,9 @@ class GlancesAutoDiscoverClient(object):
|
||||
try:
|
||||
self.zeroconf.register_service(self.info)
|
||||
except Exception as e:
|
||||
logger.error("Error while announcing Glances server: {}".format(e))
|
||||
logger.error(f"Error while announcing Glances server: {e}")
|
||||
else:
|
||||
print("Announce the Glances server on the LAN (using {} IP address)".format(zeroconf_bind_address))
|
||||
print(f"Announce the Glances server on the LAN (using {zeroconf_bind_address} IP address)")
|
||||
else:
|
||||
logger.error("Cannot announce Glances server on the network: zeroconf library not found.")
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,16 +8,16 @@
|
||||
|
||||
"""Manage the Glances client."""
|
||||
|
||||
import ujson
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
|
||||
import ujson
|
||||
|
||||
from glances import __version__
|
||||
from glances.globals import Fault, ProtocolError, ServerProxy, Transport
|
||||
from glances.logger import logger
|
||||
from glances.stats_client import GlancesStatsClient
|
||||
from glances.outputs.glances_curses import GlancesCursesClient
|
||||
from glances.stats_client import GlancesStatsClient
|
||||
from glances.timer import Counter
|
||||
|
||||
|
||||
@ -29,7 +28,7 @@ class GlancesClientTransport(Transport):
|
||||
self.timeout = timeout
|
||||
|
||||
|
||||
class GlancesClient(object):
|
||||
class GlancesClient:
|
||||
"""This class creates and manages the TCP client."""
|
||||
|
||||
def __init__(self, config=None, args=None, timeout=7, return_to_browser=False):
|
||||
@ -48,10 +47,12 @@ class GlancesClient(object):
|
||||
|
||||
# Build the URI
|
||||
if args.password != "":
|
||||
self.uri = 'http://{}:{}@{}:{}'.format(args.username, args.password, args.client, args.port)
|
||||
self.uri = f'http://{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))
|
||||
self.uri = f'http://{args.client}:{args.port}'
|
||||
|
||||
# Avoid logging user credentials
|
||||
logger.debug(f"Try to connect to 'http://{args.client}:{args.port}'")
|
||||
|
||||
# Try to connect to the URI
|
||||
transport = GlancesClientTransport()
|
||||
@ -60,7 +61,7 @@ class GlancesClient(object):
|
||||
try:
|
||||
self.client = ServerProxy(self.uri, transport=transport)
|
||||
except Exception as e:
|
||||
self.log_and_exit("Client couldn't create socket {}: {}".format(self.uri, e))
|
||||
self.log_and_exit(f"Client couldn't create socket {self.uri}: {e}")
|
||||
|
||||
@property
|
||||
def quiet(self):
|
||||
@ -93,10 +94,10 @@ class GlancesClient(object):
|
||||
client_version = None
|
||||
try:
|
||||
client_version = self.client.init()
|
||||
except socket.error as err:
|
||||
except OSError as err:
|
||||
# Fallback to SNMP
|
||||
self.client_mode = 'snmp'
|
||||
logger.error("Connection to Glances server failed ({} {})".format(err.errno, err.strerror))
|
||||
logger.error(f"Connection to Glances server failed ({err.errno} {err.strerror})")
|
||||
fall_back_msg = 'No Glances server found. Trying fallback to SNMP...'
|
||||
if not self.return_to_browser:
|
||||
print(fall_back_msg)
|
||||
@ -104,11 +105,11 @@ class GlancesClient(object):
|
||||
logger.info(fall_back_msg)
|
||||
except ProtocolError as err:
|
||||
# Other errors
|
||||
msg = "Connection to server {} failed".format(self.uri)
|
||||
msg = f"Connection to server {self.uri} failed"
|
||||
if err.errcode == 401:
|
||||
msg += " (Bad username/password)"
|
||||
else:
|
||||
msg += " ({} {})".format(err.errcode, err.errmsg)
|
||||
msg += f" ({err.errcode} {err.errmsg})"
|
||||
self.log_and_exit(msg)
|
||||
return False
|
||||
|
||||
@ -118,13 +119,11 @@ class GlancesClient(object):
|
||||
# Init stats
|
||||
self.stats = GlancesStatsClient(config=self.config, args=self.args)
|
||||
self.stats.set_plugins(ujson.loads(self.client.getAllPlugins()))
|
||||
logger.debug("Client version: {} / Server version: {}".format(__version__, client_version))
|
||||
logger.debug(f"Client version: {__version__} / Server version: {client_version}")
|
||||
else:
|
||||
self.log_and_exit(
|
||||
(
|
||||
'Client and server not compatible: '
|
||||
'Client version: {} / Server version: {}'.format(__version__, client_version)
|
||||
)
|
||||
'Client and server not compatible: '
|
||||
f'Client version: {__version__} / Server version: {client_version}'
|
||||
)
|
||||
return False
|
||||
|
||||
@ -180,12 +179,12 @@ class GlancesClient(object):
|
||||
"""Update stats from Glances/SNMP server."""
|
||||
if self.client_mode == 'glances':
|
||||
return self.update_glances()
|
||||
elif self.client_mode == 'snmp':
|
||||
if self.client_mode == 'snmp':
|
||||
return self.update_snmp()
|
||||
else:
|
||||
self.end()
|
||||
logger.critical("Unknown server mode: {}".format(self.client_mode))
|
||||
sys.exit(2)
|
||||
|
||||
self.end()
|
||||
logger.critical(f"Unknown server mode: {self.client_mode}")
|
||||
sys.exit(2)
|
||||
|
||||
def update_glances(self):
|
||||
"""Get stats from Glances server.
|
||||
@ -197,7 +196,7 @@ class GlancesClient(object):
|
||||
# Update the stats
|
||||
try:
|
||||
server_stats = ujson.loads(self.client.getAll())
|
||||
except socket.error:
|
||||
except OSError:
|
||||
# Client cannot get server stats
|
||||
return "Disconnected"
|
||||
except Fault:
|
||||
@ -240,12 +239,12 @@ class GlancesClient(object):
|
||||
# Update the stats
|
||||
counter = Counter()
|
||||
cs_status = self.update()
|
||||
logger.debug('Stats updated duration: {} seconds'.format(counter.get()))
|
||||
logger.debug(f'Stats updated duration: {counter.get()} seconds')
|
||||
|
||||
# Export stats using export modules
|
||||
counter_export = Counter()
|
||||
self.stats.export(self.stats)
|
||||
logger.debug('Stats exported duration: {} seconds'.format(counter_export.get()))
|
||||
logger.debug(f'Stats exported duration: {counter_export.get()} seconds')
|
||||
|
||||
# Patch for issue1326 to avoid < 0 refresh
|
||||
adapted_refresh = self.refresh_time - counter.get()
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,20 +8,20 @@
|
||||
|
||||
"""Manage the Glances client browser (list of Glances server)."""
|
||||
|
||||
import ujson
|
||||
import socket
|
||||
import threading
|
||||
|
||||
from glances.globals import Fault, ProtocolError, ServerProxy
|
||||
import ujson
|
||||
|
||||
from glances.autodiscover import GlancesAutoDiscoverServer
|
||||
from glances.client import GlancesClient, GlancesClientTransport
|
||||
from glances.logger import logger, LOG_FILENAME
|
||||
from glances.globals import Fault, ProtocolError, ServerProxy
|
||||
from glances.logger import LOG_FILENAME, logger
|
||||
from glances.outputs.glances_curses_browser import GlancesCursesBrowser
|
||||
from glances.password_list import GlancesPasswordList as GlancesPassword
|
||||
from glances.static_list import GlancesStaticServer
|
||||
from glances.autodiscover import GlancesAutoDiscoverServer
|
||||
from glances.outputs.glances_curses_browser import GlancesCursesBrowser
|
||||
|
||||
|
||||
class GlancesClientBrowser(object):
|
||||
class GlancesClientBrowser:
|
||||
"""This class creates and manages the TCP client browser (servers list)."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
@ -76,8 +75,7 @@ class GlancesClientBrowser(object):
|
||||
if clear_password is not None:
|
||||
server['password'] = self.password.get_hash(clear_password)
|
||||
return 'http://{}:{}@{}:{}'.format(server['username'], server['password'], server['ip'], server['port'])
|
||||
else:
|
||||
return 'http://{}:{}'.format(server['ip'], server['port'])
|
||||
return 'http://{}:{}'.format(server['ip'], server['port'])
|
||||
|
||||
def __update_stats(self, server):
|
||||
"""Update stats for the given server (picked from the server list)"""
|
||||
@ -92,19 +90,19 @@ class GlancesClientBrowser(object):
|
||||
try:
|
||||
s = ServerProxy(uri, transport=t)
|
||||
except Exception as e:
|
||||
logger.warning("Client browser couldn't create socket ({})".format(e))
|
||||
logger.warning(f"Client browser couldn't create socket ({e})")
|
||||
else:
|
||||
# Mandatory stats
|
||||
try:
|
||||
# CPU%
|
||||
cpu_percent = 100 - ujson.loads(s.getCpu())['idle']
|
||||
server['cpu_percent'] = '{:.1f}'.format(cpu_percent)
|
||||
server['cpu_percent'] = f'{cpu_percent:.1f}'
|
||||
# MEM%
|
||||
server['mem_percent'] = ujson.loads(s.getMem())['percent']
|
||||
# OS (Human Readable name)
|
||||
server['hr_name'] = ujson.loads(s.getSystem())['hr_name']
|
||||
except (socket.error, Fault, KeyError) as e:
|
||||
logger.debug("Error while grabbing stats form server ({})".format(e))
|
||||
except (OSError, Fault, KeyError) as e:
|
||||
logger.debug(f"Error while grabbing stats form server ({e})")
|
||||
server['status'] = 'OFFLINE'
|
||||
except ProtocolError as e:
|
||||
if e.errcode == 401:
|
||||
@ -114,7 +112,7 @@ class GlancesClientBrowser(object):
|
||||
server['status'] = 'PROTECTED'
|
||||
else:
|
||||
server['status'] = 'OFFLINE'
|
||||
logger.debug("Cannot grab stats from server ({} {})".format(e.errcode, e.errmsg))
|
||||
logger.debug(f"Cannot grab stats from server ({e.errcode} {e.errmsg})")
|
||||
else:
|
||||
# Status
|
||||
server['status'] = 'ONLINE'
|
||||
@ -123,16 +121,16 @@ class GlancesClientBrowser(object):
|
||||
try:
|
||||
# LOAD
|
||||
load_min5 = ujson.loads(s.getLoad())['min5']
|
||||
server['load_min5'] = '{:.2f}'.format(load_min5)
|
||||
server['load_min5'] = f'{load_min5:.2f}'
|
||||
except Exception as e:
|
||||
logger.warning("Error while grabbing stats form server ({})".format(e))
|
||||
logger.warning(f"Error while grabbing stats form server ({e})")
|
||||
|
||||
return server
|
||||
|
||||
def __display_server(self, server):
|
||||
"""Connect and display the given server"""
|
||||
# Display the Glances client for the selected server
|
||||
logger.debug("Selected server {}".format(server))
|
||||
logger.debug(f"Selected server {server}")
|
||||
|
||||
# Connection can take time
|
||||
# Display a popup
|
||||
@ -201,7 +199,7 @@ class GlancesClientBrowser(object):
|
||||
# For each server in the list, grab elementary stats (CPU, LOAD, MEM, OS...)
|
||||
thread_list = {}
|
||||
while not self.screen.is_end:
|
||||
logger.debug("Iter through the following server list: {}".format(self.get_servers_list()))
|
||||
logger.debug(f"Iter through the following server list: {self.get_servers_list()}")
|
||||
for v in self.get_servers_list():
|
||||
key = v["key"]
|
||||
thread = thread_list.get(key, None)
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,13 +8,13 @@
|
||||
|
||||
"""Manage the configuration file."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import builtins
|
||||
import multiprocessing
|
||||
from io import open
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from glances.globals import ConfigParser, NoOptionError, NoSectionError, system_exec, BSD, LINUX, MACOS, SUNOS, WINDOWS
|
||||
from glances.globals import BSD, LINUX, MACOS, SUNOS, WINDOWS, ConfigParser, NoOptionError, NoSectionError, system_exec
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
@ -94,7 +93,7 @@ def default_config_dir():
|
||||
return [path]
|
||||
|
||||
|
||||
class Config(object):
|
||||
class Config:
|
||||
"""This class is used to access/read config file, if it exists.
|
||||
|
||||
:param config_dir: the path to search for config file
|
||||
@ -153,15 +152,15 @@ class Config(object):
|
||||
def read(self):
|
||||
"""Read the config file, if it exists. Using defaults otherwise."""
|
||||
for config_file in self.config_file_paths():
|
||||
logger.debug('Search glances.conf file in {}'.format(config_file))
|
||||
logger.debug(f'Search glances.conf file in {config_file}')
|
||||
if os.path.exists(config_file):
|
||||
try:
|
||||
with open(config_file, encoding='utf-8') as f:
|
||||
with builtins.open(config_file, encoding='utf-8') as f:
|
||||
self.parser.read_file(f)
|
||||
self.parser.read(f)
|
||||
logger.info("Read configuration file '{}'".format(config_file))
|
||||
logger.info(f"Read configuration file '{config_file}'")
|
||||
except UnicodeDecodeError as err:
|
||||
logger.error("Can not read configuration file '{}': {}".format(config_file, err))
|
||||
logger.error(f"Can not read configuration file '{config_file}': {err}")
|
||||
sys.exit(1)
|
||||
# Save the loaded configuration file path (issue #374)
|
||||
self._loaded_config_file = config_file
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,13 +8,13 @@
|
||||
|
||||
"""CPU percent stats shared between CPU and Quicklook plugins."""
|
||||
|
||||
import psutil
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.timer import Timer
|
||||
|
||||
import psutil
|
||||
|
||||
|
||||
class CpuPercent(object):
|
||||
class CpuPercent:
|
||||
"""Get and store the CPU percent."""
|
||||
|
||||
def __init__(self, cached_timer_cpu=3):
|
||||
@ -46,8 +45,7 @@ class CpuPercent(object):
|
||||
If percpu, return the percpu stats"""
|
||||
if percpu:
|
||||
return self.__get_percpu()
|
||||
else:
|
||||
return self.__get_cpu()
|
||||
return self.__get_cpu()
|
||||
|
||||
def get_info(self):
|
||||
"""Get additional information about the CPU"""
|
||||
@ -57,7 +55,7 @@ class CpuPercent(object):
|
||||
try:
|
||||
cpu_freq = psutil.cpu_freq()
|
||||
except Exception as e:
|
||||
logger.debug('Can not grab CPU information ({})'.format(e))
|
||||
logger.debug(f'Can not grab CPU information ({e})')
|
||||
else:
|
||||
if hasattr(cpu_freq, 'current'):
|
||||
self.cpu_info['cpu_hz_current'] = cpu_freq.current
|
||||
@ -75,7 +73,7 @@ class CpuPercent(object):
|
||||
# Get the CPU name once from the /proc/cpuinfo file
|
||||
# TODO: Multisystem...
|
||||
try:
|
||||
self.cpu_info['cpu_name'] = open('/proc/cpuinfo', 'r').readlines()[4].split(':')[1].strip()
|
||||
self.cpu_info['cpu_name'] = open('/proc/cpuinfo').readlines()[4].split(':')[1].strip()
|
||||
except (FileNotFoundError, PermissionError, IndexError, KeyError, AttributeError):
|
||||
self.cpu_info['cpu_name'] = 'CPU'
|
||||
return self.cpu_info['cpu_name']
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -10,12 +9,12 @@
|
||||
"""Manage Glances events list (previously Glances logs in Glances < 3.1)."""
|
||||
|
||||
import time
|
||||
from datetime import datetime
|
||||
from dataclasses import asdict
|
||||
from datetime import datetime
|
||||
|
||||
from glances.event import GlancesEvent
|
||||
from glances.processes import glances_processes
|
||||
from glances.thresholds import glances_thresholds
|
||||
from glances.event import GlancesEvent
|
||||
|
||||
# Static decision tree for the global alert message
|
||||
# - msg: Message to be displayed (result of the decision tree)
|
||||
@ -158,11 +157,10 @@ def build_global_message():
|
||||
if themax['weight'] >= themax['thresholds_min']:
|
||||
# Check if the weight is > to the minimal threshold value
|
||||
return themax['msg']
|
||||
else:
|
||||
return tree[0]['msg']
|
||||
return tree[0]['msg']
|
||||
|
||||
|
||||
class GlancesEventsList(object):
|
||||
class GlancesEventsList:
|
||||
"""This class manages events inside the Glances software.
|
||||
GlancesEventsList is a list of GlancesEvent.
|
||||
GlancesEvent is defined in the event.py file
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -13,13 +12,12 @@ I am your father...
|
||||
...for all Glances exports IF.
|
||||
"""
|
||||
|
||||
from glances.globals import json_dumps
|
||||
from glances.globals import NoOptionError, NoSectionError, iteritems, iterkeys
|
||||
from glances.timer import Counter
|
||||
from glances.globals import NoOptionError, NoSectionError, iteritems, iterkeys, json_dumps
|
||||
from glances.logger import logger
|
||||
from glances.timer import Counter
|
||||
|
||||
|
||||
class GlancesExport(object):
|
||||
class GlancesExport:
|
||||
"""Main class for Glances export IF."""
|
||||
|
||||
# List of non exportable plugins
|
||||
@ -40,7 +38,7 @@ class GlancesExport(object):
|
||||
"""Init the export class."""
|
||||
# Export name
|
||||
self.export_name = self.__class__.__module__
|
||||
logger.debug("Init export module %s" % self.export_name)
|
||||
logger.debug(f"Init export module {self.export_name}")
|
||||
|
||||
# Init the config & args
|
||||
self.config = config
|
||||
@ -64,18 +62,16 @@ class GlancesExport(object):
|
||||
counter = Counter()
|
||||
ret = fct(*args, **kw)
|
||||
duration = counter.get()
|
||||
logger.debug(
|
||||
"{} {} {} return {} in {} seconds".format(
|
||||
args[0].__class__.__name__, args[0].__class__.__module__, fct.__name__, ret, duration
|
||||
)
|
||||
)
|
||||
class_name = args[0].__class__.__name__
|
||||
class_module = args[0].__class__.__module__
|
||||
logger.debug(f"{class_name} {class_module} {fct.__name__} return {ret} in {duration} seconds")
|
||||
return ret
|
||||
|
||||
return wrapper
|
||||
|
||||
def exit(self):
|
||||
"""Close the export module."""
|
||||
logger.debug("Finalise export interface %s" % self.export_name)
|
||||
logger.debug(f"Finalise export interface {self.export_name}")
|
||||
|
||||
def load_conf(self, section, mandatories=['host', 'port'], options=None):
|
||||
"""Load the export <section> configuration in the Glances configuration file.
|
||||
@ -96,10 +92,10 @@ class GlancesExport(object):
|
||||
for opt in mandatories:
|
||||
setattr(self, opt, self.config.get_value(section, opt))
|
||||
except NoSectionError:
|
||||
logger.error("No {} configuration found".format(section))
|
||||
logger.error(f"No {section} configuration found")
|
||||
return False
|
||||
except NoOptionError as e:
|
||||
logger.error("Error in the {} configuration ({})".format(section, e))
|
||||
logger.error(f"Error in the {section} configuration ({e})")
|
||||
return False
|
||||
|
||||
# Load options
|
||||
@ -109,8 +105,8 @@ class GlancesExport(object):
|
||||
except NoOptionError:
|
||||
pass
|
||||
|
||||
logger.debug("Load {} from the Glances configuration file".format(section))
|
||||
logger.debug("{} parameters: {}".format(section, {opt: getattr(self, opt) for opt in mandatories + options}))
|
||||
logger.debug(f"Load {section} from the Glances configuration file")
|
||||
logger.debug(f"{section} parameters: {({opt: getattr(self, opt) for opt in mandatories + options})}")
|
||||
|
||||
return True
|
||||
|
||||
@ -120,11 +116,10 @@ class GlancesExport(object):
|
||||
try:
|
||||
ret = item[item['key']]
|
||||
except KeyError:
|
||||
logger.error("No 'key' available in {}".format(item))
|
||||
logger.error(f"No 'key' available in {item}")
|
||||
if isinstance(ret, list):
|
||||
return ret[0]
|
||||
else:
|
||||
return ret
|
||||
return ret
|
||||
|
||||
def parse_tags(self, tags):
|
||||
"""Parse tags into a dict.
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -13,13 +12,13 @@ import sys
|
||||
from datetime import datetime
|
||||
from numbers import Number
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
|
||||
from cassandra import InvalidRequest
|
||||
from cassandra.auth import PlainTextAuthProvider
|
||||
from cassandra.cluster import Cluster
|
||||
from cassandra.util import uuid_from_time
|
||||
from cassandra import InvalidRequest
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
@ -27,7 +26,7 @@ class Export(GlancesExport):
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the Cassandra export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
self.keyspace = None
|
||||
@ -69,53 +68,52 @@ class Export(GlancesExport):
|
||||
)
|
||||
session = cluster.connect()
|
||||
except Exception as e:
|
||||
logger.critical("Cannot connect to Cassandra cluster '%s:%s' (%s)" % (self.host, self.port, e))
|
||||
logger.critical(f"Cannot connect to Cassandra cluster '{self.host}:{self.port}' ({e})")
|
||||
sys.exit(2)
|
||||
|
||||
# Keyspace
|
||||
try:
|
||||
session.set_keyspace(self.keyspace)
|
||||
except InvalidRequest:
|
||||
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,
|
||||
logger.info(f"Create keyspace {self.keyspace} on the Cassandra cluster")
|
||||
c = (
|
||||
f"CREATE KEYSPACE {self.keyspace} WITH "
|
||||
f"replication = {{ 'class': 'SimpleStrategy', 'replication_factor': '{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
|
||||
)
|
||||
f"Stats will be exported to Cassandra cluster {cluster.metadata.cluster_name} "
|
||||
f"({cluster.metadata.all_hosts()}) in keyspace {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
|
||||
f"CREATE TABLE {self.table} "
|
||||
f"(plugin text, time timeuuid, stat map<text,float>, PRIMARY KEY (plugin, time)) "
|
||||
f"WITH CLUSTERING ORDER BY (time DESC)"
|
||||
)
|
||||
except Exception:
|
||||
logger.debug("Cassandra table %s already exist" % self.table)
|
||||
logger.debug(f"Cassandra table {self.table} already exist")
|
||||
|
||||
return cluster, session
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Write the points to the Cassandra cluster."""
|
||||
logger.debug("Export {} stats to Cassandra".format(name))
|
||||
logger.debug(f"Export {name} stats to Cassandra")
|
||||
|
||||
# Remove non number stats and convert all to float (for Boolean)
|
||||
data = {k: float(v) for (k, v) in dict(zip(columns, points)).iteritems() if isinstance(v, Number)}
|
||||
|
||||
# Write input to the Cassandra table
|
||||
try:
|
||||
stmt = "INSERT INTO {} (plugin, time, stat) VALUES (?, ?, ?)".format(self.table)
|
||||
stmt = f"INSERT INTO {self.table} (plugin, time, stat) VALUES (?, ?, ?)"
|
||||
query = self.session.prepare(stmt)
|
||||
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))
|
||||
logger.error(f"Cannot export {name} stats to Cassandra ({e})")
|
||||
|
||||
def exit(self):
|
||||
"""Close the Cassandra export module."""
|
||||
@ -123,4 +121,4 @@ class Export(GlancesExport):
|
||||
self.session.shutdown()
|
||||
self.cluster.shutdown()
|
||||
# Call the father method
|
||||
super(Export, self).exit()
|
||||
super().exit()
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -20,18 +19,18 @@
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
|
||||
import pycouchdb
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
"""This class manages the CouchDB export module."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the CouchDB export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Load the CouchDB configuration file section
|
||||
# User and Password are mandatory with CouchDB 3.0 and higher
|
||||
@ -48,15 +47,15 @@ class Export(GlancesExport):
|
||||
return None
|
||||
|
||||
# @TODO: https
|
||||
server_uri = 'http://{}:{}@{}:{}/'.format(self.user, self.password, self.host, self.port)
|
||||
server_uri = f'http://{self.user}:{self.password}@{self.host}:{self.port}/'
|
||||
|
||||
try:
|
||||
s = pycouchdb.Server(server_uri)
|
||||
except Exception as e:
|
||||
logger.critical("Cannot connect to CouchDB server (%s)" % e)
|
||||
logger.critical(f"Cannot connect to CouchDB server ({e})")
|
||||
sys.exit(2)
|
||||
else:
|
||||
logger.info("Connected to the CouchDB server version %s" % s.info()['version'])
|
||||
logger.info("Connected to the CouchDB server version {}".format(s.info()['version']))
|
||||
|
||||
try:
|
||||
s.database(self.db)
|
||||
@ -64,15 +63,15 @@ class Export(GlancesExport):
|
||||
# Database did not exist
|
||||
# Create it...
|
||||
s.create(self.db)
|
||||
logger.info("Create CouchDB database %s" % self.db)
|
||||
logger.info(f"Create CouchDB database {self.db}")
|
||||
else:
|
||||
logger.info("CouchDB database %s already exist" % self.db)
|
||||
logger.info(f"CouchDB database {self.db} already exist")
|
||||
|
||||
return s.database(self.db)
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Write the points to the CouchDB server."""
|
||||
logger.debug("Export {} stats to CouchDB".format(name))
|
||||
logger.debug(f"Export {name} stats to CouchDB")
|
||||
|
||||
# Create DB input
|
||||
data = dict(zip(columns, points))
|
||||
@ -85,4 +84,4 @@ class Export(GlancesExport):
|
||||
try:
|
||||
self.client.save(data)
|
||||
except Exception as e:
|
||||
logger.error("Cannot export {} stats to CouchDB ({})".format(name, e))
|
||||
logger.error(f"Cannot export {name} stats to CouchDB ({e})")
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,13 +8,13 @@
|
||||
|
||||
"""CSV interface class."""
|
||||
|
||||
import os.path
|
||||
import csv
|
||||
import os.path
|
||||
import sys
|
||||
import time
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
@ -23,7 +22,7 @@ class Export(GlancesExport):
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the CSV export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# CSV file name
|
||||
self.csv_filename = args.export_csv_file
|
||||
@ -42,8 +41,8 @@ class Export(GlancesExport):
|
||||
try:
|
||||
self.csv_file = open_csv_file(self.csv_filename, 'r')
|
||||
reader = csv.reader(self.csv_file)
|
||||
except IOError as e:
|
||||
logger.critical("Cannot open existing CSV file: {}".format(e))
|
||||
except OSError as e:
|
||||
logger.critical(f"Cannot open existing CSV file: {e}")
|
||||
sys.exit(2)
|
||||
self.old_header = next(reader, None)
|
||||
self.csv_file.close()
|
||||
@ -51,11 +50,11 @@ class Export(GlancesExport):
|
||||
try:
|
||||
self.csv_file = open_csv_file(self.csv_filename, file_mode)
|
||||
self.writer = csv.writer(self.csv_file)
|
||||
except IOError as e:
|
||||
logger.critical("Cannot create the CSV file: {}".format(e))
|
||||
except OSError as e:
|
||||
logger.critical(f"Cannot create the CSV file: {e}")
|
||||
sys.exit(2)
|
||||
|
||||
logger.info("Stats exported to CSV file: {}".format(self.csv_filename))
|
||||
logger.info(f"Stats exported to CSV file: {self.csv_filename}")
|
||||
|
||||
self.export_enable = True
|
||||
|
||||
@ -63,7 +62,7 @@ class Export(GlancesExport):
|
||||
|
||||
def exit(self):
|
||||
"""Close the CSV file."""
|
||||
logger.debug("Finalise export interface %s" % self.export_name)
|
||||
logger.debug(f"Finalise export interface {self.export_name}")
|
||||
self.csv_file.close()
|
||||
|
||||
def update(self, stats):
|
||||
@ -95,8 +94,8 @@ class Export(GlancesExport):
|
||||
if self.old_header != csv_header and self.old_header is not None:
|
||||
# Header are different, log an error and do not write data
|
||||
logger.error("Cannot append data to existing CSV file. Headers are different.")
|
||||
logger.debug("Old header: {}".format(self.old_header))
|
||||
logger.debug("New header: {}".format(csv_header))
|
||||
logger.debug(f"Old header: {self.old_header}")
|
||||
logger.debug(f"New header: {csv_header}")
|
||||
else:
|
||||
# Header are equals, ready to write data
|
||||
self.old_header = None
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -12,18 +11,18 @@
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
|
||||
from elasticsearch import Elasticsearch, helpers
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
"""This class manages the ElasticSearch (ES) export module."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the ES export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
self.index = None
|
||||
@ -44,24 +43,22 @@ class Export(GlancesExport):
|
||||
return None
|
||||
|
||||
try:
|
||||
es = Elasticsearch(hosts=['{}://{}:{}'.format(self.scheme, self.host, self.port)])
|
||||
es = Elasticsearch(hosts=[f'{self.scheme}://{self.host}:{self.port}'])
|
||||
except Exception as e:
|
||||
logger.critical(
|
||||
"Cannot connect to ElasticSearch server %s://%s:%s (%s)" % (self.scheme, self.host, self.port, e)
|
||||
)
|
||||
logger.critical(f"Cannot connect to ElasticSearch server {self.scheme}://{self.host}:{self.port} ({e})")
|
||||
sys.exit(2)
|
||||
|
||||
if not es.ping():
|
||||
logger.critical("Cannot ping the ElasticSearch server %s://%s:%s" % (self.scheme, self.host, self.port))
|
||||
logger.critical(f"Cannot ping the ElasticSearch server {self.scheme}://{self.host}:{self.port}")
|
||||
sys.exit(2)
|
||||
else:
|
||||
logger.info("Connected to the ElasticSearch server %s://%s:%s" % (self.scheme, self.host, self.port))
|
||||
logger.info(f"Connected to the ElasticSearch server {self.scheme}://{self.host}:{self.port}")
|
||||
|
||||
return es
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Write the points to the ES server."""
|
||||
logger.debug("Export {} stats to ElasticSearch".format(name))
|
||||
logger.debug(f"Export {name} stats to ElasticSearch")
|
||||
|
||||
# Generate index name with the index field + current day
|
||||
index = '{}-{}'.format(self.index, datetime.utcnow().strftime("%Y.%m.%d"))
|
||||
@ -72,17 +69,17 @@ class Export(GlancesExport):
|
||||
dt_now = datetime.utcnow().isoformat('T')
|
||||
action = {
|
||||
"_index": index,
|
||||
"_id": '{}.{}'.format(name, dt_now),
|
||||
"_type": 'glances-{}'.format(name),
|
||||
"_id": f'{name}.{dt_now}',
|
||||
"_type": f'glances-{name}',
|
||||
"_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(f"Exporting the following object to elasticsearch: {action}")
|
||||
|
||||
# Write input to the ES index
|
||||
try:
|
||||
helpers.bulk(self.client, actions)
|
||||
except Exception as e:
|
||||
logger.error("Cannot export {} stats to ElasticSearch ({})".format(name, e))
|
||||
logger.error(f"Cannot export {name} stats to ElasticSearch ({e})")
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,17 +8,18 @@
|
||||
|
||||
"""Graph exporter interface class."""
|
||||
|
||||
from pygal import DateTimeLine
|
||||
import pygal.style
|
||||
import sys
|
||||
import os
|
||||
import tempfile
|
||||
import errno
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import pygal.style
|
||||
from pygal import DateTimeLine
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.globals import iteritems, time_serie_subsample
|
||||
from glances.logger import logger
|
||||
from glances.timer import Timer
|
||||
from glances.globals import iteritems, time_serie_subsample
|
||||
from glances.exports.export import GlancesExport
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
@ -27,7 +27,7 @@ class Export(GlancesExport):
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__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'])
|
||||
@ -44,19 +44,19 @@ class Export(GlancesExport):
|
||||
os.makedirs(self.path)
|
||||
except OSError as e:
|
||||
if e.errno != errno.EEXIST:
|
||||
logger.critical("Cannot create the Graph output folder {} ({})".format(self.path, e))
|
||||
logger.critical(f"Cannot create the Graph output folder {self.path} ({e})")
|
||||
sys.exit(2)
|
||||
|
||||
# Check if output folder is writeable
|
||||
try:
|
||||
tempfile.TemporaryFile(dir=self.path)
|
||||
except OSError:
|
||||
logger.critical("Graph output folder {} is not writeable".format(self.path))
|
||||
logger.critical(f"Graph output folder {self.path} is not writeable")
|
||||
sys.exit(2)
|
||||
|
||||
logger.info("Graphs will be created in the {} folder".format(self.path))
|
||||
logger.info(f"Graphs will be created in the {self.path} folder")
|
||||
if self.generate_every != 0:
|
||||
logger.info("Graphs will be created automatically every {} seconds".format(self.generate_every))
|
||||
logger.info(f"Graphs will be created automatically every {self.generate_every} seconds")
|
||||
logger.info("or when 'g' key is pressed (only through the CLI interface)")
|
||||
# Start the timer
|
||||
self._timer = Timer(self.generate_every)
|
||||
@ -66,7 +66,7 @@ class Export(GlancesExport):
|
||||
|
||||
def exit(self):
|
||||
"""Close the files."""
|
||||
logger.debug("Finalise export interface %s" % self.export_name)
|
||||
logger.debug(f"Finalise export interface {self.export_name}")
|
||||
|
||||
def update(self, stats):
|
||||
"""Generate Graph file in the output folder."""
|
||||
@ -84,7 +84,7 @@ class Export(GlancesExport):
|
||||
if plugin_name in self.plugins_to_export(stats):
|
||||
self.export(plugin_name, plugin.get_export_history())
|
||||
|
||||
logger.info("Graphs created in {}".format(self.path))
|
||||
logger.info(f"Graphs created in {self.path}")
|
||||
self.args.generate_graph = False
|
||||
|
||||
def export(self, title, data):
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -12,18 +11,18 @@
|
||||
import sys
|
||||
from numbers import Number
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
|
||||
from graphitesend import GraphiteClient
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
"""This class manages the Graphite export module."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the Graphite export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
# N/A
|
||||
@ -74,25 +73,25 @@ class Export(GlancesExport):
|
||||
debug=self.debug,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error("Can not write data to Graphite server: {}:{} ({})".format(self.host, self.port, e))
|
||||
logger.error(f"Can not write data to Graphite server: {self.host}:{self.port} ({e})")
|
||||
client = None
|
||||
else:
|
||||
logger.info("Stats will be exported to Graphite server: {}:{}".format(self.host, self.port))
|
||||
logger.info(f"Stats will be exported to Graphite server: {self.host}:{self.port}")
|
||||
return client
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""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))
|
||||
before_filtering_dict = dict(zip([normalize(f'{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:
|
||||
logger.error("Can not export stats to Graphite (%s)" % e)
|
||||
logger.error(f"Can not export stats to Graphite ({e})")
|
||||
return False
|
||||
else:
|
||||
logger.debug("Export {} stats to Graphite".format(name))
|
||||
logger.debug(f"Export {name} stats to Graphite")
|
||||
return True
|
||||
|
||||
|
||||
@ -100,6 +99,4 @@ def normalize(name):
|
||||
"""Normalize name for the Graphite convention"""
|
||||
|
||||
# Name should not contain space
|
||||
ret = name.replace(' ', '_')
|
||||
|
||||
return ret
|
||||
return name.replace(' ', '_')
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -12,12 +11,12 @@
|
||||
import sys
|
||||
from platform import node
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
|
||||
from influxdb import InfluxDBClient
|
||||
from influxdb.client import InfluxDBClientError
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
FIELD_TO_TAG = ['name', 'cmdline', 'type']
|
||||
|
||||
|
||||
@ -26,7 +25,7 @@ class Export(GlancesExport):
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the InfluxDB export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
self.user = None
|
||||
@ -75,13 +74,13 @@ class Export(GlancesExport):
|
||||
)
|
||||
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))
|
||||
logger.critical(f"Cannot connect to InfluxDB database '{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(f"Stats will be exported to InfluxDB server: {db._baseurl}")
|
||||
else:
|
||||
logger.critical("InfluxDB database '%s' did not exist. Please create it" % self.db)
|
||||
logger.critical(f"InfluxDB database '{self.db}' did not exist. Please create it")
|
||||
sys.exit(2)
|
||||
|
||||
return db
|
||||
@ -106,9 +105,7 @@ class Export(GlancesExport):
|
||||
# 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))
|
||||
k.replace(f'{measurement}.', ''): data_dict[k] for k in data_dict if k.startswith(f'{measurement}.')
|
||||
}
|
||||
else:
|
||||
fields = data_dict
|
||||
@ -155,12 +152,12 @@ class Export(GlancesExport):
|
||||
name = self.prefix + '.' + name
|
||||
# Write input to the InfluxDB database
|
||||
if len(points) == 0:
|
||||
logger.debug("Cannot export empty {} stats to InfluxDB".format(name))
|
||||
logger.debug(f"Cannot export empty {name} stats to InfluxDB")
|
||||
else:
|
||||
try:
|
||||
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))
|
||||
logger.debug(f"Cannot export {name} stats to InfluxDB ({e})")
|
||||
else:
|
||||
logger.debug("Export {} stats to InfluxDB".format(name))
|
||||
logger.debug(f"Export {name} stats to InfluxDB")
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -12,11 +11,11 @@
|
||||
import sys
|
||||
from platform import node
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
|
||||
from influxdb_client import InfluxDBClient, WriteOptions
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
FIELD_TO_TAG = ['name', 'cmdline', 'type']
|
||||
|
||||
|
||||
@ -25,7 +24,7 @@ class Export(GlancesExport):
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the InfluxDB export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
self.org = None
|
||||
@ -58,7 +57,7 @@ class Export(GlancesExport):
|
||||
self.interval = 0
|
||||
# and should be set to the Glances refresh time if the value is 0
|
||||
self.interval = self.interval if self.interval > 0 else self.args.time
|
||||
logger.debug("InfluxDB export interval is set to {} seconds".format(self.interval))
|
||||
logger.debug(f"InfluxDB export interval is set to {self.interval} seconds")
|
||||
|
||||
# The hostname is always add as a tag
|
||||
self.hostname = node().split('.')[0]
|
||||
@ -71,20 +70,18 @@ class Export(GlancesExport):
|
||||
if not self.export_enable:
|
||||
return None
|
||||
|
||||
url = '{}://{}:{}'.format(self.protocol, self.host, self.port)
|
||||
url = f'{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)
|
||||
except Exception as e:
|
||||
logger.critical("Cannot connect to InfluxDB server '%s' (%s)" % (url, e))
|
||||
logger.critical(f"Cannot connect to InfluxDB server '{url}' ({e})")
|
||||
sys.exit(2)
|
||||
else:
|
||||
logger.info(
|
||||
"Connected to InfluxDB server version {} ({})".format(client.health().version, client.health().message)
|
||||
)
|
||||
logger.info(f"Connected to InfluxDB server version {client.health().version} ({client.health().message})")
|
||||
|
||||
# Create the write client
|
||||
write_client = client.write_api(
|
||||
return client.write_api(
|
||||
write_options=WriteOptions(
|
||||
batch_size=500,
|
||||
flush_interval=self.interval * 1000,
|
||||
@ -95,7 +92,6 @@ class Export(GlancesExport):
|
||||
exponential_base=2,
|
||||
)
|
||||
)
|
||||
return write_client
|
||||
|
||||
def _normalize(self, name, columns, points):
|
||||
"""Normalize data for the InfluxDB's data model.
|
||||
@ -117,9 +113,7 @@ class Export(GlancesExport):
|
||||
# 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))
|
||||
k.replace(f'{measurement}.', ''): data_dict[k] for k in data_dict if k.startswith(f'{measurement}.')
|
||||
}
|
||||
else:
|
||||
fields = data_dict
|
||||
@ -166,12 +160,12 @@ class Export(GlancesExport):
|
||||
name = self.prefix + '.' + name
|
||||
# Write input to the InfluxDB database
|
||||
if len(points) == 0:
|
||||
logger.debug("Cannot export empty {} stats to InfluxDB".format(name))
|
||||
logger.debug(f"Cannot export empty {name} stats to InfluxDB")
|
||||
else:
|
||||
try:
|
||||
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))
|
||||
logger.debug(f"Cannot export {name} stats to InfluxDB ({e})")
|
||||
else:
|
||||
logger.debug("Export {} stats to InfluxDB".format(name))
|
||||
logger.debug(f"Export {name} stats to InfluxDB")
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
import sys
|
||||
|
||||
from glances.globals import listkeys, json_dumps
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.globals import json_dumps, listkeys
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
@ -12,7 +12,7 @@ class Export(GlancesExport):
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the JSON export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# JSON file name
|
||||
self.json_filename = args.export_json_file
|
||||
@ -21,11 +21,11 @@ class Export(GlancesExport):
|
||||
try:
|
||||
self.json_file = open(self.json_filename, 'w')
|
||||
self.json_file.close()
|
||||
except IOError as e:
|
||||
logger.critical("Cannot create the JSON file: {}".format(e))
|
||||
except OSError as e:
|
||||
logger.critical(f"Cannot create the JSON file: {e}")
|
||||
sys.exit(2)
|
||||
|
||||
logger.info("Exporting stats to file: {}".format(self.json_filename))
|
||||
logger.info(f"Exporting stats to file: {self.json_filename}")
|
||||
|
||||
self.export_enable = True
|
||||
|
||||
@ -34,7 +34,7 @@ class Export(GlancesExport):
|
||||
|
||||
def exit(self):
|
||||
"""Close the JSON file."""
|
||||
logger.debug("Finalise export interface %s" % self.export_name)
|
||||
logger.debug(f"Finalise export interface {self.export_name}")
|
||||
self.json_file.close()
|
||||
|
||||
def export(self, name, columns, points):
|
||||
@ -44,11 +44,11 @@ class Export(GlancesExport):
|
||||
if name == self.last_exported_list()[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(f"Exporting stats ({listkeys(self.buffer)}) to JSON file ({self.json_filename})")
|
||||
|
||||
# Export stats to JSON file
|
||||
with open(self.json_filename, "w") as self.json_file:
|
||||
self.json_file.write("{}\n".format(json_dumps(self.buffer)))
|
||||
self.json_file.write(f"{json_dumps(self.buffer)}\n")
|
||||
|
||||
# Reset buffer
|
||||
self.buffer = {}
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -11,19 +10,19 @@
|
||||
|
||||
import sys
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.globals import json_dumps
|
||||
from glances.exports.export import GlancesExport
|
||||
|
||||
from kafka import KafkaProducer
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.globals import json_dumps
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
"""This class manages the Kafka export module."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the Kafka export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
self.topic = None
|
||||
@ -48,7 +47,7 @@ class Export(GlancesExport):
|
||||
return None
|
||||
|
||||
# Build the server URI with host and port
|
||||
server_uri = '{}:{}'.format(self.host, self.port)
|
||||
server_uri = f'{self.host}:{self.port}'
|
||||
|
||||
try:
|
||||
s = KafkaProducer(
|
||||
@ -57,16 +56,16 @@ class Export(GlancesExport):
|
||||
compression_type=self.compression,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.critical("Cannot connect to Kafka server %s (%s)" % (server_uri, e))
|
||||
logger.critical(f"Cannot connect to Kafka server {server_uri} ({e})")
|
||||
sys.exit(2)
|
||||
else:
|
||||
logger.info("Connected to the Kafka server %s" % server_uri)
|
||||
logger.info(f"Connected to the Kafka server {server_uri}")
|
||||
|
||||
return s
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Write the points to the kafka server."""
|
||||
logger.debug("Export {} stats to Kafka".format(name))
|
||||
logger.debug(f"Export {name} stats to Kafka")
|
||||
|
||||
# Create DB input
|
||||
data = dict(zip(columns, points))
|
||||
@ -84,7 +83,7 @@ class Export(GlancesExport):
|
||||
value=data,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error("Cannot export {} stats to Kafka ({})".format(name, e))
|
||||
logger.error(f"Cannot export {name} stats to Kafka ({e})")
|
||||
|
||||
def exit(self):
|
||||
"""Close the Kafka export module."""
|
||||
@ -92,4 +91,4 @@ class Export(GlancesExport):
|
||||
self.client.flush()
|
||||
self.client.close()
|
||||
# Call the father method
|
||||
super(Export, self).exit()
|
||||
super().exit()
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -10,12 +9,12 @@
|
||||
"""MongoDB interface class."""
|
||||
|
||||
import sys
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
import pymongo
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
@ -23,7 +22,7 @@ class Export(GlancesExport):
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the MongoDB export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
self.db = None
|
||||
@ -45,13 +44,13 @@ class Export(GlancesExport):
|
||||
if not self.export_enable:
|
||||
return None
|
||||
|
||||
server_uri = 'mongodb://%s:%s@%s:%s' % (quote_plus(self.user), quote_plus(self.password), self.host, self.port)
|
||||
server_uri = f'mongodb://{quote_plus(self.user)}:{quote_plus(self.password)}@{self.host}:{self.port}'
|
||||
|
||||
try:
|
||||
client = pymongo.MongoClient(server_uri)
|
||||
client.admin.command('ping')
|
||||
except Exception as e:
|
||||
logger.critical("Cannot connect to MongoDB server %s:%s (%s)" % (self.host, self.port, e))
|
||||
logger.critical(f"Cannot connect to MongoDB server {self.host}:{self.port} ({e})")
|
||||
sys.exit(2)
|
||||
else:
|
||||
logger.info("Connected to the MongoDB server")
|
||||
@ -64,7 +63,7 @@ class Export(GlancesExport):
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Write the points to the MongoDB server."""
|
||||
logger.debug("Export {} stats to MongoDB".format(name))
|
||||
logger.debug(f"Export {name} stats to MongoDB")
|
||||
|
||||
# Create DB input
|
||||
data = dict(zip(columns, points))
|
||||
@ -73,4 +72,4 @@ class Export(GlancesExport):
|
||||
try:
|
||||
self.database()[name].insert_one(data)
|
||||
except Exception as e:
|
||||
logger.error("Cannot export {} stats to MongoDB ({})".format(name, e))
|
||||
logger.error(f"Cannot export {name} stats to MongoDB ({e})")
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -13,21 +12,21 @@ import socket
|
||||
import string
|
||||
import sys
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.globals import json_dumps
|
||||
|
||||
# Import paho for MQTT
|
||||
import certifi
|
||||
import paho.mqtt.client as paho
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.globals import json_dumps
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
"""This class manages the MQTT export module."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the MQTT export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
self.user = None
|
||||
@ -87,7 +86,7 @@ class Export(GlancesExport):
|
||||
client.loop_start()
|
||||
return client
|
||||
except Exception as e:
|
||||
logger.critical("Connection to MQTT server %s:%s failed with error: %s " % (self.host, self.port, e))
|
||||
logger.critical(f"Connection to MQTT server {self.host}:{self.port} failed with error: {e} ")
|
||||
return None
|
||||
|
||||
def export(self, name, columns, points):
|
||||
@ -109,14 +108,14 @@ class Export(GlancesExport):
|
||||
|
||||
self.client.publish(topic, value)
|
||||
except Exception as e:
|
||||
logger.error("Can not export stats to MQTT server (%s)" % e)
|
||||
logger.error(f"Can not export stats to MQTT server ({e})")
|
||||
elif self.topic_structure == 'per-plugin':
|
||||
try:
|
||||
topic = '/'.join([self.topic, self.devicename, name])
|
||||
sensor_values = dict(zip(columns, points))
|
||||
|
||||
# Build the value to output
|
||||
output_value = dict()
|
||||
output_value = {}
|
||||
for key in sensor_values:
|
||||
split_key = key.split('.')
|
||||
|
||||
@ -124,7 +123,7 @@ class Export(GlancesExport):
|
||||
current_level = output_value
|
||||
for depth in range(len(split_key) - 1):
|
||||
if split_key[depth] not in current_level:
|
||||
current_level[split_key[depth]] = dict()
|
||||
current_level[split_key[depth]] = {}
|
||||
current_level = current_level[split_key[depth]]
|
||||
|
||||
# Add the value
|
||||
@ -133,4 +132,4 @@ class Export(GlancesExport):
|
||||
json_value = json_dumps(output_value)
|
||||
self.client.publish(topic, json_value)
|
||||
except Exception as e:
|
||||
logger.error("Can not export stats to MQTT server (%s)" % e)
|
||||
logger.error(f"Can not export stats to MQTT server ({e})")
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -12,18 +11,18 @@
|
||||
import sys
|
||||
from numbers import Number
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
|
||||
import potsdb
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
"""This class manages the OpenTSDB export module."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the OpenTSDB export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
# N/A
|
||||
@ -52,7 +51,7 @@ class Export(GlancesExport):
|
||||
try:
|
||||
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))
|
||||
logger.critical(f"Cannot connect to OpenTSDB server {self.host}:{self.port} ({e})")
|
||||
sys.exit(2)
|
||||
|
||||
return db
|
||||
@ -62,18 +61,18 @@ class Export(GlancesExport):
|
||||
for i in range(len(columns)):
|
||||
if not isinstance(points[i], Number):
|
||||
continue
|
||||
stat_name = '{}.{}.{}'.format(self.prefix, name, columns[i])
|
||||
stat_name = f'{self.prefix}.{name}.{columns[i]}'
|
||||
stat_value = points[i]
|
||||
tags = self.parse_tags(self.tags)
|
||||
try:
|
||||
self.client.send(stat_name, stat_value, **tags)
|
||||
except Exception as e:
|
||||
logger.error("Can not export stats %s to OpenTSDB (%s)" % (name, e))
|
||||
logger.debug("Export {} stats to OpenTSDB".format(name))
|
||||
logger.error(f"Can not export stats {name} to OpenTSDB ({e})")
|
||||
logger.debug(f"Export {name} stats to OpenTSDB")
|
||||
|
||||
def exit(self):
|
||||
"""Close the OpenTSDB export module."""
|
||||
# Waits for all outstanding metrics to be sent and background thread closes
|
||||
self.client.wait()
|
||||
# Call the father method
|
||||
super(Export, self).exit()
|
||||
super().exit()
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -12,11 +11,11 @@
|
||||
import sys
|
||||
from numbers import Number
|
||||
|
||||
from glances.logger import logger
|
||||
from prometheus_client import Gauge, start_http_server
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.globals import iteritems, listkeys
|
||||
|
||||
from prometheus_client import start_http_server, Gauge
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
@ -26,7 +25,7 @@ class Export(GlancesExport):
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the Prometheus export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Load the Prometheus configuration file section
|
||||
self.export_enable = self.load_conf('prometheus', mandatories=['host', 'port', 'labels'], options=['prefix'])
|
||||
@ -52,14 +51,14 @@ class Export(GlancesExport):
|
||||
try:
|
||||
start_http_server(port=int(self.port), addr=self.host)
|
||||
except Exception as e:
|
||||
logger.critical("Can not start Prometheus exporter on {}:{} ({})".format(self.host, self.port, e))
|
||||
logger.critical(f"Can not start Prometheus exporter on {self.host}:{self.port} ({e})")
|
||||
sys.exit(2)
|
||||
else:
|
||||
logger.info("Start Prometheus exporter on {}:{}".format(self.host, self.port))
|
||||
logger.info(f"Start Prometheus exporter on {self.host}:{self.port}")
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Write the points to the Prometheus exporter using Gauge."""
|
||||
logger.debug("Export {} stats to Prometheus exporter".format(name))
|
||||
logger.debug(f"Export {name} stats to Prometheus exporter")
|
||||
|
||||
# Remove non number stats and convert all to float (for Boolean)
|
||||
data = {k: float(v) for (k, v) in iteritems(dict(zip(columns, points))) if isinstance(v, Number)}
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -14,19 +13,19 @@ import socket
|
||||
import sys
|
||||
from numbers import Number
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
|
||||
# Import pika for RabbitMQ
|
||||
import pika
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
"""This class manages the rabbitMQ export module."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the RabbitMQ export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
self.user = None
|
||||
@ -67,10 +66,9 @@ class Export(GlancesExport):
|
||||
self.protocol + '://' + self.user + ':' + self.password + '@' + self.host + ':' + self.port + '/'
|
||||
)
|
||||
connection = pika.BlockingConnection(parameters)
|
||||
channel = connection.channel()
|
||||
return channel
|
||||
return connection.channel()
|
||||
except Exception as e:
|
||||
logger.critical("Connection to rabbitMQ server %s:%s failed. %s" % (self.host, self.port, e))
|
||||
logger.critical(f"Connection to rabbitMQ server {self.host}:{self.port} failed. {e}")
|
||||
sys.exit(2)
|
||||
|
||||
def export(self, name, columns, points):
|
||||
@ -79,10 +77,10 @@ class Export(GlancesExport):
|
||||
for i in range(len(columns)):
|
||||
if not isinstance(points[i], Number):
|
||||
continue
|
||||
else:
|
||||
data += ", " + columns[i] + "=" + str(points[i])
|
||||
data += ", " + columns[i] + "=" + str(points[i])
|
||||
|
||||
logger.debug(data)
|
||||
try:
|
||||
self.client.basic_publish(exchange='', routing_key=self.queue, body=data)
|
||||
except Exception as e:
|
||||
logger.error("Can not export stats to RabbitMQ (%s)" % e)
|
||||
logger.error(f"Can not export stats to RabbitMQ ({e})")
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,12 +8,11 @@
|
||||
|
||||
"""RESTful interface class."""
|
||||
|
||||
from requests import post
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.globals import listkeys
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
|
||||
from requests import post
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
@ -23,7 +21,7 @@ class Export(GlancesExport):
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the RESTful export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
self.protocol = None
|
||||
@ -46,15 +44,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 = f'{self.protocol}://{self.host}:{self.port}{self.path}'
|
||||
logger.info(f"Stats will be exported to the RESTful endpoint {url}")
|
||||
return url
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Export the stats to the Statsd server."""
|
||||
if name == self.last_exported_list()[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(f"Export stats ({listkeys(self.buffer)}) to RESTful endpoint ({self.client})")
|
||||
# Export stats
|
||||
post(self.client, json=self.buffer, allow_redirects=True)
|
||||
# Reset buffer
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -12,19 +11,19 @@
|
||||
import socket
|
||||
from numbers import Number
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
|
||||
# Import bernhard for Riemann
|
||||
import bernhard
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
"""This class manages the Riemann export module."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the Riemann export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
# N/A
|
||||
@ -48,10 +47,9 @@ class Export(GlancesExport):
|
||||
if not self.export_enable:
|
||||
return None
|
||||
try:
|
||||
client = bernhard.Client(host=self.host, port=self.port)
|
||||
return client
|
||||
return bernhard.Client(host=self.host, port=self.port)
|
||||
except Exception as e:
|
||||
logger.critical("Connection to Riemann failed : %s " % e)
|
||||
logger.critical(f"Connection to Riemann failed : {e} ")
|
||||
return None
|
||||
|
||||
def export(self, name, columns, points):
|
||||
@ -59,10 +57,10 @@ class Export(GlancesExport):
|
||||
for i in range(len(columns)):
|
||||
if not isinstance(points[i], Number):
|
||||
continue
|
||||
else:
|
||||
data = {'host': self.hostname, 'service': name + " " + columns[i], 'metric': points[i]}
|
||||
logger.debug(data)
|
||||
try:
|
||||
self.client.send(data)
|
||||
except Exception as e:
|
||||
logger.error("Cannot export stats to Riemann (%s)" % e)
|
||||
|
||||
data = {'host': self.hostname, 'service': name + " " + columns[i], 'metric': points[i]}
|
||||
logger.debug(data)
|
||||
try:
|
||||
self.client.send(data)
|
||||
except Exception as e:
|
||||
logger.error(f"Cannot export stats to Riemann ({e})")
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -11,18 +10,18 @@
|
||||
|
||||
from numbers import Number
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
|
||||
from statsd import StatsClient
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
"""This class manages the Statsd export module."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the Statsd export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
# N/A
|
||||
@ -46,7 +45,7 @@ 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))
|
||||
logger.info(f"Stats will be exported to StatsD server: {self.host}:{self.port}")
|
||||
return StatsClient(self.host, int(self.port), prefix=self.prefix)
|
||||
|
||||
def export(self, name, columns, points):
|
||||
@ -54,13 +53,13 @@ class Export(GlancesExport):
|
||||
for i in range(len(columns)):
|
||||
if not isinstance(points[i], Number):
|
||||
continue
|
||||
stat_name = '{}.{}'.format(name, columns[i])
|
||||
stat_name = f'{name}.{columns[i]}'
|
||||
stat_value = points[i]
|
||||
try:
|
||||
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))
|
||||
logger.error(f"Can not export stats to Statsd ({e})")
|
||||
logger.debug(f"Export {name} stats to Statsd")
|
||||
|
||||
|
||||
def normalize(name):
|
||||
@ -69,6 +68,4 @@ def normalize(name):
|
||||
# Name should not contain some specials chars (issue #1068)
|
||||
ret = name.replace(':', '')
|
||||
ret = ret.replace('%', '')
|
||||
ret = ret.replace(' ', '_')
|
||||
|
||||
return ret
|
||||
return ret.replace(' ', '_')
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -11,21 +10,20 @@
|
||||
|
||||
import sys
|
||||
|
||||
from glances.globals import b
|
||||
from glances.logger import logger
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.globals import json_dumps
|
||||
|
||||
import zmq
|
||||
from zmq.utils.strtypes import asbytes
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.globals import b, json_dumps
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
"""This class manages the ZeroMQ export module."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the ZeroMQ export IF."""
|
||||
super(Export, self).__init__(config=config, args=args)
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
self.prefix = None
|
||||
@ -47,17 +45,17 @@ class Export(GlancesExport):
|
||||
if not self.export_enable:
|
||||
return None
|
||||
|
||||
server_uri = 'tcp://{}:{}'.format(self.host, self.port)
|
||||
server_uri = f'tcp://{self.host}:{self.port}'
|
||||
|
||||
try:
|
||||
self.context = zmq.Context()
|
||||
publisher = self.context.socket(zmq.PUB)
|
||||
publisher.bind(server_uri)
|
||||
except Exception as e:
|
||||
logger.critical("Cannot connect to ZeroMQ server %s (%s)" % (server_uri, e))
|
||||
logger.critical(f"Cannot connect to ZeroMQ server {server_uri} ({e})")
|
||||
sys.exit(2)
|
||||
else:
|
||||
logger.info("Connected to the ZeroMQ server %s" % server_uri)
|
||||
logger.info(f"Connected to the ZeroMQ server {server_uri}")
|
||||
|
||||
return publisher
|
||||
|
||||
@ -70,7 +68,7 @@ class Export(GlancesExport):
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Write the points to the ZeroMQ server."""
|
||||
logger.debug("Export {} stats to ZeroMQ".format(name))
|
||||
logger.debug(f"Export {name} stats to ZeroMQ")
|
||||
|
||||
# Create DB input
|
||||
data = dict(zip(columns, points))
|
||||
@ -90,6 +88,6 @@ class Export(GlancesExport):
|
||||
try:
|
||||
self.client.send_multipart(message)
|
||||
except Exception as e:
|
||||
logger.error("Cannot export {} stats to ZeroMQ ({})".format(name, e))
|
||||
logger.error(f"Cannot export {name} stats to ZeroMQ ({e})")
|
||||
|
||||
return True
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -12,7 +11,7 @@ import re
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class GlancesFilterList(object):
|
||||
class GlancesFilterList:
|
||||
"""Manage a lis of GlancesFilter objects
|
||||
|
||||
>>> fl = GlancesFilterList()
|
||||
@ -55,7 +54,7 @@ class GlancesFilterList(object):
|
||||
return False
|
||||
|
||||
|
||||
class GlancesFilter(object):
|
||||
class GlancesFilter:
|
||||
"""Allow Glances to filter processes
|
||||
|
||||
>>> f = GlancesFilter()
|
||||
@ -127,9 +126,9 @@ class GlancesFilter(object):
|
||||
# Compute the regular expression
|
||||
try:
|
||||
self._filter_re = re.compile(self.filter)
|
||||
logger.debug("Filter regex compilation OK: {}".format(self.filter))
|
||||
logger.debug(f"Filter regex compilation OK: {self.filter}")
|
||||
except Exception as e:
|
||||
logger.error("Cannot compile filter regex: {} ({})".format(self.filter, e))
|
||||
logger.error(f"Cannot compile filter regex: {self.filter} ({e})")
|
||||
self._filter = None
|
||||
self._filter_re = None
|
||||
self._filter_key = None
|
||||
@ -156,9 +155,9 @@ 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')
|
||||
else:
|
||||
# Apply filter on <key>
|
||||
return self._is_process_filtered(process)
|
||||
|
||||
# Apply filter on <key>
|
||||
return self._is_process_filtered(process)
|
||||
|
||||
def _is_process_filtered(self, process, key=None):
|
||||
"""Return True if the process[key] should be filtered according to the current filter"""
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -8,15 +7,13 @@
|
||||
#
|
||||
|
||||
"""Manage the folder list."""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
from glances.timer import Timer
|
||||
from glances.globals import nativestr, folder_size
|
||||
from glances.globals import folder_size, nativestr
|
||||
from glances.logger import logger
|
||||
from glances.timer import Timer
|
||||
|
||||
|
||||
class FolderList(object):
|
||||
class FolderList:
|
||||
"""This class describes the optional monitored folder list.
|
||||
|
||||
The folder list is a list of 'important' folder to monitor.
|
||||
@ -67,8 +64,7 @@ class FolderList(object):
|
||||
value['path'] = self.config.get_value(section, key + 'path')
|
||||
if value['path'] is None:
|
||||
continue
|
||||
else:
|
||||
value['path'] = nativestr(value['path'])
|
||||
value['path'] = nativestr(value['path'])
|
||||
|
||||
# Optional conf keys
|
||||
# Refresh time
|
||||
|
@ -1,4 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ruff: noqa: F401
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -13,29 +13,27 @@
|
||||
# GLOBAL IMPORTS
|
||||
################
|
||||
|
||||
import errno
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
import ujson
|
||||
from operator import itemgetter, methodcaller
|
||||
import unicodedata
|
||||
import types
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
import re
|
||||
import base64
|
||||
import errno
|
||||
import functools
|
||||
import weakref
|
||||
|
||||
import os
|
||||
import platform
|
||||
import queue
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import weakref
|
||||
from configparser import ConfigParser, NoOptionError, NoSectionError
|
||||
from datetime import datetime
|
||||
from operator import itemgetter, methodcaller
|
||||
from statistics import mean
|
||||
from xmlrpc.client import Fault, ProtocolError, ServerProxy, Transport, Server
|
||||
from xmlrpc.server import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
|
||||
from urllib.request import urlopen, Request
|
||||
from urllib.error import HTTPError, URLError
|
||||
from urllib.parse import urlparse
|
||||
from urllib.request import Request, urlopen
|
||||
from xmlrpc.client import Fault, ProtocolError, Server, ServerProxy, Transport
|
||||
from xmlrpc.server import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
|
||||
|
||||
import ujson
|
||||
|
||||
# Correct issue #1025 by monkey path the xmlrpc lib
|
||||
from defusedxml.xmlrpc import monkey_patch
|
||||
@ -136,10 +134,9 @@ def b(s, errors='replace'):
|
||||
def nativestr(s, errors='replace'):
|
||||
if isinstance(s, text_type):
|
||||
return s
|
||||
elif isinstance(s, (int, float)):
|
||||
if isinstance(s, (int, float)):
|
||||
return s.__str__()
|
||||
else:
|
||||
return s.decode('utf-8', errors=errors)
|
||||
return s.decode('utf-8', errors=errors)
|
||||
|
||||
|
||||
def system_exec(command):
|
||||
@ -147,7 +144,7 @@ def system_exec(command):
|
||||
try:
|
||||
res = subprocess.run(command.split(' '), stdout=subprocess.PIPE).stdout.decode('utf-8')
|
||||
except Exception as e:
|
||||
res = 'ERROR: {}'.format(e)
|
||||
res = f'ERROR: {e}'
|
||||
return res.rstrip()
|
||||
|
||||
|
||||
@ -204,7 +201,7 @@ def is_admin():
|
||||
try:
|
||||
return ctypes.windll.shell32.IsUserAnAdmin()
|
||||
except Exception as e:
|
||||
print("Admin check failed with error: %s" % e)
|
||||
print(f"Admin check failed with error: {e}")
|
||||
traceback.print_exc()
|
||||
return False
|
||||
else:
|
||||
@ -301,7 +298,7 @@ def urlopen_auth(url, username, password):
|
||||
return urlopen(
|
||||
Request(
|
||||
url,
|
||||
headers={'Authorization': 'Basic ' + base64.b64encode(('%s:%s' % (username, password)).encode()).decode()},
|
||||
headers={'Authorization': 'Basic ' + base64.b64encode((f'{username}:{password}').encode()).decode()},
|
||||
)
|
||||
)
|
||||
|
||||
@ -339,8 +336,7 @@ def json_dumps_dictlist(data, item):
|
||||
dl = dictlist(data, item)
|
||||
if dl is None:
|
||||
return None
|
||||
else:
|
||||
return json_dumps(dl)
|
||||
return json_dumps(dl)
|
||||
|
||||
|
||||
def string_value_to_float(s):
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -12,7 +11,7 @@
|
||||
from glances.attribute import GlancesAttribute
|
||||
|
||||
|
||||
class GlancesHistory(object):
|
||||
class GlancesHistory:
|
||||
"""This class manage a dict of GlancesAttribute
|
||||
- key: stats name
|
||||
- value: GlancesAttribute"""
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,13 +8,12 @@
|
||||
|
||||
"""Custom logger class."""
|
||||
|
||||
import os
|
||||
import json
|
||||
import getpass
|
||||
import tempfile
|
||||
|
||||
import json
|
||||
import logging
|
||||
import logging.config
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from glances.globals import safe_makedirs
|
||||
|
||||
@ -37,7 +35,7 @@ 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(), f'glances-{getpass.getuser()}.log')
|
||||
|
||||
# Define the logging configuration
|
||||
LOGGING_CFG = {
|
||||
@ -89,7 +87,7 @@ def glances_logger(env_key='LOG_CFG'):
|
||||
user_file = os.getenv(env_key, None)
|
||||
if user_file and os.path.exists(user_file):
|
||||
# A user file as been defined. Use it...
|
||||
with open(user_file, 'rt') as f:
|
||||
with open(user_file) as f:
|
||||
config = json.load(f)
|
||||
|
||||
# Load the configuration
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -15,14 +14,14 @@ import tempfile
|
||||
from logging import DEBUG
|
||||
from warnings import simplefilter
|
||||
|
||||
from glances import __version__, psutil_version, __apiversion__
|
||||
from glances.globals import WINDOWS, disable, enable
|
||||
from glances import __apiversion__, __version__, psutil_version
|
||||
from glances.config import Config
|
||||
from glances.globals import WINDOWS, disable, enable
|
||||
from glances.logger import LOG_FILENAME, logger
|
||||
from glances.processes import sort_processes_key_list
|
||||
from glances.logger import logger, LOG_FILENAME
|
||||
|
||||
|
||||
class GlancesMain(object):
|
||||
class GlancesMain:
|
||||
"""Main class to manage Glances instance."""
|
||||
|
||||
# Default stats' minimum refresh time is 2 seconds
|
||||
@ -101,10 +100,10 @@ Examples of use:
|
||||
|
||||
def version_msg(self):
|
||||
"""Return the version message."""
|
||||
version = 'Glances version:\t{}\n'.format(__version__)
|
||||
version += 'Glances API version:\t{}\n'.format(__apiversion__)
|
||||
version += 'PsUtil version:\t\t{}\n'.format(psutil_version)
|
||||
version += 'Log file:\t\t{}\n'.format(LOG_FILENAME)
|
||||
version = f'Glances version:\t{__version__}\n'
|
||||
version += f'Glances API version:\t{__apiversion__}\n'
|
||||
version += f'PsUtil version:\t\t{psutil_version}\n'
|
||||
version += f'Log file:\t\t{LOG_FILENAME}\n'
|
||||
return version
|
||||
|
||||
def init_args(self):
|
||||
@ -241,7 +240,7 @@ Examples of use:
|
||||
)
|
||||
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',
|
||||
@ -255,14 +254,14 @@ Examples of use:
|
||||
default=True,
|
||||
dest='enable_separator',
|
||||
help='disable separator in the UI (between top and others modules)',
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--disable-cursor',
|
||||
action='store_true',
|
||||
default=False,
|
||||
dest='disable_cursor',
|
||||
help='disable cursor (process selection) in the UI',
|
||||
),
|
||||
)
|
||||
# Sort processes list
|
||||
parser.add_argument(
|
||||
'--sort-processes',
|
||||
@ -334,7 +333,7 @@ Examples of use:
|
||||
default=None,
|
||||
type=int,
|
||||
dest='port',
|
||||
help='define the client/server TCP port [default: {}]'.format(self.server_port),
|
||||
help=f'define the client/server TCP port [default: {self.server_port}]',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-B',
|
||||
@ -374,7 +373,7 @@ Examples of use:
|
||||
default=self.DEFAULT_REFRESH_TIME,
|
||||
type=float,
|
||||
dest='time',
|
||||
help='set minimum refresh rate in seconds [default: {} sec]'.format(self.DEFAULT_REFRESH_TIME),
|
||||
help=f'set minimum refresh rate in seconds [default: {self.DEFAULT_REFRESH_TIME} sec]',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-w',
|
||||
@ -389,7 +388,7 @@ Examples of use:
|
||||
default=self.cached_time,
|
||||
type=int,
|
||||
dest='cached_time',
|
||||
help='set the server cache time [default: {} sec]'.format(self.cached_time),
|
||||
help=f'set the server cache time [default: {self.cached_time} sec]',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--stop-after',
|
||||
@ -577,7 +576,7 @@ Examples of use:
|
||||
if args.time == self.DEFAULT_REFRESH_TIME:
|
||||
args.time = global_refresh
|
||||
|
||||
logger.debug('Global refresh rate is set to {} seconds'.format(args.time))
|
||||
logger.debug(f'Global refresh rate is set to {args.time} seconds')
|
||||
|
||||
def init_plugins(self, args):
|
||||
"""Init Glances plugins"""
|
||||
@ -585,7 +584,7 @@ Examples of use:
|
||||
for s in self.config.sections():
|
||||
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))
|
||||
logger.debug(f'{s} disabled by the configuration file')
|
||||
# The configuration key can be overwrite from the command line
|
||||
if args and args.disable_plugin and 'all' in args.disable_plugin.split(','):
|
||||
if not args.enable_plugin:
|
||||
@ -664,19 +663,19 @@ 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=f'Define the Glances server password ({args.username} username): ',
|
||||
confirm=True,
|
||||
username=args.username,
|
||||
)
|
||||
elif args.webserver:
|
||||
args.password = self.__get_password(
|
||||
description='Define the Glances webserver password ({} username): '.format(args.username),
|
||||
description=f'Define the Glances webserver password ({args.username} username): ',
|
||||
confirm=True,
|
||||
username=args.username,
|
||||
)
|
||||
elif args.client:
|
||||
args.password = self.__get_password(
|
||||
description='Enter the Glances server password ({} username): '.format(args.username),
|
||||
description=f'Enter the Glances server password ({args.username} username): ',
|
||||
clear=True,
|
||||
username=args.username,
|
||||
)
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,16 +8,16 @@
|
||||
|
||||
"""Manage Glances update."""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
import threading
|
||||
import json
|
||||
import pickle
|
||||
import os
|
||||
import pickle
|
||||
import threading
|
||||
from datetime import datetime, timedelta
|
||||
from ssl import CertificateError
|
||||
|
||||
from glances import __version__
|
||||
from glances.globals import nativestr, urlopen, HTTPError, URLError, safe_makedirs
|
||||
from glances.config import user_cache_dir
|
||||
from glances.globals import HTTPError, URLError, nativestr, safe_makedirs, urlopen
|
||||
from glances.logger import logger
|
||||
|
||||
try:
|
||||
@ -26,13 +25,13 @@ try:
|
||||
|
||||
PACKAGING_IMPORT = True
|
||||
except Exception as e:
|
||||
logger.warning("Unable to import 'packaging' module ({}). Glances cannot check for updates.".format(e))
|
||||
logger.warning(f"Unable to import 'packaging' module ({e}). Glances cannot check for updates.")
|
||||
PACKAGING_IMPORT = False
|
||||
|
||||
PYPI_API_URL = 'https://pypi.python.org/pypi/Glances/json'
|
||||
|
||||
|
||||
class Outdated(object):
|
||||
class Outdated:
|
||||
"""
|
||||
This class aims at providing methods to warn the user when a new Glances
|
||||
version is available on the PyPI repository (https://pypi.python.org/pypi/Glances/).
|
||||
@ -46,7 +45,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 = {'installed_version': __version__, 'latest_version': '0.0', 'refresh_date': datetime.now()}
|
||||
|
||||
# Disable update check if `packaging` is not installed
|
||||
if not PACKAGING_IMPORT:
|
||||
@ -56,7 +55,7 @@ class Outdated(object):
|
||||
if not self.args.disable_check_update:
|
||||
self.load_config(config)
|
||||
|
||||
logger.debug("Check Glances version up-to-date: {}".format(not self.args.disable_check_update))
|
||||
logger.debug(f"Check Glances version up-to-date: {not self.args.disable_check_update}")
|
||||
|
||||
# And update !
|
||||
self.get_pypi_version()
|
||||
@ -68,7 +67,7 @@ class Outdated(object):
|
||||
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))
|
||||
logger.debug(f"Cannot find section {global_section} in the configuration file")
|
||||
return False
|
||||
|
||||
return True
|
||||
@ -110,9 +109,7 @@ 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(f"Check Glances version (installed: {self.installed_version()} / latest: {self.latest_version()})")
|
||||
return Version(self.latest_version()) > Version(self.installed_version())
|
||||
|
||||
def _load_cache(self):
|
||||
@ -124,7 +121,7 @@ class Outdated(object):
|
||||
with open(self.cache_file, 'rb') as f:
|
||||
cached_data = pickle.load(f)
|
||||
except Exception as e:
|
||||
logger.debug("Cannot read version from cache file: {} ({})".format(self.cache_file, e))
|
||||
logger.debug(f"Cannot read version from cache file: {self.cache_file} ({e})")
|
||||
else:
|
||||
logger.debug("Read version from cache file")
|
||||
if (
|
||||
@ -147,21 +144,21 @@ class Outdated(object):
|
||||
with open(self.cache_file, 'wb') as f:
|
||||
pickle.dump(self.data, f)
|
||||
except Exception as e:
|
||||
logger.error("Cannot write version to cache file {} ({})".format(self.cache_file, e))
|
||||
logger.error(f"Cannot write version to cache file {self.cache_file} ({e})")
|
||||
|
||||
def _update_pypi_version(self):
|
||||
"""Get the latest PyPI version (as a string) via the RESTful JSON API"""
|
||||
logger.debug("Get latest Glances version from the PyPI RESTful API ({})".format(PYPI_API_URL))
|
||||
logger.debug(f"Get latest Glances version from the PyPI RESTful API ({PYPI_API_URL})")
|
||||
|
||||
# Update the current time
|
||||
self.data[u'refresh_date'] = datetime.now()
|
||||
self.data['refresh_date'] = datetime.now()
|
||||
|
||||
try:
|
||||
res = urlopen(PYPI_API_URL, timeout=3).read()
|
||||
except (HTTPError, URLError, CertificateError) as e:
|
||||
logger.debug("Cannot get Glances version from the PyPI RESTful API ({})".format(e))
|
||||
logger.debug(f"Cannot get Glances version from the PyPI RESTful API ({e})")
|
||||
else:
|
||||
self.data[u'latest_version'] = json.loads(nativestr(res))['info']['version']
|
||||
self.data['latest_version'] = json.loads(nativestr(res))['info']['version']
|
||||
logger.debug("Save Glances version to the cache file")
|
||||
|
||||
# Save result to the cache file
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,12 +8,10 @@
|
||||
|
||||
"""Manage bars for Glances output."""
|
||||
|
||||
from __future__ import division
|
||||
|
||||
from math import modf
|
||||
|
||||
|
||||
class Bar(object):
|
||||
class Bar:
|
||||
"""Manage bar (progression or status).
|
||||
|
||||
import sys
|
||||
@ -76,6 +73,7 @@ class Bar(object):
|
||||
return self.__size
|
||||
if self.__display_value:
|
||||
return self.__size - 6
|
||||
return None
|
||||
|
||||
@property
|
||||
def percent(self):
|
||||
@ -114,7 +112,7 @@ class Bar(object):
|
||||
ret, '>' if self.percent > self.max_value else ' ', self.max_value, self.__unit_char
|
||||
)
|
||||
else:
|
||||
ret = '{}{:5.1f}{}'.format(ret, self.percent, self.__unit_char)
|
||||
ret = f'{ret}{self.percent:5.1f}{self.__unit_char}'
|
||||
|
||||
# Add overlay
|
||||
if overlay and len(overlay) < len(ret) - 6:
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -8,16 +7,15 @@
|
||||
#
|
||||
|
||||
"""Curses interface class."""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import sys
|
||||
import getpass
|
||||
import sys
|
||||
|
||||
from glances.globals import MACOS, WINDOWS, nativestr, u, itervalues, enable, disable
|
||||
from glances.logger import logger
|
||||
from glances.events_list import glances_events
|
||||
from glances.processes import glances_processes, sort_processes_key_list
|
||||
from glances.globals import MACOS, WINDOWS, disable, enable, itervalues, nativestr, u
|
||||
from glances.logger import logger
|
||||
from glances.outputs.glances_unicode import unicode_message
|
||||
from glances.processes import glances_processes, sort_processes_key_list
|
||||
from glances.timer import Timer
|
||||
|
||||
# Import curses library for "normal" operating system
|
||||
@ -32,7 +30,7 @@ except ImportError:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class _GlancesCurses(object):
|
||||
class _GlancesCurses:
|
||||
"""This class manages the curses display (and key pressed).
|
||||
|
||||
Note: It is a private class, use GlancesCursesClient or GlancesCursesBrowser.
|
||||
@ -147,15 +145,15 @@ class _GlancesCurses(object):
|
||||
logger.critical("Cannot init the curses library.\n")
|
||||
sys.exit(1)
|
||||
else:
|
||||
logger.debug("Curses library initialized with term: {}".format(curses.longname()))
|
||||
logger.debug(f"Curses library initialized with term: {curses.longname()}")
|
||||
except Exception as e:
|
||||
if args.export:
|
||||
logger.info("Cannot init the curses library, quiet mode on and export.")
|
||||
args.quiet = True
|
||||
return
|
||||
else:
|
||||
logger.critical("Cannot init the curses library ({})".format(e))
|
||||
sys.exit(1)
|
||||
|
||||
logger.critical(f"Cannot init the curses library ({e})")
|
||||
sys.exit(1)
|
||||
|
||||
# Load configuration file
|
||||
self.load_config(config)
|
||||
@ -223,11 +221,11 @@ class _GlancesCurses(object):
|
||||
try:
|
||||
if hasattr(curses, 'start_color'):
|
||||
curses.start_color()
|
||||
logger.debug('Curses interface compatible with {} colors'.format(curses.COLORS))
|
||||
logger.debug(f'Curses interface compatible with {curses.COLORS} colors')
|
||||
if hasattr(curses, 'use_default_colors'):
|
||||
curses.use_default_colors()
|
||||
except Exception as e:
|
||||
logger.warning('Error initializing terminal color ({})'.format(e))
|
||||
logger.warning(f'Error initializing terminal color ({e})')
|
||||
|
||||
# Init colors
|
||||
if self.args.disable_bold:
|
||||
@ -358,8 +356,7 @@ class _GlancesCurses(object):
|
||||
|
||||
def get_key(self, window):
|
||||
# TODO: Check issue #163
|
||||
ret = window.getch()
|
||||
return ret
|
||||
return window.getch()
|
||||
|
||||
def __catch_key(self, return_to_browser=False):
|
||||
# Catch the pressed key
|
||||
@ -368,7 +365,7 @@ class _GlancesCurses(object):
|
||||
return -1
|
||||
|
||||
# Actions (available in the global hotkey dict)...
|
||||
logger.debug("Keypressed (code: {})".format(self.pressedkey))
|
||||
logger.debug(f"Keypressed (code: {self.pressedkey})")
|
||||
for hotkey in self._hotkeys:
|
||||
if self.pressedkey == ord(hotkey) and 'switch' in self._hotkeys[hotkey]:
|
||||
self._handle_switch(hotkey)
|
||||
@ -495,7 +492,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(f"Stop Glances (keypressed: {self.pressedkey})")
|
||||
|
||||
def _handle_refresh(self):
|
||||
pass
|
||||
@ -716,7 +713,7 @@ class _GlancesCurses(object):
|
||||
# Display graph generation popup
|
||||
if self.args.generate_graph:
|
||||
if 'graph' in stats.getExportsList():
|
||||
self.display_popup('Generate graph in {}'.format(self.args.export_graph_path))
|
||||
self.display_popup(f'Generate graph in {self.args.export_graph_path}')
|
||||
else:
|
||||
logger.warning('Graph export module is disable. Run Glances with --export graph to enable it.')
|
||||
self.args.generate_graph = False
|
||||
@ -735,7 +732,7 @@ class _GlancesCurses(object):
|
||||
:param process
|
||||
:return: None
|
||||
"""
|
||||
logger.debug("Selected process to kill: {}".format(process))
|
||||
logger.debug(f"Selected process to kill: {process}")
|
||||
|
||||
if 'childrens' in process:
|
||||
pid_to_kill = process['childrens']
|
||||
@ -755,9 +752,9 @@ class _GlancesCurses(object):
|
||||
try:
|
||||
ret_kill = glances_processes.kill(pid)
|
||||
except Exception as e:
|
||||
logger.error('Can not kill process {} ({})'.format(pid, e))
|
||||
logger.error(f'Can not kill process {pid} ({e})')
|
||||
else:
|
||||
logger.info('Kill signal has been sent to process {} (return code: {})'.format(pid, ret_kill))
|
||||
logger.info(f'Kill signal has been sent to process {pid} (return code: {ret_kill})')
|
||||
|
||||
def __display_header(self, stat_display):
|
||||
"""Display the firsts lines (header) in the Curses interface.
|
||||
@ -829,7 +826,7 @@ class _GlancesCurses(object):
|
||||
max_width=quicklook_width, args=self.args
|
||||
)
|
||||
except AttributeError as e:
|
||||
logger.debug("Quicklook plugin not available (%s)" % e)
|
||||
logger.debug(f"Quicklook plugin not available ({e})")
|
||||
else:
|
||||
plugin_widths['quicklook'] = self.get_stats_display_width(stat_display["quicklook"])
|
||||
stats_width = sum(itervalues(plugin_widths)) + 1
|
||||
@ -989,7 +986,8 @@ class _GlancesCurses(object):
|
||||
popup.refresh()
|
||||
self.wait(duration * 1000)
|
||||
return True
|
||||
elif popup_type == 'input':
|
||||
|
||||
if popup_type == 'input':
|
||||
logger.info(popup_type)
|
||||
logger.info(is_password)
|
||||
# Create a sub-window for the text field
|
||||
@ -1009,17 +1007,17 @@ class _GlancesCurses(object):
|
||||
self.set_cursor(0)
|
||||
if textbox != '':
|
||||
return textbox
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
textbox = GlancesTextbox(sub_pop, insert_mode=True)
|
||||
textbox.edit()
|
||||
self.set_cursor(0)
|
||||
if textbox.gather() != '':
|
||||
return textbox.gather()[:-1]
|
||||
else:
|
||||
return None
|
||||
elif popup_type == 'yesno':
|
||||
return None
|
||||
|
||||
# No password
|
||||
textbox = GlancesTextbox(sub_pop, insert_mode=True)
|
||||
textbox.edit()
|
||||
self.set_cursor(0)
|
||||
if textbox.gather() != '':
|
||||
return textbox.gather()[:-1]
|
||||
return None
|
||||
|
||||
if popup_type == 'yesno':
|
||||
# # Create a sub-window for the text field
|
||||
sub_pop = popup.derwin(1, 2, len(sentence_list) + 1, len(m) + 2)
|
||||
sub_pop.attron(self.colors_list['FILTER'])
|
||||
@ -1037,6 +1035,8 @@ class _GlancesCurses(object):
|
||||
# self.term_window.keypad(0)
|
||||
return textbox.gather()
|
||||
|
||||
return None
|
||||
|
||||
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.
|
||||
|
||||
@ -1131,6 +1131,7 @@ class _GlancesCurses(object):
|
||||
|
||||
# Have empty lines after the plugins
|
||||
self.next_line += add_space
|
||||
return None
|
||||
|
||||
def clear(self):
|
||||
"""Erase the content of the screen.
|
||||
@ -1213,8 +1214,7 @@ class _GlancesCurses(object):
|
||||
if isexitkey and self.args.help_tag:
|
||||
# Quit from help should return to main screen, not exit #1874
|
||||
self.args.help_tag = not self.args.help_tag
|
||||
isexitkey = False
|
||||
return isexitkey
|
||||
return False
|
||||
|
||||
if not isexitkey and pressedkey > -1:
|
||||
# Redraw display
|
||||
@ -1255,7 +1255,7 @@ class _GlancesCurses(object):
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug('ERROR: Can not compute plugin width ({})'.format(e))
|
||||
logger.debug(f'ERROR: Can not compute plugin width ({e})')
|
||||
return 0
|
||||
else:
|
||||
return c
|
||||
@ -1268,7 +1268,7 @@ class _GlancesCurses(object):
|
||||
try:
|
||||
c = [i['msg'] for i in curse_msg['msgdict']].count('\n')
|
||||
except Exception as e:
|
||||
logger.debug('ERROR: Can not compute plugin height ({})'.format(e))
|
||||
logger.debug(f'ERROR: Can not compute plugin height ({e})')
|
||||
return 0
|
||||
else:
|
||||
return c + 1
|
||||
@ -1282,21 +1282,21 @@ class GlancesCursesClient(_GlancesCurses):
|
||||
"""Class for the Glances curse client."""
|
||||
|
||||
|
||||
class GlancesTextbox(Textbox, object):
|
||||
class GlancesTextbox(Textbox):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(GlancesTextbox, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def do_command(self, ch):
|
||||
if ch == 10: # Enter
|
||||
return 0
|
||||
if ch == 127: # Back
|
||||
return 8
|
||||
return super(GlancesTextbox, self).do_command(ch)
|
||||
return super().do_command(ch)
|
||||
|
||||
|
||||
class GlancesTextboxYesNo(Textbox, object):
|
||||
class GlancesTextboxYesNo(Textbox):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(GlancesTextboxYesNo, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def do_command(self, ch):
|
||||
return super(GlancesTextboxYesNo, self).do_command(ch)
|
||||
return super().do_command(ch)
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,11 +8,11 @@
|
||||
|
||||
"""Curses browser interface class ."""
|
||||
|
||||
import math
|
||||
import curses
|
||||
from glances.outputs.glances_curses import _GlancesCurses
|
||||
import math
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.outputs.glances_curses import _GlancesCurses
|
||||
from glances.timer import Timer
|
||||
|
||||
|
||||
@ -22,7 +21,7 @@ class GlancesCursesBrowser(_GlancesCurses):
|
||||
|
||||
def __init__(self, args=None):
|
||||
"""Init the father class."""
|
||||
super(GlancesCursesBrowser, self).__init__(args=args)
|
||||
super().__init__(args=args)
|
||||
|
||||
_colors_list = {
|
||||
'UNKNOWN': self.no_color,
|
||||
@ -151,7 +150,7 @@ class GlancesCursesBrowser(_GlancesCurses):
|
||||
self.pressedkey = self.get_key(self.term_window)
|
||||
refresh = False
|
||||
if self.pressedkey != -1:
|
||||
logger.debug("Key pressed. Code=%s" % self.pressedkey)
|
||||
logger.debug(f"Key pressed. Code={self.pressedkey}")
|
||||
|
||||
# Actions...
|
||||
if self.pressedkey == ord('\x1b') or self.pressedkey == ord('q'):
|
||||
@ -163,23 +162,23 @@ class GlancesCursesBrowser(_GlancesCurses):
|
||||
elif self.pressedkey == 10:
|
||||
# 'ENTER' > Run Glances on the selected server
|
||||
self.active_server = self._current_page * self._page_max_lines + self.cursor_position
|
||||
logger.debug("Server {}/{} selected".format(self.active_server, len(stats)))
|
||||
logger.debug(f"Server {self.active_server}/{len(stats)} selected")
|
||||
elif self.pressedkey == curses.KEY_UP or self.pressedkey == 65:
|
||||
# 'UP' > Up in the server list
|
||||
self.cursor_up(stats)
|
||||
logger.debug("Server {}/{} selected".format(self.cursor + 1, len(stats)))
|
||||
logger.debug(f"Server {self.cursor + 1}/{len(stats)} selected")
|
||||
elif self.pressedkey == curses.KEY_DOWN or self.pressedkey == 66:
|
||||
# 'DOWN' > Down in the server list
|
||||
self.cursor_down(stats)
|
||||
logger.debug("Server {}/{} selected".format(self.cursor + 1, len(stats)))
|
||||
logger.debug(f"Server {self.cursor + 1}/{len(stats)} selected")
|
||||
elif self.pressedkey == curses.KEY_PPAGE:
|
||||
# 'Page UP' > Prev page in the server list
|
||||
self.cursor_pageup(stats)
|
||||
logger.debug("PageUP: Server ({}/{}) pages.".format(self._current_page + 1, self._page_max))
|
||||
logger.debug(f"PageUP: Server ({self._current_page + 1}/{self._page_max}) pages.")
|
||||
elif self.pressedkey == curses.KEY_NPAGE:
|
||||
# 'Page Down' > Next page in the server list
|
||||
self.cursor_pagedown(stats)
|
||||
logger.debug("PageDown: Server {}/{} pages".format(self._current_page + 1, self._page_max))
|
||||
logger.debug(f"PageDown: Server {self._current_page + 1}/{self._page_max} pages")
|
||||
elif self.pressedkey == ord('1'):
|
||||
self._stats_list = None
|
||||
refresh = True
|
||||
@ -211,7 +210,7 @@ class GlancesCursesBrowser(_GlancesCurses):
|
||||
:param return_to_browser:
|
||||
"""
|
||||
# Flush display
|
||||
logger.debug('Servers list: {}'.format(stats))
|
||||
logger.debug(f'Servers list: {stats}')
|
||||
self.flush(stats)
|
||||
|
||||
# Wait
|
||||
@ -268,19 +267,19 @@ class GlancesCursesBrowser(_GlancesCurses):
|
||||
elif len(stats) == 1:
|
||||
msg = 'One Glances server available'
|
||||
else:
|
||||
msg = '{} Glances servers available'.format(stats_len)
|
||||
msg = f'{stats_len} Glances servers available'
|
||||
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'])
|
||||
|
||||
msg = '{}'.format(self._get_status_count(stats))
|
||||
msg = f'{self._get_status_count(stats)}'
|
||||
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)
|
||||
)
|
||||
page_lines = self.get_pagelines(stats)
|
||||
status_count = self._get_status_count(stats)
|
||||
msg = f'{page_lines} servers displayed.({self._current_page + 1}/{self._page_max}) {status_count}'
|
||||
self.term_window.addnstr(y + 1, x, msg, screen_x - x)
|
||||
|
||||
if stats_len == 0:
|
||||
@ -335,7 +334,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(f"Cannot grab stats {c[0]} from server (KeyError: {e})")
|
||||
server_stat[c[0]] = '?'
|
||||
# Display alias instead of name
|
||||
try:
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -12,7 +11,6 @@
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from io import open
|
||||
import webbrowser
|
||||
from urllib.parse import urljoin
|
||||
|
||||
@ -23,20 +21,20 @@ except ImportError:
|
||||
# To be removed when Python 3.8 support will be dropped
|
||||
from typing_extensions import Annotated
|
||||
|
||||
from glances import __version__, __apiversion__
|
||||
from glances import __apiversion__, __version__
|
||||
from glances.logger import logger
|
||||
from glances.password import GlancesPassword
|
||||
from glances.timer import Timer
|
||||
from glances.logger import logger
|
||||
|
||||
# FastAPI import
|
||||
try:
|
||||
from fastapi import FastAPI, Depends, HTTPException, status, APIRouter, Request
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from fastapi import APIRouter, Depends, FastAPI, HTTPException, Request, status
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.middleware.gzip import GZipMiddleware
|
||||
from fastapi.responses import HTMLResponse, ORJSONResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
except ImportError:
|
||||
logger.critical('FastAPI import error. Glances cannot start in web server mode.')
|
||||
sys.exit(2)
|
||||
@ -46,6 +44,7 @@ try:
|
||||
except ImportError:
|
||||
logger.critical('Uvicorn import error. Glances cannot start in web server mode.')
|
||||
sys.exit(2)
|
||||
import builtins
|
||||
import contextlib
|
||||
import threading
|
||||
import time
|
||||
@ -77,7 +76,7 @@ class GlancesUvicornServer(uvicorn.Server):
|
||||
thread.join()
|
||||
|
||||
|
||||
class GlancesRestfulApi(object):
|
||||
class GlancesRestfulApi:
|
||||
"""This class manages the Restful API server."""
|
||||
|
||||
API_VERSION = __apiversion__
|
||||
@ -102,7 +101,7 @@ class GlancesRestfulApi(object):
|
||||
self.load_config(config)
|
||||
|
||||
# Set the bind URL
|
||||
self.bind_url = urljoin('http://{}:{}/'.format(self.args.bind_address, self.args.port), self.url_prefix)
|
||||
self.bind_url = urljoin(f'http://{self.args.bind_address}:{self.args.port}/', self.url_prefix)
|
||||
|
||||
# FastAPI Init
|
||||
if self.args.password:
|
||||
@ -151,9 +150,9 @@ class GlancesRestfulApi(object):
|
||||
self.url_prefix = '/'
|
||||
if config is not None and config.has_section('outputs'):
|
||||
n = config.get_value('outputs', 'max_processes_display', default=None)
|
||||
logger.debug('Number of processes to display in the WebUI: {}'.format(n))
|
||||
logger.debug(f'Number of processes to display in the WebUI: {n}')
|
||||
self.url_prefix = config.get_value('outputs', 'url_prefix', default='/')
|
||||
logger.debug('URL prefix: {}'.format(self.url_prefix))
|
||||
logger.debug(f'URL prefix: {self.url_prefix}')
|
||||
|
||||
def __update__(self):
|
||||
# Never update more than 1 time per cached_time
|
||||
@ -184,92 +183,92 @@ class GlancesRestfulApi(object):
|
||||
|
||||
# REST API
|
||||
router.add_api_route(
|
||||
'/api/%s/status' % self.API_VERSION,
|
||||
f'/api/{self.API_VERSION}/status',
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_class=ORJSONResponse,
|
||||
endpoint=self._api_status,
|
||||
)
|
||||
|
||||
router.add_api_route(
|
||||
'/api/%s/config' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_config
|
||||
f'/api/{self.API_VERSION}/config', response_class=ORJSONResponse, endpoint=self._api_config
|
||||
)
|
||||
router.add_api_route(
|
||||
'/api/%s/config/{section}' % self.API_VERSION,
|
||||
f'/api/{self.API_VERSION}/config/{{section}}',
|
||||
response_class=ORJSONResponse,
|
||||
endpoint=self._api_config_section,
|
||||
)
|
||||
router.add_api_route(
|
||||
'/api/%s/config/{section}/{item}' % self.API_VERSION,
|
||||
f'/api/{self.API_VERSION}/config/{{section}}/{{item}}',
|
||||
response_class=ORJSONResponse,
|
||||
endpoint=self._api_config_section_item,
|
||||
)
|
||||
|
||||
router.add_api_route('/api/%s/args' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_args)
|
||||
router.add_api_route(f'/api/{self.API_VERSION}/args', response_class=ORJSONResponse, endpoint=self._api_args)
|
||||
router.add_api_route(
|
||||
'/api/%s/args/{item}' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_args_item
|
||||
f'/api/{self.API_VERSION}/args/{{item}}', response_class=ORJSONResponse, endpoint=self._api_args_item
|
||||
)
|
||||
|
||||
router.add_api_route(
|
||||
'/api/%s/pluginslist' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_plugins
|
||||
f'/api/{self.API_VERSION}/pluginslist', response_class=ORJSONResponse, endpoint=self._api_plugins
|
||||
)
|
||||
router.add_api_route('/api/%s/all' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_all)
|
||||
router.add_api_route(f'/api/{self.API_VERSION}/all', response_class=ORJSONResponse, endpoint=self._api_all)
|
||||
router.add_api_route(
|
||||
'/api/%s/all/limits' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_all_limits
|
||||
f'/api/{self.API_VERSION}/all/limits', response_class=ORJSONResponse, endpoint=self._api_all_limits
|
||||
)
|
||||
router.add_api_route(
|
||||
'/api/%s/all/views' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_all_views
|
||||
f'/api/{self.API_VERSION}/all/views', response_class=ORJSONResponse, endpoint=self._api_all_views
|
||||
)
|
||||
|
||||
router.add_api_route('/api/%s/help' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_help)
|
||||
router.add_api_route('/api/%s/{plugin}' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api)
|
||||
router.add_api_route(f'/api/{self.API_VERSION}/help', response_class=ORJSONResponse, endpoint=self._api_help)
|
||||
router.add_api_route(f'/api/{self.API_VERSION}/{{plugin}}', response_class=ORJSONResponse, endpoint=self._api)
|
||||
router.add_api_route(
|
||||
'/api/%s/{plugin}/history' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_history
|
||||
f'/api/{self.API_VERSION}/{{plugin}}/history', response_class=ORJSONResponse, endpoint=self._api_history
|
||||
)
|
||||
router.add_api_route(
|
||||
'/api/%s/{plugin}/history/{nb}' % self.API_VERSION,
|
||||
f'/api/{self.API_VERSION}/{{plugin}}/history/{{nb}}',
|
||||
response_class=ORJSONResponse,
|
||||
endpoint=self._api_history,
|
||||
)
|
||||
router.add_api_route(
|
||||
'/api/%s/{plugin}/top/{nb}' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_top
|
||||
f'/api/{self.API_VERSION}/{{plugin}}/top/{{nb}}', response_class=ORJSONResponse, endpoint=self._api_top
|
||||
)
|
||||
router.add_api_route(
|
||||
'/api/%s/{plugin}/limits' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_limits
|
||||
f'/api/{self.API_VERSION}/{{plugin}}/limits', response_class=ORJSONResponse, endpoint=self._api_limits
|
||||
)
|
||||
router.add_api_route(
|
||||
'/api/%s/{plugin}/views' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_views
|
||||
f'/api/{self.API_VERSION}/{{plugin}}/views', response_class=ORJSONResponse, endpoint=self._api_views
|
||||
)
|
||||
router.add_api_route(
|
||||
'/api/%s/{plugin}/{item}' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_item
|
||||
f'/api/{self.API_VERSION}/{{plugin}}/{{item}}', response_class=ORJSONResponse, endpoint=self._api_item
|
||||
)
|
||||
router.add_api_route(
|
||||
'/api/%s/{plugin}/{item}/history' % self.API_VERSION,
|
||||
f'/api/{self.API_VERSION}/{{plugin}}/{{item}}/history',
|
||||
response_class=ORJSONResponse,
|
||||
endpoint=self._api_item_history,
|
||||
)
|
||||
router.add_api_route(
|
||||
'/api/%s/{plugin}/{item}/history/{nb}' % self.API_VERSION,
|
||||
f'/api/{self.API_VERSION}/{{plugin}}/{{item}}/history/{{nb}}',
|
||||
response_class=ORJSONResponse,
|
||||
endpoint=self._api_item_history,
|
||||
)
|
||||
router.add_api_route(
|
||||
'/api/%s/{plugin}/{item}/description' % self.API_VERSION,
|
||||
f'/api/{self.API_VERSION}/{{plugin}}/{{item}}/description',
|
||||
response_class=ORJSONResponse,
|
||||
endpoint=self._api_item_description,
|
||||
)
|
||||
router.add_api_route(
|
||||
'/api/%s/{plugin}/{item}/unit' % self.API_VERSION,
|
||||
f'/api/{self.API_VERSION}/{{plugin}}/{{item}}/unit',
|
||||
response_class=ORJSONResponse,
|
||||
endpoint=self._api_item_unit,
|
||||
)
|
||||
router.add_api_route(
|
||||
'/api/%s/{plugin}/{item}/{value}' % self.API_VERSION,
|
||||
f'/api/{self.API_VERSION}/{{plugin}}/{{item}}/{{value}}',
|
||||
response_class=ORJSONResponse,
|
||||
endpoint=self._api_value,
|
||||
)
|
||||
|
||||
# Restful API
|
||||
bindmsg = 'Glances RESTful API Server started on {}api/{}'.format(self.bind_url, self.API_VERSION)
|
||||
bindmsg = f'Glances RESTful API Server started on {self.bind_url}api/{self.API_VERSION}'
|
||||
logger.info(bindmsg)
|
||||
|
||||
# WEB UI
|
||||
@ -280,9 +279,9 @@ class GlancesRestfulApi(object):
|
||||
# Statics files
|
||||
self._app.mount("/static", StaticFiles(directory=self.STATIC_PATH), name="static")
|
||||
|
||||
logger.info("Get WebUI in {}".format(self.STATIC_PATH))
|
||||
logger.info(f"Get WebUI in {self.STATIC_PATH}")
|
||||
|
||||
bindmsg = 'Glances Web User Interface started on {}'.format(self.bind_url)
|
||||
bindmsg = f'Glances Web User Interface started on {self.bind_url}'
|
||||
else:
|
||||
bindmsg = 'The WebUI is disable (--disable-webui)'
|
||||
|
||||
@ -317,7 +316,7 @@ class GlancesRestfulApi(object):
|
||||
try:
|
||||
self.uvicorn_server = GlancesUvicornServer(config=uvicorn_config)
|
||||
except Exception as e:
|
||||
logger.critical('Error: Can not ran Glances Web server ({})'.format(e))
|
||||
logger.critical(f'Error: Can not ran Glances Web server ({e})')
|
||||
self.uvicorn_server = None
|
||||
else:
|
||||
with self.uvicorn_server.run_in_thread():
|
||||
@ -369,7 +368,7 @@ class GlancesRestfulApi(object):
|
||||
try:
|
||||
plist = self.stats.get_plugin("help").get_view_data()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get help view data (%s)" % str(e))
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get help view data ({str(e)})")
|
||||
|
||||
return ORJSONResponse(plist)
|
||||
|
||||
@ -405,7 +404,7 @@ class GlancesRestfulApi(object):
|
||||
try:
|
||||
plist = self.plugins_list
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get plugin list (%s)" % str(e))
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get plugin list ({str(e)})")
|
||||
|
||||
return ORJSONResponse(plist)
|
||||
|
||||
@ -420,10 +419,10 @@ class GlancesRestfulApi(object):
|
||||
if self.args.debug:
|
||||
fname = os.path.join(tempfile.gettempdir(), 'glances-debug.json')
|
||||
try:
|
||||
with open(fname) as f:
|
||||
with builtins.open(fname) as f:
|
||||
return f.read()
|
||||
except IOError:
|
||||
logger.debug("Debug file (%s) not found" % fname)
|
||||
except OSError:
|
||||
logger.debug(f"Debug file ({fname}) not found")
|
||||
|
||||
# Update the stat
|
||||
self.__update__()
|
||||
@ -432,7 +431,7 @@ class GlancesRestfulApi(object):
|
||||
# Get the RAW value of the stat ID
|
||||
statval = self.stats.getAllAsDict()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get stats (%s)" % str(e))
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get stats ({str(e)})")
|
||||
|
||||
return ORJSONResponse(statval)
|
||||
|
||||
@ -448,7 +447,7 @@ class GlancesRestfulApi(object):
|
||||
# Get the RAW value of the stat limits
|
||||
limits = self.stats.getAllLimitsAsDict()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get limits (%s)" % str(e))
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get limits ({str(e)})")
|
||||
|
||||
return ORJSONResponse(limits)
|
||||
|
||||
@ -464,7 +463,7 @@ class GlancesRestfulApi(object):
|
||||
# Get the RAW value of the stat view
|
||||
limits = self.stats.getAllViewsAsDict()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get views (%s)" % str(e))
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get views ({str(e)})")
|
||||
|
||||
return ORJSONResponse(limits)
|
||||
|
||||
@ -479,7 +478,7 @@ class GlancesRestfulApi(object):
|
||||
if plugin not in self.plugins_list:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
|
||||
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
|
||||
)
|
||||
|
||||
# Update the stat
|
||||
@ -489,9 +488,7 @@ class GlancesRestfulApi(object):
|
||||
# Get the RAW value of the stat ID
|
||||
statval = self.stats.get_plugin(plugin).get_raw()
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get plugin %s (%s)" % (plugin, str(e))
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get plugin {plugin} ({str(e)})")
|
||||
|
||||
return ORJSONResponse(statval)
|
||||
|
||||
@ -508,7 +505,7 @@ class GlancesRestfulApi(object):
|
||||
if plugin not in self.plugins_list:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
|
||||
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
|
||||
)
|
||||
|
||||
# Update the stat
|
||||
@ -518,9 +515,7 @@ class GlancesRestfulApi(object):
|
||||
# Get the RAW value of the stat ID
|
||||
statval = self.stats.get_plugin(plugin).get_raw()
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get plugin %s (%s)" % (plugin, str(e))
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get plugin {plugin} ({str(e)})")
|
||||
|
||||
print(statval)
|
||||
|
||||
@ -541,7 +536,7 @@ class GlancesRestfulApi(object):
|
||||
if plugin not in self.plugins_list:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
|
||||
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
|
||||
)
|
||||
|
||||
# Update the stat
|
||||
@ -552,7 +547,7 @@ class GlancesRestfulApi(object):
|
||||
statval = self.stats.get_plugin(plugin).get_raw_history(nb=int(nb))
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get plugin history %s (%s)" % (plugin, str(e))
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get plugin history {plugin} ({str(e)})"
|
||||
)
|
||||
|
||||
return statval
|
||||
@ -568,7 +563,7 @@ class GlancesRestfulApi(object):
|
||||
if plugin not in self.plugins_list:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
|
||||
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
|
||||
)
|
||||
|
||||
try:
|
||||
@ -576,7 +571,7 @@ class GlancesRestfulApi(object):
|
||||
ret = self.stats.get_plugin(plugin).limits
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get limits for plugin %s (%s)" % (plugin, str(e))
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get limits for plugin {plugin} ({str(e)})"
|
||||
)
|
||||
|
||||
return ORJSONResponse(ret)
|
||||
@ -592,7 +587,7 @@ class GlancesRestfulApi(object):
|
||||
if plugin not in self.plugins_list:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
|
||||
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
|
||||
)
|
||||
|
||||
try:
|
||||
@ -600,7 +595,7 @@ class GlancesRestfulApi(object):
|
||||
ret = self.stats.get_plugin(plugin).get_views()
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get views for plugin %s (%s)" % (plugin, str(e))
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get views for plugin {plugin} ({str(e)})"
|
||||
)
|
||||
|
||||
return ORJSONResponse(ret)
|
||||
@ -616,7 +611,7 @@ class GlancesRestfulApi(object):
|
||||
if plugin not in self.plugins_list:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
|
||||
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
|
||||
)
|
||||
|
||||
# Update the stat
|
||||
@ -628,7 +623,7 @@ class GlancesRestfulApi(object):
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Cannot get item %s in plugin %s (%s)" % (item, plugin, str(e)),
|
||||
detail=f"Cannot get item {item} in plugin {plugin} ({str(e)})",
|
||||
)
|
||||
|
||||
return ORJSONResponse(ret)
|
||||
@ -645,7 +640,7 @@ class GlancesRestfulApi(object):
|
||||
if plugin not in self.plugins_list:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
|
||||
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
|
||||
)
|
||||
|
||||
# Update the stat
|
||||
@ -656,7 +651,7 @@ class GlancesRestfulApi(object):
|
||||
ret = self.stats.get_plugin(plugin).get_raw_history(item, nb=nb)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get history for plugin %s (%s)" % (plugin, str(e))
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get history for plugin {plugin} ({str(e)})"
|
||||
)
|
||||
else:
|
||||
return ORJSONResponse(ret)
|
||||
@ -672,7 +667,7 @@ class GlancesRestfulApi(object):
|
||||
if plugin not in self.plugins_list:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
|
||||
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
|
||||
)
|
||||
|
||||
try:
|
||||
@ -681,7 +676,7 @@ class GlancesRestfulApi(object):
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Cannot get %s description for plugin %s (%s)" % (item, plugin, str(e)),
|
||||
detail=f"Cannot get {item} description for plugin {plugin} ({str(e)})",
|
||||
)
|
||||
else:
|
||||
return ORJSONResponse(ret)
|
||||
@ -697,7 +692,7 @@ class GlancesRestfulApi(object):
|
||||
if plugin not in self.plugins_list:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
|
||||
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
|
||||
)
|
||||
|
||||
try:
|
||||
@ -706,7 +701,7 @@ class GlancesRestfulApi(object):
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Cannot get %s unit for plugin %s (%s)" % (item, plugin, str(e)),
|
||||
detail=f"Cannot get {item} unit for plugin {plugin} ({str(e)})",
|
||||
)
|
||||
else:
|
||||
return ORJSONResponse(ret)
|
||||
@ -722,7 +717,7 @@ class GlancesRestfulApi(object):
|
||||
if plugin not in self.plugins_list:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
|
||||
detail=f"Unknown plugin {plugin} (available plugins: {self.plugins_list})",
|
||||
)
|
||||
|
||||
# Update the stat
|
||||
@ -734,7 +729,7 @@ class GlancesRestfulApi(object):
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Cannot get %s = %s for plugin %s (%s)" % (item, value, plugin, str(e)),
|
||||
detail=f"Cannot get {item} = {value} for plugin {plugin} ({str(e)})",
|
||||
)
|
||||
else:
|
||||
return ORJSONResponse(ret)
|
||||
@ -750,7 +745,7 @@ class GlancesRestfulApi(object):
|
||||
# Get the RAW value of the config' dict
|
||||
args_json = self.config.as_dict()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get config (%s)" % str(e))
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get config ({str(e)})")
|
||||
else:
|
||||
return ORJSONResponse(args_json)
|
||||
|
||||
@ -764,16 +759,14 @@ class GlancesRestfulApi(object):
|
||||
"""
|
||||
config_dict = self.config.as_dict()
|
||||
if section not in config_dict:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST, detail="Unknown configuration item %s" % section
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"Unknown configuration item {section}")
|
||||
|
||||
try:
|
||||
# Get the RAW value of the config' dict
|
||||
ret_section = config_dict[section]
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get config section %s (%s)" % (section, str(e))
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get config section {section} ({str(e)})"
|
||||
)
|
||||
|
||||
return ORJSONResponse(ret_section)
|
||||
@ -788,16 +781,14 @@ class GlancesRestfulApi(object):
|
||||
"""
|
||||
config_dict = self.config.as_dict()
|
||||
if section not in config_dict:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST, detail="Unknown configuration item %s" % section
|
||||
)
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"Unknown configuration item {section}")
|
||||
|
||||
try:
|
||||
# Get the RAW value of the config' dict section
|
||||
ret_section = config_dict[section]
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get config section %s (%s)" % (section, str(e))
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get config section {section} ({str(e)})"
|
||||
)
|
||||
|
||||
try:
|
||||
@ -806,7 +797,7 @@ class GlancesRestfulApi(object):
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Cannot get item %s in config section %s (%s)" % (item, section, str(e)),
|
||||
detail=f"Cannot get item {item} in config section {section} ({str(e)})",
|
||||
)
|
||||
|
||||
return ORJSONResponse(ret_item)
|
||||
@ -824,7 +815,7 @@ class GlancesRestfulApi(object):
|
||||
# Source: https://docs.python.org/%s/library/functions.html#vars
|
||||
args_json = vars(self.args)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get args (%s)" % str(e))
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get args ({str(e)})")
|
||||
|
||||
return ORJSONResponse(args_json)
|
||||
|
||||
@ -837,7 +828,7 @@ class GlancesRestfulApi(object):
|
||||
HTTP/404 if others error
|
||||
"""
|
||||
if item not in self.args:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Unknown argument item %s" % item)
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"Unknown argument item {item}")
|
||||
|
||||
try:
|
||||
# Get the RAW value of the args' dict
|
||||
@ -845,6 +836,6 @@ class GlancesRestfulApi(object):
|
||||
# Source: https://docs.python.org/%s/library/functions.html#vars
|
||||
args_json = vars(self.args)[item]
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get args item (%s)" % str(e))
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get args item ({str(e)})")
|
||||
|
||||
return ORJSONResponse(args_json)
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,28 +8,27 @@
|
||||
|
||||
"""Manage sparklines for Glances output."""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import division
|
||||
import sys
|
||||
from glances.logger import logger
|
||||
|
||||
from glances.globals import nativestr
|
||||
from glances.logger import logger
|
||||
|
||||
sparklines_module = True
|
||||
|
||||
try:
|
||||
from sparklines import sparklines
|
||||
except ImportError as e:
|
||||
logger.warning("Sparklines module not found ({})".format(e))
|
||||
logger.warning(f"Sparklines module not found ({e})")
|
||||
sparklines_module = False
|
||||
|
||||
try:
|
||||
'┌┬┐╔╦╗╒╤╕╓╥╖│║─═├┼┤╠╬╣╞╪╡╟╫╢└┴┘╚╩╝╘╧╛╙╨╜'.encode(sys.stdout.encoding)
|
||||
except (UnicodeEncodeError, TypeError) as e:
|
||||
logger.warning("UTF-8 is mandatory for sparklines ({})".format(e))
|
||||
logger.warning(f"UTF-8 is mandatory for sparklines ({e})")
|
||||
sparklines_module = False
|
||||
|
||||
|
||||
class Sparkline(object):
|
||||
class Sparkline:
|
||||
"""Manage sparklines (see https://pypi.org/project/sparklines/)."""
|
||||
|
||||
def __init__(self, size, pre_char='[', post_char=']', unit_char='%', display_value=True):
|
||||
@ -58,6 +56,7 @@ class Sparkline(object):
|
||||
return self.__size
|
||||
if self.__display_value:
|
||||
return self.__size - 6
|
||||
return None
|
||||
|
||||
@property
|
||||
def percents(self):
|
||||
@ -81,7 +80,7 @@ class Sparkline(object):
|
||||
if self.__display_value:
|
||||
percents_without_none = [x for x in self.percents if x is not None]
|
||||
if len(percents_without_none) > 0:
|
||||
ret = '{}{:5.1f}{}'.format(ret, percents_without_none[-1], self.__unit_char)
|
||||
ret = f'{ret}{percents_without_none[-1]:5.1f}{self.__unit_char}'
|
||||
ret = nativestr(ret)
|
||||
if overwrite and len(overwrite) < len(ret) - 6:
|
||||
ret = overwrite + ret[len(overwrite) :]
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -11,11 +10,11 @@
|
||||
|
||||
import time
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.globals import printandflush
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class GlancesStdout(object):
|
||||
class GlancesStdout:
|
||||
"""This class manages the Stdout display."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
@ -65,9 +64,9 @@ class GlancesStdout(object):
|
||||
# With attribute
|
||||
if isinstance(stat, dict):
|
||||
try:
|
||||
printandflush("{}.{}: {}".format(plugin, attribute, stat[attribute]))
|
||||
printandflush(f"{plugin}.{attribute}: {stat[attribute]}")
|
||||
except KeyError as err:
|
||||
logger.error("Can not display stat {}.{} ({})".format(plugin, attribute, err))
|
||||
logger.error(f"Can not display stat {plugin}.{attribute} ({err})")
|
||||
elif isinstance(stat, list):
|
||||
for i in stat:
|
||||
if key is None:
|
||||
@ -77,12 +76,12 @@ class GlancesStdout(object):
|
||||
else:
|
||||
continue
|
||||
try:
|
||||
printandflush("{}.{}.{}: {}".format(plugin, i_key, attribute, i[attribute]))
|
||||
printandflush(f"{plugin}.{i_key}.{attribute}: {i[attribute]}")
|
||||
except KeyError as err:
|
||||
logger.error("Can not display stat {}.{} ({})".format(plugin, attribute, err))
|
||||
logger.error(f"Can not display stat {plugin}.{attribute} ({err})")
|
||||
else:
|
||||
# Without attribute
|
||||
printandflush("{}: {}".format(plugin, stat))
|
||||
printandflush(f"{plugin}: {stat}")
|
||||
|
||||
# Wait until next refresh
|
||||
if duration > 0:
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,24 +8,23 @@
|
||||
|
||||
"""Fields description interface class."""
|
||||
|
||||
from pprint import pformat
|
||||
import json
|
||||
import time
|
||||
from pprint import pformat
|
||||
|
||||
from glances import __apiversion__
|
||||
from glances.logger import logger
|
||||
from glances.globals import iteritems
|
||||
from glances.logger import logger
|
||||
|
||||
API_URL = f"http://localhost:61208/api/{__apiversion__}"
|
||||
|
||||
API_URL = "http://localhost:61208/api/{api_version}".format(api_version=__apiversion__)
|
||||
|
||||
APIDOC_HEADER = """\
|
||||
APIDOC_HEADER = f"""\
|
||||
.. _api:
|
||||
|
||||
API (Restfull/JSON) documentation
|
||||
=================================
|
||||
|
||||
This documentation describes the Glances API version {api_version} (Restfull/JSON) interface.
|
||||
This documentation describes the Glances API version {__apiversion__} (Restfull/JSON) interface.
|
||||
|
||||
For Glances version 3, please have a look on:
|
||||
``https://github.com/nicolargo/glances/blob/support/glancesv3/docs/api.rst``
|
||||
@ -45,7 +43,7 @@ It is also ran automatically when Glances is started in Web server mode (-w).
|
||||
API URL
|
||||
-------
|
||||
|
||||
The default root API URL is ``http://localhost:61208/api/{api_version}``.
|
||||
The default root API URL is ``http://localhost:61208/api/{__apiversion__}``.
|
||||
|
||||
The bind address and port could be changed using the ``--bind`` and ``--port`` command line options.
|
||||
|
||||
@ -60,7 +58,7 @@ For example:
|
||||
[outputs]
|
||||
url_prefix = /glances/
|
||||
|
||||
will change the root API URL to ``http://localhost:61208/glances/api/{api_version}`` and the Web UI URL to
|
||||
will change the root API URL to ``http://localhost:61208/glances/api/{__apiversion__}`` and the Web UI URL to
|
||||
``http://localhost:61208/glances/``
|
||||
|
||||
API documentation URL
|
||||
@ -75,9 +73,7 @@ WebUI refresh
|
||||
It is possible to change the Web UI refresh rate (default is 2 seconds) using the following option in the URL:
|
||||
``http://localhost:61208/glances/?refresh=5``
|
||||
|
||||
""".format(
|
||||
api_version=__apiversion__
|
||||
)
|
||||
"""
|
||||
|
||||
|
||||
def indent_stat(stat, indent=' '):
|
||||
@ -85,8 +81,7 @@ def indent_stat(stat, indent=' '):
|
||||
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).replace("'", '"')
|
||||
else:
|
||||
return indent + pformat(stat).replace('\n', '\n' + indent).replace("'", '"')
|
||||
return indent + pformat(stat).replace('\n', '\n' + indent).replace("'", '"')
|
||||
|
||||
|
||||
def print_api_status():
|
||||
@ -99,7 +94,7 @@ def print_api_status():
|
||||
print('')
|
||||
print('Get the Rest API status::')
|
||||
print('')
|
||||
print(' # curl -I {}/status'.format(API_URL))
|
||||
print(f' # curl -I {API_URL}/status')
|
||||
print(indent_stat('HTTP/1.0 200 OK'))
|
||||
print('')
|
||||
|
||||
@ -111,20 +106,20 @@ def print_plugins_list(stat):
|
||||
print('')
|
||||
print('Get the plugins list::')
|
||||
print('')
|
||||
print(' # curl {}/pluginslist'.format(API_URL))
|
||||
print(f' # curl {API_URL}/pluginslist')
|
||||
print(indent_stat(stat))
|
||||
print('')
|
||||
|
||||
|
||||
def print_plugin_stats(plugin, stat):
|
||||
sub_title = 'GET {}'.format(plugin)
|
||||
sub_title = f'GET {plugin}'
|
||||
print(sub_title)
|
||||
print('-' * len(sub_title))
|
||||
print('')
|
||||
|
||||
print('Get plugin stats::')
|
||||
print('')
|
||||
print(' # curl {}/{}'.format(API_URL, plugin))
|
||||
print(f' # curl {API_URL}/{plugin}')
|
||||
print(indent_stat(json.loads(stat.get_stats())))
|
||||
print('')
|
||||
|
||||
@ -183,7 +178,7 @@ def print_plugin_description(plugin, stat):
|
||||
|
||||
print('')
|
||||
else:
|
||||
logger.error('No fields_description variable defined for plugin {}'.format(plugin))
|
||||
logger.error(f'No fields_description variable defined for plugin {plugin}')
|
||||
|
||||
|
||||
def print_plugin_item_value(plugin, stat, stat_export):
|
||||
@ -205,13 +200,13 @@ def print_plugin_item_value(plugin, stat, stat_export):
|
||||
value = stat_item[item]
|
||||
print('Get a specific field::')
|
||||
print('')
|
||||
print(' # curl {}/{}/{}'.format(API_URL, plugin, item))
|
||||
print(f' # curl {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 matches the given value::')
|
||||
print('')
|
||||
print(' # curl {}/{}/{}/{}'.format(API_URL, plugin, item, value))
|
||||
print(f' # curl {API_URL}/{plugin}/{item}/{value}')
|
||||
print(indent_stat(json.loads(stat.get_stats_value(item, value))))
|
||||
print('')
|
||||
|
||||
@ -223,7 +218,7 @@ def print_all():
|
||||
print('')
|
||||
print('Get all Glances stats::')
|
||||
print('')
|
||||
print(' # curl {}/all'.format(API_URL))
|
||||
print(f' # curl {API_URL}/all')
|
||||
print(' Return a very big dictionary (avoid using this request, performances will be poor)...')
|
||||
print('')
|
||||
|
||||
@ -237,7 +232,7 @@ def print_top(stats):
|
||||
print('')
|
||||
print('Get top 2 processes of the processlist plugin::')
|
||||
print('')
|
||||
print(' # curl {}/processlist/top/2'.format(API_URL))
|
||||
print(f' # curl {API_URL}/processlist/top/2')
|
||||
print(indent_stat(stats.get_plugin('processlist').get_export()[:2]))
|
||||
print('')
|
||||
print('Note: Only work for plugin with a list of items')
|
||||
@ -250,7 +245,7 @@ def print_fields_info(stats):
|
||||
print('-' * len(sub_title))
|
||||
print('Get item description (human readable) for a specific plugin/item::')
|
||||
print('')
|
||||
print(' # curl {}/diskio/read_bytes/description'.format(API_URL))
|
||||
print(f' # curl {API_URL}/diskio/read_bytes/description')
|
||||
print(indent_stat(stats.get_plugin('diskio').get_item_info('read_bytes', 'description')))
|
||||
print('')
|
||||
print('Note: the description is defined in the fields_description variable of the plugin.')
|
||||
@ -260,7 +255,7 @@ def print_fields_info(stats):
|
||||
print('-' * len(sub_title))
|
||||
print('Get item unit for a specific plugin/item::')
|
||||
print('')
|
||||
print(' # curl {}/diskio/read_bytes/unit'.format(API_URL))
|
||||
print(f' # curl {API_URL}/diskio/read_bytes/unit')
|
||||
print(indent_stat(stats.get_plugin('diskio').get_item_info('read_bytes', 'unit')))
|
||||
print('')
|
||||
print('Note: the description is defined in the fields_description variable of the plugin.')
|
||||
@ -278,22 +273,22 @@ def print_history(stats):
|
||||
print('')
|
||||
print('History of a plugin::')
|
||||
print('')
|
||||
print(' # curl {}/cpu/history'.format(API_URL))
|
||||
print(f' # curl {API_URL}/cpu/history')
|
||||
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(f' # curl {API_URL}/cpu/history/2')
|
||||
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(f' # curl {API_URL}/cpu/system/history')
|
||||
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(f' # curl {API_URL}/cpu/system/history')
|
||||
print(indent_stat(json.loads(stats.get_plugin('cpu').get_stats_history('system', nb=2))))
|
||||
print('')
|
||||
|
||||
@ -305,17 +300,17 @@ def print_limits(stats):
|
||||
print('')
|
||||
print('All limits/thresholds::')
|
||||
print('')
|
||||
print(' # curl {}/all/limits'.format(API_URL))
|
||||
print(f' # curl {API_URL}/all/limits')
|
||||
print(indent_stat(stats.getAllLimitsAsDict()))
|
||||
print('')
|
||||
print('Limits/thresholds for the cpu plugin::')
|
||||
print('')
|
||||
print(' # curl {}/cpu/limits'.format(API_URL))
|
||||
print(f' # curl {API_URL}/cpu/limits')
|
||||
print(indent_stat(stats.get_plugin('cpu').limits))
|
||||
print('')
|
||||
|
||||
|
||||
class GlancesStdoutApiDoc(object):
|
||||
class GlancesStdoutApiDoc:
|
||||
"""This class manages the fields description display."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -14,7 +13,7 @@ import time
|
||||
from glances.globals import printandflush
|
||||
|
||||
|
||||
class GlancesStdoutCsv(object):
|
||||
class GlancesStdoutCsv:
|
||||
"""This class manages the StdoutCsv display."""
|
||||
|
||||
separator = ','
|
||||
@ -53,18 +52,18 @@ class GlancesStdoutCsv(object):
|
||||
line = ''
|
||||
|
||||
if attribute is not None:
|
||||
line += '{}.{}{}'.format(plugin, attribute, self.separator)
|
||||
line += f'{plugin}.{attribute}{self.separator}'
|
||||
else:
|
||||
if isinstance(stat, dict):
|
||||
for k in stat.keys():
|
||||
line += '{}.{}{}'.format(plugin, str(k), self.separator)
|
||||
line += f'{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)
|
||||
else:
|
||||
line += '{}{}'.format(plugin, self.separator)
|
||||
line += f'{plugin}{self.separator}'
|
||||
|
||||
return line
|
||||
|
||||
@ -73,18 +72,18 @@ class GlancesStdoutCsv(object):
|
||||
line = ''
|
||||
|
||||
if attribute is not None:
|
||||
line += '{}{}'.format(str(stat.get(attribute, self.na)), self.separator)
|
||||
line += f'{str(stat.get(attribute, self.na))}{self.separator}'
|
||||
else:
|
||||
if isinstance(stat, dict):
|
||||
for v in stat.values():
|
||||
line += '{}{}'.format(str(v), self.separator)
|
||||
line += f'{str(v)}{self.separator}'
|
||||
elif isinstance(stat, list):
|
||||
for i in stat:
|
||||
if isinstance(i, dict) and 'key' in i:
|
||||
for v in i.values():
|
||||
line += '{}{}'.format(str(v), self.separator)
|
||||
line += f'{str(v)}{self.separator}'
|
||||
else:
|
||||
line += '{}{}'.format(str(stat), self.separator)
|
||||
line += f'{str(stat)}{self.separator}'
|
||||
|
||||
return line
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -10,16 +9,16 @@
|
||||
"""Issue interface class."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
import time
|
||||
import pprint
|
||||
|
||||
from glances.timer import Counter
|
||||
from glances import __version__, psutil_version
|
||||
import sys
|
||||
import time
|
||||
|
||||
import psutil
|
||||
|
||||
import glances
|
||||
from glances import __version__, psutil_version
|
||||
from glances.timer import Counter
|
||||
|
||||
TERMINAL_WIDTH = 79
|
||||
|
||||
@ -39,7 +38,7 @@ class colors:
|
||||
self.NO = ''
|
||||
|
||||
|
||||
class GlancesStdoutIssue(object):
|
||||
class GlancesStdoutIssue:
|
||||
"""This class manages the Issue display."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
@ -52,18 +51,14 @@ 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(f'Glances {colors.BLUE + __version__ + colors.NO} ({os.path.realpath(glances.__file__)})\n')
|
||||
sys.stdout.write(f'Python {colors.BLUE + platform.python_version() + colors.NO} ({sys.executable})\n')
|
||||
sys.stdout.write(f'PsUtil {colors.BLUE + psutil_version + colors.NO} ({os.path.realpath(psutil.__file__)})\n')
|
||||
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(f'{colors.BLUE + plugin}{result}{message}')
|
||||
sys.stdout.write(colors.NO + '\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
@ -108,9 +103,7 @@ 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 + f' {counter.get():.5f}s ').rjust(41 - len(plugin))
|
||||
if isinstance(stat, list) and len(stat) > 0 and 'key' in stat[0]:
|
||||
key = 'key={} '.format(stat[0]['key'])
|
||||
stat_output = pprint.pformat([stat[0]], compact=True, width=120, depth=3)
|
||||
@ -118,9 +111,7 @@ class GlancesStdoutIssue(object):
|
||||
else:
|
||||
message = '\n' + colors.NO + pprint.pformat(stat, compact=True, width=120, depth=2)
|
||||
else:
|
||||
result = (colors.RED + '[ERROR]' + colors.BLUE + ' {:.5f}s '.format(counter.get())).rjust(
|
||||
41 - len(plugin)
|
||||
)
|
||||
result = (colors.RED + '[ERROR]' + colors.BLUE + f' {counter.get():.5f}s ').rjust(41 - len(plugin))
|
||||
message = colors.NO + str(stat_error)[0 : TERMINAL_WIDTH - 41]
|
||||
|
||||
# Display the result
|
||||
@ -128,7 +119,7 @@ class GlancesStdoutIssue(object):
|
||||
|
||||
# Display total time need to update all plugins
|
||||
sys.stdout.write('=' * TERMINAL_WIDTH + '\n')
|
||||
print("Total time to update all stats: {}{:.5f}s{}".format(colors.BLUE, counter_total.get(), colors.NO))
|
||||
print(f"Total time to update all stats: {colors.BLUE}{counter_total.get():.5f}s{colors.NO}")
|
||||
sys.stdout.write('=' * TERMINAL_WIDTH + '\n')
|
||||
|
||||
# Return True to exit directly (no refresh)
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -14,7 +13,7 @@ import time
|
||||
from glances.globals import printandflush
|
||||
|
||||
|
||||
class GlancesStdoutJson(object):
|
||||
class GlancesStdoutJson:
|
||||
"""This class manages the Stdout JSON display."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
@ -47,7 +46,7 @@ class GlancesStdoutJson(object):
|
||||
else:
|
||||
continue
|
||||
# Display stats
|
||||
printandflush('{}: {}'.format(plugin, stat))
|
||||
printandflush(f'{plugin}: {stat}')
|
||||
|
||||
# Wait until next refresh
|
||||
if duration > 0:
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -10,14 +9,14 @@
|
||||
"""Manage unicode message for Glances output."""
|
||||
|
||||
_unicode_message = {
|
||||
'ARROW_LEFT': [u'\u2190', u'<'],
|
||||
'ARROW_RIGHT': [u'\u2192', u'>'],
|
||||
'ARROW_UP': [u'\u2191', u'^'],
|
||||
'ARROW_DOWN': [u'\u2193', u'v'],
|
||||
'CHECK': [u'\u2713', u''],
|
||||
'PROCESS_SELECTOR': [u'>', u'>'],
|
||||
'MEDIUM_LINE': [u'\u23AF', u'-'],
|
||||
'LOW_LINE': [u'\u2581', u'_'],
|
||||
'ARROW_LEFT': ['\u2190', '<'],
|
||||
'ARROW_RIGHT': ['\u2192', '>'],
|
||||
'ARROW_UP': ['\u2191', '^'],
|
||||
'ARROW_DOWN': ['\u2193', 'v'],
|
||||
'CHECK': ['\u2713', ''],
|
||||
'PROCESS_SELECTOR': ['>', '>'],
|
||||
'MEDIUM_LINE': ['\u23af', '-'],
|
||||
'LOW_LINE': ['\u2581', '_'],
|
||||
}
|
||||
|
||||
|
||||
@ -25,5 +24,4 @@ def unicode_message(key, args=None):
|
||||
"""Return the unicode message for the given key."""
|
||||
if args and hasattr(args, 'disable_unicode') and args.disable_unicode:
|
||||
return _unicode_message[key][1]
|
||||
else:
|
||||
return _unicode_message[key][0]
|
||||
return _unicode_message[key][0]
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,19 +8,19 @@
|
||||
|
||||
"""Manage password."""
|
||||
|
||||
import builtins
|
||||
import getpass
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
from io import open
|
||||
|
||||
from glances.globals import b, safe_makedirs, weak_lru_cache
|
||||
from glances.config import user_config_dir
|
||||
from glances.globals import b, safe_makedirs, weak_lru_cache
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class GlancesPassword(object):
|
||||
class GlancesPassword:
|
||||
"""This class contains all the methods relating to password."""
|
||||
|
||||
def __init__(self, username='glances', config=None):
|
||||
@ -38,8 +37,7 @@ class GlancesPassword(object):
|
||||
"""
|
||||
if self.config is None:
|
||||
return user_config_dir()[0]
|
||||
else:
|
||||
return self.config.get_value('passwords', 'local_password_path', default=user_config_dir()[0])
|
||||
return self.config.get_value('passwords', 'local_password_path', default=user_config_dir()[0])
|
||||
|
||||
@weak_lru_cache(maxsize=32)
|
||||
def get_hash(self, plain_password, salt=''):
|
||||
@ -78,7 +76,7 @@ class GlancesPassword(object):
|
||||
"""
|
||||
if os.path.exists(self.password_file) and not clear:
|
||||
# If the password file exist then use it
|
||||
logger.info("Read password from file {}".format(self.password_file))
|
||||
logger.info(f"Read password from file {self.password_file}")
|
||||
password = self.load_password()
|
||||
else:
|
||||
# password_hash is the plain SHA-pbkdf2_hmac password
|
||||
@ -113,13 +111,11 @@ class GlancesPassword(object):
|
||||
safe_makedirs(self.password_dir)
|
||||
|
||||
# Create/overwrite the password file
|
||||
with open(self.password_file, 'wb') as file_pwd:
|
||||
with builtins.open(self.password_file, 'wb') as file_pwd:
|
||||
file_pwd.write(b(hashed_password))
|
||||
|
||||
def load_password(self):
|
||||
"""Load the hashed password from the Glances folder."""
|
||||
# Read the password file, if it exists
|
||||
with open(self.password_file, 'r') as file_pwd:
|
||||
hashed_password = file_pwd.read()
|
||||
|
||||
return hashed_password
|
||||
with builtins.open(self.password_file) as file_pwd:
|
||||
return file_pwd.read()
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -19,7 +18,7 @@ class GlancesPasswordList(GlancesPassword):
|
||||
_section = "passwords"
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
super(GlancesPasswordList, self).__init__()
|
||||
super().__init__()
|
||||
# password_dict is a dict (JSON compliant)
|
||||
# {'host': 'password', ... }
|
||||
# Load the configuration file
|
||||
@ -32,14 +31,14 @@ class GlancesPasswordList(GlancesPassword):
|
||||
if config is None:
|
||||
logger.warning("No configuration file available. Cannot load password list.")
|
||||
elif not config.has_section(self._section):
|
||||
logger.warning("No [%s] section in the configuration file. Cannot load password list." % self._section)
|
||||
logger.warning(f"No [{self._section}] section in the configuration file. Cannot load password list.")
|
||||
else:
|
||||
logger.info("Start reading the [%s] section in the configuration file" % self._section)
|
||||
logger.info(f"Start reading the [{self._section}] section in the configuration file")
|
||||
|
||||
password_dict = dict(config.items(self._section))
|
||||
|
||||
# Password list loaded
|
||||
logger.info("%s password(s) loaded from the configuration file" % len(password_dict))
|
||||
logger.info(f"{len(password_dict)} password(s) loaded from the configuration file")
|
||||
|
||||
return password_dict
|
||||
|
||||
@ -51,14 +50,14 @@ class GlancesPasswordList(GlancesPassword):
|
||||
"""
|
||||
if host is None:
|
||||
return self._password_dict
|
||||
else:
|
||||
|
||||
try:
|
||||
return self._password_dict[host]
|
||||
except (KeyError, TypeError):
|
||||
try:
|
||||
return self._password_dict[host]
|
||||
return self._password_dict['default']
|
||||
except (KeyError, TypeError):
|
||||
try:
|
||||
return self._password_dict['default']
|
||||
except (KeyError, TypeError):
|
||||
return None
|
||||
return None
|
||||
|
||||
def set_password(self, host, password):
|
||||
"""Set a password for a specific host."""
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -101,9 +100,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
args=args, config=config, stats_init_value=[], fields_description=fields_description
|
||||
)
|
||||
super().__init__(args=args, config=config, stats_init_value=[], fields_description=fields_description)
|
||||
|
||||
# We want to display the stat in the curse interface
|
||||
self.display_curse = True
|
||||
@ -174,7 +171,7 @@ class PluginModel(GlancesPluginModel):
|
||||
# Top processes
|
||||
top_process = ', '.join(alert['top'])
|
||||
if top_process != '':
|
||||
msg = ': {}'.format(top_process)
|
||||
msg = f': {top_process}'
|
||||
ret.append(self.curse_add_line(msg))
|
||||
|
||||
return ret
|
||||
@ -183,5 +180,4 @@ class PluginModel(GlancesPluginModel):
|
||||
"""Compare a with b using the tolerance (if numerical)."""
|
||||
if str(int(a)).isdigit() and str(int(b)).isdigit():
|
||||
return abs(a - b) <= max(abs(a), abs(b)) * tolerance
|
||||
else:
|
||||
return a == b
|
||||
return a == b
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,8 +8,8 @@
|
||||
|
||||
"""Monitor plugin."""
|
||||
|
||||
from glances.globals import iteritems
|
||||
from glances.amps_list import AmpsList as glancesAmpsList
|
||||
from glances.globals import iteritems
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
# Fields description
|
||||
@ -35,9 +34,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
args=args, config=config, stats_init_value=[], fields_description=fields_description
|
||||
)
|
||||
super().__init__(args=args, config=config, stats_init_value=[], fields_description=fields_description)
|
||||
self.args = args
|
||||
self.config = config
|
||||
|
||||
@ -93,13 +90,11 @@ class PluginModel(GlancesPluginModel):
|
||||
if nbprocess > 0:
|
||||
if int(countmin) <= int(nbprocess) <= int(countmax):
|
||||
return 'OK'
|
||||
else:
|
||||
return 'WARNING'
|
||||
else:
|
||||
if int(countmin) == 0:
|
||||
return 'OK'
|
||||
else:
|
||||
return 'CRITICAL'
|
||||
return 'WARNING'
|
||||
|
||||
if int(countmin) == 0:
|
||||
return 'OK'
|
||||
return 'CRITICAL'
|
||||
|
||||
def msg_curse(self, args=None, max_width=None):
|
||||
"""Return the dict to display in the curse interface."""
|
||||
@ -121,10 +116,10 @@ class PluginModel(GlancesPluginModel):
|
||||
second_column = '{}'.format(m['count'] if m['regex'] else '')
|
||||
for line in m['result'].split('\n'):
|
||||
# Display first column with the process name...
|
||||
msg = '{:<16} '.format(first_column)
|
||||
msg = f'{first_column:<16} '
|
||||
ret.append(self.curse_add_line(msg, first_column_style))
|
||||
# ... and second column with the number of matching processes...
|
||||
msg = '{:<4} '.format(second_column)
|
||||
msg = f'{second_column:<4} '
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# ... only on the first line
|
||||
first_column = second_column = ''
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -17,8 +16,8 @@ Supported Cloud API:
|
||||
import threading
|
||||
|
||||
from glances.globals import iteritems, to_ascii
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
from glances.logger import logger
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
# Import plugin specific dependency
|
||||
try:
|
||||
@ -26,7 +25,7 @@ try:
|
||||
except ImportError as e:
|
||||
import_error_tag = True
|
||||
# Display debug message if import error
|
||||
logger.warning("Missing Python Lib ({}), Cloud plugin is disabled".format(e))
|
||||
logger.warning(f"Missing Python Lib ({e}), Cloud plugin is disabled")
|
||||
else:
|
||||
import_error_tag = False
|
||||
|
||||
@ -44,7 +43,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(args=args, config=config)
|
||||
super().__init__(args=args, config=config)
|
||||
|
||||
# We want to display the stat in the curse interface
|
||||
self.display_curse = True
|
||||
@ -65,7 +64,7 @@ class PluginModel(GlancesPluginModel):
|
||||
self.OPENSTACK.stop()
|
||||
self.OPENSTACKEC2.stop()
|
||||
# Call the father class
|
||||
super(PluginModel, self).exit()
|
||||
super().exit()
|
||||
|
||||
@GlancesPluginModel._check_decorator
|
||||
@GlancesPluginModel._log_result_decorator
|
||||
@ -145,7 +144,7 @@ class ThreadOpenStack(threading.Thread):
|
||||
def __init__(self):
|
||||
"""Init the class."""
|
||||
logger.debug("cloud plugin - Create thread for OpenStack metadata")
|
||||
super(ThreadOpenStack, self).__init__()
|
||||
super().__init__()
|
||||
# Event needed to stop properly the thread
|
||||
self._stopper = threading.Event()
|
||||
# The class return the stats as a dict
|
||||
@ -161,12 +160,12 @@ class ThreadOpenStack(threading.Thread):
|
||||
return False
|
||||
|
||||
for k, v in iteritems(self.OPENSTACK_API_METADATA):
|
||||
r_url = '{}/{}'.format(self.OPENSTACK_API_URL, v)
|
||||
r_url = f'{self.OPENSTACK_API_URL}/{v}'
|
||||
try:
|
||||
# Local request, a timeout of 3 seconds is OK
|
||||
r = requests.get(r_url, timeout=3)
|
||||
except Exception as e:
|
||||
logger.debug('cloud plugin - Cannot connect to the OpenStack metadata API {}: {}'.format(r_url, e))
|
||||
logger.debug(f'cloud plugin - Cannot connect to the OpenStack metadata API {r_url}: {e}')
|
||||
break
|
||||
else:
|
||||
if r.ok:
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -8,14 +7,13 @@
|
||||
#
|
||||
|
||||
"""Connections plugin."""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
from glances.globals import nativestr
|
||||
|
||||
import psutil
|
||||
|
||||
from glances.globals import nativestr
|
||||
from glances.logger import logger
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
# Fields description
|
||||
# description: human readable description
|
||||
# short_name: shortname to use un UI
|
||||
@ -93,7 +91,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
super().__init__(
|
||||
args=args,
|
||||
config=config,
|
||||
# items_history_list=items_history_list,
|
||||
@ -122,7 +120,7 @@ class PluginModel(GlancesPluginModel):
|
||||
try:
|
||||
net_connections = psutil.net_connections(kind="tcp")
|
||||
except Exception as e:
|
||||
logger.warning('Can not get network connections stats ({})'.format(e))
|
||||
logger.warning(f'Can not get network connections stats ({e})')
|
||||
logger.info('Disable connections stats')
|
||||
stats['net_connections_enabled'] = False
|
||||
self.stats = stats
|
||||
@ -145,10 +143,10 @@ class PluginModel(GlancesPluginModel):
|
||||
# Grab connections track directly from the /proc file
|
||||
for i in self.conntrack:
|
||||
try:
|
||||
with open(self.conntrack[i], 'r') as f:
|
||||
with open(self.conntrack[i]) as f:
|
||||
stats[i] = float(f.readline().rstrip("\n"))
|
||||
except (IOError, FileNotFoundError) as e:
|
||||
logger.warning('Can not get network connections track ({})'.format(e))
|
||||
except (OSError, FileNotFoundError) as e:
|
||||
logger.warning(f'Can not get network connections track ({e})')
|
||||
logger.info('Disable connections track')
|
||||
stats['nf_conntrack_enabled'] = False
|
||||
self.stats = stats
|
||||
@ -171,7 +169,7 @@ class PluginModel(GlancesPluginModel):
|
||||
def update_views(self):
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
super(PluginModel, self).update_views()
|
||||
super().update_views()
|
||||
|
||||
# Add specific information
|
||||
try:
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -127,7 +126,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
super().__init__(
|
||||
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
|
||||
)
|
||||
|
||||
@ -164,8 +163,7 @@ class PluginModel(GlancesPluginModel):
|
||||
conf_podman_sock = self.get_conf_value('podman_sock')
|
||||
if len(conf_podman_sock) == 0:
|
||||
return "unix:///run/user/1000/podman/podman.sock"
|
||||
else:
|
||||
return conf_podman_sock[0]
|
||||
return conf_podman_sock[0]
|
||||
|
||||
def exit(self):
|
||||
"""Overwrite the exit method to close threads."""
|
||||
@ -174,7 +172,7 @@ class PluginModel(GlancesPluginModel):
|
||||
if self.podman_extension:
|
||||
self.podman_extension.stop()
|
||||
# Call the father class
|
||||
super(PluginModel, self).exit()
|
||||
super().exit()
|
||||
|
||||
def get_key(self):
|
||||
"""Return the key of the list."""
|
||||
@ -189,7 +187,7 @@ class PluginModel(GlancesPluginModel):
|
||||
try:
|
||||
ret = deepcopy(self.stats)
|
||||
except KeyError as e:
|
||||
logger.debug("docker plugin - Docker export error {}".format(e))
|
||||
logger.debug(f"docker plugin - Docker export error {e}")
|
||||
ret = []
|
||||
|
||||
# Remove fields uses to compute rate
|
||||
@ -209,8 +207,7 @@ class PluginModel(GlancesPluginModel):
|
||||
all_tag = self.get_conf_value('all')
|
||||
if len(all_tag) == 0:
|
||||
return False
|
||||
else:
|
||||
return all_tag[0].lower() == 'true'
|
||||
return all_tag[0].lower() == 'true'
|
||||
|
||||
@GlancesPluginModel._check_decorator
|
||||
@GlancesPluginModel._log_result_decorator
|
||||
@ -262,7 +259,7 @@ class PluginModel(GlancesPluginModel):
|
||||
def update_views(self):
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
super(PluginModel, self).update_views()
|
||||
super().update_views()
|
||||
|
||||
if not self.stats:
|
||||
return False
|
||||
@ -302,7 +299,7 @@ class PluginModel(GlancesPluginModel):
|
||||
show_pod_name = True
|
||||
self.views['show_pod_name'] = show_pod_name
|
||||
show_engine_name = False
|
||||
if len(set(ct["engine"] for ct in self.stats)) > 1:
|
||||
if len({ct["engine"] for ct in self.stats}) > 1:
|
||||
show_engine_name = True
|
||||
self.views['show_engine_name'] = show_engine_name
|
||||
|
||||
@ -321,9 +318,9 @@ class PluginModel(GlancesPluginModel):
|
||||
# Title
|
||||
msg = '{}'.format('CONTAINERS')
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
msg = ' {}'.format(len(self.stats))
|
||||
msg = f' {len(self.stats)}'
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = ' sorted by {}'.format(sort_for_human[self.sort_key])
|
||||
msg = f' sorted by {sort_for_human[self.sort_key]}'
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
# Header
|
||||
@ -404,13 +401,13 @@ class PluginModel(GlancesPluginModel):
|
||||
unit = 'B'
|
||||
try:
|
||||
value = self.auto_unit(int(container['io_rx'])) + unit
|
||||
msg = '{:>7}'.format(value)
|
||||
msg = f'{value:>7}'
|
||||
except (KeyError, TypeError):
|
||||
msg = '{:>7}'.format('_')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
try:
|
||||
value = self.auto_unit(int(container['io_wx'])) + unit
|
||||
msg = ' {:<7}'.format(value)
|
||||
msg = f' {value:<7}'
|
||||
except (KeyError, TypeError):
|
||||
msg = ' {:<7}'.format('_')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
@ -425,13 +422,13 @@ class PluginModel(GlancesPluginModel):
|
||||
unit = 'b'
|
||||
try:
|
||||
value = self.auto_unit(int(container['network_rx'] * to_bit)) + unit
|
||||
msg = '{:>7}'.format(value)
|
||||
msg = f'{value:>7}'
|
||||
except (KeyError, TypeError):
|
||||
msg = '{:>7}'.format('_')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
try:
|
||||
value = self.auto_unit(int(container['network_tx'] * to_bit)) + unit
|
||||
msg = ' {:<7}'.format(value)
|
||||
msg = f' {value:<7}'
|
||||
except (KeyError, TypeError):
|
||||
msg = ' {:<7}'.format('_')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
@ -453,12 +450,11 @@ class PluginModel(GlancesPluginModel):
|
||||
"""Analyse the container status."""
|
||||
if status == 'running':
|
||||
return 'OK'
|
||||
elif status == 'exited':
|
||||
if status == 'exited':
|
||||
return 'WARNING'
|
||||
elif status == 'dead':
|
||||
if status == 'dead':
|
||||
return 'CRITICAL'
|
||||
else:
|
||||
return 'CAREFUL'
|
||||
return 'CAREFUL'
|
||||
|
||||
|
||||
def sort_docker_stats(stats):
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -8,6 +7,7 @@
|
||||
#
|
||||
|
||||
"""Docker Extension unit for Glances' Containers plugin."""
|
||||
|
||||
import time
|
||||
|
||||
from glances.globals import iterkeys, itervalues, nativestr, pretty_date, replace_special_chars
|
||||
@ -17,13 +17,13 @@ from glances.plugins.containers.stats_streamer import StatsStreamer
|
||||
# Docker-py library (optional and Linux-only)
|
||||
# https://github.com/docker/docker-py
|
||||
try:
|
||||
import requests
|
||||
import docker
|
||||
import requests
|
||||
from dateutil import parser, tz
|
||||
except Exception as e:
|
||||
import_docker_error_tag = True
|
||||
# Display debug message if import KeyError
|
||||
logger.warning("Error loading Docker deps Lib. Docker plugin is disabled ({})".format(e))
|
||||
logger.warning(f"Error loading Docker deps Lib. Docker plugin is disabled ({e})")
|
||||
else:
|
||||
import_docker_error_tag = False
|
||||
|
||||
@ -46,7 +46,7 @@ class DockerStatsFetcher:
|
||||
self._streamer = StatsStreamer(stats_iterable, initial_stream_value={})
|
||||
|
||||
def _log_debug(self, msg, exception=None):
|
||||
logger.debug("containers (Docker) ID: {} - {} ({}) ".format(self._container.id, msg, exception))
|
||||
logger.debug(f"containers (Docker) ID: {self._container.id} - {msg} ({exception}) ")
|
||||
logger.debug(self._streamer.stats)
|
||||
|
||||
def stop(self):
|
||||
@ -70,13 +70,12 @@ class DockerStatsFetcher:
|
||||
memory_stats = self._get_memory_stats()
|
||||
network_stats = self._get_network_stats()
|
||||
|
||||
computed_stats = {
|
||||
return {
|
||||
"io": io_stats or {},
|
||||
"memory": memory_stats or {},
|
||||
"network": network_stats or {},
|
||||
"cpu": cpu_stats or {"total": 0.0},
|
||||
}
|
||||
return computed_stats
|
||||
|
||||
@property
|
||||
def time_since_update(self):
|
||||
@ -229,7 +228,7 @@ class DockerContainersExtension:
|
||||
# Do not use the timeout option (see issue #1878)
|
||||
self.client = docker.from_env()
|
||||
except Exception as e:
|
||||
logger.error("{} plugin - Can't connect to Docker ({})".format(self.ext_name, e))
|
||||
logger.error(f"{self.ext_name} plugin - Can't connect to Docker ({e})")
|
||||
self.client = None
|
||||
|
||||
def update_version(self):
|
||||
@ -256,7 +255,7 @@ class DockerContainersExtension:
|
||||
# The Containers/all key of the configuration file should be set to True
|
||||
containers = self.client.containers.list(all=all_tag)
|
||||
except Exception as e:
|
||||
logger.error("{} plugin - Can't get containers list ({})".format(self.ext_name, e))
|
||||
logger.error(f"{self.ext_name} plugin - Can't get containers list ({e})")
|
||||
return version_stats, []
|
||||
|
||||
# Start new thread for new container
|
||||
@ -264,14 +263,14 @@ class DockerContainersExtension:
|
||||
if container.id not in self.stats_fetchers:
|
||||
# StatsFetcher did not exist in the internal dict
|
||||
# Create it, add it to the internal dict
|
||||
logger.debug("{} plugin - Create thread for container {}".format(self.ext_name, container.id[:12]))
|
||||
logger.debug(f"{self.ext_name} plugin - Create thread for container {container.id[:12]}")
|
||||
self.stats_fetchers[container.id] = DockerStatsFetcher(container)
|
||||
|
||||
# Stop threads for non-existing containers
|
||||
absent_containers = set(iterkeys(self.stats_fetchers)) - set(c.id for c in containers)
|
||||
absent_containers = set(iterkeys(self.stats_fetchers)) - {c.id for c in containers}
|
||||
for container_id in absent_containers:
|
||||
# Stop the StatsFetcher
|
||||
logger.debug("{} plugin - Stop thread for old container {}".format(self.ext_name, container_id[:12]))
|
||||
logger.debug(f"{self.ext_name} plugin - Stop thread for old container {container_id[:12]}")
|
||||
self.stats_fetchers[container_id].stop()
|
||||
# Delete the StatsFetcher from the dict
|
||||
del self.stats_fetchers[container_id]
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -7,9 +6,10 @@
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
"""Podman Extension unit for Glances' Containers plugin."""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from glances.globals import iterkeys, itervalues, nativestr, pretty_date, string_value_to_float, replace_special_chars
|
||||
from glances.globals import iterkeys, itervalues, nativestr, pretty_date, replace_special_chars, string_value_to_float
|
||||
from glances.logger import logger
|
||||
from glances.plugins.containers.stats_streamer import StatsStreamer
|
||||
|
||||
@ -20,7 +20,7 @@ try:
|
||||
except Exception as e:
|
||||
import_podman_error_tag = True
|
||||
# Display debug message if import KeyError
|
||||
logger.warning("Error loading Podman deps Lib. Podman feature in the Containers plugin is disabled ({})".format(e))
|
||||
logger.warning(f"Error loading Podman deps Lib. Podman feature in the Containers plugin is disabled ({e})")
|
||||
else:
|
||||
import_podman_error_tag = False
|
||||
|
||||
@ -36,7 +36,7 @@ class PodmanContainerStatsFetcher:
|
||||
self._streamer = StatsStreamer(stats_iterable, initial_stream_value={})
|
||||
|
||||
def _log_debug(self, msg, exception=None):
|
||||
logger.debug("containers (Podman) ID: {} - {} ({})".format(self._container.id, msg, exception))
|
||||
logger.debug(f"containers (Podman) ID: {self._container.id} - {msg} ({exception})")
|
||||
logger.debug(self._streamer.stats)
|
||||
|
||||
def stop(self):
|
||||
@ -95,7 +95,7 @@ class PodmanPodStatsFetcher:
|
||||
self._streamer = StatsStreamer(stats_iterable, initial_stream_value={}, sleep_duration=2)
|
||||
|
||||
def _log_debug(self, msg, exception=None):
|
||||
logger.debug("containers (Podman): Pod Manager - {} ({})".format(msg, exception))
|
||||
logger.debug(f"containers (Podman): Pod Manager - {msg} ({exception})")
|
||||
logger.debug(self._streamer.stats)
|
||||
|
||||
def stop(self):
|
||||
@ -234,7 +234,7 @@ class PodmanContainersExtension:
|
||||
# PodmanClient works lazily, so make a ping to determine if socket is open
|
||||
self.client.ping()
|
||||
except Exception as e:
|
||||
logger.debug("{} plugin - Can't connect to Podman ({})".format(self.ext_name, e))
|
||||
logger.debug(f"{self.ext_name} plugin - Can't connect to Podman ({e})")
|
||||
self.client = None
|
||||
|
||||
def update_version(self):
|
||||
@ -266,7 +266,7 @@ class PodmanContainersExtension:
|
||||
if not self.pods_stats_fetcher:
|
||||
self.pods_stats_fetcher = PodmanPodStatsFetcher(self.client.pods)
|
||||
except Exception as e:
|
||||
logger.error("{} plugin - Can't get containers list ({})".format(self.ext_name, e))
|
||||
logger.error(f"{self.ext_name} plugin - Can't get containers list ({e})")
|
||||
return version_stats, []
|
||||
|
||||
# Start new thread for new container
|
||||
@ -274,14 +274,14 @@ class PodmanContainersExtension:
|
||||
if container.id not in self.container_stats_fetchers:
|
||||
# StatsFetcher did not exist in the internal dict
|
||||
# Create it, add it to the internal dict
|
||||
logger.debug("{} plugin - Create thread for container {}".format(self.ext_name, container.id[:12]))
|
||||
logger.debug(f"{self.ext_name} plugin - Create thread for container {container.id[:12]}")
|
||||
self.container_stats_fetchers[container.id] = PodmanContainerStatsFetcher(container)
|
||||
|
||||
# Stop threads for non-existing containers
|
||||
absent_containers = set(iterkeys(self.container_stats_fetchers)) - set(c.id for c in containers)
|
||||
absent_containers = set(iterkeys(self.container_stats_fetchers)) - {c.id for c in containers}
|
||||
for container_id in absent_containers:
|
||||
# Stop the StatsFetcher
|
||||
logger.debug("{} plugin - Stop thread for old container {}".format(self.ext_name, container_id[:12]))
|
||||
logger.debug(f"{self.ext_name} plugin - Stop thread for old container {container_id[:12]}")
|
||||
self.container_stats_fetchers[container_id].stop()
|
||||
# Delete the StatsFetcher from the dict
|
||||
del self.container_stats_fetchers[container_id]
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -63,7 +62,7 @@ class StatsStreamer:
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
logger.debug("docker plugin - Exception thrown during run ({})".format(e))
|
||||
logger.debug(f"docker plugin - Exception thrown during run ({e})")
|
||||
self.stop()
|
||||
|
||||
def _pre_update_hook(self):
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,10 +8,10 @@
|
||||
|
||||
"""CPU core plugin."""
|
||||
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
import psutil
|
||||
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
# Fields description
|
||||
fields_description = {
|
||||
'phys': {'description': 'Number of physical cores (hyper thread CPUs are excluded).', 'unit': 'number'},
|
||||
@ -34,7 +33,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(args=args, config=config, fields_description=fields_description)
|
||||
super().__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
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,13 +8,13 @@
|
||||
|
||||
"""CPU plugin."""
|
||||
|
||||
from glances.globals import LINUX, WINDOWS, SUNOS, iterkeys
|
||||
import psutil
|
||||
|
||||
from glances.cpu_percent import cpu_percent
|
||||
from glances.globals import LINUX, SUNOS, WINDOWS, iterkeys
|
||||
from glances.plugins.core import PluginModel as CorePluginModel
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
import psutil
|
||||
|
||||
# Fields description
|
||||
# description: human readable description
|
||||
# short_name: shortname to use un UI
|
||||
@ -144,7 +143,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the CPU plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
super().__init__(
|
||||
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
|
||||
)
|
||||
|
||||
@ -273,7 +272,7 @@ class PluginModel(GlancesPluginModel):
|
||||
def update_views(self):
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
super(PluginModel, self).update_views()
|
||||
super().update_views()
|
||||
|
||||
# Add specifics information
|
||||
# Alert and log
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -8,14 +7,13 @@
|
||||
#
|
||||
|
||||
"""Disk I/O plugin."""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.globals import nativestr
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
import psutil
|
||||
|
||||
from glances.globals import nativestr
|
||||
from glances.logger import logger
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
# Fields description
|
||||
# description: human readable description
|
||||
# short_name: shortname to use un UI
|
||||
@ -61,7 +59,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
super().__init__(
|
||||
args=args,
|
||||
config=config,
|
||||
items_history_list=items_history_list,
|
||||
@ -141,7 +139,7 @@ class PluginModel(GlancesPluginModel):
|
||||
def update_views(self):
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
super(PluginModel, self).update_views()
|
||||
super().update_views()
|
||||
|
||||
# Check if the stats should be hidden
|
||||
self.update_views_hidden()
|
||||
@ -171,7 +169,7 @@ class PluginModel(GlancesPluginModel):
|
||||
name_max_width = max_width - 13
|
||||
else:
|
||||
# No max_width defined, return an emptu curse message
|
||||
logger.debug("No max_width defined for the {} plugin, it will not be displayed.".format(self.plugin_name))
|
||||
logger.debug(f"No max_width defined for the {self.plugin_name} plugin, it will not be displayed.")
|
||||
return ret
|
||||
|
||||
# Header
|
||||
@ -190,7 +188,7 @@ class PluginModel(GlancesPluginModel):
|
||||
# Disk list (sorted by name)
|
||||
for i in self.sorted_stats():
|
||||
# Hide stats if never be different from 0 (issue #1787)
|
||||
if all([self.get_views(item=i[self.get_key()], key=f, option='hidden') for f in self.hide_zero_fields]):
|
||||
if all(self.get_views(item=i[self.get_key()], key=f, option='hidden') for f in self.hide_zero_fields):
|
||||
continue
|
||||
# Is there an alias for the disk name ?
|
||||
disk_name = i['alias'] if 'alias' in i else i['disk_name']
|
||||
@ -205,13 +203,13 @@ class PluginModel(GlancesPluginModel):
|
||||
# count
|
||||
txps = self.auto_unit(i.get('read_count_rate_per_sec', None))
|
||||
rxps = self.auto_unit(i.get('write_count_rate_per_sec', None))
|
||||
msg = '{:>7}'.format(txps)
|
||||
msg = f'{txps:>7}'
|
||||
ret.append(
|
||||
self.curse_add_line(
|
||||
msg, self.get_views(item=i[self.get_key()], key='read_count', option='decoration')
|
||||
)
|
||||
)
|
||||
msg = '{:>7}'.format(rxps)
|
||||
msg = f'{rxps:>7}'
|
||||
ret.append(
|
||||
self.curse_add_line(
|
||||
msg, self.get_views(item=i[self.get_key()], key='write_count', option='decoration')
|
||||
@ -221,13 +219,13 @@ class PluginModel(GlancesPluginModel):
|
||||
# Bitrate
|
||||
txps = self.auto_unit(i.get('read_bytes_rate_per_sec', None))
|
||||
rxps = self.auto_unit(i.get('write_bytes_rate_per_sec', None))
|
||||
msg = '{:>7}'.format(txps)
|
||||
msg = f'{txps:>7}'
|
||||
ret.append(
|
||||
self.curse_add_line(
|
||||
msg, self.get_views(item=i[self.get_key()], key='read_bytes', option='decoration')
|
||||
)
|
||||
)
|
||||
msg = '{:>7}'.format(rxps)
|
||||
msg = f'{rxps:>7}'
|
||||
ret.append(
|
||||
self.curse_add_line(
|
||||
msg, self.get_views(item=i[self.get_key()], key='write_bytes', option='decoration')
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -8,14 +7,12 @@
|
||||
#
|
||||
|
||||
"""Folder plugin."""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.globals import nativestr
|
||||
from glances.folder_list import FolderList as glancesFolderList
|
||||
from glances.globals import nativestr
|
||||
from glances.logger import logger
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
|
||||
# Fields description
|
||||
# description: human readable description
|
||||
# short_name: shortname to use un UI
|
||||
@ -56,9 +53,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
args=args, config=config, stats_init_value=[], fields_description=fields_description
|
||||
)
|
||||
super().__init__(args=args, config=config, stats_init_value=[], fields_description=fields_description)
|
||||
|
||||
self.args = args
|
||||
self.config = config
|
||||
@ -138,7 +133,7 @@ class PluginModel(GlancesPluginModel):
|
||||
name_max_width = max_width - 7
|
||||
else:
|
||||
# No max_width defined, return an emptu curse message
|
||||
logger.debug("No max_width defined for the {} plugin, it will not be displayed.".format(self.plugin_name))
|
||||
logger.debug(f"No max_width defined for the {self.plugin_name} plugin, it will not be displayed.")
|
||||
return ret
|
||||
|
||||
# Header
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -8,16 +7,15 @@
|
||||
#
|
||||
|
||||
"""File system plugin."""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import operator
|
||||
|
||||
from glances.globals import u, nativestr, PermissionError
|
||||
import psutil
|
||||
|
||||
from glances.globals import PermissionError, nativestr, u
|
||||
from glances.logger import logger
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
import psutil
|
||||
|
||||
# Fields description
|
||||
# description: human readable description
|
||||
# short_name: shortname to use un UI
|
||||
@ -97,7 +95,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
super().__init__(
|
||||
args=args,
|
||||
config=config,
|
||||
items_history_list=items_history_list,
|
||||
@ -141,7 +139,7 @@ class PluginModel(GlancesPluginModel):
|
||||
logger.debug("Plugin - fs: PsUtil extended fetch failed")
|
||||
else:
|
||||
# Discard duplicates (#2299) and add entries matching allowed fs types
|
||||
tracked_mnt_points = set(f.mountpoint for f in fs_stat)
|
||||
tracked_mnt_points = {f.mountpoint for f in fs_stat}
|
||||
for f in all_mounted_fs:
|
||||
if (
|
||||
any(f.fstype.find(fs_type) >= 0 for fs_type in allowed_fs_types)
|
||||
@ -166,7 +164,7 @@ class PluginModel(GlancesPluginModel):
|
||||
'device_name': fs.device,
|
||||
'fs_type': fs.fstype,
|
||||
# Manage non breaking space (see issue #1065)
|
||||
'mnt_point': u(fs.mountpoint).replace(u'\u00A0', ' '),
|
||||
'mnt_point': u(fs.mountpoint).replace('\u00a0', ' '),
|
||||
'size': fs_usage.total,
|
||||
'used': fs_usage.used,
|
||||
'free': fs_usage.free,
|
||||
@ -215,8 +213,7 @@ class PluginModel(GlancesPluginModel):
|
||||
# Do not take hidden file system into account
|
||||
if self.is_hide(fs_current['mnt_point']):
|
||||
continue
|
||||
else:
|
||||
stats.append(fs_current)
|
||||
stats.append(fs_current)
|
||||
else:
|
||||
# Default behavior
|
||||
for fs in fs_stat:
|
||||
@ -231,8 +228,7 @@ class PluginModel(GlancesPluginModel):
|
||||
# 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
|
||||
else:
|
||||
stats.append(fs_current)
|
||||
stats.append(fs_current)
|
||||
|
||||
# Update the stats
|
||||
self.stats = stats
|
||||
@ -242,7 +238,7 @@ class PluginModel(GlancesPluginModel):
|
||||
def update_views(self):
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
super(PluginModel, self).update_views()
|
||||
super().update_views()
|
||||
|
||||
# Add specifics information
|
||||
# Alert
|
||||
@ -265,7 +261,7 @@ class PluginModel(GlancesPluginModel):
|
||||
name_max_width = max_width - 13
|
||||
else:
|
||||
# No max_width defined, return an emptu curse message
|
||||
logger.debug("No max_width defined for the {} plugin, it will not be displayed.".format(self.plugin_name))
|
||||
logger.debug(f"No max_width defined for the {self.plugin_name} plugin, it will not be displayed.")
|
||||
return ret
|
||||
|
||||
# Build the string message
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -16,8 +15,8 @@ Currently supported:
|
||||
"""
|
||||
|
||||
from glances.globals import to_fahrenheit
|
||||
from glances.plugins.gpu.cards.nvidia import NvidiaGPU
|
||||
from glances.plugins.gpu.cards.amd import AmdGPU
|
||||
from glances.plugins.gpu.cards.nvidia import NvidiaGPU
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
# Fields description
|
||||
@ -67,7 +66,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
super().__init__(
|
||||
args=args,
|
||||
config=config,
|
||||
items_history_list=items_history_list,
|
||||
@ -89,7 +88,7 @@ class PluginModel(GlancesPluginModel):
|
||||
self.amd.exit()
|
||||
|
||||
# Call the father exit method
|
||||
super(PluginModel, self).exit()
|
||||
super().exit()
|
||||
|
||||
def get_key(self):
|
||||
"""Return the key of the list."""
|
||||
@ -150,7 +149,7 @@ class PluginModel(GlancesPluginModel):
|
||||
def update_views(self):
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
super(PluginModel, self).update_views()
|
||||
super().update_views()
|
||||
|
||||
# Add specifics information
|
||||
# Alert
|
||||
@ -190,7 +189,7 @@ class PluginModel(GlancesPluginModel):
|
||||
# Header
|
||||
header = ''
|
||||
if len(self.stats) > 1:
|
||||
header += '{}'.format(len(self.stats))
|
||||
header += f'{len(self.stats)}'
|
||||
if same_name:
|
||||
header += ' {}'.format(gpu_stats['name'])
|
||||
else:
|
||||
@ -213,7 +212,7 @@ class PluginModel(GlancesPluginModel):
|
||||
except TypeError:
|
||||
mean_proc_msg = '{:>4}'.format('N/A')
|
||||
else:
|
||||
mean_proc_msg = '{:>3.0f}%'.format(mean_proc)
|
||||
mean_proc_msg = f'{mean_proc:>3.0f}%'
|
||||
if len(self.stats) > 1:
|
||||
msg = '{:13}'.format('proc mean:')
|
||||
else:
|
||||
@ -232,7 +231,7 @@ class PluginModel(GlancesPluginModel):
|
||||
except TypeError:
|
||||
mean_mem_msg = '{:>4}'.format('N/A')
|
||||
else:
|
||||
mean_mem_msg = '{:>3.0f}%'.format(mean_mem)
|
||||
mean_mem_msg = f'{mean_mem:>3.0f}%'
|
||||
if len(self.stats) > 1:
|
||||
msg = '{:13}'.format('mem mean:')
|
||||
else:
|
||||
@ -255,7 +254,7 @@ class PluginModel(GlancesPluginModel):
|
||||
if args.fahrenheit:
|
||||
mean_temperature = to_fahrenheit(mean_temperature)
|
||||
unit = 'F'
|
||||
mean_temperature_msg = '{:>3.0f}{}'.format(mean_temperature, unit)
|
||||
mean_temperature_msg = f'{mean_temperature:>3.0f}{unit}'
|
||||
if len(self.stats) > 1:
|
||||
msg = '{:13}'.format('temp mean:')
|
||||
else:
|
||||
@ -283,7 +282,7 @@ class PluginModel(GlancesPluginModel):
|
||||
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 = f'{id_msg} {proc_msg} mem {mem_msg}'
|
||||
ret.append(self.curse_add_line(msg))
|
||||
|
||||
return ret
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -35,8 +34,9 @@ See: https://wiki.archlinux.org/title/AMDGPU#Manually
|
||||
# └── 0
|
||||
# └── amdgpu_pm_info
|
||||
|
||||
import re
|
||||
import os
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
DRM_ROOT_FOLDER: str = '/sys/class/drm'
|
||||
CARD_REGEX: str = r"^card\d$"
|
||||
@ -64,7 +64,7 @@ class AmdGPU:
|
||||
stats = []
|
||||
|
||||
for index, device in enumerate(self.device_folders):
|
||||
device_stats = dict()
|
||||
device_stats = {}
|
||||
# Dictionary key is the GPU_ID
|
||||
device_stats['key'] = 'gpu_id'
|
||||
# GPU id (for multiple GPU, start at 0)
|
||||
@ -104,7 +104,7 @@ def get_device_name(device_folder: str) -> str:
|
||||
return 'AMD GPU'
|
||||
|
||||
|
||||
def get_mem(device_folder: str) -> int:
|
||||
def get_mem(device_folder: str) -> Optional[int]:
|
||||
"""Return the memory consumption in %."""
|
||||
mem_info_vram_total = os.path.join(device_folder, GPU_MEM_TOTAL)
|
||||
mem_info_vram_used = os.path.join(device_folder, GPU_MEM_USED)
|
||||
@ -118,7 +118,7 @@ def get_mem(device_folder: str) -> int:
|
||||
return None
|
||||
|
||||
|
||||
def get_proc(device_folder: str) -> int:
|
||||
def get_proc(device_folder: str) -> Optional[int]:
|
||||
"""Return the processor consumption in %."""
|
||||
gpu_busy_percent = os.path.join(device_folder, GPU_PROC_PERCENT)
|
||||
if os.path.isfile(gpu_busy_percent):
|
||||
@ -127,7 +127,7 @@ def get_proc(device_folder: str) -> int:
|
||||
return None
|
||||
|
||||
|
||||
def get_temperature(device_folder: str) -> int:
|
||||
def get_temperature(device_folder: str) -> Optional[int]:
|
||||
"""Return the processor temperature in °C (mean of all HWMON)"""
|
||||
temp_input = []
|
||||
for root, dirs, _ in os.walk(device_folder):
|
||||
@ -140,10 +140,9 @@ def get_temperature(device_folder: str) -> int:
|
||||
temp_input.append(int(f.read()))
|
||||
if len(temp_input) > 0:
|
||||
return round(sum(temp_input) / len(temp_input) / 1000)
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def get_fan_speed(device_folder: str) -> int:
|
||||
def get_fan_speed(device_folder: str) -> Optional[int]:
|
||||
"""Return the fan speed in %."""
|
||||
return None
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,15 +8,15 @@
|
||||
|
||||
"""NVidia Extension unit for Glances' GPU plugin."""
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.globals import nativestr
|
||||
from glances.logger import logger
|
||||
|
||||
try:
|
||||
import pynvml
|
||||
except Exception as e:
|
||||
nvidia_gpu_enable = False
|
||||
# Display debug message if import KeyError
|
||||
logger.warning("Missing Python Lib ({}), Nvidia GPU plugin is disabled".format(e))
|
||||
logger.warning(f"Missing Python Lib ({e}), Nvidia GPU plugin is disabled")
|
||||
else:
|
||||
nvidia_gpu_enable = True
|
||||
|
||||
@ -43,14 +42,14 @@ class NvidiaGPU:
|
||||
try:
|
||||
pynvml.nvmlShutdown()
|
||||
except Exception as e:
|
||||
logger.debug("pynvml failed to shutdown correctly ({})".format(e))
|
||||
logger.debug(f"pynvml failed to shutdown correctly ({e})")
|
||||
|
||||
def get_device_stats(self):
|
||||
"""Get Nvidia GPU stats."""
|
||||
stats = []
|
||||
|
||||
for index, device_handle in enumerate(self.device_handles):
|
||||
device_stats = dict()
|
||||
device_stats = {}
|
||||
# Dictionary key is the GPU_ID
|
||||
device_stats['key'] = 'gpu_id'
|
||||
# GPU id (for multiple GPU, start at 0)
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -12,19 +11,20 @@ Help plugin.
|
||||
|
||||
Just a stupid plugin to display the help screen.
|
||||
"""
|
||||
import sys
|
||||
from glances.globals import iteritems
|
||||
from glances import __version__, psutil_version
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
from itertools import chain
|
||||
|
||||
from glances import __version__, psutil_version
|
||||
from glances.globals import iteritems
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
|
||||
class PluginModel(GlancesPluginModel):
|
||||
"""Glances help plugin."""
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(args=args, config=config)
|
||||
super().__init__(args=args, config=config)
|
||||
|
||||
# Set the config instance
|
||||
self.config = config
|
||||
@ -34,12 +34,7 @@ class PluginModel(GlancesPluginModel):
|
||||
self.display_curse = True
|
||||
|
||||
# init data dictionary, to preserve insertion order
|
||||
if sys.version_info < (3, 6):
|
||||
from collections import OrderedDict
|
||||
|
||||
self.view_data = OrderedDict()
|
||||
else:
|
||||
self.view_data = {}
|
||||
self.view_data = {}
|
||||
self.generate_view_data()
|
||||
|
||||
def reset(self):
|
||||
@ -51,10 +46,10 @@ class PluginModel(GlancesPluginModel):
|
||||
def generate_view_data(self):
|
||||
"""Generate the views."""
|
||||
self.view_data['version'] = '{} {}'.format('Glances', __version__)
|
||||
self.view_data['psutil_version'] = ' with psutil {}'.format(psutil_version)
|
||||
self.view_data['psutil_version'] = f' with psutil {psutil_version}'
|
||||
|
||||
try:
|
||||
self.view_data['configuration_file'] = 'Configuration file: {}'.format(self.config.loaded_config_file)
|
||||
self.view_data['configuration_file'] = f'Configuration file: {self.config.loaded_config_file}'
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -10,20 +9,20 @@
|
||||
"""IP plugin."""
|
||||
|
||||
import threading
|
||||
|
||||
from ujson import loads
|
||||
|
||||
from glances.globals import queue, urlopen_auth
|
||||
from glances.logger import logger
|
||||
from glances.timer import Timer
|
||||
from glances.timer import getTimeSinceLastUpdate
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
from glances.timer import Timer, getTimeSinceLastUpdate
|
||||
|
||||
# Import plugin specific dependency
|
||||
try:
|
||||
import netifaces
|
||||
except ImportError as e:
|
||||
import_error_tag = True
|
||||
logger.warning("Missing Python Lib ({}), IP plugin is disabled".format(e))
|
||||
logger.warning(f"Missing Python Lib ({e}), IP plugin is disabled")
|
||||
else:
|
||||
import_error_tag = False
|
||||
|
||||
@ -66,7 +65,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(args=args, config=config, fields_description=fields_description)
|
||||
super().__init__(args=args, config=config, fields_description=fields_description)
|
||||
|
||||
# We want to display the stat in the curse interface
|
||||
self.display_curse = True
|
||||
@ -104,7 +103,7 @@ class PluginModel(GlancesPluginModel):
|
||||
try:
|
||||
default_gw = netifaces.gateways()['default'][netifaces.AF_INET]
|
||||
except (KeyError, AttributeError) as e:
|
||||
logger.debug("Cannot grab default gateway IP address ({})".format(e))
|
||||
logger.debug(f"Cannot grab default gateway IP address ({e})")
|
||||
return self.get_init_value()
|
||||
else:
|
||||
stats['gateway'] = default_gw[0]
|
||||
@ -113,7 +112,7 @@ class PluginModel(GlancesPluginModel):
|
||||
address = netifaces.ifaddresses(default_gw[1])[netifaces.AF_INET][0]['addr']
|
||||
mask = netifaces.ifaddresses(default_gw[1])[netifaces.AF_INET][0]['netmask']
|
||||
except (KeyError, AttributeError) as e:
|
||||
logger.debug("Cannot grab private IP address ({})".format(e))
|
||||
logger.debug(f"Cannot grab private IP address ({e})")
|
||||
return self.get_init_value()
|
||||
else:
|
||||
stats['address'] = address
|
||||
@ -129,7 +128,7 @@ class PluginModel(GlancesPluginModel):
|
||||
self.public_info = PublicIpInfo(self.public_api, self.public_username, self.public_password).get()
|
||||
self.public_address = self.public_info['ip']
|
||||
except (KeyError, AttributeError, TypeError) as e:
|
||||
logger.debug("Cannot grab public IP information ({})".format(e))
|
||||
logger.debug(f"Cannot grab public IP information ({e})")
|
||||
else:
|
||||
stats['public_address'] = (
|
||||
self.public_address if not self.args.hide_public_info else self.__hide_ip(self.public_address)
|
||||
@ -211,7 +210,7 @@ class PluginModel(GlancesPluginModel):
|
||||
return sum(bin(int(x)).count('1') for x in ip.split('.'))
|
||||
|
||||
|
||||
class PublicIpInfo(object):
|
||||
class PublicIpInfo:
|
||||
"""Get public IP information from online service."""
|
||||
|
||||
def __init__(self, url, username, password, timeout=2):
|
||||
@ -242,11 +241,11 @@ class PublicIpInfo(object):
|
||||
try:
|
||||
response = urlopen_auth(url, username, password).read()
|
||||
except Exception as e:
|
||||
logger.debug("IP plugin - Cannot get public IP information from {} ({})".format(url, e))
|
||||
logger.debug(f"IP plugin - Cannot get public IP information from {url} ({e})")
|
||||
queue_target.put(None)
|
||||
else:
|
||||
try:
|
||||
queue_target.put(loads(response))
|
||||
except (ValueError, KeyError) as e:
|
||||
logger.debug("IP plugin - Cannot load public IP information from {} ({})".format(url, e))
|
||||
logger.debug(f"IP plugin - Cannot load public IP information from {url} ({e})")
|
||||
queue_target.put(None)
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,14 +8,13 @@
|
||||
|
||||
"""IRQ plugin."""
|
||||
|
||||
import os
|
||||
import operator
|
||||
import os
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.globals import LINUX
|
||||
from glances.timer import getTimeSinceLastUpdate
|
||||
from glances.logger import logger
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
from glances.timer import getTimeSinceLastUpdate
|
||||
|
||||
# Fields description
|
||||
# description: human readable description
|
||||
@ -43,9 +41,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
args=args, config=config, stats_init_value=[], fields_description=fields_description
|
||||
)
|
||||
super().__init__(args=args, config=config, stats_init_value=[], fields_description=fields_description)
|
||||
|
||||
# We want to display the stat in the curse interface
|
||||
self.display_curse = True
|
||||
@ -87,7 +83,7 @@ class PluginModel(GlancesPluginModel):
|
||||
def update_views(self):
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
super(PluginModel, self).update_views()
|
||||
super().update_views()
|
||||
|
||||
def msg_curse(self, args=None, max_width=None):
|
||||
"""Return the dict to display in the curse interface."""
|
||||
@ -104,7 +100,7 @@ class PluginModel(GlancesPluginModel):
|
||||
name_max_width = max_width - 7
|
||||
else:
|
||||
# No max_width defined, return an emptu curse message
|
||||
logger.debug("No max_width defined for the {} plugin, it will not be displayed.".format(self.plugin_name))
|
||||
logger.debug(f"No max_width defined for the {self.plugin_name} plugin, it will not be displayed.")
|
||||
return ret
|
||||
|
||||
# Build the string message
|
||||
@ -124,7 +120,7 @@ class PluginModel(GlancesPluginModel):
|
||||
return ret
|
||||
|
||||
|
||||
class GlancesIRQ(object):
|
||||
class GlancesIRQ:
|
||||
"""This class manages the IRQ file."""
|
||||
|
||||
IRQ_FILE = '/proc/interrupts'
|
||||
@ -170,7 +166,7 @@ class GlancesIRQ(object):
|
||||
irq_line = splitted_line[0].replace(':', '')
|
||||
if irq_line.isdigit():
|
||||
# If the first column is a digit, use the alias (last column)
|
||||
irq_line += '_{}'.format(splitted_line[-1])
|
||||
irq_line += f'_{splitted_line[-1]}'
|
||||
return irq_line
|
||||
|
||||
def __sum(self, line):
|
||||
@ -217,7 +213,7 @@ class GlancesIRQ(object):
|
||||
}
|
||||
self.stats.append(irq_current)
|
||||
self.lasts[irq_line] = current_irqs
|
||||
except (OSError, IOError):
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
return self.stats
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -10,12 +9,13 @@
|
||||
"""Load plugin."""
|
||||
|
||||
import os
|
||||
|
||||
import psutil
|
||||
|
||||
from glances.globals import iteritems
|
||||
from glances.logger import logger
|
||||
from glances.plugins.core import PluginModel as CorePluginModel
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
from glances.logger import logger
|
||||
|
||||
# Fields description
|
||||
fields_description = {
|
||||
@ -65,7 +65,7 @@ nb_phys_core = 1
|
||||
try:
|
||||
core = CorePluginModel().update()
|
||||
except Exception as e:
|
||||
logger.warning('Error: Can not retrieve the CPU core number (set it to 1) ({})'.format(e))
|
||||
logger.warning(f'Error: Can not retrieve the CPU core number (set it to 1) ({e})')
|
||||
else:
|
||||
if 'log' in core:
|
||||
nb_log_core = core['log']
|
||||
@ -81,7 +81,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
super().__init__(
|
||||
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
|
||||
)
|
||||
|
||||
@ -110,8 +110,7 @@ class PluginModel(GlancesPluginModel):
|
||||
stats = self.get_stats_snmp(snmp_oid=snmp_oid)
|
||||
|
||||
if stats['min1'] == '':
|
||||
stats = self.get_init_value()
|
||||
return stats
|
||||
return self.get_init_value()
|
||||
|
||||
# Python 3 return a dict like:
|
||||
# {'min1': "b'0.08'", 'min5': "b'0.12'", 'min15': "b'0.15'"}
|
||||
@ -128,7 +127,7 @@ class PluginModel(GlancesPluginModel):
|
||||
def update_views(self):
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
super(PluginModel, self).update_views()
|
||||
super().update_views()
|
||||
|
||||
# Add specifics information
|
||||
try:
|
||||
@ -164,17 +163,17 @@ class PluginModel(GlancesPluginModel):
|
||||
# Loop over 1min, 5min and 15min load
|
||||
for load_time in ['1', '5', '15']:
|
||||
ret.append(self.curse_new_line())
|
||||
msg = '{:7}'.format('{} min'.format(load_time))
|
||||
msg = '{:7}'.format(f'{load_time} min')
|
||||
ret.append(self.curse_add_line(msg))
|
||||
if args.disable_irix and get_nb_log_core() != 0:
|
||||
# Enable Irix mode for load (see issue #1554)
|
||||
load_stat = self.stats['min{}'.format(load_time)] / get_nb_log_core() * 100
|
||||
msg = '{:>5.1f}%'.format(load_stat)
|
||||
load_stat = self.stats[f'min{load_time}'] / get_nb_log_core() * 100
|
||||
msg = f'{load_stat:>5.1f}%'
|
||||
else:
|
||||
# Default mode for load
|
||||
load_stat = self.stats['min{}'.format(load_time)]
|
||||
msg = '{:>6.2f}'.format(load_stat)
|
||||
ret.append(self.curse_add_line(msg, self.get_views(key='min{}'.format(load_time), option='decoration')))
|
||||
load_stat = self.stats[f'min{load_time}']
|
||||
msg = f'{load_stat:>6.2f}'
|
||||
ret.append(self.curse_add_line(msg, self.get_views(key=f'min{load_time}', option='decoration')))
|
||||
|
||||
return ret
|
||||
|
||||
@ -205,5 +204,4 @@ def get_load_average(percent: bool = False):
|
||||
|
||||
if load_average and percent:
|
||||
return tuple([round(i / get_nb_log_core() * 100, 1) for i in load_average])
|
||||
else:
|
||||
return load_average
|
||||
return load_average
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,10 +8,10 @@
|
||||
|
||||
"""Virtual memory plugin."""
|
||||
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
import psutil
|
||||
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
# Fields description
|
||||
fields_description = {
|
||||
'total': {'description': 'Total physical memory available.', 'unit': 'bytes', 'min_symbol': 'K'},
|
||||
@ -113,7 +112,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
super().__init__(
|
||||
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
|
||||
)
|
||||
|
||||
@ -219,7 +218,7 @@ class PluginModel(GlancesPluginModel):
|
||||
def update_views(self):
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
super(PluginModel, self).update_views()
|
||||
super().update_views()
|
||||
|
||||
# Add specifics information
|
||||
# Alert and log
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,12 +8,12 @@
|
||||
|
||||
"""Swap memory plugin."""
|
||||
|
||||
from glances.globals import iterkeys
|
||||
from glances.timer import getTimeSinceLastUpdate
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
import psutil
|
||||
|
||||
from glances.globals import iterkeys
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
from glances.timer import getTimeSinceLastUpdate
|
||||
|
||||
# Fields description
|
||||
fields_description = {
|
||||
'total': {'description': 'Total swap memory.', 'unit': 'bytes', 'min_symbol': 'K'},
|
||||
@ -60,7 +59,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
super().__init__(
|
||||
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
|
||||
)
|
||||
|
||||
@ -145,7 +144,7 @@ class PluginModel(GlancesPluginModel):
|
||||
def update_views(self):
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
super(PluginModel, self).update_views()
|
||||
super().update_views()
|
||||
|
||||
# Add specifics information
|
||||
# Alert and log
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -8,13 +7,12 @@
|
||||
#
|
||||
|
||||
"""Network plugin."""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
from glances.logger import logger
|
||||
|
||||
import psutil
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
# Fields description
|
||||
# description: human readable description
|
||||
# short_name: shortname to use un UI
|
||||
@ -72,7 +70,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
super().__init__(
|
||||
args=args,
|
||||
config=config,
|
||||
items_history_list=items_history_list,
|
||||
@ -130,7 +128,7 @@ class PluginModel(GlancesPluginModel):
|
||||
try:
|
||||
net_io_counters = psutil.net_io_counters(pernic=True)
|
||||
except UnicodeDecodeError as e:
|
||||
logger.debug('Can not get network interface counters ({})'.format(e))
|
||||
logger.debug(f'Can not get network interface counters ({e})')
|
||||
return self.stats
|
||||
|
||||
# Grab interface's status (issue #765)
|
||||
@ -144,7 +142,7 @@ class PluginModel(GlancesPluginModel):
|
||||
net_status = psutil.net_if_stats()
|
||||
except OSError as e:
|
||||
# see psutil #797/glances #1106
|
||||
logger.debug('Can not get network interface status ({})'.format(e))
|
||||
logger.debug(f'Can not get network interface status ({e})')
|
||||
|
||||
for interface_name, interface_stat in net_io_counters.items():
|
||||
# Do not take hidden interface into account
|
||||
@ -180,7 +178,7 @@ class PluginModel(GlancesPluginModel):
|
||||
def update_views(self):
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
super(PluginModel, self).update_views()
|
||||
super().update_views()
|
||||
|
||||
# Check if the stats should be hidden
|
||||
self.update_views_hidden()
|
||||
@ -226,7 +224,7 @@ class PluginModel(GlancesPluginModel):
|
||||
name_max_width = max_width - 12
|
||||
else:
|
||||
# No max_width defined, return an emptu curse message
|
||||
logger.debug("No max_width defined for the {} plugin, it will not be displayed.".format(self.plugin_name))
|
||||
logger.debug(f"No max_width defined for the {self.plugin_name} plugin, it will not be displayed.")
|
||||
return ret
|
||||
|
||||
# Header
|
||||
@ -261,7 +259,7 @@ class PluginModel(GlancesPluginModel):
|
||||
if ('is_up' in i) and (i['is_up'] is False):
|
||||
continue
|
||||
# Hide stats if never be different from 0 (issue #1787)
|
||||
if all([self.get_views(item=i[self.get_key()], key=f, option='hidden') for f in self.hide_zero_fields]):
|
||||
if all(self.get_views(item=i[self.get_key()], key=f, option='hidden') for f in self.hide_zero_fields):
|
||||
continue
|
||||
# Format stats
|
||||
# Is there an alias for the interface name ?
|
||||
@ -300,16 +298,16 @@ class PluginModel(GlancesPluginModel):
|
||||
msg = '{:{width}}'.format(if_name, width=name_max_width)
|
||||
ret.append(self.curse_add_line(msg))
|
||||
if args.network_sum:
|
||||
msg = '{:>14}'.format(ax)
|
||||
msg = f'{ax:>14}'
|
||||
ret.append(self.curse_add_line(msg))
|
||||
else:
|
||||
msg = '{:>7}'.format(rx)
|
||||
msg = f'{rx:>7}'
|
||||
ret.append(
|
||||
self.curse_add_line(
|
||||
msg, self.get_views(item=i[self.get_key()], key='bytes_recv', option='decoration')
|
||||
)
|
||||
)
|
||||
msg = '{:>7}'.format(tx)
|
||||
msg = f'{tx:>7}'
|
||||
ret.append(
|
||||
self.curse_add_line(
|
||||
msg, self.get_views(item=i[self.get_key()], key='bytes_sent', option='decoration')
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,8 +8,9 @@
|
||||
|
||||
"""Now (current date) plugin."""
|
||||
|
||||
from time import tzname, strftime
|
||||
import datetime
|
||||
from time import strftime, tzname
|
||||
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
# Fields description
|
||||
@ -37,9 +37,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
args=args, config=config, fields_description=fields_description, stats_init_value={}
|
||||
)
|
||||
super().__init__(args=args, config=config, fields_description=fields_description, stats_init_value={})
|
||||
|
||||
# We want to display the stat in the curse interface
|
||||
self.display_curse = True
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -96,7 +95,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
super().__init__(
|
||||
args=args,
|
||||
config=config,
|
||||
items_history_list=items_history_list,
|
||||
@ -159,7 +158,7 @@ class PluginModel(GlancesPluginModel):
|
||||
for stat in header:
|
||||
if stat not in self.stats[0]:
|
||||
continue
|
||||
msg = '{:>7}'.format(stat)
|
||||
msg = f'{stat:>7}'
|
||||
ret.append(self.curse_add_line(msg))
|
||||
|
||||
# Manage the maximum number of CPU to display (related to enhancement request #2734)
|
||||
@ -176,9 +175,9 @@ class PluginModel(GlancesPluginModel):
|
||||
try:
|
||||
cpu_id = cpu[cpu['key']]
|
||||
if cpu_id < 10:
|
||||
msg = 'CPU{:1} '.format(cpu_id)
|
||||
msg = f'CPU{cpu_id:1} '
|
||||
else:
|
||||
msg = '{:4} '.format(cpu_id)
|
||||
msg = f'{cpu_id:4} '
|
||||
except TypeError:
|
||||
# TypeError: string indices must be integers (issue #1027)
|
||||
msg = '{:4} '.format('?')
|
||||
@ -187,7 +186,7 @@ class PluginModel(GlancesPluginModel):
|
||||
if stat not in self.stats[0]:
|
||||
continue
|
||||
try:
|
||||
msg = '{:6.1f}%'.format(cpu[stat])
|
||||
msg = f'{cpu[stat]:6.1f}%'
|
||||
except TypeError:
|
||||
msg = '{:>6}%'.format('?')
|
||||
ret.append(self.curse_add_line(msg, self.get_alert(cpu[stat], header=stat)))
|
||||
@ -204,7 +203,7 @@ class PluginModel(GlancesPluginModel):
|
||||
[i[stat] for i in percpu_list[0 : self.max_cpu_display]]
|
||||
)
|
||||
try:
|
||||
msg = '{:6.1f}%'.format(cpu_stat)
|
||||
msg = f'{cpu_stat:6.1f}%'
|
||||
except TypeError:
|
||||
msg = '{:>6}%'.format('?')
|
||||
ret.append(self.curse_add_line(msg, self.get_alert(cpu_stat, header=stat)))
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -13,18 +12,17 @@ I am your father...
|
||||
...of all Glances model plugins.
|
||||
"""
|
||||
|
||||
import re
|
||||
import copy
|
||||
import re
|
||||
|
||||
from glances.globals import iterkeys, itervalues, listkeys, mean, nativestr, json_dumps, json_dumps_dictlist, dictlist
|
||||
from glances.actions import GlancesActions
|
||||
from glances.events_list import glances_events
|
||||
from glances.globals import dictlist, iterkeys, itervalues, json_dumps, json_dumps_dictlist, listkeys, mean, nativestr
|
||||
from glances.history import GlancesHistory
|
||||
from glances.logger import logger
|
||||
from glances.events_list import glances_events
|
||||
from glances.outputs.glances_unicode import unicode_message
|
||||
from glances.thresholds import glances_thresholds
|
||||
from glances.timer import Counter, Timer, getTimeSinceLastUpdate
|
||||
from glances.outputs.glances_unicode import unicode_message
|
||||
|
||||
|
||||
fields_unit_short = {'percent': '%'}
|
||||
|
||||
@ -44,7 +42,7 @@ fields_unit_type = {
|
||||
}
|
||||
|
||||
|
||||
class GlancesPluginModel(object):
|
||||
class GlancesPluginModel:
|
||||
"""Main class for Glances plugin model."""
|
||||
|
||||
def __init__(self, args=None, config=None, items_history_list=None, stats_init_value={}, fields_description=None):
|
||||
@ -78,7 +76,7 @@ class GlancesPluginModel(object):
|
||||
|
||||
if self.plugin_name.startswith('glances_'):
|
||||
self.plugin_name = self.plugin_name.split('glances_')[1]
|
||||
logger.debug("Init {} plugin".format(self.plugin_name))
|
||||
logger.debug(f"Init {self.plugin_name} plugin")
|
||||
|
||||
# Init the args
|
||||
self.args = args
|
||||
@ -95,9 +93,9 @@ class GlancesPluginModel(object):
|
||||
self.stats_history = self.init_stats_history()
|
||||
|
||||
# Init the limits (configuration keys) dictionary
|
||||
self._limits = dict()
|
||||
self._limits = {}
|
||||
if config is not None:
|
||||
logger.debug('Load section {} in {}'.format(self.plugin_name, config.config_file_paths()))
|
||||
logger.debug(f'Load section {self.plugin_name} in {config.config_file_paths()}')
|
||||
self.load_limits(config=config)
|
||||
|
||||
# Init the alias (dictionnary)
|
||||
@ -107,7 +105,7 @@ class GlancesPluginModel(object):
|
||||
self.actions = GlancesActions(args=args)
|
||||
|
||||
# Init the views
|
||||
self.views = dict()
|
||||
self.views = {}
|
||||
|
||||
# Hide stats if all the hide_zero_fields has never been != 0
|
||||
# Default is False, always display stats
|
||||
@ -148,11 +146,11 @@ class GlancesPluginModel(object):
|
||||
|
||||
def exit(self):
|
||||
"""Just log an event when Glances exit."""
|
||||
logger.debug("Stop the {} plugin".format(self.plugin_name))
|
||||
logger.debug(f"Stop the {self.plugin_name} plugin")
|
||||
|
||||
def get_key(self):
|
||||
"""Return the key of the list."""
|
||||
return None
|
||||
return
|
||||
|
||||
def is_enabled(self, plugin_name=None):
|
||||
"""Return true if plugin is enabled."""
|
||||
@ -175,14 +173,14 @@ class GlancesPluginModel(object):
|
||||
"""Init the stats history (dict of GlancesAttribute)."""
|
||||
if self.history_enable():
|
||||
init_list = [a['name'] for a in self.get_items_history_list()]
|
||||
logger.debug("Stats history activated for plugin {} (items: {})".format(self.plugin_name, init_list))
|
||||
logger.debug(f"Stats history activated for plugin {self.plugin_name} (items: {init_list})")
|
||||
return GlancesHistory()
|
||||
|
||||
def reset_stats_history(self):
|
||||
"""Reset the stats history (dict of GlancesAttribute)."""
|
||||
if self.history_enable():
|
||||
reset_list = [a['name'] for a in self.get_items_history_list()]
|
||||
logger.debug("Reset history for plugin {} (items: {})".format(self.plugin_name, reset_list))
|
||||
logger.debug(f"Reset history for plugin {self.plugin_name} (items: {reset_list})")
|
||||
self.stats_history.reset()
|
||||
|
||||
def update_stats_history(self):
|
||||
@ -228,11 +226,9 @@ class GlancesPluginModel(object):
|
||||
s = self.stats_history.get(nb=nb)
|
||||
if item is None:
|
||||
return s
|
||||
else:
|
||||
if item in s:
|
||||
return s[item]
|
||||
else:
|
||||
return None
|
||||
if item in s:
|
||||
return s[item]
|
||||
return None
|
||||
|
||||
def get_export_history(self, item=None):
|
||||
"""Return the stats history object to export."""
|
||||
@ -288,10 +284,8 @@ class GlancesPluginModel(object):
|
||||
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]),
|
||||
)
|
||||
int(part) if part.isdigit() else part.lower()
|
||||
for part in re.split(r"(\d+|\D+)", self.has_alias(stat[key]) or stat[key])
|
||||
),
|
||||
)
|
||||
except TypeError:
|
||||
@ -299,7 +293,7 @@ class GlancesPluginModel(object):
|
||||
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]))
|
||||
part.lower() for part in re.split(r"(\d+|\D+)", self.has_alias(stat[key]) or stat[key])
|
||||
),
|
||||
)
|
||||
|
||||
@ -411,14 +405,14 @@ class GlancesPluginModel(object):
|
||||
"""
|
||||
if not isinstance(self.get_raw(), list):
|
||||
return None
|
||||
else:
|
||||
if (not isinstance(value, int) and not isinstance(value, float)) and value.isdigit():
|
||||
value = int(value)
|
||||
try:
|
||||
return {value: [i for i in self.get_raw() if i[item] == value]}
|
||||
except (KeyError, ValueError) as e:
|
||||
logger.error("Cannot get item({})=value({}) ({})".format(item, value, e))
|
||||
return None
|
||||
|
||||
if (not isinstance(value, int) and not isinstance(value, float)) and value.isdigit():
|
||||
value = int(value)
|
||||
try:
|
||||
return {value: [i for i in self.get_raw() if i[item] == value]}
|
||||
except (KeyError, ValueError) as e:
|
||||
logger.error(f"Cannot get item({item})=value({value}) ({e})")
|
||||
return None
|
||||
|
||||
def get_stats_value(self, item, value):
|
||||
"""Return the stats object for a specific item=value in JSON format.
|
||||
@ -428,15 +422,13 @@ class GlancesPluginModel(object):
|
||||
rsv = self.get_raw_stats_value(item, value)
|
||||
if rsv is None:
|
||||
return None
|
||||
else:
|
||||
return json_dumps(rsv)
|
||||
return json_dumps(rsv)
|
||||
|
||||
def get_item_info(self, item, key, default=None):
|
||||
"""Return the item info grabbed into self.fields_description."""
|
||||
if self.fields_description is None or item not in self.fields_description:
|
||||
return default
|
||||
else:
|
||||
return self.fields_description[item].get(key, default)
|
||||
return self.fields_description[item].get(key, default)
|
||||
|
||||
def update_views_hidden(self):
|
||||
"""Update the hidden views
|
||||
@ -459,7 +451,7 @@ class GlancesPluginModel(object):
|
||||
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]):
|
||||
if any(i[f] for f in self.hide_zero_fields):
|
||||
for f in self.hide_zero_fields:
|
||||
self.views[i[self.get_key()]][f]['_zero'] = self.views[i[self.get_key()]][f]['hidden']
|
||||
for f in self.hide_zero_fields:
|
||||
@ -471,7 +463,7 @@ class GlancesPluginModel(object):
|
||||
#
|
||||
# Stats are stored in a dict (ex: CPU, LOAD...)
|
||||
for key in listkeys(self.get_raw()):
|
||||
if any([self.get_raw()[f] for f in self.hide_zero_fields]):
|
||||
if any(self.get_raw()[f] for f in self.hide_zero_fields):
|
||||
for f in self.hide_zero_fields:
|
||||
self.views[f]['_zero'] = self.views[f]['hidden']
|
||||
for f in self.hide_zero_fields:
|
||||
@ -536,7 +528,7 @@ class GlancesPluginModel(object):
|
||||
|
||||
def reset_views(self):
|
||||
"""Reset the views to input_views."""
|
||||
self.views = dict()
|
||||
self.views = {}
|
||||
|
||||
def get_views(self, item=None, key=None, option=None):
|
||||
"""Return the views object.
|
||||
@ -554,14 +546,11 @@ class GlancesPluginModel(object):
|
||||
|
||||
if key is None:
|
||||
return item_views
|
||||
else:
|
||||
if option is None:
|
||||
return item_views[key]
|
||||
else:
|
||||
if option in item_views[key]:
|
||||
return item_views[key][option]
|
||||
else:
|
||||
return 'DEFAULT'
|
||||
if option is None:
|
||||
return item_views[key]
|
||||
if option in item_views[key]:
|
||||
return item_views[key][option]
|
||||
return 'DEFAULT'
|
||||
|
||||
def get_json_views(self, item=None, key=None, option=None):
|
||||
"""Return the views (in JSON)."""
|
||||
@ -590,7 +579,7 @@ class GlancesPluginModel(object):
|
||||
self._limits[limit] = config.get_float_value(self.plugin_name, level)
|
||||
except ValueError:
|
||||
self._limits[limit] = config.get_value(self.plugin_name, level).split(",")
|
||||
logger.debug("Load limit: {} = {}".format(limit, self._limits[limit]))
|
||||
logger.debug(f"Load limit: {limit} = {self._limits[limit]}")
|
||||
|
||||
return True
|
||||
|
||||
@ -621,14 +610,13 @@ class GlancesPluginModel(object):
|
||||
|
||||
def set_limits(self, item, value):
|
||||
"""Set the limits object."""
|
||||
self._limits['{}_{}'.format(self.plugin_name, item)] = value
|
||||
self._limits[f'{self.plugin_name}_{item}'] = value
|
||||
|
||||
def get_limits(self, item=None):
|
||||
"""Return the limits object."""
|
||||
if item is None:
|
||||
return self._limits
|
||||
else:
|
||||
return self._limits.get('{}_{}'.format(self.plugin_name, item), None)
|
||||
return self._limits.get(f'{self.plugin_name}_{item}', None)
|
||||
|
||||
def get_stats_action(self):
|
||||
"""Return stats for the action.
|
||||
@ -743,12 +731,11 @@ class GlancesPluginModel(object):
|
||||
"""Filter the stats to keep only the fields we want (the one defined in fields_description)."""
|
||||
if hasattr(stats, '_asdict'):
|
||||
return {k: v for k, v in stats._asdict().items() if k in self.fields_description}
|
||||
elif isinstance(stats, dict):
|
||||
if isinstance(stats, dict):
|
||||
return {k: v for k, v in stats.items() if k in self.fields_description}
|
||||
elif isinstance(stats, list):
|
||||
if isinstance(stats, list):
|
||||
return [self.filter_stats(s) for s in stats]
|
||||
else:
|
||||
return stats
|
||||
return stats
|
||||
|
||||
def manage_threshold(self, stat_name, trigger):
|
||||
"""Manage the threshold for the current stat."""
|
||||
@ -794,8 +781,7 @@ class GlancesPluginModel(object):
|
||||
"""Return true if the criticality limit exist for the given stat_name"""
|
||||
if stat_name == "":
|
||||
return self.plugin_name + '_' + criticality in self._limits
|
||||
else:
|
||||
return stat_name + '_' + criticality in self._limits
|
||||
return stat_name + '_' + criticality in self._limits
|
||||
|
||||
def get_limit(self, criticality=None, stat_name=""):
|
||||
"""Return the limit value for the given criticality.
|
||||
@ -807,7 +793,7 @@ class GlancesPluginModel(object):
|
||||
# Example: network_wlan0_rx_careful
|
||||
if stat_name + '_' + criticality in self._limits:
|
||||
return self._limits[stat_name + '_' + criticality]
|
||||
elif self.plugin_name + '_' + criticality in self._limits:
|
||||
if self.plugin_name + '_' + criticality in self._limits:
|
||||
return self._limits[self.plugin_name + '_' + criticality]
|
||||
|
||||
# No key found, the raise an error
|
||||
@ -841,10 +827,9 @@ class GlancesPluginModel(object):
|
||||
# Example: network_wlan0_rx_log
|
||||
if stat_name + '_log' in self._limits:
|
||||
return self._limits[stat_name + '_log'][0].lower() == 'true'
|
||||
elif self.plugin_name + '_log' in self._limits:
|
||||
if self.plugin_name + '_log' in self._limits:
|
||||
return self._limits[self.plugin_name + '_log'][0].lower() == 'true'
|
||||
else:
|
||||
return default_action
|
||||
return default_action
|
||||
|
||||
def get_conf_value(self, value, header="", plugin_name=None, default=[]):
|
||||
"""Return the configuration (header_) value for the current plugin.
|
||||
@ -896,14 +881,12 @@ class GlancesPluginModel(object):
|
||||
"""Return True if the value should be displayed in the UI"""
|
||||
if self.get_conf_value('show', header=header) != []:
|
||||
return self.is_show(value, header=header)
|
||||
else:
|
||||
return not self.is_hide(value, header=header)
|
||||
return not self.is_hide(value, header=header)
|
||||
|
||||
def read_alias(self):
|
||||
if self.plugin_name + '_' + 'alias' in self._limits:
|
||||
return {i.split(':')[0].lower(): i.split(':')[1] for i in self._limits[self.plugin_name + '_' + 'alias']}
|
||||
else:
|
||||
return dict()
|
||||
return {}
|
||||
|
||||
def has_alias(self, header):
|
||||
"""Return the alias name for the relative header it it exists otherwise None."""
|
||||
@ -1039,7 +1022,7 @@ class GlancesPluginModel(object):
|
||||
value = self.stats.get(key, None)
|
||||
|
||||
if width is None:
|
||||
msg_item = header + '{}'.format(key_name) + separator
|
||||
msg_item = header + f'{key_name}' + separator
|
||||
msg_template_float = '{:.1f}{}'
|
||||
msg_template = '{}{}'
|
||||
else:
|
||||
@ -1138,7 +1121,7 @@ class GlancesPluginModel(object):
|
||||
elif symbol in 'K':
|
||||
decimal_precision = 0
|
||||
return '{:.{decimal}f}{symbol}'.format(value, decimal=decimal_precision, symbol=symbol)
|
||||
return '{!s}'.format(number)
|
||||
return f'{number!s}'
|
||||
|
||||
def trend_msg(self, trend, significant=1):
|
||||
"""Return the trend message.
|
||||
@ -1184,11 +1167,9 @@ class GlancesPluginModel(object):
|
||||
counter = Counter()
|
||||
ret = fct(*args, **kw)
|
||||
duration = counter.get()
|
||||
logger.debug(
|
||||
"{} {} {} return {} in {} seconds".format(
|
||||
args[0].__class__.__name__, args[0].__class__.__module__, fct.__name__, ret, duration
|
||||
)
|
||||
)
|
||||
class_name = args[0].__class__.__name__
|
||||
class_module = args[0].__class__.__module__
|
||||
logger.debug(f"{class_name} {class_module} {fct.__name__} return {ret} in {duration} seconds")
|
||||
return ret
|
||||
|
||||
return wrapper
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,19 +8,19 @@
|
||||
|
||||
"""Ports scanner plugin."""
|
||||
|
||||
import numbers
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import threading
|
||||
import socket
|
||||
import time
|
||||
import numbers
|
||||
|
||||
from glances.globals import WINDOWS, MACOS, BSD, bool_type
|
||||
from glances.ports_list import GlancesPortsList
|
||||
from glances.web_list import GlancesWebList
|
||||
from glances.timer import Counter
|
||||
from glances.globals import BSD, MACOS, WINDOWS, bool_type
|
||||
from glances.logger import logger
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
from glances.ports_list import GlancesPortsList
|
||||
from glances.timer import Counter
|
||||
from glances.web_list import GlancesWebList
|
||||
|
||||
try:
|
||||
import requests
|
||||
@ -29,7 +28,7 @@ try:
|
||||
requests_tag = True
|
||||
except ImportError as e:
|
||||
requests_tag = False
|
||||
logger.warning("Missing Python Lib ({}), Ports plugin is limited to port scanning".format(e))
|
||||
logger.warning(f"Missing Python Lib ({e}), Ports plugin is limited to port scanning")
|
||||
|
||||
# Fields description
|
||||
# description: human readable description
|
||||
@ -72,9 +71,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
args=args, config=config, stats_init_value=[], fields_description=fields_description
|
||||
)
|
||||
super().__init__(args=args, config=config, stats_init_value=[], fields_description=fields_description)
|
||||
self.args = args
|
||||
self.config = config
|
||||
|
||||
@ -95,7 +92,7 @@ class PluginModel(GlancesPluginModel):
|
||||
if self._thread is not None:
|
||||
self._thread.stop()
|
||||
# Call the father class
|
||||
super(PluginModel, self).exit()
|
||||
super().exit()
|
||||
|
||||
def get_key(self):
|
||||
"""Return the key of the list."""
|
||||
@ -183,7 +180,7 @@ class PluginModel(GlancesPluginModel):
|
||||
name_max_width = max_width - 7
|
||||
else:
|
||||
# No max_width defined, return an emptu curse message
|
||||
logger.debug("No max_width defined for the {} plugin, it will not be displayed.".format(self.plugin_name))
|
||||
logger.debug(f"No max_width defined for the {self.plugin_name} plugin, it will not be displayed.")
|
||||
return ret
|
||||
|
||||
# Build the string message
|
||||
@ -199,11 +196,11 @@ class PluginModel(GlancesPluginModel):
|
||||
status = 'Timeout'
|
||||
else:
|
||||
# Convert second to ms
|
||||
status = '{0:.0f}ms'.format(p['status'] * 1000.0)
|
||||
status = '{:.0f}ms'.format(p['status'] * 1000.0)
|
||||
|
||||
msg = '{:{width}}'.format(p['description'][0:name_max_width], width=name_max_width)
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{:>9}'.format(status)
|
||||
msg = f'{status:>9}'
|
||||
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:
|
||||
@ -215,7 +212,7 @@ class PluginModel(GlancesPluginModel):
|
||||
status = 'Scanning'
|
||||
else:
|
||||
status = p['status']
|
||||
msg = '{:>9}'.format(status)
|
||||
msg = f'{status:>9}'
|
||||
ret.append(self.curse_add_line(msg, self.get_web_alert(p, header=p['indice'] + '_rtt')))
|
||||
ret.append(self.curse_new_line())
|
||||
|
||||
@ -237,8 +234,8 @@ class ThreadScanner(threading.Thread):
|
||||
|
||||
def __init__(self, stats):
|
||||
"""Init the class."""
|
||||
logger.debug("ports plugin - Create thread for scan list {}".format(stats))
|
||||
super(ThreadScanner, self).__init__()
|
||||
logger.debug(f"ports plugin - Create thread for scan list {stats}")
|
||||
super().__init__()
|
||||
# Event needed to stop properly the thread
|
||||
self._stopper = threading.Event()
|
||||
# The class return the stats as a list of dict
|
||||
@ -283,7 +280,7 @@ class ThreadScanner(threading.Thread):
|
||||
|
||||
def stop(self, timeout=None):
|
||||
"""Stop the thread."""
|
||||
logger.debug("ports plugin - Close thread for scan list {}".format(self._stats))
|
||||
logger.debug(f"ports plugin - Close thread for scan list {self._stats}")
|
||||
self._stopper.set()
|
||||
|
||||
def stopped(self):
|
||||
@ -313,8 +310,7 @@ class ThreadScanner(threading.Thread):
|
||||
"""Scan the port structure (dict) and update the status key."""
|
||||
if int(port['port']) == 0:
|
||||
return self._port_scan_icmp(port)
|
||||
else:
|
||||
return self._port_scan_tcp(port)
|
||||
return self._port_scan_tcp(port)
|
||||
|
||||
def _resolv_name(self, hostname):
|
||||
"""Convert hostname to IP address."""
|
||||
@ -322,7 +318,7 @@ class ThreadScanner(threading.Thread):
|
||||
try:
|
||||
ip = socket.gethostbyname(hostname)
|
||||
except Exception as e:
|
||||
logger.debug("{}: Cannot convert {} to IP address ({})".format(self.plugin_name, hostname, e))
|
||||
logger.debug(f"{self.plugin_name}: Cannot convert {hostname} to IP address ({e})")
|
||||
return ip
|
||||
|
||||
def _port_scan_icmp(self, port):
|
||||
@ -380,7 +376,7 @@ class ThreadScanner(threading.Thread):
|
||||
socket.setdefaulttimeout(port['timeout'])
|
||||
_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
except Exception as e:
|
||||
logger.debug("{}: Error while creating scanning socket ({})".format(self.plugin_name, e))
|
||||
logger.debug(f"{self.plugin_name}: Error while creating scanning socket ({e})")
|
||||
|
||||
# Scan port
|
||||
ip = self._resolv_name(port['host'])
|
||||
@ -388,7 +384,7 @@ class ThreadScanner(threading.Thread):
|
||||
try:
|
||||
ret = _socket.connect_ex((ip, int(port['port'])))
|
||||
except Exception as e:
|
||||
logger.debug("{}: Error while scanning port {} ({})".format(self.plugin_name, port, e))
|
||||
logger.debug(f"{self.plugin_name}: Error while scanning port {port} ({e})")
|
||||
else:
|
||||
if ret == 0:
|
||||
port['status'] = counter.get()
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,8 +8,8 @@
|
||||
|
||||
"""Process count plugin."""
|
||||
|
||||
from glances.processes import glances_processes, sort_for_human
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
from glances.processes import glances_processes, sort_for_human
|
||||
|
||||
# Fields description
|
||||
# description: human readable description
|
||||
@ -58,7 +57,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
super().__init__(
|
||||
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
|
||||
)
|
||||
|
||||
@ -113,9 +112,9 @@ class PluginModel(GlancesPluginModel):
|
||||
if glances_processes.process_filter is not None:
|
||||
msg = 'Processes filter:'
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
msg = ' {} '.format(glances_processes.process_filter)
|
||||
msg = f' {glances_processes.process_filter} '
|
||||
if glances_processes.process_filter_key is not None:
|
||||
msg += 'on column {} '.format(glances_processes.process_filter_key)
|
||||
msg += f'on column {glances_processes.process_filter_key} '
|
||||
ret.append(self.curse_add_line(msg, "FILTER"))
|
||||
msg = '(\'ENTER\' to edit, \'E\' to reset)'
|
||||
ret.append(self.curse_add_line(msg))
|
||||
@ -144,7 +143,7 @@ class PluginModel(GlancesPluginModel):
|
||||
msg = ' {} slp,'.format(self.stats['sleeping'])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
|
||||
msg = ' {} oth '.format(other)
|
||||
msg = f' {other} oth '
|
||||
ret.append(self.curse_add_line(msg))
|
||||
|
||||
# Display sort information
|
||||
@ -156,9 +155,9 @@ class PluginModel(GlancesPluginModel):
|
||||
if glances_processes.auto_sort:
|
||||
msg += ' sorted automatically'
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = ' by {}'.format(sort_human)
|
||||
msg = f' by {sort_human}'
|
||||
else:
|
||||
msg += ' sorted by {}'.format(sort_human)
|
||||
msg += f' sorted by {sort_human}'
|
||||
ret.append(self.curse_add_line(msg))
|
||||
|
||||
# Return the message with decoration
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,15 +8,15 @@
|
||||
|
||||
"""Process list plugin."""
|
||||
|
||||
import os
|
||||
import copy
|
||||
import os
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.globals import WINDOWS, key_exist_value_not_none_not_v, replace_special_chars
|
||||
from glances.processes import glances_processes, sort_stats
|
||||
from glances.logger import logger
|
||||
from glances.outputs.glances_unicode import unicode_message
|
||||
from glances.plugins.core import PluginModel as CorePluginModel
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
from glances.processes import glances_processes, sort_stats
|
||||
|
||||
# Fields description
|
||||
# description: human readable description
|
||||
@ -155,9 +154,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
args=args, config=config, fields_description=fields_description, stats_init_value=[]
|
||||
)
|
||||
super().__init__(args=args, config=config, fields_description=fields_description, stats_init_value=[])
|
||||
|
||||
# We want to display the stat in the curse interface
|
||||
self.display_curse = True
|
||||
@ -341,11 +338,11 @@ class PluginModel(GlancesPluginModel):
|
||||
else:
|
||||
hours, minutes, seconds = seconds_to_hms(user_system_time)
|
||||
if hours > 99:
|
||||
msg = '{:<7}h'.format(hours)
|
||||
msg = f'{hours:<7}h'
|
||||
elif 0 < hours < 100:
|
||||
msg = '{}h{}:{}'.format(hours, minutes, seconds)
|
||||
msg = f'{hours}h{minutes}:{seconds}'
|
||||
else:
|
||||
msg = '{}:{}'.format(minutes, seconds)
|
||||
msg = f'{minutes}:{seconds}'
|
||||
msg = self.layout_stat['time'].format(msg)
|
||||
if hours > 0:
|
||||
ret = self.curse_add_line(msg, decoration='CPU_TIME', optional=True)
|
||||
@ -502,7 +499,7 @@ class PluginModel(GlancesPluginModel):
|
||||
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
|
||||
logger.debug("Can not decode command line '{}' ({})".format(cmdline, e))
|
||||
logger.debug(f"Can not decode command line '{cmdline}' ({e})")
|
||||
ret.append(self.curse_add_line('', splittable=True))
|
||||
|
||||
return ret
|
||||
@ -643,7 +640,7 @@ class PluginModel(GlancesPluginModel):
|
||||
# value is a number which goes from 0 to 7.
|
||||
# The higher the value, the lower the I/O priority of the process.
|
||||
if hasattr(p['ionice'], 'value') and p['ionice'].value != 0:
|
||||
msg += ' (value %s/7)' % str(p['ionice'].value)
|
||||
msg += ' (value {}/7)'.format(str(p['ionice'].value))
|
||||
ret.append(self.curse_add_line(msg, splittable=True))
|
||||
|
||||
# Second line is memory info
|
||||
@ -826,7 +823,7 @@ class PluginModel(GlancesPluginModel):
|
||||
msg = ' < {}'.format('current')
|
||||
ret.append(self.curse_add_line(msg, optional=True))
|
||||
else:
|
||||
msg = ' < {}'.format(mmm)
|
||||
msg = f' < {mmm}'
|
||||
ret.append(self.curse_add_line(msg, optional=True))
|
||||
msg = ' (\'M\' to reset)'
|
||||
ret.append(self.curse_add_line(msg, optional=True))
|
||||
@ -835,8 +832,7 @@ class PluginModel(GlancesPluginModel):
|
||||
"""Return the decoration string for the current mmm status."""
|
||||
if mmm is not None:
|
||||
return 'DEFAULT'
|
||||
else:
|
||||
return 'FILTER'
|
||||
return 'FILTER'
|
||||
|
||||
def __mmm_reset(self):
|
||||
"""Reset the MMM stats."""
|
||||
@ -902,6 +898,6 @@ class PluginModel(GlancesPluginModel):
|
||||
"""Return the maximum PID size in number of char."""
|
||||
if self.pid_max is not None:
|
||||
return len(str(self.pid_max))
|
||||
else:
|
||||
# By default return 5 (corresponding to 99999 PID number)
|
||||
return 5
|
||||
|
||||
# By default return 5 (corresponding to 99999 PID number)
|
||||
return 5
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -22,7 +21,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(args=args, config=config)
|
||||
super().__init__(args=args, config=config)
|
||||
|
||||
self.reset()
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -9,15 +8,15 @@
|
||||
|
||||
"""Quicklook plugin."""
|
||||
|
||||
from glances.logger import logger
|
||||
import psutil
|
||||
|
||||
from glances.cpu_percent import cpu_percent
|
||||
from glances.plugins.load import get_load_average, get_nb_log_core, get_nb_phys_core
|
||||
from glances.logger import logger
|
||||
from glances.outputs.glances_bars import Bar
|
||||
from glances.outputs.glances_sparklines import Sparkline
|
||||
from glances.plugins.load import get_load_average, get_nb_log_core, get_nb_phys_core
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
|
||||
import psutil
|
||||
|
||||
# Fields description
|
||||
# description: human readable description
|
||||
# short_name: shortname to use un UI
|
||||
@ -84,7 +83,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the quicklook plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
super().__init__(
|
||||
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
|
||||
)
|
||||
|
||||
@ -97,7 +96,7 @@ class PluginModel(GlancesPluginModel):
|
||||
# Define the stats list
|
||||
self.stats_list = self.get_conf_value('list', default=self.DEFAULT_STATS_LIST)
|
||||
if not set(self.stats_list).issubset(self.AVAILABLE_STATS_LIST):
|
||||
logger.warning('Quicklook plugin: Invalid stats list: {}'.format(self.stats_list))
|
||||
logger.warning(f'Quicklook plugin: Invalid stats list: {self.stats_list}')
|
||||
self.stats_list = self.AVAILABLE_STATS_LIST
|
||||
|
||||
@GlancesPluginModel._check_decorator
|
||||
@ -152,7 +151,7 @@ class PluginModel(GlancesPluginModel):
|
||||
def update_views(self):
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
super(PluginModel, self).update_views()
|
||||
super().update_views()
|
||||
|
||||
# Alert for CPU, MEM and SWAP
|
||||
for key in self.stats_list:
|
||||
@ -176,11 +175,11 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
if not max_width:
|
||||
# No max_width defined, return an empty message
|
||||
logger.debug("No max_width defined for the {} plugin, it will not be displayed.".format(self.plugin_name))
|
||||
logger.debug(f"No max_width defined for the {self.plugin_name} plugin, it will not be displayed.")
|
||||
return ret
|
||||
|
||||
# Define the data: Bar (default behavior) or Sparkline
|
||||
data = dict()
|
||||
data = {}
|
||||
for key in self.stats_list:
|
||||
if self.args.sparkline and self.history_enable() and not self.args.client:
|
||||
data[key] = Sparkline(max_width)
|
||||
@ -221,7 +220,7 @@ class PluginModel(GlancesPluginModel):
|
||||
else:
|
||||
# Bar only the last value
|
||||
data[key].percent = self.stats[key]
|
||||
msg = '{:4} '.format(key.upper())
|
||||
msg = f'{key.upper():4} '
|
||||
ret.extend(self._msg_create_line(msg, data[key], key))
|
||||
ret.append(self.curse_new_line())
|
||||
|
||||
@ -258,9 +257,9 @@ class PluginModel(GlancesPluginModel):
|
||||
# Bar will only display the last value
|
||||
data[key].percent = cpu['total']
|
||||
if cpu_id < 10:
|
||||
msg = '{:3}{} '.format(key.upper(), cpu_id)
|
||||
msg = f'{key.upper():3}{cpu_id} '
|
||||
else:
|
||||
msg = '{:4} '.format(cpu_id)
|
||||
msg = f'{cpu_id:4} '
|
||||
ret.extend(self._msg_create_line(msg, data[key], key))
|
||||
ret.append(self.curse_new_line())
|
||||
|
||||
@ -282,7 +281,7 @@ class PluginModel(GlancesPluginModel):
|
||||
sum_other.percent = sum([i['total'] for i in percpu_list[self.max_cpu_display :]]) / len(
|
||||
percpu_list[self.max_cpu_display :]
|
||||
)
|
||||
msg = msg = '{:3}* '.format(key.upper())
|
||||
msg = msg = f'{key.upper():3}* '
|
||||
ret.extend(self._msg_create_line(msg, sum_other, key))
|
||||
ret.append(self.curse_new_line())
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -18,7 +17,7 @@ try:
|
||||
from pymdstat import MdStat
|
||||
except ImportError as e:
|
||||
import_error_tag = True
|
||||
logger.warning("Missing Python Lib ({}), Raid plugin is disabled".format(e))
|
||||
logger.warning(f"Missing Python Lib ({e}), Raid plugin is disabled")
|
||||
else:
|
||||
import_error_tag = False
|
||||
|
||||
@ -31,7 +30,7 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(args=args, config=config)
|
||||
super().__init__(args=args, config=config)
|
||||
|
||||
# We want to display the stat in the curse interface
|
||||
self.display_curse = True
|
||||
@ -54,7 +53,7 @@ class PluginModel(GlancesPluginModel):
|
||||
# mds = MdStat(path='/home/nicolargo/dev/pymdstat/tests/mdstat.10')
|
||||
stats = mds.get_stats()['arrays']
|
||||
except Exception as e:
|
||||
logger.debug("Can not grab RAID stats (%s)" % e)
|
||||
logger.debug(f"Can not grab RAID stats ({e})")
|
||||
return self.stats
|
||||
|
||||
elif self.input_method == 'snmp':
|
||||
@ -81,7 +80,7 @@ class PluginModel(GlancesPluginModel):
|
||||
name_max_width = max_width - 12
|
||||
else:
|
||||
# No max_width defined, return an emptu curse message
|
||||
logger.debug("No max_width defined for the {} plugin, it will not be displayed.".format(self.plugin_name))
|
||||
logger.debug(f"No max_width defined for the {self.plugin_name} plugin, it will not be displayed.")
|
||||
return ret
|
||||
|
||||
# Header
|
||||
@ -108,7 +107,7 @@ class PluginModel(GlancesPluginModel):
|
||||
# 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)
|
||||
full_name = f'{array_type} {array}'
|
||||
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':
|
||||
@ -134,7 +133,7 @@ class PluginModel(GlancesPluginModel):
|
||||
ret.append(self.curse_new_line())
|
||||
msg = ' {} disk {}: '.format(tree_char, self.stats[array]['components'][component])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{}'.format(component)
|
||||
msg = f'{component}'
|
||||
ret.append(self.curse_add_line(msg))
|
||||
if self.stats[array]['type'] != 'raid0' and (self.stats[array]['used'] < self.stats[array]['available']):
|
||||
# Display current array configuration
|
||||
@ -161,6 +160,6 @@ class PluginModel(GlancesPluginModel):
|
||||
return 'CRITICAL'
|
||||
if used is None or available is None:
|
||||
return 'DEFAULT'
|
||||
elif used < available:
|
||||
if used < available:
|
||||
return 'WARNING'
|
||||
return 'OK'
|
||||
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
@ -8,20 +7,21 @@
|
||||
#
|
||||
|
||||
"""Sensors plugin."""
|
||||
from enum import Enum
|
||||
|
||||
import warnings
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from typing import List, Dict, Literal, Any
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Literal
|
||||
|
||||
import psutil
|
||||
import warnings
|
||||
|
||||
from glances.logger import logger
|
||||
from glances.globals import to_fahrenheit
|
||||
from glances.timer import Counter
|
||||
from glances.plugins.sensors.sensor.glances_batpercent import PluginModel as BatPercentPluginModel
|
||||
from glances.plugins.sensors.sensor.glances_hddtemp import PluginModel as HddTempPluginModel
|
||||
from glances.logger import logger
|
||||
from glances.outputs.glances_unicode import unicode_message
|
||||
from glances.plugins.plugin.model import GlancesPluginModel
|
||||
from glances.plugins.sensors.sensor.glances_batpercent import PluginModel as BatPercentPluginModel
|
||||
from glances.plugins.sensors.sensor.glances_hddtemp import PluginModel as HddTempPluginModel
|
||||
from glances.timer import Counter
|
||||
|
||||
|
||||
class SensorType(str, Enum):
|
||||
@ -83,29 +83,27 @@ class PluginModel(GlancesPluginModel):
|
||||
|
||||
def __init__(self, args=None, config=None):
|
||||
"""Init the plugin."""
|
||||
super(PluginModel, self).__init__(
|
||||
args=args, config=config, stats_init_value=[], fields_description=fields_description
|
||||
)
|
||||
super().__init__(args=args, config=config, stats_init_value=[], fields_description=fields_description)
|
||||
start_duration = Counter()
|
||||
|
||||
# Init the sensor class
|
||||
start_duration.reset()
|
||||
glances_grab_sensors_cpu_temp = GlancesGrabSensors(SensorType.CPU_TEMP)
|
||||
logger.debug("CPU Temp sensor plugin init duration: {} seconds".format(start_duration.get()))
|
||||
logger.debug(f"CPU Temp sensor plugin init duration: {start_duration.get()} seconds")
|
||||
|
||||
start_duration.reset()
|
||||
glances_grab_sensors_fan_speed = GlancesGrabSensors(SensorType.FAN_SPEED)
|
||||
logger.debug("Fan speed sensor plugin init duration: {} seconds".format(start_duration.get()))
|
||||
logger.debug(f"Fan speed sensor plugin init duration: {start_duration.get()} seconds")
|
||||
|
||||
# Instance for the HDDTemp Plugin in order to display the hard disks temperatures
|
||||
start_duration.reset()
|
||||
hddtemp_plugin = HddTempPluginModel(args=args, config=config)
|
||||
logger.debug("HDDTemp sensor plugin init duration: {} seconds".format(start_duration.get()))
|
||||
logger.debug(f"HDDTemp sensor plugin init duration: {start_duration.get()} seconds")
|
||||
|
||||
# Instance for the BatPercent in order to display the batteries capacities
|
||||
start_duration.reset()
|
||||
batpercent_plugin = BatPercentPluginModel(args=args, config=config)
|
||||
logger.debug("Battery sensor plugin init duration: {} seconds".format(start_duration.get()))
|
||||
logger.debug(f"Battery sensor plugin init duration: {start_duration.get()} seconds")
|
||||
|
||||
self.sensors_grab_map: Dict[SensorType, Any] = {}
|
||||
|
||||
@ -153,8 +151,7 @@ class PluginModel(GlancesPluginModel):
|
||||
# Remove duplicates thanks to https://stackoverflow.com/a/9427216/1919431
|
||||
stats_transformed = [dict(t) for t in {tuple(d.items()) for d in stats_transformed}]
|
||||
# Sort by label
|
||||
stats_transformed = sorted(stats_transformed, key=lambda d: d['label'])
|
||||
return stats_transformed
|
||||
return sorted(stats_transformed, key=lambda d: d['label'])
|
||||
|
||||
@GlancesPluginModel._check_decorator
|
||||
@GlancesPluginModel._log_result_decorator
|
||||
@ -191,10 +188,9 @@ class PluginModel(GlancesPluginModel):
|
||||
# Get the alias for each stat
|
||||
if self.has_alias(stats["label"].lower()):
|
||||
return self.has_alias(stats["label"].lower())
|
||||
elif self.has_alias("{}_{}".format(stats["label"], stats["type"]).lower()):
|
||||
if self.has_alias("{}_{}".format(stats["label"], stats["type"]).lower()):
|
||||
return self.has_alias("{}_{}".format(stats["label"], stats["type"]).lower())
|
||||
else:
|
||||
return stats["label"]
|
||||
return stats["label"]
|
||||
|
||||
def __set_type(self, stats: List[Dict[str, Any]], sensor_type: SensorType) -> List[Dict[str, Any]]:
|
||||
"""Set the plugin type.
|
||||
@ -216,7 +212,7 @@ class PluginModel(GlancesPluginModel):
|
||||
def update_views(self):
|
||||
"""Update stats views."""
|
||||
# Call the father's method
|
||||
super(PluginModel, self).update_views()
|
||||
super().update_views()
|
||||
|
||||
# Add specifics information
|
||||
# Alert
|
||||
@ -254,9 +250,9 @@ class PluginModel(GlancesPluginModel):
|
||||
return ''
|
||||
if stats['status'].startswith('Charg'):
|
||||
return unicode_message('ARROW_UP')
|
||||
elif stats['status'].startswith('Discharg'):
|
||||
if stats['status'].startswith('Discharg'):
|
||||
return unicode_message('ARROW_DOWN')
|
||||
elif stats['status'].startswith('Full'):
|
||||
if stats['status'].startswith('Full'):
|
||||
return unicode_message('CHECK')
|
||||
return ''
|
||||
|
||||
@ -274,7 +270,7 @@ class PluginModel(GlancesPluginModel):
|
||||
name_max_width = max_width - 12
|
||||
else:
|
||||
# No max_width defined, return an emptu curse message
|
||||
logger.debug("No max_width defined for the {} plugin, it will not be displayed.".format(self.plugin_name))
|
||||
logger.debug(f"No max_width defined for the {self.plugin_name} plugin, it will not be displayed.")
|
||||
return ret
|
||||
|
||||
# Header
|
||||
@ -305,8 +301,8 @@ class PluginModel(GlancesPluginModel):
|
||||
value = i['value']
|
||||
unit = i['unit']
|
||||
try:
|
||||
msg = '{:.0f}{}{}'.format(value, unit, trend)
|
||||
msg = '{:>14}'.format(msg)
|
||||
msg = f'{value:.0f}{unit}{trend}'
|
||||
msg = f'{msg:>14}'
|
||||
ret.append(
|
||||
self.curse_add_line(
|
||||
msg, self.get_views(item=i[self.get_key()], key='value', option='decoration')
|
||||
@ -318,7 +314,7 @@ class PluginModel(GlancesPluginModel):
|
||||
return ret
|
||||
|
||||
|
||||
class GlancesGrabSensors(object):
|
||||
class GlancesGrabSensors:
|
||||
"""Get sensors stats."""
|
||||
|
||||
def __init__(self, sensor_type: Literal[SensorType.FAN_SPEED, SensorType.CPU_TEMP]):
|
||||
@ -370,8 +366,7 @@ class GlancesGrabSensors(object):
|
||||
sensors_current['label'] = feature.label
|
||||
# Sensors value, limit and unit
|
||||
sensors_current['unit'] = self.sensor_unit
|
||||
sensors_current['value'] = int(
|
||||
getattr(feature, 'current', 0) if getattr(feature, 'current', 0) else 0)
|
||||
sensors_current['value'] = int(getattr(feature, 'current', 0) if getattr(feature, 'current', 0) else 0)
|
||||
system_warning = getattr(feature, 'high', None)
|
||||
system_critical = getattr(feature, 'critical', None)
|
||||
sensors_current['warning'] = int(system_warning) if system_warning is not None else None
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user