Merge Ruff formating and solve some conflicts #2779

This commit is contained in:
nicolargo 2024-05-18 10:37:57 +02:00
commit 74f9606e65
135 changed files with 1524 additions and 1992 deletions

View File

@ -1,2 +0,0 @@
[bandit]
exclude: ./docs,./glances/outputs/static/node_modules

View File

@ -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

View File

@ -7,8 +7,32 @@ on:
jobs: 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: test-linux:
needs: source-code-checks
# https://github.com/actions/runner-images?tab=readme-ov-file#available-images # https://github.com/actions/runner-images?tab=readme-ov-file#available-images
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
strategy: strategy:
@ -28,22 +52,8 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install flake8
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 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 - name: Unitary tests
run: | run: |
python ./unittest-core.py python ./unittest-core.py
@ -84,6 +94,7 @@ jobs:
test-macos: test-macos:
needs: source-code-checks
# https://github.com/actions/runner-images?tab=readme-ov-file#available-images # https://github.com/actions/runner-images?tab=readme-ov-file#available-images
runs-on: macos-14 runs-on: macos-14
strategy: strategy:

22
.pre-commit-config.yaml Normal file
View 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]

View File

@ -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 they should reciprocate that respect in addressing your issue or assessing
patches and features. patches and features.
## Using the issue tracker ## Using the issue tracker
The [issue tracker](https://github.com/nicolargo/glances/issues) is 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 * Please **do not** derail or troll issues. Keep the discussion on topic and
respect the opinions of others. respect the opinions of others.
## Bug reports ## Bug reports
A bug is a _demonstrable problem_ that is caused by the code in the repository. 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 > causing the bug, and potential solutions (and your opinions on their
> merits). > 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. > 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
Feature requests are welcome. But take a moment to find out whether your idea 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 case to convince the project's developers of the merits of this feature. Please
provide as much detail and context as possible. provide as much detail and context as possible.
## Pull requests ## Pull requests
Good pull requests—patches, improvements, new features—are a fantastic 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: 5. Test you code using the Makefile:
* make format ==> Format your code thanks to the Ruff linter
* make run ==> Run Glances * make run ==> Run Glances
* make run-webserver ==> Run a Glances Web Server * make run-webserver ==> Run a Glances Web Server
* make test ==> Run unit tests * make test ==> Run unit tests

View File

@ -52,6 +52,7 @@ venv-dev-python: ## Install Python 3 venv
venv-dev: venv-python ## Install Python 3 dev dependencies venv-dev: venv-python ## Install Python 3 dev dependencies
./venv-dev/bin/pip install -r dev-requirements.txt ./venv-dev/bin/pip install -r dev-requirements.txt
./venv-dev/bin/pip install -r doc-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-upgrade: ## Upgrade Python 3 dev dependencies
./venv-dev/bin/pip install --upgrade pip ./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 format: ## Format the code
@git ls-files 'glances/*.py' | xargs ./venv-dev/bin/python -m autopep8 --in-place --jobs 0 --global-config=.flake8 ./venv-dev/bin/python -m ruff format .
@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
flake8: ## Run flake8 linter. lint: ## Lint the code.
@git ls-files 'glances/ *.py' | xargs ./venv-dev/bin/python -m flake8 --config=.flake8 ./venv-dev/bin/python -m ruff check . --fix
ruff: ## Run Ruff (fastest) linter.
./venv-dev/bin/python -m ruff check . --config=./pyproject.toml
codespell: ## Run codespell to fix common misspellings in text files 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 ./venv-dev/bin/codespell -S .git,./docs/_build,./Glances.egg-info,./venv*,./glances/outputs,*.svg -L hart,bu,te,statics

View File

@ -87,7 +87,6 @@ Requirements
- ``defusedxml`` (in order to monkey patch xmlrpc) - ``defusedxml`` (in order to monkey patch xmlrpc)
- ``packaging`` (for the version comparison) - ``packaging`` (for the version comparison)
- ``ujson`` (an optimized alternative to the standard json module) - ``ujson`` (an optimized alternative to the standard json module)
- ``pydantic`` (for the data validation support)
*Note for Python 2 users* *Note for Python 2 users*

View File

@ -1,17 +1,14 @@
py-spy
gprof2dot
black
pyright
requirements-parser
flake8
autopep8
autoflake
ruff
codespell codespell
memory-profiler fonttools>=4.43.0 # not directly required, pinned by Snyk to avoid a vulnerability
gprof2dot
matplotlib matplotlib
semgrep memory-profiler
setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
numpy>=1.22.2 # not directly required, pinned by Snyk to avoid a vulnerability 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 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

View File

@ -1,5 +1,5 @@
reuse
setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
sphinx sphinx
sphinx_rtd_theme sphinx_rtd_theme
ujson ujson
reuse
setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability

View File

@ -2,9 +2,9 @@
-r requirements.txt -r requirements.txt
docker>=6.1.1; python_version >= "3.7" docker>=6.1.1; python_version >= "3.7"
podman; python_version >= "3.6"
packaging; python_version >= "3.7" packaging; python_version >= "3.7"
podman; python_version >= "3.6"
python-dateutil python-dateutil
requests
six six
urllib3 urllib3
requests

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# Glances documentation build configuration file, created by # Glances documentation build configuration file, created by
# sphinx-quickstart on Tue Mar 1 10:53:59 2016. # 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 # All configuration values have a default; values that are commented out
# serve to show the default. # serve to show the default.
import sys
import os import os
import sys
from datetime import datetime from datetime import datetime
# If extensions (or modules to document with autodoc) are in another directory, # 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. # WARNING: Do not move this import before the sys.path.insert() call.
from glances import __version__ from glances import __version__
# -- General configuration ------------------------------------------------ # -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here. # 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 # 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 # further. For a list of options available for each theme, see the
# documentation. # documentation.
html_theme_options = { html_theme_options = {}
}
# Add any paths that contain custom themes here, relative to this directory. # Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = [] # html_theme_path = []
@ -166,14 +163,7 @@ html_static_path = ['_static']
# html_use_smartypants = True # html_use_smartypants = True
# Custom sidebar templates, maps document names to template names. # Custom sidebar templates, maps document names to template names.
html_sidebars = { html_sidebars = {'**': ['about.html', 'navigation.html', 'links.html', 'searchbox.html']}
'**': [
'about.html',
'navigation.html',
'links.html',
'searchbox.html'
]
}
# Additional templates that should be rendered to pages, maps page names to # Additional templates that should be rendered to pages, maps page names to
# template names. # template names.
@ -227,13 +217,10 @@ htmlhelp_basename = 'Glancesdoc'
latex_elements = { latex_elements = {
# The paper size ('letterpaper' or 'a4paper'). # The paper size ('letterpaper' or 'a4paper').
# 'papersize': 'letterpaper', # 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt'). # The font size ('10pt', '11pt' or '12pt').
# 'pointsize': '10pt', # 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble. # Additional stuff for the LaTeX preamble.
# 'preamble': '', # 'preamble': '',
# Latex figure (float) alignment # Latex figure (float) alignment
# 'figure_align': 'htbp', # 'figure_align': 'htbp',
} }
@ -242,8 +229,7 @@ latex_elements = {
# (source start file, target name, title, # (source start file, target name, title,
# author, documentclass [howto, manual, or own class]). # author, documentclass [howto, manual, or own class]).
latex_documents = [ latex_documents = [
(master_doc, 'Glances.tex', 'Glances Documentation', (master_doc, 'Glances.tex', 'Glances Documentation', 'Nicolas Hennion', 'manual'),
'Nicolas Hennion', 'manual'),
] ]
# The name of an image file (relative to this directory) to place at the top of # 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 # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [ man_pages = [('glances', 'glances', 'An eye on your system', '', 1)]
('glances', 'glances', 'An eye on your system',
'', 1)
]
# If true, show URL addresses after external links. # If true, show URL addresses after external links.
# man_show_urls = False # man_show_urls = False
@ -286,9 +269,15 @@ man_pages = [
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [
(master_doc, 'Glances', 'Glances Documentation', (
author, 'Glances', 'One line description of project.', master_doc,
'Miscellaneous'), 'Glances',
'Glances Documentation',
author,
'Glances',
'One line description of project.',
'Miscellaneous',
),
] ]
# Documents to append as an appendix to all manuals. # Documents to append as an appendix to all manuals.

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -11,11 +10,11 @@
"""Init the Glances software.""" """Init the Glances software."""
# Import system libs # Import system libs
import tracemalloc
import locale import locale
import platform import platform
import signal import signal
import sys import sys
import tracemalloc
# Global name # Global name
# Version should start and end with a numerical char # Version should start and end with a numerical char
@ -44,11 +43,6 @@ try:
except locale.Error: except locale.Error:
print("Warning: Unable to set locale. Expect encoding problems.") 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 # Check psutil version
psutil_min_version = (5, 3, 0) psutil_min_version = (5, 3, 0)
psutil_version_info = tuple([int(num) for num in psutil_version.split('.')]) 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.') print('psutil 5.3.0 or higher is needed. Glances cannot start.')
sys.exit(1) sys.exit(1)
# Trac malloc is only available on Python 3.4 or higher # Trac malloc is only available on Python 3.4 or higher
def __signal_handler(signal, frame): def __signal_handler(signal, frame):
logger.debug("Signal {} catched".format(signal)) logger.debug(f"Signal {signal} catched")
end() end()
@ -103,20 +98,16 @@ def start(config, args):
from glances.webserver import GlancesWebServer as GlancesMode from glances.webserver import GlancesWebServer as GlancesMode
# Init the mode # Init the mode
logger.info("Start {} mode".format(GlancesMode.__name__)) logger.info(f"Start {GlancesMode.__name__} mode")
mode = GlancesMode(config=config, args=args) mode = GlancesMode(config=config, args=args)
# Start the main loop # 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: 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: if args.memory_leak:
print( print(f'Memory leak detection, please wait ~{args.stop_after * args.time * args.memory_leak * 2} seconds...')
'Memory leak detection, please wait ~{} seconds...'.format(
args.stop_after * args.time * args.memory_leak * 2
)
)
# First run without dump to fill the memory # First run without dump to fill the memory
mode.serve_n(args.stop_after) mode.serve_n(args.stop_after)
# Then start the memory-leak loop # Then start the memory-leak loop
@ -133,7 +124,7 @@ def start(config, args):
snapshot_end = tracemalloc.take_snapshot() snapshot_end = tracemalloc.take_snapshot()
snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename') snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
memory_leak = sum([s.size_diff for s in snapshot_diff]) 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):") logger.info("Memory consumption (top 5):")
for stat in snapshot_diff[:5]: for stat in snapshot_diff[:5]:
logger.info(stat) logger.info(stat)
@ -165,12 +156,10 @@ def main():
signal.signal(sig, __signal_handler) signal.signal(sig, __signal_handler)
# Log Glances and psutil version # Log Glances and psutil version
logger.info('Start Glances {}'.format(__version__)) logger.info(f'Start Glances {__version__}')
logger.info( python_impl = platform.python_implementation()
'{} {} ({}) and psutil {} detected'.format( python_ver = platform.python_version()
platform.python_implementation(), platform.python_version(), sys.executable, psutil_version logger.info(f'{python_impl} {python_ver} ({sys.executable}) and psutil {psutil_version} detected')
)
)
# Share global var # Share global var
global core global core

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# Glances - An eye on your system # Glances - An eye on your system
# #

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -10,8 +9,8 @@
"""Manage on alert actions.""" """Manage on alert actions."""
from glances.logger import logger from glances.logger import logger
from glances.timer import Timer
from glances.secure import secure_popen from glances.secure import secure_popen
from glances.timer import Timer
try: try:
import chevron import chevron
@ -22,7 +21,7 @@ else:
chevron_tag = True chevron_tag = True
class GlancesActions(object): class GlancesActions:
"""This class manage action if an alert is reached.""" """This class manage action if an alert is reached."""
def __init__(self, args=None): def __init__(self, args=None):
@ -80,13 +79,13 @@ class GlancesActions(object):
else: else:
cmd_full = cmd cmd_full = cmd
# Execute the action # 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: try:
ret = secure_popen(cmd_full) ret = secure_popen(cmd_full)
except OSError as e: 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: 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) self.set(stat_name, criticality)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # 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.globals import u
from glances.timer import Timer
from glances.logger import logger from glances.logger import logger
from glances.timer import Timer
class GlancesAmp(object): class GlancesAmp:
"""Main class for Glances AMP.""" """Main class for Glances AMP."""
NAME = '?' NAME = '?'
@ -38,7 +37,7 @@ class GlancesAmp(object):
def __init__(self, name=None, args=None): def __init__(self, name=None, args=None):
"""Init AMP class.""" """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_) # AMP name (= module name without glances_)
if name is None: if name is None:
@ -74,7 +73,7 @@ class GlancesAmp(object):
amp_section = 'amp_' + self.amp_name amp_section = 'amp_' + self.amp_name
if hasattr(config, 'has_section') and config.has_section(amp_section): if hasattr(config, 'has_section') and config.has_section(amp_section):
logger.debug("AMP - {}: Load configuration".format(self.NAME)) logger.debug(f"AMP - {self.NAME}: Load configuration")
for param, _ in config.items(amp_section): for param, _ in config.items(amp_section):
try: try:
self.configs[param] = config.get_float_value(amp_section, param) 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(',') self.configs[param] = config.get_value(amp_section, param).split(',')
if len(self.configs[param]) == 1: if len(self.configs[param]) == 1:
self.configs[param] = self.configs[param][0] 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: 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 return False
if self.enable(): if self.enable():
@ -92,13 +91,12 @@ class GlancesAmp(object):
for k in ['refresh']: for k in ['refresh']:
if k not in self.configs: if k not in self.configs:
logger.warning( logger.warning(
"AMP - {}: Can not find configuration key {} in section {} (the AMP will be disabled)".format( f"AMP - {self.NAME}: Can not find configuration key {k} in section {self.amp_name} "
self.NAME, k, self.amp_name f"(the AMP will be disabled)"
)
) )
self.configs['enable'] = 'false' self.configs['enable'] = 'false'
else: else:
logger.debug("AMP - {} is disabled".format(self.NAME)) logger.debug(f"AMP - {self.NAME} is disabled")
# Init the count to 0 # Init the count to 0
self.configs['count'] = 0 self.configs['count'] = 0
@ -109,7 +107,6 @@ class GlancesAmp(object):
"""Generic method to get the item in the AMP configuration""" """Generic method to get the item in the AMP configuration"""
if key in self.configs: if key in self.configs:
return self.configs[key] return self.configs[key]
else:
return None return None
def enable(self): def enable(self):
@ -117,7 +114,6 @@ class GlancesAmp(object):
ret = self.get('enable') ret = self.get('enable')
if ret is None: if ret is None:
return False return False
else:
return ret.lower().startswith('true') return ret.lower().startswith('true')
def regex(self): def regex(self):
@ -133,7 +129,6 @@ class GlancesAmp(object):
ret = self.get('one_line') ret = self.get('one_line')
if ret is None: if ret is None:
return False return False
else:
return ret.lower().startswith('true') return ret.lower().startswith('true')
def time_until_refresh(self): def time_until_refresh(self):
@ -193,5 +188,4 @@ class GlancesAmp(object):
# Call the children update method # Call the children update method
if self.should_update(): if self.should_update():
return self.update(process_list) return self.update(process_list)
else:
return self.result() return self.result()

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -25,11 +24,11 @@ one_line=false
command=foo status 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.amps.amp import GlancesAmp
from glances.globals import to_ascii, u
from glances.logger import logger
class Amp(GlancesAmp): class Amp(GlancesAmp):
@ -44,7 +43,7 @@ class Amp(GlancesAmp):
def __init__(self, name=None, args=None): def __init__(self, name=None, args=None):
"""Init the AMP.""" """Init the AMP."""
self.NAME = name.capitalize() self.NAME = name.capitalize()
super(Amp, self).__init__(name=name, args=args) super().__init__(name=name, args=args)
def update(self, process_list): def update(self, process_list):
"""Update the AMP""" """Update the AMP"""
@ -54,7 +53,7 @@ class Amp(GlancesAmp):
try: try:
res = self.get('command') res = self.get('command')
except OSError as e: 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() return self.result()
# No command found, use default message # No command found, use default message
if res is None: if res is None:

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -46,8 +45,8 @@ status_url=http://localhost/nginx_status
import requests import requests
from glances.logger import logger
from glances.amps.amp import GlancesAmp from glances.amps.amp import GlancesAmp
from glances.logger import logger
class Amp(GlancesAmp): class Amp(GlancesAmp):

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -35,11 +34,11 @@ one_line=true
systemctl_cmd=/usr/bin/systemctl --plain 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.amps.amp import GlancesAmp
from glances.globals import iteritems, to_ascii
from glances.logger import logger
class Amp(GlancesAmp): class Amp(GlancesAmp):
@ -62,7 +61,7 @@ class Amp(GlancesAmp):
try: try:
res = check_output(self.get('systemctl_cmd').split()) res = check_output(self.get('systemctl_cmd').split())
except (OSError, CalledProcessError) as e: 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: else:
status = {} status = {}
# For each line # For each line
@ -79,7 +78,7 @@ class Amp(GlancesAmp):
# Build the output (string) message # Build the output (string) message
output = 'Services\n' output = 'Services\n'
for k, v in iteritems(status): for k, v in iteritems(status):
output += '{}: {}\n'.format(k, v) output += f'{k}: {v}\n'
self.set_result(output, separator=' ') self.set_result(output, separator=' ')
return self.result() return self.result()

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -34,11 +33,11 @@ one_line=true
service_cmd=/usr/bin/service --status-all 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.amps.amp import GlancesAmp
from glances.globals import iteritems
from glances.logger import logger
class Amp(GlancesAmp): class Amp(GlancesAmp):
@ -61,7 +60,7 @@ class Amp(GlancesAmp):
try: try:
res = check_output(self.get('service_cmd').split(), stderr=STDOUT).decode('utf-8') res = check_output(self.get('service_cmd').split(), stderr=STDOUT).decode('utf-8')
except OSError as e: 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: else:
status = {'running': 0, 'stopped': 0, 'upstart': 0} status = {'running': 0, 'stopped': 0, 'upstart': 0}
# For each line # For each line
@ -79,7 +78,7 @@ class Amp(GlancesAmp):
# Build the output (string) message # Build the output (string) message
output = 'Services\n' output = 'Services\n'
for k, v in iteritems(status): for k, v in iteritems(status):
output += '{}: {}\n'.format(k, v) output += f'{k}: {v}\n'
self.set_result(output, separator=' ') self.set_result(output, separator=' ')
return self.result() return self.result()

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -13,12 +12,12 @@ import os
import re import re
import threading 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.logger import logger
from glances.processes import glances_processes from glances.processes import glances_processes
class AmpsList(object): class AmpsList:
"""This class describes the optional application monitoring process list. """This class describes the optional application monitoring process list.
The AMP list is a list of processes with a specific monitoring action. The AMP list is a list of processes with a specific monitoring action.
@ -57,9 +56,9 @@ class AmpsList(object):
try: try:
amp = __import__(os.path.basename(amp_module)) amp = __import__(os.path.basename(amp_module))
except ImportError as e: 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: except Exception as e:
logger.warning("Cannot load AMP {} ({})".format(amp_name, e)) logger.warning(f"Cannot load AMP {amp_name} ({e})")
else: else:
# Add the AMP to the dictionary # Add the AMP to the dictionary
# The key is the AMP name # The key is the AMP name
@ -69,7 +68,7 @@ class AmpsList(object):
# Load the AMP configuration # Load the AMP configuration
self.__amps_dict[amp_name].load_config(self.config) self.__amps_dict[amp_name].load_config(self.config)
# Log AMPs list # Log AMPs list
logger.debug("AMPs list: {}".format(self.getList())) logger.debug(f"AMPs list: {self.getList()}")
return True return True
@ -108,7 +107,7 @@ class AmpsList(object):
if len(amps_list) > 0: if len(amps_list) > 0:
# At least one process is matching the regex # 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 # Call the AMP update method
thread = threading.Thread(target=v.update_wrapper, args=[amps_list]) thread = threading.Thread(target=v.update_wrapper, args=[amps_list])
thread.start() thread.start()
@ -140,7 +139,7 @@ class AmpsList(object):
) )
except (TypeError, KeyError) as e: 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 return ret

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -12,7 +11,7 @@
from datetime import datetime from datetime import datetime
class GlancesAttribute(object): class GlancesAttribute:
def __init__(self, name, description='', history_max_size=None): def __init__(self, name, description='', history_max_size=None):
"""Init the attribute """Init the attribute
@ -66,7 +65,6 @@ class GlancesAttribute(object):
def value(self): def value(self):
if self.history_len() > 0: if self.history_len() > 0:
return (self._value[1] - self.history_value()[1]) / (self._value[0] - self.history_value()[0]) return (self._value[1] - self.history_value()[1]) / (self._value[0] - self.history_value()[0])
else:
return None return None
@value.setter @value.setter

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -16,7 +15,8 @@ from glances.globals import BSD
from glances.logger import logger from glances.logger import logger
try: 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 zeroconf_tag = True
except ImportError: except ImportError:
@ -26,7 +26,7 @@ except ImportError:
if zeroconf_tag: if zeroconf_tag:
zeroconf_min_version = (0, 17, 0) zeroconf_min_version = (0, 17, 0)
zeroconf_version = tuple([int(num) for num in __zeroconf_version.split('.')]) 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: if zeroconf_version < zeroconf_min_version:
logger.critical("Please install zeroconf 0.17 or higher.") logger.critical("Please install zeroconf 0.17 or higher.")
sys.exit(1) sys.exit(1)
@ -34,10 +34,10 @@ if zeroconf_tag:
# Global var # Global var
# Recent versions of the zeroconf python package doesn't like a zeroconf type that ends with '._tcp.'. # 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 # 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.""" """Class to manage the auto discovered servers dict."""
def __init__(self): def __init__(self):
@ -66,7 +66,7 @@ class AutoDiscovered(object):
'type': 'DYNAMIC', 'type': 'DYNAMIC',
} # Server type: 'STATIC' or 'DYNAMIC' } # Server type: 'STATIC' or 'DYNAMIC'
self._server_list.append(new_server) 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): def remove_server(self, name):
"""Remove a server from the dict.""" """Remove a server from the dict."""
@ -74,13 +74,13 @@ class AutoDiscovered(object):
if i['key'] == name: if i['key'] == name:
try: try:
self._server_list.remove(i) self._server_list.remove(i)
logger.debug("Remove server %s from the list" % name) logger.debug(f"Remove server {name} from the list")
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}")
except ValueError: 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.""" """Zeroconf listener for Glances server."""
def __init__(self): def __init__(self):
@ -104,7 +104,7 @@ class GlancesAutoDiscoverListener(object):
""" """
if srv_type != zeroconf_type: if srv_type != zeroconf_type:
return False 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) info = zeroconf.get_service_info(srv_type, srv_name)
if info and (info.addresses or info.parsed_addresses): if info and (info.addresses or info.parsed_addresses):
address = info.addresses[0] if info.addresses else info.parsed_addresses[0] 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 # Add server to the global dict
self.servers.add_server(srv_name, new_server_ip, new_server_port) 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: else:
logger.warning("New Glances server detected, but failed to be get Zeroconf ServiceInfo ") logger.warning("New Glances server detected, but failed to be get Zeroconf ServiceInfo ")
return True return True
@ -121,10 +121,10 @@ class GlancesAutoDiscoverListener(object):
def remove_service(self, zeroconf, srv_type, srv_name): def remove_service(self, zeroconf, srv_type, srv_name):
"""Remove the server from the list.""" """Remove the server from the list."""
self.servers.remove_server(srv_name) 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).""" """Implementation of the Zeroconf protocol (server side for the Glances client)."""
def __init__(self, args=None): def __init__(self, args=None):
@ -132,8 +132,8 @@ class GlancesAutoDiscoverServer(object):
logger.info("Init autodiscover mode (Zeroconf protocol)") logger.info("Init autodiscover mode (Zeroconf protocol)")
try: try:
self.zeroconf = Zeroconf() self.zeroconf = Zeroconf()
except socket.error as e: except OSError as e:
logger.error("Cannot start Zeroconf (%s)" % e) logger.error(f"Cannot start Zeroconf ({e})")
self.zeroconf_enable_tag = False self.zeroconf_enable_tag = False
else: else:
self.listener = GlancesAutoDiscoverListener() self.listener = GlancesAutoDiscoverListener()
@ -147,7 +147,6 @@ class GlancesAutoDiscoverServer(object):
"""Return the current server list (dict of dict).""" """Return the current server list (dict of dict)."""
if zeroconf_tag and self.zeroconf_enable_tag: if zeroconf_tag and self.zeroconf_enable_tag:
return self.listener.get_servers_list() return self.listener.get_servers_list()
else:
return [] return []
def set_server(self, server_pos, key, value): def set_server(self, server_pos, key, value):
@ -160,7 +159,7 @@ class GlancesAutoDiscoverServer(object):
self.zeroconf.close() self.zeroconf.close()
class GlancesAutoDiscoverClient(object): class GlancesAutoDiscoverClient:
"""Implementation of the zeroconf protocol (client side for the Glances server).""" """Implementation of the zeroconf protocol (client side for the Glances server)."""
def __init__(self, hostname, args=None): def __init__(self, hostname, args=None):
@ -168,8 +167,8 @@ class GlancesAutoDiscoverClient(object):
zeroconf_bind_address = args.bind_address zeroconf_bind_address = args.bind_address
try: try:
self.zeroconf = Zeroconf() self.zeroconf = Zeroconf()
except socket.error as e: except OSError as e:
logger.error("Cannot start zeroconf: {}".format(e)) logger.error(f"Cannot start zeroconf: {e}")
# XXX *BSDs: Segmentation fault (core dumped) # XXX *BSDs: Segmentation fault (core dumped)
# -- https://bitbucket.org/al45tair/netifaces/issues/15 # -- https://bitbucket.org/al45tair/netifaces/issues/15
@ -192,7 +191,7 @@ class GlancesAutoDiscoverClient(object):
try: try:
self.info = ServiceInfo( self.info = ServiceInfo(
zeroconf_type, zeroconf_type,
'{}:{}.{}'.format(hostname, args.port, zeroconf_type), f'{hostname}:{args.port}.{zeroconf_type}',
address=socket.inet_pton(address_family, zeroconf_bind_address), address=socket.inet_pton(address_family, zeroconf_bind_address),
port=args.port, port=args.port,
weight=0, weight=0,
@ -205,7 +204,7 @@ class GlancesAutoDiscoverClient(object):
# address (only one address) is replaced by addresses (list of addresses) # address (only one address) is replaced by addresses (list of addresses)
self.info = ServiceInfo( self.info = ServiceInfo(
zeroconf_type, 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)], addresses=[socket.inet_pton(address_family, zeroconf_bind_address)],
port=args.port, port=args.port,
weight=0, weight=0,
@ -216,9 +215,9 @@ class GlancesAutoDiscoverClient(object):
try: try:
self.zeroconf.register_service(self.info) self.zeroconf.register_service(self.info)
except Exception as e: except Exception as e:
logger.error("Error while announcing Glances server: {}".format(e)) logger.error(f"Error while announcing Glances server: {e}")
else: 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: else:
logger.error("Cannot announce Glances server on the network: zeroconf library not found.") logger.error("Cannot announce Glances server on the network: zeroconf library not found.")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,16 +8,16 @@
"""Manage the Glances client.""" """Manage the Glances client."""
import ujson
import socket
import sys import sys
import time import time
import ujson
from glances import __version__ from glances import __version__
from glances.globals import Fault, ProtocolError, ServerProxy, Transport from glances.globals import Fault, ProtocolError, ServerProxy, Transport
from glances.logger import logger from glances.logger import logger
from glances.stats_client import GlancesStatsClient
from glances.outputs.glances_curses import GlancesCursesClient from glances.outputs.glances_curses import GlancesCursesClient
from glances.stats_client import GlancesStatsClient
from glances.timer import Counter from glances.timer import Counter
@ -29,7 +28,7 @@ class GlancesClientTransport(Transport):
self.timeout = timeout self.timeout = timeout
class GlancesClient(object): class GlancesClient:
"""This class creates and manages the TCP client.""" """This class creates and manages the TCP client."""
def __init__(self, config=None, args=None, timeout=7, return_to_browser=False): def __init__(self, config=None, args=None, timeout=7, return_to_browser=False):
@ -48,10 +47,12 @@ class GlancesClient(object):
# Build the URI # Build the URI
if args.password != "": 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: else:
self.uri = 'http://{}:{}'.format(args.client, args.port) self.uri = f'http://{args.client}:{args.port}'
logger.debug("Try to connect to {}".format(self.uri))
# Avoid logging user credentials
logger.debug(f"Try to connect to 'http://{args.client}:{args.port}'")
# Try to connect to the URI # Try to connect to the URI
transport = GlancesClientTransport() transport = GlancesClientTransport()
@ -60,7 +61,7 @@ class GlancesClient(object):
try: try:
self.client = ServerProxy(self.uri, transport=transport) self.client = ServerProxy(self.uri, transport=transport)
except Exception as e: 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 @property
def quiet(self): def quiet(self):
@ -93,10 +94,10 @@ class GlancesClient(object):
client_version = None client_version = None
try: try:
client_version = self.client.init() client_version = self.client.init()
except socket.error as err: except OSError as err:
# Fallback to SNMP # Fallback to SNMP
self.client_mode = '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...' fall_back_msg = 'No Glances server found. Trying fallback to SNMP...'
if not self.return_to_browser: if not self.return_to_browser:
print(fall_back_msg) print(fall_back_msg)
@ -104,11 +105,11 @@ class GlancesClient(object):
logger.info(fall_back_msg) logger.info(fall_back_msg)
except ProtocolError as err: except ProtocolError as err:
# Other errors # Other errors
msg = "Connection to server {} failed".format(self.uri) msg = f"Connection to server {self.uri} failed"
if err.errcode == 401: if err.errcode == 401:
msg += " (Bad username/password)" msg += " (Bad username/password)"
else: else:
msg += " ({} {})".format(err.errcode, err.errmsg) msg += f" ({err.errcode} {err.errmsg})"
self.log_and_exit(msg) self.log_and_exit(msg)
return False return False
@ -118,13 +119,11 @@ class GlancesClient(object):
# Init stats # Init stats
self.stats = GlancesStatsClient(config=self.config, args=self.args) self.stats = GlancesStatsClient(config=self.config, args=self.args)
self.stats.set_plugins(ujson.loads(self.client.getAllPlugins())) 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: else:
self.log_and_exit( self.log_and_exit(
(
'Client and server not compatible: ' 'Client and server not compatible: '
'Client version: {} / Server version: {}'.format(__version__, client_version) f'Client version: {__version__} / Server version: {client_version}'
)
) )
return False return False
@ -180,11 +179,11 @@ class GlancesClient(object):
"""Update stats from Glances/SNMP server.""" """Update stats from Glances/SNMP server."""
if self.client_mode == 'glances': if self.client_mode == 'glances':
return self.update_glances() return self.update_glances()
elif self.client_mode == 'snmp': if self.client_mode == 'snmp':
return self.update_snmp() return self.update_snmp()
else:
self.end() self.end()
logger.critical("Unknown server mode: {}".format(self.client_mode)) logger.critical(f"Unknown server mode: {self.client_mode}")
sys.exit(2) sys.exit(2)
def update_glances(self): def update_glances(self):
@ -197,7 +196,7 @@ class GlancesClient(object):
# Update the stats # Update the stats
try: try:
server_stats = ujson.loads(self.client.getAll()) server_stats = ujson.loads(self.client.getAll())
except socket.error: except OSError:
# Client cannot get server stats # Client cannot get server stats
return "Disconnected" return "Disconnected"
except Fault: except Fault:
@ -240,12 +239,12 @@ class GlancesClient(object):
# Update the stats # Update the stats
counter = Counter() counter = Counter()
cs_status = self.update() 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 # Export stats using export modules
counter_export = Counter() counter_export = Counter()
self.stats.export(self.stats) 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 # Patch for issue1326 to avoid < 0 refresh
adapted_refresh = self.refresh_time - counter.get() adapted_refresh = self.refresh_time - counter.get()

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,20 +8,20 @@
"""Manage the Glances client browser (list of Glances server).""" """Manage the Glances client browser (list of Glances server)."""
import ujson
import socket
import threading import threading
from glances.globals import Fault, ProtocolError, ServerProxy import ujson
from glances.autodiscover import GlancesAutoDiscoverServer
from glances.client import GlancesClient, GlancesClientTransport 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.password_list import GlancesPasswordList as GlancesPassword
from glances.static_list import GlancesStaticServer 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).""" """This class creates and manages the TCP client browser (servers list)."""
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
@ -76,7 +75,6 @@ class GlancesClientBrowser(object):
if clear_password is not None: if clear_password is not None:
server['password'] = self.password.get_hash(clear_password) server['password'] = self.password.get_hash(clear_password)
return 'http://{}:{}@{}:{}'.format(server['username'], server['password'], server['ip'], server['port']) return 'http://{}:{}@{}:{}'.format(server['username'], server['password'], server['ip'], server['port'])
else:
return 'http://{}:{}'.format(server['ip'], server['port']) return 'http://{}:{}'.format(server['ip'], server['port'])
def __update_stats(self, server): def __update_stats(self, server):
@ -92,19 +90,19 @@ class GlancesClientBrowser(object):
try: try:
s = ServerProxy(uri, transport=t) s = ServerProxy(uri, transport=t)
except Exception as e: 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: else:
# Mandatory stats # Mandatory stats
try: try:
# CPU% # CPU%
cpu_percent = 100 - ujson.loads(s.getCpu())['idle'] cpu_percent = 100 - ujson.loads(s.getCpu())['idle']
server['cpu_percent'] = '{:.1f}'.format(cpu_percent) server['cpu_percent'] = f'{cpu_percent:.1f}'
# MEM% # MEM%
server['mem_percent'] = ujson.loads(s.getMem())['percent'] server['mem_percent'] = ujson.loads(s.getMem())['percent']
# OS (Human Readable name) # OS (Human Readable name)
server['hr_name'] = ujson.loads(s.getSystem())['hr_name'] server['hr_name'] = ujson.loads(s.getSystem())['hr_name']
except (socket.error, Fault, KeyError) as e: except (OSError, Fault, KeyError) as e:
logger.debug("Error while grabbing stats form server ({})".format(e)) logger.debug(f"Error while grabbing stats form server ({e})")
server['status'] = 'OFFLINE' server['status'] = 'OFFLINE'
except ProtocolError as e: except ProtocolError as e:
if e.errcode == 401: if e.errcode == 401:
@ -114,7 +112,7 @@ class GlancesClientBrowser(object):
server['status'] = 'PROTECTED' server['status'] = 'PROTECTED'
else: else:
server['status'] = 'OFFLINE' 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: else:
# Status # Status
server['status'] = 'ONLINE' server['status'] = 'ONLINE'
@ -123,16 +121,16 @@ class GlancesClientBrowser(object):
try: try:
# LOAD # LOAD
load_min5 = ujson.loads(s.getLoad())['min5'] 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: 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 return server
def __display_server(self, server): def __display_server(self, server):
"""Connect and display the given server""" """Connect and display the given server"""
# Display the Glances client for the selected 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 # Connection can take time
# Display a popup # Display a popup
@ -201,7 +199,7 @@ class GlancesClientBrowser(object):
# For each server in the list, grab elementary stats (CPU, LOAD, MEM, OS...) # For each server in the list, grab elementary stats (CPU, LOAD, MEM, OS...)
thread_list = {} thread_list = {}
while not self.screen.is_end: 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(): for v in self.get_servers_list():
key = v["key"] key = v["key"]
thread = thread_list.get(key, None) thread = thread_list.get(key, None)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,13 +8,13 @@
"""Manage the configuration file.""" """Manage the configuration file."""
import os import builtins
import sys
import multiprocessing import multiprocessing
from io import open import os
import re 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 from glances.logger import logger
@ -94,7 +93,7 @@ def default_config_dir():
return [path] return [path]
class Config(object): class Config:
"""This class is used to access/read config file, if it exists. """This class is used to access/read config file, if it exists.
:param config_dir: the path to search for config file :param config_dir: the path to search for config file
@ -153,15 +152,15 @@ class Config(object):
def read(self): def read(self):
"""Read the config file, if it exists. Using defaults otherwise.""" """Read the config file, if it exists. Using defaults otherwise."""
for config_file in self.config_file_paths(): 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): if os.path.exists(config_file):
try: 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_file(f)
self.parser.read(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: 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) sys.exit(1)
# Save the loaded configuration file path (issue #374) # Save the loaded configuration file path (issue #374)
self._loaded_config_file = config_file self._loaded_config_file = config_file

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,13 +8,13 @@
"""CPU percent stats shared between CPU and Quicklook plugins.""" """CPU percent stats shared between CPU and Quicklook plugins."""
import psutil
from glances.logger import logger from glances.logger import logger
from glances.timer import Timer from glances.timer import Timer
import psutil
class CpuPercent:
class CpuPercent(object):
"""Get and store the CPU percent.""" """Get and store the CPU percent."""
def __init__(self, cached_timer_cpu=3): def __init__(self, cached_timer_cpu=3):
@ -46,7 +45,6 @@ class CpuPercent(object):
If percpu, return the percpu stats""" If percpu, return the percpu stats"""
if percpu: if percpu:
return self.__get_percpu() return self.__get_percpu()
else:
return self.__get_cpu() return self.__get_cpu()
def get_info(self): def get_info(self):
@ -57,7 +55,7 @@ class CpuPercent(object):
try: try:
cpu_freq = psutil.cpu_freq() cpu_freq = psutil.cpu_freq()
except Exception as e: except Exception as e:
logger.debug('Can not grab CPU information ({})'.format(e)) logger.debug(f'Can not grab CPU information ({e})')
else: else:
if hasattr(cpu_freq, 'current'): if hasattr(cpu_freq, 'current'):
self.cpu_info['cpu_hz_current'] = 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 # Get the CPU name once from the /proc/cpuinfo file
# TODO: Multisystem... # TODO: Multisystem...
try: 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): except (FileNotFoundError, PermissionError, IndexError, KeyError, AttributeError):
self.cpu_info['cpu_name'] = 'CPU' self.cpu_info['cpu_name'] = 'CPU'
return self.cpu_info['cpu_name'] return self.cpu_info['cpu_name']

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -8,7 +7,8 @@
# #
"""Manage Glances event class """Manage Glances event class
This class is a Pydantic data class for the Glances event.
This class is a data class for the Glances event.
event_state = "OK|CAREFUL|WARNING|CRITICAL" event_state = "OK|CAREFUL|WARNING|CRITICAL"
event_type = "CPU*|LOAD|MEM|MON" event_type = "CPU*|LOAD|MEM|MON"

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -10,12 +9,12 @@
"""Manage Glances events list (previously Glances logs in Glances < 3.1).""" """Manage Glances events list (previously Glances logs in Glances < 3.1)."""
import time import time
from datetime import datetime
from dataclasses import asdict from dataclasses import asdict
from datetime import datetime
from glances.event import GlancesEvent
from glances.processes import glances_processes from glances.processes import glances_processes
from glances.thresholds import glances_thresholds from glances.thresholds import glances_thresholds
from glances.event import GlancesEvent
# Static decision tree for the global alert message # Static decision tree for the global alert message
# - msg: Message to be displayed (result of the decision tree) # - msg: Message to be displayed (result of the decision tree)
@ -158,11 +157,10 @@ def build_global_message():
if themax['weight'] >= themax['thresholds_min']: if themax['weight'] >= themax['thresholds_min']:
# Check if the weight is > to the minimal threshold value # Check if the weight is > to the minimal threshold value
return themax['msg'] 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. """This class manages events inside the Glances software.
GlancesEventsList is a list of GlancesEvent. GlancesEventsList is a list of GlancesEvent.
GlancesEvent is defined in the event.py file GlancesEvent is defined in the event.py file

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -13,13 +12,12 @@ I am your father...
...for all Glances exports IF. ...for all Glances exports IF.
""" """
from glances.globals import json_dumps from glances.globals import NoOptionError, NoSectionError, iteritems, iterkeys, json_dumps
from glances.globals import NoOptionError, NoSectionError, iteritems, iterkeys
from glances.timer import Counter
from glances.logger import logger from glances.logger import logger
from glances.timer import Counter
class GlancesExport(object): class GlancesExport:
"""Main class for Glances export IF.""" """Main class for Glances export IF."""
# List of non exportable plugins # List of non exportable plugins
@ -40,7 +38,7 @@ class GlancesExport(object):
"""Init the export class.""" """Init the export class."""
# Export name # Export name
self.export_name = self.__class__.__module__ 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 # Init the config & args
self.config = config self.config = config
@ -64,18 +62,16 @@ class GlancesExport(object):
counter = Counter() counter = Counter()
ret = fct(*args, **kw) ret = fct(*args, **kw)
duration = counter.get() duration = counter.get()
logger.debug( class_name = args[0].__class__.__name__
"{} {} {} return {} in {} seconds".format( class_module = args[0].__class__.__module__
args[0].__class__.__name__, args[0].__class__.__module__, fct.__name__, ret, duration logger.debug(f"{class_name} {class_module} {fct.__name__} return {ret} in {duration} seconds")
)
)
return ret return ret
return wrapper return wrapper
def exit(self): def exit(self):
"""Close the export module.""" """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): def load_conf(self, section, mandatories=['host', 'port'], options=None):
"""Load the export <section> configuration in the Glances configuration file. """Load the export <section> configuration in the Glances configuration file.
@ -96,10 +92,10 @@ class GlancesExport(object):
for opt in mandatories: for opt in mandatories:
setattr(self, opt, self.config.get_value(section, opt)) setattr(self, opt, self.config.get_value(section, opt))
except NoSectionError: except NoSectionError:
logger.error("No {} configuration found".format(section)) logger.error(f"No {section} configuration found")
return False return False
except NoOptionError as e: except NoOptionError as e:
logger.error("Error in the {} configuration ({})".format(section, e)) logger.error(f"Error in the {section} configuration ({e})")
return False return False
# Load options # Load options
@ -109,8 +105,8 @@ class GlancesExport(object):
except NoOptionError: except NoOptionError:
pass pass
logger.debug("Load {} from the Glances configuration file".format(section)) logger.debug(f"Load {section} from the Glances configuration file")
logger.debug("{} parameters: {}".format(section, {opt: getattr(self, opt) for opt in mandatories + options})) logger.debug(f"{section} parameters: {({opt: getattr(self, opt) for opt in mandatories + options})}")
return True return True
@ -120,10 +116,9 @@ class GlancesExport(object):
try: try:
ret = item[item['key']] ret = item[item['key']]
except KeyError: except KeyError:
logger.error("No 'key' available in {}".format(item)) logger.error(f"No 'key' available in {item}")
if isinstance(ret, list): if isinstance(ret, list):
return ret[0] return ret[0]
else:
return ret return ret
def parse_tags(self, tags): def parse_tags(self, tags):

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -13,13 +12,13 @@ import sys
from datetime import datetime from datetime import datetime
from numbers import Number from numbers import Number
from glances.logger import logger from cassandra import InvalidRequest
from glances.exports.export import GlancesExport
from cassandra.auth import PlainTextAuthProvider from cassandra.auth import PlainTextAuthProvider
from cassandra.cluster import Cluster from cassandra.cluster import Cluster
from cassandra.util import uuid_from_time 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): class Export(GlancesExport):
@ -27,7 +26,7 @@ class Export(GlancesExport):
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the Cassandra export IF.""" """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) # Mandatory configuration keys (additional to host and port)
self.keyspace = None self.keyspace = None
@ -69,53 +68,52 @@ class Export(GlancesExport):
) )
session = cluster.connect() session = cluster.connect()
except Exception as e: 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) sys.exit(2)
# Keyspace # Keyspace
try: try:
session.set_keyspace(self.keyspace) session.set_keyspace(self.keyspace)
except InvalidRequest: except InvalidRequest:
logger.info("Create keyspace {} on the Cassandra cluster".format(self.keyspace)) logger.info(f"Create keyspace {self.keyspace} on the Cassandra cluster")
c = "CREATE KEYSPACE %s WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': '%s' }" % ( c = (
self.keyspace, f"CREATE KEYSPACE {self.keyspace} WITH "
self.replication_factor, f"replication = {{ 'class': 'SimpleStrategy', 'replication_factor': '{self.replication_factor}' }}"
) )
session.execute(c) session.execute(c)
session.set_keyspace(self.keyspace) session.set_keyspace(self.keyspace)
logger.info( logger.info(
"Stats will be exported to Cassandra cluster {} ({}) in keyspace {}".format( f"Stats will be exported to Cassandra cluster {cluster.metadata.cluster_name} "
cluster.metadata.cluster_name, cluster.metadata.all_hosts(), self.keyspace f"({cluster.metadata.all_hosts()}) in keyspace {self.keyspace}"
)
) )
# Table # Table
try: try:
session.execute( session.execute(
"CREATE TABLE %s (plugin text, time timeuuid, stat map<text,float>, PRIMARY KEY (plugin, time)) \ f"CREATE TABLE {self.table} "
WITH CLUSTERING ORDER BY (time DESC)" f"(plugin text, time timeuuid, stat map<text,float>, PRIMARY KEY (plugin, time)) "
% self.table f"WITH CLUSTERING ORDER BY (time DESC)"
) )
except Exception: except Exception:
logger.debug("Cassandra table %s already exist" % self.table) logger.debug(f"Cassandra table {self.table} already exist")
return cluster, session return cluster, session
def export(self, name, columns, points): def export(self, name, columns, points):
"""Write the points to the Cassandra cluster.""" """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) # 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)} data = {k: float(v) for (k, v) in dict(zip(columns, points)).iteritems() if isinstance(v, Number)}
# Write input to the Cassandra table # Write input to the Cassandra table
try: 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) query = self.session.prepare(stmt)
self.session.execute(query, (name, uuid_from_time(datetime.now()), data)) self.session.execute(query, (name, uuid_from_time(datetime.now()), data))
except Exception as e: 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): def exit(self):
"""Close the Cassandra export module.""" """Close the Cassandra export module."""
@ -123,4 +121,4 @@ class Export(GlancesExport):
self.session.shutdown() self.session.shutdown()
self.cluster.shutdown() self.cluster.shutdown()
# Call the father method # Call the father method
super(Export, self).exit() super().exit()

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -20,18 +19,18 @@
import sys import sys
from datetime import datetime from datetime import datetime
from glances.logger import logger
from glances.exports.export import GlancesExport
import pycouchdb import pycouchdb
from glances.exports.export import GlancesExport
from glances.logger import logger
class Export(GlancesExport): class Export(GlancesExport):
"""This class manages the CouchDB export module.""" """This class manages the CouchDB export module."""
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the CouchDB export IF.""" """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 # Load the CouchDB configuration file section
# User and Password are mandatory with CouchDB 3.0 and higher # User and Password are mandatory with CouchDB 3.0 and higher
@ -48,15 +47,15 @@ class Export(GlancesExport):
return None return None
# @TODO: https # @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: try:
s = pycouchdb.Server(server_uri) s = pycouchdb.Server(server_uri)
except Exception as e: 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) sys.exit(2)
else: 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: try:
s.database(self.db) s.database(self.db)
@ -64,15 +63,15 @@ class Export(GlancesExport):
# Database did not exist # Database did not exist
# Create it... # Create it...
s.create(self.db) s.create(self.db)
logger.info("Create CouchDB database %s" % self.db) logger.info(f"Create CouchDB database {self.db}")
else: else:
logger.info("CouchDB database %s already exist" % self.db) logger.info(f"CouchDB database {self.db} already exist")
return s.database(self.db) return s.database(self.db)
def export(self, name, columns, points): def export(self, name, columns, points):
"""Write the points to the CouchDB server.""" """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 # Create DB input
data = dict(zip(columns, points)) data = dict(zip(columns, points))
@ -85,4 +84,4 @@ class Export(GlancesExport):
try: try:
self.client.save(data) self.client.save(data)
except Exception as e: except Exception as e:
logger.error("Cannot export {} stats to CouchDB ({})".format(name, e)) logger.error(f"Cannot export {name} stats to CouchDB ({e})")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,13 +8,13 @@
"""CSV interface class.""" """CSV interface class."""
import os.path
import csv import csv
import os.path
import sys import sys
import time import time
from glances.logger import logger
from glances.exports.export import GlancesExport from glances.exports.export import GlancesExport
from glances.logger import logger
class Export(GlancesExport): class Export(GlancesExport):
@ -23,7 +22,7 @@ class Export(GlancesExport):
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the CSV export IF.""" """Init the CSV export IF."""
super(Export, self).__init__(config=config, args=args) super().__init__(config=config, args=args)
# CSV file name # CSV file name
self.csv_filename = args.export_csv_file self.csv_filename = args.export_csv_file
@ -42,8 +41,8 @@ class Export(GlancesExport):
try: try:
self.csv_file = open_csv_file(self.csv_filename, 'r') self.csv_file = open_csv_file(self.csv_filename, 'r')
reader = csv.reader(self.csv_file) reader = csv.reader(self.csv_file)
except IOError as e: except OSError as e:
logger.critical("Cannot open existing CSV file: {}".format(e)) logger.critical(f"Cannot open existing CSV file: {e}")
sys.exit(2) sys.exit(2)
self.old_header = next(reader, None) self.old_header = next(reader, None)
self.csv_file.close() self.csv_file.close()
@ -51,11 +50,11 @@ class Export(GlancesExport):
try: try:
self.csv_file = open_csv_file(self.csv_filename, file_mode) self.csv_file = open_csv_file(self.csv_filename, file_mode)
self.writer = csv.writer(self.csv_file) self.writer = csv.writer(self.csv_file)
except IOError as e: except OSError as e:
logger.critical("Cannot create the CSV file: {}".format(e)) logger.critical(f"Cannot create the CSV file: {e}")
sys.exit(2) 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 self.export_enable = True
@ -63,7 +62,7 @@ class Export(GlancesExport):
def exit(self): def exit(self):
"""Close the CSV file.""" """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() self.csv_file.close()
def update(self, stats): def update(self, stats):
@ -95,8 +94,8 @@ class Export(GlancesExport):
if self.old_header != csv_header and self.old_header is not None: if self.old_header != csv_header and self.old_header is not None:
# Header are different, log an error and do not write data # Header are different, log an error and do not write data
logger.error("Cannot append data to existing CSV file. Headers are different.") logger.error("Cannot append data to existing CSV file. Headers are different.")
logger.debug("Old header: {}".format(self.old_header)) logger.debug(f"Old header: {self.old_header}")
logger.debug("New header: {}".format(csv_header)) logger.debug(f"New header: {csv_header}")
else: else:
# Header are equals, ready to write data # Header are equals, ready to write data
self.old_header = None self.old_header = None

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -12,18 +11,18 @@
import sys import sys
from datetime import datetime from datetime import datetime
from glances.logger import logger
from glances.exports.export import GlancesExport
from elasticsearch import Elasticsearch, helpers from elasticsearch import Elasticsearch, helpers
from glances.exports.export import GlancesExport
from glances.logger import logger
class Export(GlancesExport): class Export(GlancesExport):
"""This class manages the ElasticSearch (ES) export module.""" """This class manages the ElasticSearch (ES) export module."""
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the ES export IF.""" """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) # Mandatory configuration keys (additional to host and port)
self.index = None self.index = None
@ -44,24 +43,22 @@ class Export(GlancesExport):
return None return None
try: 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: except Exception as e:
logger.critical( logger.critical(f"Cannot connect to ElasticSearch server {self.scheme}://{self.host}:{self.port} ({e})")
"Cannot connect to ElasticSearch server %s://%s:%s (%s)" % (self.scheme, self.host, self.port, e)
)
sys.exit(2) sys.exit(2)
if not es.ping(): 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) sys.exit(2)
else: 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 return es
def export(self, name, columns, points): def export(self, name, columns, points):
"""Write the points to the ES server.""" """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 # Generate index name with the index field + current day
index = '{}-{}'.format(self.index, datetime.utcnow().strftime("%Y.%m.%d")) index = '{}-{}'.format(self.index, datetime.utcnow().strftime("%Y.%m.%d"))
@ -72,17 +69,17 @@ class Export(GlancesExport):
dt_now = datetime.utcnow().isoformat('T') dt_now = datetime.utcnow().isoformat('T')
action = { action = {
"_index": index, "_index": index,
"_id": '{}.{}'.format(name, dt_now), "_id": f'{name}.{dt_now}',
"_type": 'glances-{}'.format(name), "_type": f'glances-{name}',
"_source": {"plugin": name, "timestamp": dt_now}, "_source": {"plugin": name, "timestamp": dt_now},
} }
action['_source'].update(zip(columns, [str(p) for p in points])) action['_source'].update(zip(columns, [str(p) for p in points]))
actions.append(action) 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 # Write input to the ES index
try: try:
helpers.bulk(self.client, actions) helpers.bulk(self.client, actions)
except Exception as e: except Exception as e:
logger.error("Cannot export {} stats to ElasticSearch ({})".format(name, e)) logger.error(f"Cannot export {name} stats to ElasticSearch ({e})")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,17 +8,18 @@
"""Graph exporter interface class.""" """Graph exporter interface class."""
from pygal import DateTimeLine
import pygal.style
import sys
import os
import tempfile
import errno 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.logger import logger
from glances.timer import Timer from glances.timer import Timer
from glances.globals import iteritems, time_serie_subsample
from glances.exports.export import GlancesExport
class Export(GlancesExport): class Export(GlancesExport):
@ -27,7 +27,7 @@ class Export(GlancesExport):
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the export IF.""" """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) # Load the Graph configuration file section (is exists)
self.export_enable = self.load_conf('graph', options=['path', 'generate_every', 'width', 'height', 'style']) self.export_enable = self.load_conf('graph', options=['path', 'generate_every', 'width', 'height', 'style'])
@ -44,19 +44,19 @@ class Export(GlancesExport):
os.makedirs(self.path) os.makedirs(self.path)
except OSError as e: except OSError as e:
if e.errno != errno.EEXIST: 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) sys.exit(2)
# Check if output folder is writeable # Check if output folder is writeable
try: try:
tempfile.TemporaryFile(dir=self.path) tempfile.TemporaryFile(dir=self.path)
except OSError: 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) 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: 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)") logger.info("or when 'g' key is pressed (only through the CLI interface)")
# Start the timer # Start the timer
self._timer = Timer(self.generate_every) self._timer = Timer(self.generate_every)
@ -66,7 +66,7 @@ class Export(GlancesExport):
def exit(self): def exit(self):
"""Close the files.""" """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): def update(self, stats):
"""Generate Graph file in the output folder.""" """Generate Graph file in the output folder."""
@ -84,7 +84,7 @@ class Export(GlancesExport):
if plugin_name in self.plugins_to_export(stats): if plugin_name in self.plugins_to_export(stats):
self.export(plugin_name, plugin.get_export_history()) 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 self.args.generate_graph = False
def export(self, title, data): def export(self, title, data):

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -12,18 +11,18 @@
import sys import sys
from numbers import Number from numbers import Number
from glances.logger import logger
from glances.exports.export import GlancesExport
from graphitesend import GraphiteClient from graphitesend import GraphiteClient
from glances.exports.export import GlancesExport
from glances.logger import logger
class Export(GlancesExport): class Export(GlancesExport):
"""This class manages the Graphite export module.""" """This class manages the Graphite export module."""
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the Graphite export IF.""" """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) # Mandatory configuration keys (additional to host and port)
# N/A # N/A
@ -74,25 +73,25 @@ class Export(GlancesExport):
debug=self.debug, debug=self.debug,
) )
except Exception as e: 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 client = None
else: 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 return client
def export(self, name, columns, points): def export(self, name, columns, points):
"""Export the stats to the Graphite server.""" """Export the stats to the Graphite server."""
if self.client is None: if self.client is None:
return False 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())) after_filtering_dict = dict(filter(lambda i: isinstance(i[1], Number), before_filtering_dict.items()))
try: try:
self.client.send_dict(after_filtering_dict) self.client.send_dict(after_filtering_dict)
except Exception as e: 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 return False
else: else:
logger.debug("Export {} stats to Graphite".format(name)) logger.debug(f"Export {name} stats to Graphite")
return True return True
@ -100,6 +99,4 @@ def normalize(name):
"""Normalize name for the Graphite convention""" """Normalize name for the Graphite convention"""
# Name should not contain space # Name should not contain space
ret = name.replace(' ', '_') return name.replace(' ', '_')
return ret

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -12,12 +11,12 @@
import sys import sys
from platform import node from platform import node
from glances.logger import logger
from glances.exports.export import GlancesExport
from influxdb import InfluxDBClient from influxdb import InfluxDBClient
from influxdb.client import InfluxDBClientError from influxdb.client import InfluxDBClientError
from glances.exports.export import GlancesExport
from glances.logger import logger
FIELD_TO_TAG = ['name', 'cmdline', 'type'] FIELD_TO_TAG = ['name', 'cmdline', 'type']
@ -26,7 +25,7 @@ class Export(GlancesExport):
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the InfluxDB export IF.""" """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) # Mandatory configuration keys (additional to host and port)
self.user = None self.user = None
@ -75,13 +74,13 @@ class Export(GlancesExport):
) )
get_all_db = [i['name'] for i in db.get_list_database()] get_all_db = [i['name'] for i in db.get_list_database()]
except InfluxDBClientError as e: 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) sys.exit(2)
if self.db in get_all_db: 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: 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) sys.exit(2)
return db return db
@ -106,9 +105,7 @@ class Export(GlancesExport):
# Manage field # Manage field
if measurement is not None: if measurement is not None:
fields = { fields = {
k.replace('{}.'.format(measurement), ''): data_dict[k] k.replace(f'{measurement}.', ''): data_dict[k] for k in data_dict if k.startswith(f'{measurement}.')
for k in data_dict
if k.startswith('{}.'.format(measurement))
} }
else: else:
fields = data_dict fields = data_dict
@ -155,12 +152,12 @@ class Export(GlancesExport):
name = self.prefix + '.' + name name = self.prefix + '.' + name
# Write input to the InfluxDB database # Write input to the InfluxDB database
if len(points) == 0: 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: else:
try: try:
self.client.write_points(self._normalize(name, columns, points), time_precision="s") self.client.write_points(self._normalize(name, columns, points), time_precision="s")
except Exception as e: except Exception as e:
# Log level set to debug instead of error (see: issue #1561) # 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: else:
logger.debug("Export {} stats to InfluxDB".format(name)) logger.debug(f"Export {name} stats to InfluxDB")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -12,11 +11,11 @@
import sys import sys
from platform import node from platform import node
from glances.logger import logger
from glances.exports.export import GlancesExport
from influxdb_client import InfluxDBClient, WriteOptions from influxdb_client import InfluxDBClient, WriteOptions
from glances.exports.export import GlancesExport
from glances.logger import logger
FIELD_TO_TAG = ['name', 'cmdline', 'type'] FIELD_TO_TAG = ['name', 'cmdline', 'type']
@ -25,7 +24,7 @@ class Export(GlancesExport):
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the InfluxDB export IF.""" """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) # Mandatory configuration keys (additional to host and port)
self.org = None self.org = None
@ -58,7 +57,7 @@ class Export(GlancesExport):
self.interval = 0 self.interval = 0
# and should be set to the Glances refresh time if the value is 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 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 # The hostname is always add as a tag
self.hostname = node().split('.')[0] self.hostname = node().split('.')[0]
@ -71,20 +70,18 @@ class Export(GlancesExport):
if not self.export_enable: if not self.export_enable:
return None return None
url = '{}://{}:{}'.format(self.protocol, self.host, self.port) url = f'{self.protocol}://{self.host}:{self.port}'
try: try:
# See docs: https://influxdb-client.readthedocs.io/en/stable/api.html#influxdbclient # See docs: https://influxdb-client.readthedocs.io/en/stable/api.html#influxdbclient
client = InfluxDBClient(url=url, enable_gzip=False, verify_ssl=False, org=self.org, token=self.token) client = InfluxDBClient(url=url, enable_gzip=False, verify_ssl=False, org=self.org, token=self.token)
except Exception as e: 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) sys.exit(2)
else: else:
logger.info( logger.info(f"Connected to InfluxDB server version {client.health().version} ({client.health().message})")
"Connected to InfluxDB server version {} ({})".format(client.health().version, client.health().message)
)
# Create the write client # Create the write client
write_client = client.write_api( return client.write_api(
write_options=WriteOptions( write_options=WriteOptions(
batch_size=500, batch_size=500,
flush_interval=self.interval * 1000, flush_interval=self.interval * 1000,
@ -95,7 +92,6 @@ class Export(GlancesExport):
exponential_base=2, exponential_base=2,
) )
) )
return write_client
def _normalize(self, name, columns, points): def _normalize(self, name, columns, points):
"""Normalize data for the InfluxDB's data model. """Normalize data for the InfluxDB's data model.
@ -117,9 +113,7 @@ class Export(GlancesExport):
# Manage field # Manage field
if measurement is not None: if measurement is not None:
fields = { fields = {
k.replace('{}.'.format(measurement), ''): data_dict[k] k.replace(f'{measurement}.', ''): data_dict[k] for k in data_dict if k.startswith(f'{measurement}.')
for k in data_dict
if k.startswith('{}.'.format(measurement))
} }
else: else:
fields = data_dict fields = data_dict
@ -166,12 +160,12 @@ class Export(GlancesExport):
name = self.prefix + '.' + name name = self.prefix + '.' + name
# Write input to the InfluxDB database # Write input to the InfluxDB database
if len(points) == 0: 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: else:
try: try:
self.client.write(self.bucket, self.org, self._normalize(name, columns, points), time_precision="s") self.client.write(self.bucket, self.org, self._normalize(name, columns, points), time_precision="s")
except Exception as e: except Exception as e:
# Log level set to debug instead of error (see: issue #1561) # 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: else:
logger.debug("Export {} stats to InfluxDB".format(name)) logger.debug(f"Export {name} stats to InfluxDB")

View File

@ -2,9 +2,9 @@
import sys import sys
from glances.globals import listkeys, json_dumps
from glances.logger import logger
from glances.exports.export import GlancesExport from glances.exports.export import GlancesExport
from glances.globals import json_dumps, listkeys
from glances.logger import logger
class Export(GlancesExport): class Export(GlancesExport):
@ -12,7 +12,7 @@ class Export(GlancesExport):
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the JSON export IF.""" """Init the JSON export IF."""
super(Export, self).__init__(config=config, args=args) super().__init__(config=config, args=args)
# JSON file name # JSON file name
self.json_filename = args.export_json_file self.json_filename = args.export_json_file
@ -21,11 +21,11 @@ class Export(GlancesExport):
try: try:
self.json_file = open(self.json_filename, 'w') self.json_file = open(self.json_filename, 'w')
self.json_file.close() self.json_file.close()
except IOError as e: except OSError as e:
logger.critical("Cannot create the JSON file: {}".format(e)) logger.critical(f"Cannot create the JSON file: {e}")
sys.exit(2) 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 self.export_enable = True
@ -34,7 +34,7 @@ class Export(GlancesExport):
def exit(self): def exit(self):
"""Close the JSON file.""" """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() self.json_file.close()
def export(self, name, columns, points): def export(self, name, columns, points):
@ -44,11 +44,11 @@ class Export(GlancesExport):
if name == self.last_exported_list()[0] and self.buffer != {}: if name == self.last_exported_list()[0] and self.buffer != {}:
# One whole loop has been completed # One whole loop has been completed
# Flush stats to file # 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 # Export stats to JSON file
with open(self.json_filename, "w") as self.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 # Reset buffer
self.buffer = {} self.buffer = {}

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -11,19 +10,19 @@
import sys import sys
from glances.logger import logger
from glances.globals import json_dumps
from glances.exports.export import GlancesExport
from kafka import KafkaProducer from kafka import KafkaProducer
from glances.exports.export import GlancesExport
from glances.globals import json_dumps
from glances.logger import logger
class Export(GlancesExport): class Export(GlancesExport):
"""This class manages the Kafka export module.""" """This class manages the Kafka export module."""
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the Kafka export IF.""" """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) # Mandatory configuration keys (additional to host and port)
self.topic = None self.topic = None
@ -48,7 +47,7 @@ class Export(GlancesExport):
return None return None
# Build the server URI with host and port # Build the server URI with host and port
server_uri = '{}:{}'.format(self.host, self.port) server_uri = f'{self.host}:{self.port}'
try: try:
s = KafkaProducer( s = KafkaProducer(
@ -57,16 +56,16 @@ class Export(GlancesExport):
compression_type=self.compression, compression_type=self.compression,
) )
except Exception as e: 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) sys.exit(2)
else: else:
logger.info("Connected to the Kafka server %s" % server_uri) logger.info(f"Connected to the Kafka server {server_uri}")
return s return s
def export(self, name, columns, points): def export(self, name, columns, points):
"""Write the points to the kafka server.""" """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 # Create DB input
data = dict(zip(columns, points)) data = dict(zip(columns, points))
@ -84,7 +83,7 @@ class Export(GlancesExport):
value=data, value=data,
) )
except Exception as e: 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): def exit(self):
"""Close the Kafka export module.""" """Close the Kafka export module."""
@ -92,4 +91,4 @@ class Export(GlancesExport):
self.client.flush() self.client.flush()
self.client.close() self.client.close()
# Call the father method # Call the father method
super(Export, self).exit() super().exit()

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -10,12 +9,12 @@
"""MongoDB interface class.""" """MongoDB interface class."""
import sys import sys
from urllib.parse import quote_plus
from glances.logger import logger
from glances.exports.export import GlancesExport
import pymongo import pymongo
from urllib.parse import quote_plus
from glances.exports.export import GlancesExport
from glances.logger import logger
class Export(GlancesExport): class Export(GlancesExport):
@ -23,7 +22,7 @@ class Export(GlancesExport):
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the MongoDB export IF.""" """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) # Mandatory configuration keys (additional to host and port)
self.db = None self.db = None
@ -45,13 +44,13 @@ class Export(GlancesExport):
if not self.export_enable: if not self.export_enable:
return None 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: try:
client = pymongo.MongoClient(server_uri) client = pymongo.MongoClient(server_uri)
client.admin.command('ping') client.admin.command('ping')
except Exception as e: 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) sys.exit(2)
else: else:
logger.info("Connected to the MongoDB server") logger.info("Connected to the MongoDB server")
@ -64,7 +63,7 @@ class Export(GlancesExport):
def export(self, name, columns, points): def export(self, name, columns, points):
"""Write the points to the MongoDB server.""" """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 # Create DB input
data = dict(zip(columns, points)) data = dict(zip(columns, points))
@ -73,4 +72,4 @@ class Export(GlancesExport):
try: try:
self.database()[name].insert_one(data) self.database()[name].insert_one(data)
except Exception as e: except Exception as e:
logger.error("Cannot export {} stats to MongoDB ({})".format(name, e)) logger.error(f"Cannot export {name} stats to MongoDB ({e})")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -13,21 +12,21 @@ import socket
import string import string
import sys import sys
from glances.logger import logger
from glances.exports.export import GlancesExport
from glances.globals import json_dumps
# Import paho for MQTT # Import paho for MQTT
import certifi import certifi
import paho.mqtt.client as paho 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): class Export(GlancesExport):
"""This class manages the MQTT export module.""" """This class manages the MQTT export module."""
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the MQTT export IF.""" """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) # Mandatory configuration keys (additional to host and port)
self.user = None self.user = None
@ -87,7 +86,7 @@ class Export(GlancesExport):
client.loop_start() client.loop_start()
return client return client
except Exception as e: 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 return None
def export(self, name, columns, points): def export(self, name, columns, points):
@ -109,14 +108,14 @@ class Export(GlancesExport):
self.client.publish(topic, value) self.client.publish(topic, value)
except Exception as e: 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': elif self.topic_structure == 'per-plugin':
try: try:
topic = '/'.join([self.topic, self.devicename, name]) topic = '/'.join([self.topic, self.devicename, name])
sensor_values = dict(zip(columns, points)) sensor_values = dict(zip(columns, points))
# Build the value to output # Build the value to output
output_value = dict() output_value = {}
for key in sensor_values: for key in sensor_values:
split_key = key.split('.') split_key = key.split('.')
@ -124,7 +123,7 @@ class Export(GlancesExport):
current_level = output_value current_level = output_value
for depth in range(len(split_key) - 1): for depth in range(len(split_key) - 1):
if split_key[depth] not in current_level: 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]] current_level = current_level[split_key[depth]]
# Add the value # Add the value
@ -133,4 +132,4 @@ class Export(GlancesExport):
json_value = json_dumps(output_value) json_value = json_dumps(output_value)
self.client.publish(topic, json_value) self.client.publish(topic, json_value)
except Exception as e: 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})")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -12,18 +11,18 @@
import sys import sys
from numbers import Number from numbers import Number
from glances.logger import logger
from glances.exports.export import GlancesExport
import potsdb import potsdb
from glances.exports.export import GlancesExport
from glances.logger import logger
class Export(GlancesExport): class Export(GlancesExport):
"""This class manages the OpenTSDB export module.""" """This class manages the OpenTSDB export module."""
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the OpenTSDB export IF.""" """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) # Mandatory configuration keys (additional to host and port)
# N/A # N/A
@ -52,7 +51,7 @@ class Export(GlancesExport):
try: try:
db = potsdb.Client(self.host, port=int(self.port), check_host=True) db = potsdb.Client(self.host, port=int(self.port), check_host=True)
except Exception as e: 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) sys.exit(2)
return db return db
@ -62,18 +61,18 @@ class Export(GlancesExport):
for i in range(len(columns)): for i in range(len(columns)):
if not isinstance(points[i], Number): if not isinstance(points[i], Number):
continue continue
stat_name = '{}.{}.{}'.format(self.prefix, name, columns[i]) stat_name = f'{self.prefix}.{name}.{columns[i]}'
stat_value = points[i] stat_value = points[i]
tags = self.parse_tags(self.tags) tags = self.parse_tags(self.tags)
try: try:
self.client.send(stat_name, stat_value, **tags) self.client.send(stat_name, stat_value, **tags)
except Exception as e: except Exception as e:
logger.error("Can not export stats %s to OpenTSDB (%s)" % (name, e)) logger.error(f"Can not export stats {name} to OpenTSDB ({e})")
logger.debug("Export {} stats to OpenTSDB".format(name)) logger.debug(f"Export {name} stats to OpenTSDB")
def exit(self): def exit(self):
"""Close the OpenTSDB export module.""" """Close the OpenTSDB export module."""
# Waits for all outstanding metrics to be sent and background thread closes # Waits for all outstanding metrics to be sent and background thread closes
self.client.wait() self.client.wait()
# Call the father method # Call the father method
super(Export, self).exit() super().exit()

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -12,11 +11,11 @@
import sys import sys
from numbers import Number 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.exports.export import GlancesExport
from glances.globals import iteritems, listkeys from glances.globals import iteritems, listkeys
from glances.logger import logger
from prometheus_client import start_http_server, Gauge
class Export(GlancesExport): class Export(GlancesExport):
@ -26,7 +25,7 @@ class Export(GlancesExport):
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the Prometheus export IF.""" """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 # Load the Prometheus configuration file section
self.export_enable = self.load_conf('prometheus', mandatories=['host', 'port', 'labels'], options=['prefix']) self.export_enable = self.load_conf('prometheus', mandatories=['host', 'port', 'labels'], options=['prefix'])
@ -52,14 +51,14 @@ class Export(GlancesExport):
try: try:
start_http_server(port=int(self.port), addr=self.host) start_http_server(port=int(self.port), addr=self.host)
except Exception as e: 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) sys.exit(2)
else: 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): def export(self, name, columns, points):
"""Write the points to the Prometheus exporter using Gauge.""" """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) # 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)} data = {k: float(v) for (k, v) in iteritems(dict(zip(columns, points))) if isinstance(v, Number)}

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -14,19 +13,19 @@ import socket
import sys import sys
from numbers import Number from numbers import Number
from glances.logger import logger
from glances.exports.export import GlancesExport
# Import pika for RabbitMQ # Import pika for RabbitMQ
import pika import pika
from glances.exports.export import GlancesExport
from glances.logger import logger
class Export(GlancesExport): class Export(GlancesExport):
"""This class manages the rabbitMQ export module.""" """This class manages the rabbitMQ export module."""
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the RabbitMQ export IF.""" """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) # Mandatory configuration keys (additional to host and port)
self.user = None self.user = None
@ -67,10 +66,9 @@ class Export(GlancesExport):
self.protocol + '://' + self.user + ':' + self.password + '@' + self.host + ':' + self.port + '/' self.protocol + '://' + self.user + ':' + self.password + '@' + self.host + ':' + self.port + '/'
) )
connection = pika.BlockingConnection(parameters) connection = pika.BlockingConnection(parameters)
channel = connection.channel() return connection.channel()
return channel
except Exception as e: 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) sys.exit(2)
def export(self, name, columns, points): def export(self, name, columns, points):
@ -79,10 +77,10 @@ class Export(GlancesExport):
for i in range(len(columns)): for i in range(len(columns)):
if not isinstance(points[i], Number): if not isinstance(points[i], Number):
continue continue
else:
data += ", " + columns[i] + "=" + str(points[i]) data += ", " + columns[i] + "=" + str(points[i])
logger.debug(data) logger.debug(data)
try: try:
self.client.basic_publish(exchange='', routing_key=self.queue, body=data) self.client.basic_publish(exchange='', routing_key=self.queue, body=data)
except Exception as e: except Exception as e:
logger.error("Can not export stats to RabbitMQ (%s)" % e) logger.error(f"Can not export stats to RabbitMQ ({e})")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,12 +8,11 @@
"""RESTful interface class.""" """RESTful interface class."""
from requests import post
from glances.exports.export import GlancesExport
from glances.globals import listkeys from glances.globals import listkeys
from glances.logger import logger from glances.logger import logger
from glances.exports.export import GlancesExport
from requests import post
class Export(GlancesExport): class Export(GlancesExport):
@ -23,7 +21,7 @@ class Export(GlancesExport):
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the RESTful export IF.""" """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) # Mandatory configuration keys (additional to host and port)
self.protocol = None self.protocol = None
@ -46,15 +44,15 @@ class Export(GlancesExport):
if not self.export_enable: if not self.export_enable:
return None return None
# Build the RESTful URL where the stats will be posted # Build the RESTful URL where the stats will be posted
url = '{}://{}:{}{}'.format(self.protocol, self.host, self.port, self.path) url = f'{self.protocol}://{self.host}:{self.port}{self.path}'
logger.info("Stats will be exported to the RESTful endpoint {}".format(url)) logger.info(f"Stats will be exported to the RESTful endpoint {url}")
return url return url
def export(self, name, columns, points): def export(self, name, columns, points):
"""Export the stats to the Statsd server.""" """Export the stats to the Statsd server."""
if name == self.last_exported_list()[0] and self.buffer != {}: if name == self.last_exported_list()[0] and self.buffer != {}:
# One complete loop have been done # 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 # Export stats
post(self.client, json=self.buffer, allow_redirects=True) post(self.client, json=self.buffer, allow_redirects=True)
# Reset buffer # Reset buffer

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -12,19 +11,19 @@
import socket import socket
from numbers import Number from numbers import Number
from glances.logger import logger
from glances.exports.export import GlancesExport
# Import bernhard for Riemann # Import bernhard for Riemann
import bernhard import bernhard
from glances.exports.export import GlancesExport
from glances.logger import logger
class Export(GlancesExport): class Export(GlancesExport):
"""This class manages the Riemann export module.""" """This class manages the Riemann export module."""
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the Riemann export IF.""" """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) # Mandatory configuration keys (additional to host and port)
# N/A # N/A
@ -48,10 +47,9 @@ class Export(GlancesExport):
if not self.export_enable: if not self.export_enable:
return None return None
try: try:
client = bernhard.Client(host=self.host, port=self.port) return bernhard.Client(host=self.host, port=self.port)
return client
except Exception as e: except Exception as e:
logger.critical("Connection to Riemann failed : %s " % e) logger.critical(f"Connection to Riemann failed : {e} ")
return None return None
def export(self, name, columns, points): def export(self, name, columns, points):
@ -59,10 +57,10 @@ class Export(GlancesExport):
for i in range(len(columns)): for i in range(len(columns)):
if not isinstance(points[i], Number): if not isinstance(points[i], Number):
continue continue
else:
data = {'host': self.hostname, 'service': name + " " + columns[i], 'metric': points[i]} data = {'host': self.hostname, 'service': name + " " + columns[i], 'metric': points[i]}
logger.debug(data) logger.debug(data)
try: try:
self.client.send(data) self.client.send(data)
except Exception as e: except Exception as e:
logger.error("Cannot export stats to Riemann (%s)" % e) logger.error(f"Cannot export stats to Riemann ({e})")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -11,18 +10,18 @@
from numbers import Number from numbers import Number
from glances.logger import logger
from glances.exports.export import GlancesExport
from statsd import StatsClient from statsd import StatsClient
from glances.exports.export import GlancesExport
from glances.logger import logger
class Export(GlancesExport): class Export(GlancesExport):
"""This class manages the Statsd export module.""" """This class manages the Statsd export module."""
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the Statsd export IF.""" """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) # Mandatory configuration keys (additional to host and port)
# N/A # N/A
@ -46,7 +45,7 @@ class Export(GlancesExport):
"""Init the connection to the Statsd server.""" """Init the connection to the Statsd server."""
if not self.export_enable: if not self.export_enable:
return None 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) return StatsClient(self.host, int(self.port), prefix=self.prefix)
def export(self, name, columns, points): def export(self, name, columns, points):
@ -54,13 +53,13 @@ class Export(GlancesExport):
for i in range(len(columns)): for i in range(len(columns)):
if not isinstance(points[i], Number): if not isinstance(points[i], Number):
continue continue
stat_name = '{}.{}'.format(name, columns[i]) stat_name = f'{name}.{columns[i]}'
stat_value = points[i] stat_value = points[i]
try: try:
self.client.gauge(normalize(stat_name), stat_value) self.client.gauge(normalize(stat_name), stat_value)
except Exception as e: except Exception as e:
logger.error("Can not export stats to Statsd (%s)" % e) logger.error(f"Can not export stats to Statsd ({e})")
logger.debug("Export {} stats to Statsd".format(name)) logger.debug(f"Export {name} stats to Statsd")
def normalize(name): def normalize(name):
@ -69,6 +68,4 @@ def normalize(name):
# Name should not contain some specials chars (issue #1068) # Name should not contain some specials chars (issue #1068)
ret = name.replace(':', '') ret = name.replace(':', '')
ret = ret.replace('%', '') ret = ret.replace('%', '')
ret = ret.replace(' ', '_') return ret.replace(' ', '_')
return ret

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -11,21 +10,20 @@
import sys 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 import zmq
from zmq.utils.strtypes import asbytes 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): class Export(GlancesExport):
"""This class manages the ZeroMQ export module.""" """This class manages the ZeroMQ export module."""
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
"""Init the ZeroMQ export IF.""" """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) # Mandatory configuration keys (additional to host and port)
self.prefix = None self.prefix = None
@ -47,17 +45,17 @@ class Export(GlancesExport):
if not self.export_enable: if not self.export_enable:
return None return None
server_uri = 'tcp://{}:{}'.format(self.host, self.port) server_uri = f'tcp://{self.host}:{self.port}'
try: try:
self.context = zmq.Context() self.context = zmq.Context()
publisher = self.context.socket(zmq.PUB) publisher = self.context.socket(zmq.PUB)
publisher.bind(server_uri) publisher.bind(server_uri)
except Exception as e: 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) sys.exit(2)
else: else:
logger.info("Connected to the ZeroMQ server %s" % server_uri) logger.info(f"Connected to the ZeroMQ server {server_uri}")
return publisher return publisher
@ -70,7 +68,7 @@ class Export(GlancesExport):
def export(self, name, columns, points): def export(self, name, columns, points):
"""Write the points to the ZeroMQ server.""" """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 # Create DB input
data = dict(zip(columns, points)) data = dict(zip(columns, points))
@ -90,6 +88,6 @@ class Export(GlancesExport):
try: try:
self.client.send_multipart(message) self.client.send_multipart(message)
except Exception as e: 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 return True

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -12,7 +11,7 @@ import re
from glances.logger import logger from glances.logger import logger
class GlancesFilterList(object): class GlancesFilterList:
"""Manage a lis of GlancesFilter objects """Manage a lis of GlancesFilter objects
>>> fl = GlancesFilterList() >>> fl = GlancesFilterList()
@ -55,7 +54,7 @@ class GlancesFilterList(object):
return False return False
class GlancesFilter(object): class GlancesFilter:
"""Allow Glances to filter processes """Allow Glances to filter processes
>>> f = GlancesFilter() >>> f = GlancesFilter()
@ -127,9 +126,9 @@ class GlancesFilter(object):
# Compute the regular expression # Compute the regular expression
try: try:
self._filter_re = re.compile(self.filter) 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: 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 = None
self._filter_re = None self._filter_re = None
self._filter_key = None self._filter_key = None
@ -156,7 +155,7 @@ class GlancesFilter(object):
if self.filter_key is None: if self.filter_key is None:
# Apply filter on command line and process name # Apply filter on command line and process name
return self._is_process_filtered(process, key='name') or self._is_process_filtered(process, key='cmdline') return self._is_process_filtered(process, key='name') or self._is_process_filtered(process, key='cmdline')
else:
# Apply filter on <key> # Apply filter on <key>
return self._is_process_filtered(process) return self._is_process_filtered(process)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -8,15 +7,13 @@
# #
"""Manage the folder list.""" """Manage the folder list."""
from __future__ import unicode_literals
from glances.globals import folder_size, nativestr
from glances.timer import Timer
from glances.globals import nativestr, folder_size
from glances.logger import logger from glances.logger import logger
from glances.timer import Timer
class FolderList(object): class FolderList:
"""This class describes the optional monitored folder list. """This class describes the optional monitored folder list.
The folder list is a list of 'important' folder to monitor. The folder list is a list of 'important' folder to monitor.
@ -67,7 +64,6 @@ class FolderList(object):
value['path'] = self.config.get_value(section, key + 'path') value['path'] = self.config.get_value(section, key + 'path')
if value['path'] is None: if value['path'] is None:
continue continue
else:
value['path'] = nativestr(value['path']) value['path'] = nativestr(value['path'])
# Optional conf keys # Optional conf keys

View File

@ -1,4 +1,4 @@
# -*- coding: utf-8 -*- # ruff: noqa: F401
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -13,29 +13,27 @@
# GLOBAL IMPORTS # 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 base64
import errno
import functools import functools
import weakref import os
import platform
import queue import queue
import re
import subprocess
import sys
import weakref
from configparser import ConfigParser, NoOptionError, NoSectionError from configparser import ConfigParser, NoOptionError, NoSectionError
from datetime import datetime
from operator import itemgetter, methodcaller
from statistics import mean 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.error import HTTPError, URLError
from urllib.parse import urlparse 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 # Correct issue #1025 by monkey path the xmlrpc lib
from defusedxml.xmlrpc import monkey_patch from defusedxml.xmlrpc import monkey_patch
@ -136,9 +134,8 @@ def b(s, errors='replace'):
def nativestr(s, errors='replace'): def nativestr(s, errors='replace'):
if isinstance(s, text_type): if isinstance(s, text_type):
return s return s
elif isinstance(s, (int, float)): if isinstance(s, (int, float)):
return s.__str__() return s.__str__()
else:
return s.decode('utf-8', errors=errors) return s.decode('utf-8', errors=errors)
@ -147,7 +144,7 @@ def system_exec(command):
try: try:
res = subprocess.run(command.split(' '), stdout=subprocess.PIPE).stdout.decode('utf-8') res = subprocess.run(command.split(' '), stdout=subprocess.PIPE).stdout.decode('utf-8')
except Exception as e: except Exception as e:
res = 'ERROR: {}'.format(e) res = f'ERROR: {e}'
return res.rstrip() return res.rstrip()
@ -204,7 +201,7 @@ def is_admin():
try: try:
return ctypes.windll.shell32.IsUserAnAdmin() return ctypes.windll.shell32.IsUserAnAdmin()
except Exception as e: except Exception as e:
print("Admin check failed with error: %s" % e) print(f"Admin check failed with error: {e}")
traceback.print_exc() traceback.print_exc()
return False return False
else: else:
@ -301,7 +298,7 @@ def urlopen_auth(url, username, password):
return urlopen( return urlopen(
Request( Request(
url, url,
headers={'Authorization': 'Basic ' + base64.b64encode(('%s:%s' % (username, password)).encode()).decode()}, headers={'Authorization': 'Basic ' + base64.b64encode((f'{username}:{password}').encode()).decode()},
) )
) )
@ -339,7 +336,6 @@ def json_dumps_dictlist(data, item):
dl = dictlist(data, item) dl = dictlist(data, item)
if dl is None: if dl is None:
return None return None
else:
return json_dumps(dl) return json_dumps(dl)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -12,7 +11,7 @@
from glances.attribute import GlancesAttribute from glances.attribute import GlancesAttribute
class GlancesHistory(object): class GlancesHistory:
"""This class manage a dict of GlancesAttribute """This class manage a dict of GlancesAttribute
- key: stats name - key: stats name
- value: GlancesAttribute""" - value: GlancesAttribute"""

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,13 +8,12 @@
"""Custom logger class.""" """Custom logger class."""
import os
import json
import getpass import getpass
import tempfile import json
import logging import logging
import logging.config import logging.config
import os
import tempfile
from glances.globals import safe_makedirs 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')) safe_makedirs(os.path.join(_XDG_CACHE_HOME, 'glances'))
LOG_FILENAME = os.path.join(_XDG_CACHE_HOME, 'glances', 'glances.log') LOG_FILENAME = os.path.join(_XDG_CACHE_HOME, 'glances', 'glances.log')
else: 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 # Define the logging configuration
LOGGING_CFG = { LOGGING_CFG = {
@ -89,7 +87,7 @@ def glances_logger(env_key='LOG_CFG'):
user_file = os.getenv(env_key, None) user_file = os.getenv(env_key, None)
if user_file and os.path.exists(user_file): if user_file and os.path.exists(user_file):
# A user file as been defined. Use it... # 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) config = json.load(f)
# Load the configuration # Load the configuration

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -15,14 +14,14 @@ import tempfile
from logging import DEBUG from logging import DEBUG
from warnings import simplefilter from warnings import simplefilter
from glances import __version__, psutil_version, __apiversion__ from glances import __apiversion__, __version__, psutil_version
from glances.globals import WINDOWS, disable, enable
from glances.config import Config 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.processes import sort_processes_key_list
from glances.logger import logger, LOG_FILENAME
class GlancesMain(object): class GlancesMain:
"""Main class to manage Glances instance.""" """Main class to manage Glances instance."""
# Default stats' minimum refresh time is 2 seconds # Default stats' minimum refresh time is 2 seconds
@ -101,10 +100,10 @@ Examples of use:
def version_msg(self): def version_msg(self):
"""Return the version message.""" """Return the version message."""
version = 'Glances version:\t{}\n'.format(__version__) version = f'Glances version:\t{__version__}\n'
version += 'Glances API version:\t{}\n'.format(__apiversion__) version += f'Glances API version:\t{__apiversion__}\n'
version += 'PsUtil version:\t\t{}\n'.format(psutil_version) version += f'PsUtil version:\t\t{psutil_version}\n'
version += 'Log file:\t\t{}\n'.format(LOG_FILENAME) version += f'Log file:\t\t{LOG_FILENAME}\n'
return version return version
def init_args(self): def init_args(self):
@ -241,7 +240,7 @@ Examples of use:
) )
parser.add_argument( parser.add_argument(
'--enable-irq', action='store_true', default=False, dest='enable_irq', help='enable IRQ module' '--enable-irq', action='store_true', default=False, dest='enable_irq', help='enable IRQ module'
), )
parser.add_argument( parser.add_argument(
'--enable-process-extended', '--enable-process-extended',
action='store_true', action='store_true',
@ -255,14 +254,14 @@ Examples of use:
default=True, default=True,
dest='enable_separator', dest='enable_separator',
help='disable separator in the UI (between top and others modules)', help='disable separator in the UI (between top and others modules)',
), )
parser.add_argument( parser.add_argument(
'--disable-cursor', '--disable-cursor',
action='store_true', action='store_true',
default=False, default=False,
dest='disable_cursor', dest='disable_cursor',
help='disable cursor (process selection) in the UI', help='disable cursor (process selection) in the UI',
), )
# Sort processes list # Sort processes list
parser.add_argument( parser.add_argument(
'--sort-processes', '--sort-processes',
@ -334,7 +333,7 @@ Examples of use:
default=None, default=None,
type=int, type=int,
dest='port', 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( parser.add_argument(
'-B', '-B',
@ -374,7 +373,7 @@ Examples of use:
default=self.DEFAULT_REFRESH_TIME, default=self.DEFAULT_REFRESH_TIME,
type=float, type=float,
dest='time', 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( parser.add_argument(
'-w', '-w',
@ -389,7 +388,7 @@ Examples of use:
default=self.cached_time, default=self.cached_time,
type=int, type=int,
dest='cached_time', 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( parser.add_argument(
'--stop-after', '--stop-after',
@ -577,7 +576,7 @@ Examples of use:
if args.time == self.DEFAULT_REFRESH_TIME: if args.time == self.DEFAULT_REFRESH_TIME:
args.time = global_refresh 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): def init_plugins(self, args):
"""Init Glances plugins""" """Init Glances plugins"""
@ -585,7 +584,7 @@ Examples of use:
for s in self.config.sections(): for s in self.config.sections():
if self.config.has_section(s) and (self.config.get_bool_value(s, 'disable', False)): if self.config.has_section(s) and (self.config.get_bool_value(s, 'disable', False)):
disable(args, s) 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 # The configuration key can be overwrite from the command line
if args and args.disable_plugin and 'all' in args.disable_plugin.split(','): if args and args.disable_plugin and 'all' in args.disable_plugin.split(','):
if not args.enable_plugin: if not args.enable_plugin:
@ -664,19 +663,19 @@ Examples of use:
# Interactive or file password # Interactive or file password
if args.server: if args.server:
args.password = self.__get_password( 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, confirm=True,
username=args.username, username=args.username,
) )
elif args.webserver: elif args.webserver:
args.password = self.__get_password( 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, confirm=True,
username=args.username, username=args.username,
) )
elif args.client: elif args.client:
args.password = self.__get_password( 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, clear=True,
username=args.username, username=args.username,
) )

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,16 +8,16 @@
"""Manage Glances update.""" """Manage Glances update."""
from datetime import datetime, timedelta
import threading
import json import json
import pickle
import os import os
import pickle
import threading
from datetime import datetime, timedelta
from ssl import CertificateError from ssl import CertificateError
from glances import __version__ from glances import __version__
from glances.globals import nativestr, urlopen, HTTPError, URLError, safe_makedirs
from glances.config import user_cache_dir from glances.config import user_cache_dir
from glances.globals import HTTPError, URLError, nativestr, safe_makedirs, urlopen
from glances.logger import logger from glances.logger import logger
try: try:
@ -26,13 +25,13 @@ try:
PACKAGING_IMPORT = True PACKAGING_IMPORT = True
except Exception as e: 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 PACKAGING_IMPORT = False
PYPI_API_URL = 'https://pypi.python.org/pypi/Glances/json' 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 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/). 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') self.cache_file = os.path.join(self.cache_dir, 'glances-version.db')
# Set default value... # 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 # Disable update check if `packaging` is not installed
if not PACKAGING_IMPORT: if not PACKAGING_IMPORT:
@ -56,7 +55,7 @@ class Outdated(object):
if not self.args.disable_check_update: if not self.args.disable_check_update:
self.load_config(config) 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 ! # And update !
self.get_pypi_version() self.get_pypi_version()
@ -68,7 +67,7 @@ class Outdated(object):
if hasattr(config, 'has_section') and config.has_section(global_section): if hasattr(config, 'has_section') and config.has_section(global_section):
self.args.disable_check_update = config.get_value(global_section, 'check_update').lower() == 'false' self.args.disable_check_update = config.get_value(global_section, 'check_update').lower() == 'false'
else: 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 False
return True return True
@ -110,9 +109,7 @@ class Outdated(object):
# Check is disabled by configuration # Check is disabled by configuration
return False return False
logger.debug( logger.debug(f"Check Glances version (installed: {self.installed_version()} / latest: {self.latest_version()})")
"Check Glances version (installed: {} / latest: {})".format(self.installed_version(), self.latest_version())
)
return Version(self.latest_version()) > Version(self.installed_version()) return Version(self.latest_version()) > Version(self.installed_version())
def _load_cache(self): def _load_cache(self):
@ -124,7 +121,7 @@ class Outdated(object):
with open(self.cache_file, 'rb') as f: with open(self.cache_file, 'rb') as f:
cached_data = pickle.load(f) cached_data = pickle.load(f)
except Exception as e: 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: else:
logger.debug("Read version from cache file") logger.debug("Read version from cache file")
if ( if (
@ -147,21 +144,21 @@ class Outdated(object):
with open(self.cache_file, 'wb') as f: with open(self.cache_file, 'wb') as f:
pickle.dump(self.data, f) pickle.dump(self.data, f)
except Exception as e: 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): def _update_pypi_version(self):
"""Get the latest PyPI version (as a string) via the RESTful JSON API""" """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 # Update the current time
self.data[u'refresh_date'] = datetime.now() self.data['refresh_date'] = datetime.now()
try: try:
res = urlopen(PYPI_API_URL, timeout=3).read() res = urlopen(PYPI_API_URL, timeout=3).read()
except (HTTPError, URLError, CertificateError) as e: 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: 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") logger.debug("Save Glances version to the cache file")
# Save result to the cache file # Save result to the cache file

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,12 +8,10 @@
"""Manage bars for Glances output.""" """Manage bars for Glances output."""
from __future__ import division
from math import modf from math import modf
class Bar(object): class Bar:
"""Manage bar (progression or status). """Manage bar (progression or status).
import sys import sys
@ -76,6 +73,7 @@ class Bar(object):
return self.__size return self.__size
if self.__display_value: if self.__display_value:
return self.__size - 6 return self.__size - 6
return None
@property @property
def percent(self): def percent(self):
@ -114,7 +112,7 @@ class Bar(object):
ret, '>' if self.percent > self.max_value else ' ', self.max_value, self.__unit_char ret, '>' if self.percent > self.max_value else ' ', self.max_value, self.__unit_char
) )
else: else:
ret = '{}{:5.1f}{}'.format(ret, self.percent, self.__unit_char) ret = f'{ret}{self.percent:5.1f}{self.__unit_char}'
# Add overlay # Add overlay
if overlay and len(overlay) < len(ret) - 6: if overlay and len(overlay) < len(ret) - 6:

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -8,16 +7,15 @@
# #
"""Curses interface class.""" """Curses interface class."""
from __future__ import unicode_literals
import sys
import getpass 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.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.outputs.glances_unicode import unicode_message
from glances.processes import glances_processes, sort_processes_key_list
from glances.timer import Timer from glances.timer import Timer
# Import curses library for "normal" operating system # Import curses library for "normal" operating system
@ -32,7 +30,7 @@ except ImportError:
sys.exit(1) sys.exit(1)
class _GlancesCurses(object): class _GlancesCurses:
"""This class manages the curses display (and key pressed). """This class manages the curses display (and key pressed).
Note: It is a private class, use GlancesCursesClient or GlancesCursesBrowser. Note: It is a private class, use GlancesCursesClient or GlancesCursesBrowser.
@ -147,14 +145,14 @@ class _GlancesCurses(object):
logger.critical("Cannot init the curses library.\n") logger.critical("Cannot init the curses library.\n")
sys.exit(1) sys.exit(1)
else: 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: except Exception as e:
if args.export: if args.export:
logger.info("Cannot init the curses library, quiet mode on and export.") logger.info("Cannot init the curses library, quiet mode on and export.")
args.quiet = True args.quiet = True
return return
else:
logger.critical("Cannot init the curses library ({})".format(e)) logger.critical(f"Cannot init the curses library ({e})")
sys.exit(1) sys.exit(1)
# Load configuration file # Load configuration file
@ -223,11 +221,11 @@ class _GlancesCurses(object):
try: try:
if hasattr(curses, 'start_color'): if hasattr(curses, 'start_color'):
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'): if hasattr(curses, 'use_default_colors'):
curses.use_default_colors() curses.use_default_colors()
except Exception as e: except Exception as e:
logger.warning('Error initializing terminal color ({})'.format(e)) logger.warning(f'Error initializing terminal color ({e})')
# Init colors # Init colors
if self.args.disable_bold: if self.args.disable_bold:
@ -358,8 +356,7 @@ class _GlancesCurses(object):
def get_key(self, window): def get_key(self, window):
# TODO: Check issue #163 # TODO: Check issue #163
ret = window.getch() return window.getch()
return ret
def __catch_key(self, return_to_browser=False): def __catch_key(self, return_to_browser=False):
# Catch the pressed key # Catch the pressed key
@ -368,7 +365,7 @@ class _GlancesCurses(object):
return -1 return -1
# Actions (available in the global hotkey dict)... # 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: for hotkey in self._hotkeys:
if self.pressedkey == ord(hotkey) and 'switch' in self._hotkeys[hotkey]: if self.pressedkey == ord(hotkey) and 'switch' in self._hotkeys[hotkey]:
self._handle_switch(hotkey) self._handle_switch(hotkey)
@ -495,7 +492,7 @@ class _GlancesCurses(object):
if return_to_browser: if return_to_browser:
logger.info("Stop Glances client and return to the browser") logger.info("Stop Glances client and return to the browser")
else: else:
logger.info("Stop Glances (keypressed: {})".format(self.pressedkey)) logger.info(f"Stop Glances (keypressed: {self.pressedkey})")
def _handle_refresh(self): def _handle_refresh(self):
pass pass
@ -716,7 +713,7 @@ class _GlancesCurses(object):
# Display graph generation popup # Display graph generation popup
if self.args.generate_graph: if self.args.generate_graph:
if 'graph' in stats.getExportsList(): 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: else:
logger.warning('Graph export module is disable. Run Glances with --export graph to enable it.') logger.warning('Graph export module is disable. Run Glances with --export graph to enable it.')
self.args.generate_graph = False self.args.generate_graph = False
@ -735,7 +732,7 @@ class _GlancesCurses(object):
:param process :param process
:return: None :return: None
""" """
logger.debug("Selected process to kill: {}".format(process)) logger.debug(f"Selected process to kill: {process}")
if 'childrens' in process: if 'childrens' in process:
pid_to_kill = process['childrens'] pid_to_kill = process['childrens']
@ -755,9 +752,9 @@ class _GlancesCurses(object):
try: try:
ret_kill = glances_processes.kill(pid) ret_kill = glances_processes.kill(pid)
except Exception as e: except Exception as e:
logger.error('Can not kill process {} ({})'.format(pid, e)) logger.error(f'Can not kill process {pid} ({e})')
else: 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): def __display_header(self, stat_display):
"""Display the firsts lines (header) in the Curses interface. """Display the firsts lines (header) in the Curses interface.
@ -829,7 +826,7 @@ class _GlancesCurses(object):
max_width=quicklook_width, args=self.args max_width=quicklook_width, args=self.args
) )
except AttributeError as e: except AttributeError as e:
logger.debug("Quicklook plugin not available (%s)" % e) logger.debug(f"Quicklook plugin not available ({e})")
else: else:
plugin_widths['quicklook'] = self.get_stats_display_width(stat_display["quicklook"]) plugin_widths['quicklook'] = self.get_stats_display_width(stat_display["quicklook"])
stats_width = sum(itervalues(plugin_widths)) + 1 stats_width = sum(itervalues(plugin_widths)) + 1
@ -989,7 +986,8 @@ class _GlancesCurses(object):
popup.refresh() popup.refresh()
self.wait(duration * 1000) self.wait(duration * 1000)
return True return True
elif popup_type == 'input':
if popup_type == 'input':
logger.info(popup_type) logger.info(popup_type)
logger.info(is_password) logger.info(is_password)
# Create a sub-window for the text field # Create a sub-window for the text field
@ -1009,17 +1007,17 @@ class _GlancesCurses(object):
self.set_cursor(0) self.set_cursor(0)
if textbox != '': if textbox != '':
return textbox return textbox
else:
return None return None
else:
# No password
textbox = GlancesTextbox(sub_pop, insert_mode=True) textbox = GlancesTextbox(sub_pop, insert_mode=True)
textbox.edit() textbox.edit()
self.set_cursor(0) self.set_cursor(0)
if textbox.gather() != '': if textbox.gather() != '':
return textbox.gather()[:-1] return textbox.gather()[:-1]
else:
return None return None
elif popup_type == 'yesno':
if popup_type == 'yesno':
# # Create a sub-window for the text field # # Create a sub-window for the text field
sub_pop = popup.derwin(1, 2, len(sentence_list) + 1, len(m) + 2) sub_pop = popup.derwin(1, 2, len(sentence_list) + 1, len(m) + 2)
sub_pop.attron(self.colors_list['FILTER']) sub_pop.attron(self.colors_list['FILTER'])
@ -1037,6 +1035,8 @@ class _GlancesCurses(object):
# self.term_window.keypad(0) # self.term_window.keypad(0)
return textbox.gather() return textbox.gather()
return None
def display_plugin(self, plugin_stats, display_optional=True, display_additional=True, max_y=65535, add_space=0): def display_plugin(self, plugin_stats, display_optional=True, display_additional=True, max_y=65535, add_space=0):
"""Display the plugin_stats on the screen. """Display the plugin_stats on the screen.
@ -1131,6 +1131,7 @@ class _GlancesCurses(object):
# Have empty lines after the plugins # Have empty lines after the plugins
self.next_line += add_space self.next_line += add_space
return None
def clear(self): def clear(self):
"""Erase the content of the screen. """Erase the content of the screen.
@ -1213,8 +1214,7 @@ class _GlancesCurses(object):
if isexitkey and self.args.help_tag: if isexitkey and self.args.help_tag:
# Quit from help should return to main screen, not exit #1874 # Quit from help should return to main screen, not exit #1874
self.args.help_tag = not self.args.help_tag self.args.help_tag = not self.args.help_tag
isexitkey = False return False
return isexitkey
if not isexitkey and pressedkey > -1: if not isexitkey and pressedkey > -1:
# Redraw display # Redraw display
@ -1255,7 +1255,7 @@ class _GlancesCurses(object):
) )
) )
except Exception as e: 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 return 0
else: else:
return c return c
@ -1268,7 +1268,7 @@ class _GlancesCurses(object):
try: try:
c = [i['msg'] for i in curse_msg['msgdict']].count('\n') c = [i['msg'] for i in curse_msg['msgdict']].count('\n')
except Exception as e: 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 return 0
else: else:
return c + 1 return c + 1
@ -1282,21 +1282,21 @@ class GlancesCursesClient(_GlancesCurses):
"""Class for the Glances curse client.""" """Class for the Glances curse client."""
class GlancesTextbox(Textbox, object): class GlancesTextbox(Textbox):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(GlancesTextbox, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def do_command(self, ch): def do_command(self, ch):
if ch == 10: # Enter if ch == 10: # Enter
return 0 return 0
if ch == 127: # Back if ch == 127: # Back
return 8 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): def __init__(self, *args, **kwargs):
super(GlancesTextboxYesNo, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def do_command(self, ch): def do_command(self, ch):
return super(GlancesTextboxYesNo, self).do_command(ch) return super().do_command(ch)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,11 +8,11 @@
"""Curses browser interface class .""" """Curses browser interface class ."""
import math
import curses import curses
from glances.outputs.glances_curses import _GlancesCurses import math
from glances.logger import logger from glances.logger import logger
from glances.outputs.glances_curses import _GlancesCurses
from glances.timer import Timer from glances.timer import Timer
@ -22,7 +21,7 @@ class GlancesCursesBrowser(_GlancesCurses):
def __init__(self, args=None): def __init__(self, args=None):
"""Init the father class.""" """Init the father class."""
super(GlancesCursesBrowser, self).__init__(args=args) super().__init__(args=args)
_colors_list = { _colors_list = {
'UNKNOWN': self.no_color, 'UNKNOWN': self.no_color,
@ -151,7 +150,7 @@ class GlancesCursesBrowser(_GlancesCurses):
self.pressedkey = self.get_key(self.term_window) self.pressedkey = self.get_key(self.term_window)
refresh = False refresh = False
if self.pressedkey != -1: if self.pressedkey != -1:
logger.debug("Key pressed. Code=%s" % self.pressedkey) logger.debug(f"Key pressed. Code={self.pressedkey}")
# Actions... # Actions...
if self.pressedkey == ord('\x1b') or self.pressedkey == ord('q'): if self.pressedkey == ord('\x1b') or self.pressedkey == ord('q'):
@ -163,23 +162,23 @@ class GlancesCursesBrowser(_GlancesCurses):
elif self.pressedkey == 10: elif self.pressedkey == 10:
# 'ENTER' > Run Glances on the selected server # 'ENTER' > Run Glances on the selected server
self.active_server = self._current_page * self._page_max_lines + self.cursor_position self.active_server = self._current_page * self._page_max_lines + self.cursor_position
logger.debug("Server {}/{} selected".format(self.active_server, len(stats))) logger.debug(f"Server {self.active_server}/{len(stats)} selected")
elif self.pressedkey == curses.KEY_UP or self.pressedkey == 65: elif self.pressedkey == curses.KEY_UP or self.pressedkey == 65:
# 'UP' > Up in the server list # 'UP' > Up in the server list
self.cursor_up(stats) 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: elif self.pressedkey == curses.KEY_DOWN or self.pressedkey == 66:
# 'DOWN' > Down in the server list # 'DOWN' > Down in the server list
self.cursor_down(stats) 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: elif self.pressedkey == curses.KEY_PPAGE:
# 'Page UP' > Prev page in the server list # 'Page UP' > Prev page in the server list
self.cursor_pageup(stats) 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: elif self.pressedkey == curses.KEY_NPAGE:
# 'Page Down' > Next page in the server list # 'Page Down' > Next page in the server list
self.cursor_pagedown(stats) 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'): elif self.pressedkey == ord('1'):
self._stats_list = None self._stats_list = None
refresh = True refresh = True
@ -211,7 +210,7 @@ class GlancesCursesBrowser(_GlancesCurses):
:param return_to_browser: :param return_to_browser:
""" """
# Flush display # Flush display
logger.debug('Servers list: {}'.format(stats)) logger.debug(f'Servers list: {stats}')
self.flush(stats) self.flush(stats)
# Wait # Wait
@ -268,19 +267,19 @@ class GlancesCursesBrowser(_GlancesCurses):
elif len(stats) == 1: elif len(stats) == 1:
msg = 'One Glances server available' msg = 'One Glances server available'
else: else:
msg = '{} Glances servers available'.format(stats_len) msg = f'{stats_len} Glances servers available'
if self.args.disable_autodiscover: if self.args.disable_autodiscover:
msg += ' (auto discover is disabled)' msg += ' (auto discover is disabled)'
if screen_y > 1: if screen_y > 1:
self.term_window.addnstr(y, x, msg, screen_x - x, self.colors_list['TITLE']) self.term_window.addnstr(y, x, msg, screen_x - x, self.colors_list['TITLE'])
msg = '{}'.format(self._get_status_count(stats)) msg = f'{self._get_status_count(stats)}'
self.term_window.addnstr(y + 1, x, msg, screen_x - x) self.term_window.addnstr(y + 1, x, msg, screen_x - x)
if stats_len > stats_max and screen_y > 2: if stats_len > stats_max and screen_y > 2:
msg = '{} servers displayed.({}/{}) {}'.format( page_lines = self.get_pagelines(stats)
self.get_pagelines(stats), self._current_page + 1, self._page_max, self._get_status_count(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) self.term_window.addnstr(y + 1, x, msg, screen_x - x)
if stats_len == 0: if stats_len == 0:
@ -335,7 +334,7 @@ class GlancesCursesBrowser(_GlancesCurses):
try: try:
server_stat[c[0]] = v[c[0]] server_stat[c[0]] = v[c[0]]
except KeyError as e: 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]] = '?' server_stat[c[0]] = '?'
# Display alias instead of name # Display alias instead of name
try: try:

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -12,7 +11,6 @@
import os import os
import sys import sys
import tempfile import tempfile
from io import open
import webbrowser import webbrowser
from urllib.parse import urljoin from urllib.parse import urljoin
@ -23,20 +21,20 @@ except ImportError:
# To be removed when Python 3.8 support will be dropped # To be removed when Python 3.8 support will be dropped
from typing_extensions import Annotated 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.password import GlancesPassword
from glances.timer import Timer from glances.timer import Timer
from glances.logger import logger
# FastAPI import # FastAPI import
try: try:
from fastapi import FastAPI, Depends, HTTPException, status, APIRouter, Request from fastapi import APIRouter, Depends, FastAPI, HTTPException, Request, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware from fastapi.middleware.gzip import GZipMiddleware
from fastapi.responses import HTMLResponse, ORJSONResponse from fastapi.responses import HTMLResponse, ORJSONResponse
from fastapi.templating import Jinja2Templates from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
except ImportError: except ImportError:
logger.critical('FastAPI import error. Glances cannot start in web server mode.') logger.critical('FastAPI import error. Glances cannot start in web server mode.')
sys.exit(2) sys.exit(2)
@ -46,6 +44,7 @@ try:
except ImportError: except ImportError:
logger.critical('Uvicorn import error. Glances cannot start in web server mode.') logger.critical('Uvicorn import error. Glances cannot start in web server mode.')
sys.exit(2) sys.exit(2)
import builtins
import contextlib import contextlib
import threading import threading
import time import time
@ -77,7 +76,7 @@ class GlancesUvicornServer(uvicorn.Server):
thread.join() thread.join()
class GlancesRestfulApi(object): class GlancesRestfulApi:
"""This class manages the Restful API server.""" """This class manages the Restful API server."""
API_VERSION = __apiversion__ API_VERSION = __apiversion__
@ -102,7 +101,7 @@ class GlancesRestfulApi(object):
self.load_config(config) self.load_config(config)
# Set the bind URL # 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 # FastAPI Init
if self.args.password: if self.args.password:
@ -151,9 +150,9 @@ class GlancesRestfulApi(object):
self.url_prefix = '/' self.url_prefix = '/'
if config is not None and config.has_section('outputs'): if config is not None and config.has_section('outputs'):
n = config.get_value('outputs', 'max_processes_display', default=None) 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='/') 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): def __update__(self):
# Never update more than 1 time per cached_time # Never update more than 1 time per cached_time
@ -184,92 +183,92 @@ class GlancesRestfulApi(object):
# REST API # REST API
router.add_api_route( router.add_api_route(
'/api/%s/status' % self.API_VERSION, f'/api/{self.API_VERSION}/status',
status_code=status.HTTP_200_OK, status_code=status.HTTP_200_OK,
response_class=ORJSONResponse, response_class=ORJSONResponse,
endpoint=self._api_status, endpoint=self._api_status,
) )
router.add_api_route( 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( router.add_api_route(
'/api/%s/config/{section}' % self.API_VERSION, f'/api/{self.API_VERSION}/config/{{section}}',
response_class=ORJSONResponse, response_class=ORJSONResponse,
endpoint=self._api_config_section, endpoint=self._api_config_section,
) )
router.add_api_route( router.add_api_route(
'/api/%s/config/{section}/{item}' % self.API_VERSION, f'/api/{self.API_VERSION}/config/{{section}}/{{item}}',
response_class=ORJSONResponse, response_class=ORJSONResponse,
endpoint=self._api_config_section_item, 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( 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( 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( 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( 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(f'/api/{self.API_VERSION}/help', 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}/{{plugin}}', response_class=ORJSONResponse, endpoint=self._api)
router.add_api_route( 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( router.add_api_route(
'/api/%s/{plugin}/history/{nb}' % self.API_VERSION, f'/api/{self.API_VERSION}/{{plugin}}/history/{{nb}}',
response_class=ORJSONResponse, response_class=ORJSONResponse,
endpoint=self._api_history, endpoint=self._api_history,
) )
router.add_api_route( 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( 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( 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( 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( router.add_api_route(
'/api/%s/{plugin}/{item}/history' % self.API_VERSION, f'/api/{self.API_VERSION}/{{plugin}}/{{item}}/history',
response_class=ORJSONResponse, response_class=ORJSONResponse,
endpoint=self._api_item_history, endpoint=self._api_item_history,
) )
router.add_api_route( 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, response_class=ORJSONResponse,
endpoint=self._api_item_history, endpoint=self._api_item_history,
) )
router.add_api_route( router.add_api_route(
'/api/%s/{plugin}/{item}/description' % self.API_VERSION, f'/api/{self.API_VERSION}/{{plugin}}/{{item}}/description',
response_class=ORJSONResponse, response_class=ORJSONResponse,
endpoint=self._api_item_description, endpoint=self._api_item_description,
) )
router.add_api_route( router.add_api_route(
'/api/%s/{plugin}/{item}/unit' % self.API_VERSION, f'/api/{self.API_VERSION}/{{plugin}}/{{item}}/unit',
response_class=ORJSONResponse, response_class=ORJSONResponse,
endpoint=self._api_item_unit, endpoint=self._api_item_unit,
) )
router.add_api_route( router.add_api_route(
'/api/%s/{plugin}/{item}/{value}' % self.API_VERSION, f'/api/{self.API_VERSION}/{{plugin}}/{{item}}/{{value}}',
response_class=ORJSONResponse, response_class=ORJSONResponse,
endpoint=self._api_value, endpoint=self._api_value,
) )
# Restful API # 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) logger.info(bindmsg)
# WEB UI # WEB UI
@ -280,9 +279,9 @@ class GlancesRestfulApi(object):
# Statics files # Statics files
self._app.mount("/static", StaticFiles(directory=self.STATIC_PATH), name="static") 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: else:
bindmsg = 'The WebUI is disable (--disable-webui)' bindmsg = 'The WebUI is disable (--disable-webui)'
@ -317,7 +316,7 @@ class GlancesRestfulApi(object):
try: try:
self.uvicorn_server = GlancesUvicornServer(config=uvicorn_config) self.uvicorn_server = GlancesUvicornServer(config=uvicorn_config)
except Exception as e: 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 self.uvicorn_server = None
else: else:
with self.uvicorn_server.run_in_thread(): with self.uvicorn_server.run_in_thread():
@ -369,7 +368,7 @@ class GlancesRestfulApi(object):
try: try:
plist = self.stats.get_plugin("help").get_view_data() plist = self.stats.get_plugin("help").get_view_data()
except Exception as e: 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) return ORJSONResponse(plist)
@ -405,7 +404,7 @@ class GlancesRestfulApi(object):
try: try:
plist = self.plugins_list plist = self.plugins_list
except Exception as e: 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) return ORJSONResponse(plist)
@ -420,10 +419,10 @@ class GlancesRestfulApi(object):
if self.args.debug: if self.args.debug:
fname = os.path.join(tempfile.gettempdir(), 'glances-debug.json') fname = os.path.join(tempfile.gettempdir(), 'glances-debug.json')
try: try:
with open(fname) as f: with builtins.open(fname) as f:
return f.read() return f.read()
except IOError: except OSError:
logger.debug("Debug file (%s) not found" % fname) logger.debug(f"Debug file ({fname}) not found")
# Update the stat # Update the stat
self.__update__() self.__update__()
@ -432,7 +431,7 @@ class GlancesRestfulApi(object):
# Get the RAW value of the stat ID # Get the RAW value of the stat ID
statval = self.stats.getAllAsDict() statval = self.stats.getAllAsDict()
except Exception as e: 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) return ORJSONResponse(statval)
@ -448,7 +447,7 @@ class GlancesRestfulApi(object):
# Get the RAW value of the stat limits # Get the RAW value of the stat limits
limits = self.stats.getAllLimitsAsDict() limits = self.stats.getAllLimitsAsDict()
except Exception as e: 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) return ORJSONResponse(limits)
@ -464,7 +463,7 @@ class GlancesRestfulApi(object):
# Get the RAW value of the stat view # Get the RAW value of the stat view
limits = self.stats.getAllViewsAsDict() limits = self.stats.getAllViewsAsDict()
except Exception as e: 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) return ORJSONResponse(limits)
@ -479,7 +478,7 @@ class GlancesRestfulApi(object):
if plugin not in self.plugins_list: if plugin not in self.plugins_list:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, 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 # Update the stat
@ -489,9 +488,7 @@ class GlancesRestfulApi(object):
# Get the RAW value of the stat ID # Get the RAW value of the stat ID
statval = self.stats.get_plugin(plugin).get_raw() statval = self.stats.get_plugin(plugin).get_raw()
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get plugin {plugin} ({str(e)})")
status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get plugin %s (%s)" % (plugin, str(e))
)
return ORJSONResponse(statval) return ORJSONResponse(statval)
@ -508,7 +505,7 @@ class GlancesRestfulApi(object):
if plugin not in self.plugins_list: if plugin not in self.plugins_list:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, 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 # Update the stat
@ -518,9 +515,7 @@ class GlancesRestfulApi(object):
# Get the RAW value of the stat ID # Get the RAW value of the stat ID
statval = self.stats.get_plugin(plugin).get_raw() statval = self.stats.get_plugin(plugin).get_raw()
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Cannot get plugin {plugin} ({str(e)})")
status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get plugin %s (%s)" % (plugin, str(e))
)
print(statval) print(statval)
@ -541,7 +536,7 @@ class GlancesRestfulApi(object):
if plugin not in self.plugins_list: if plugin not in self.plugins_list:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, 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 # Update the stat
@ -552,7 +547,7 @@ class GlancesRestfulApi(object):
statval = self.stats.get_plugin(plugin).get_raw_history(nb=int(nb)) statval = self.stats.get_plugin(plugin).get_raw_history(nb=int(nb))
except Exception as e: except Exception as e:
raise HTTPException( 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 return statval
@ -568,7 +563,7 @@ class GlancesRestfulApi(object):
if plugin not in self.plugins_list: if plugin not in self.plugins_list:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, 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: try:
@ -576,7 +571,7 @@ class GlancesRestfulApi(object):
ret = self.stats.get_plugin(plugin).limits ret = self.stats.get_plugin(plugin).limits
except Exception as e: except Exception as e:
raise HTTPException( 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) return ORJSONResponse(ret)
@ -592,7 +587,7 @@ class GlancesRestfulApi(object):
if plugin not in self.plugins_list: if plugin not in self.plugins_list:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, 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: try:
@ -600,7 +595,7 @@ class GlancesRestfulApi(object):
ret = self.stats.get_plugin(plugin).get_views() ret = self.stats.get_plugin(plugin).get_views()
except Exception as e: except Exception as e:
raise HTTPException( 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) return ORJSONResponse(ret)
@ -616,7 +611,7 @@ class GlancesRestfulApi(object):
if plugin not in self.plugins_list: if plugin not in self.plugins_list:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, 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 # Update the stat
@ -628,7 +623,7 @@ class GlancesRestfulApi(object):
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, 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) return ORJSONResponse(ret)
@ -645,7 +640,7 @@ class GlancesRestfulApi(object):
if plugin not in self.plugins_list: if plugin not in self.plugins_list:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, 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 # Update the stat
@ -656,7 +651,7 @@ class GlancesRestfulApi(object):
ret = self.stats.get_plugin(plugin).get_raw_history(item, nb=nb) ret = self.stats.get_plugin(plugin).get_raw_history(item, nb=nb)
except Exception as e: except Exception as e:
raise HTTPException( 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: else:
return ORJSONResponse(ret) return ORJSONResponse(ret)
@ -672,7 +667,7 @@ class GlancesRestfulApi(object):
if plugin not in self.plugins_list: if plugin not in self.plugins_list:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, 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: try:
@ -681,7 +676,7 @@ class GlancesRestfulApi(object):
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, 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: else:
return ORJSONResponse(ret) return ORJSONResponse(ret)
@ -697,7 +692,7 @@ class GlancesRestfulApi(object):
if plugin not in self.plugins_list: if plugin not in self.plugins_list:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, 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: try:
@ -706,7 +701,7 @@ class GlancesRestfulApi(object):
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, 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: else:
return ORJSONResponse(ret) return ORJSONResponse(ret)
@ -722,7 +717,7 @@ class GlancesRestfulApi(object):
if plugin not in self.plugins_list: if plugin not in self.plugins_list:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, 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 # Update the stat
@ -734,7 +729,7 @@ class GlancesRestfulApi(object):
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, 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: else:
return ORJSONResponse(ret) return ORJSONResponse(ret)
@ -750,7 +745,7 @@ class GlancesRestfulApi(object):
# Get the RAW value of the config' dict # Get the RAW value of the config' dict
args_json = self.config.as_dict() args_json = self.config.as_dict()
except Exception as e: 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: else:
return ORJSONResponse(args_json) return ORJSONResponse(args_json)
@ -764,16 +759,14 @@ class GlancesRestfulApi(object):
""" """
config_dict = self.config.as_dict() config_dict = self.config.as_dict()
if section not in config_dict: if section not in config_dict:
raise HTTPException( raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"Unknown configuration item {section}")
status_code=status.HTTP_400_BAD_REQUEST, detail="Unknown configuration item %s" % section
)
try: try:
# Get the RAW value of the config' dict # Get the RAW value of the config' dict
ret_section = config_dict[section] ret_section = config_dict[section]
except Exception as e: except Exception as e:
raise HTTPException( 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) return ORJSONResponse(ret_section)
@ -788,16 +781,14 @@ class GlancesRestfulApi(object):
""" """
config_dict = self.config.as_dict() config_dict = self.config.as_dict()
if section not in config_dict: if section not in config_dict:
raise HTTPException( raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"Unknown configuration item {section}")
status_code=status.HTTP_400_BAD_REQUEST, detail="Unknown configuration item %s" % section
)
try: try:
# Get the RAW value of the config' dict section # Get the RAW value of the config' dict section
ret_section = config_dict[section] ret_section = config_dict[section]
except Exception as e: except Exception as e:
raise HTTPException( 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: try:
@ -806,7 +797,7 @@ class GlancesRestfulApi(object):
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, 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) return ORJSONResponse(ret_item)
@ -824,7 +815,7 @@ class GlancesRestfulApi(object):
# Source: https://docs.python.org/%s/library/functions.html#vars # Source: https://docs.python.org/%s/library/functions.html#vars
args_json = vars(self.args) args_json = vars(self.args)
except Exception as e: 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) return ORJSONResponse(args_json)
@ -837,7 +828,7 @@ class GlancesRestfulApi(object):
HTTP/404 if others error HTTP/404 if others error
""" """
if item not in self.args: 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: try:
# Get the RAW value of the args' dict # 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 # Source: https://docs.python.org/%s/library/functions.html#vars
args_json = vars(self.args)[item] args_json = vars(self.args)[item]
except Exception as e: 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) return ORJSONResponse(args_json)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,28 +8,27 @@
"""Manage sparklines for Glances output.""" """Manage sparklines for Glances output."""
from __future__ import unicode_literals
from __future__ import division
import sys import sys
from glances.logger import logger
from glances.globals import nativestr from glances.globals import nativestr
from glances.logger import logger
sparklines_module = True sparklines_module = True
try: try:
from sparklines import sparklines from sparklines import sparklines
except ImportError as e: except ImportError as e:
logger.warning("Sparklines module not found ({})".format(e)) logger.warning(f"Sparklines module not found ({e})")
sparklines_module = False sparklines_module = False
try: try:
'┌┬┐╔╦╗╒╤╕╓╥╖│║─═├┼┤╠╬╣╞╪╡╟╫╢└┴┘╚╩╝╘╧╛╙╨╜'.encode(sys.stdout.encoding) '┌┬┐╔╦╗╒╤╕╓╥╖│║─═├┼┤╠╬╣╞╪╡╟╫╢└┴┘╚╩╝╘╧╛╙╨╜'.encode(sys.stdout.encoding)
except (UnicodeEncodeError, TypeError) as e: 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 sparklines_module = False
class Sparkline(object): class Sparkline:
"""Manage sparklines (see https://pypi.org/project/sparklines/).""" """Manage sparklines (see https://pypi.org/project/sparklines/)."""
def __init__(self, size, pre_char='[', post_char=']', unit_char='%', display_value=True): def __init__(self, size, pre_char='[', post_char=']', unit_char='%', display_value=True):
@ -58,6 +56,7 @@ class Sparkline(object):
return self.__size return self.__size
if self.__display_value: if self.__display_value:
return self.__size - 6 return self.__size - 6
return None
@property @property
def percents(self): def percents(self):
@ -81,7 +80,7 @@ class Sparkline(object):
if self.__display_value: if self.__display_value:
percents_without_none = [x for x in self.percents if x is not None] percents_without_none = [x for x in self.percents if x is not None]
if len(percents_without_none) > 0: 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) ret = nativestr(ret)
if overwrite and len(overwrite) < len(ret) - 6: if overwrite and len(overwrite) < len(ret) - 6:
ret = overwrite + ret[len(overwrite) :] ret = overwrite + ret[len(overwrite) :]

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -11,11 +10,11 @@
import time import time
from glances.logger import logger
from glances.globals import printandflush from glances.globals import printandflush
from glances.logger import logger
class GlancesStdout(object): class GlancesStdout:
"""This class manages the Stdout display.""" """This class manages the Stdout display."""
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
@ -65,9 +64,9 @@ class GlancesStdout(object):
# With attribute # With attribute
if isinstance(stat, dict): if isinstance(stat, dict):
try: try:
printandflush("{}.{}: {}".format(plugin, attribute, stat[attribute])) printandflush(f"{plugin}.{attribute}: {stat[attribute]}")
except KeyError as err: 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): elif isinstance(stat, list):
for i in stat: for i in stat:
if key is None: if key is None:
@ -77,12 +76,12 @@ class GlancesStdout(object):
else: else:
continue continue
try: try:
printandflush("{}.{}.{}: {}".format(plugin, i_key, attribute, i[attribute])) printandflush(f"{plugin}.{i_key}.{attribute}: {i[attribute]}")
except KeyError as err: 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: else:
# Without attribute # Without attribute
printandflush("{}: {}".format(plugin, stat)) printandflush(f"{plugin}: {stat}")
# Wait until next refresh # Wait until next refresh
if duration > 0: if duration > 0:

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,24 +8,23 @@
"""Fields description interface class.""" """Fields description interface class."""
from pprint import pformat
import json import json
import time import time
from pprint import pformat
from glances import __apiversion__ from glances import __apiversion__
from glances.logger import logger
from glances.globals import iteritems 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 = f"""\
APIDOC_HEADER = """\
.. _api: .. _api:
API (Restfull/JSON) documentation 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: For Glances version 3, please have a look on:
``https://github.com/nicolargo/glances/blob/support/glancesv3/docs/api.rst`` ``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 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. The bind address and port could be changed using the ``--bind`` and ``--port`` command line options.
@ -60,7 +58,7 @@ For example:
[outputs] [outputs]
url_prefix = /glances/ 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/`` ``http://localhost:61208/glances/``
API documentation URL 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: 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`` ``http://localhost:61208/glances/?refresh=5``
""".format( """
api_version=__apiversion__
)
def indent_stat(stat, indent=' '): def indent_stat(stat, indent=' '):
@ -85,7 +81,6 @@ def indent_stat(stat, indent=' '):
if isinstance(stat, list) and len(stat) > 1 and isinstance(stat[0], dict): if isinstance(stat, list) and len(stat) > 1 and isinstance(stat[0], dict):
# Only display two first items # Only display two first items
return indent + pformat(stat[0:2]).replace('\n', '\n' + indent).replace("'", '"') 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("'", '"')
@ -99,7 +94,7 @@ def print_api_status():
print('') print('')
print('Get the Rest API status::') print('Get the Rest API status::')
print('') print('')
print(' # curl -I {}/status'.format(API_URL)) print(f' # curl -I {API_URL}/status')
print(indent_stat('HTTP/1.0 200 OK')) print(indent_stat('HTTP/1.0 200 OK'))
print('') print('')
@ -111,20 +106,20 @@ def print_plugins_list(stat):
print('') print('')
print('Get the plugins list::') print('Get the plugins list::')
print('') print('')
print(' # curl {}/pluginslist'.format(API_URL)) print(f' # curl {API_URL}/pluginslist')
print(indent_stat(stat)) print(indent_stat(stat))
print('') print('')
def print_plugin_stats(plugin, stat): def print_plugin_stats(plugin, stat):
sub_title = 'GET {}'.format(plugin) sub_title = f'GET {plugin}'
print(sub_title) print(sub_title)
print('-' * len(sub_title)) print('-' * len(sub_title))
print('') print('')
print('Get plugin stats::') print('Get plugin stats::')
print('') print('')
print(' # curl {}/{}'.format(API_URL, plugin)) print(f' # curl {API_URL}/{plugin}')
print(indent_stat(json.loads(stat.get_stats()))) print(indent_stat(json.loads(stat.get_stats())))
print('') print('')
@ -183,7 +178,7 @@ def print_plugin_description(plugin, stat):
print('') print('')
else: 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): 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] value = stat_item[item]
print('Get a specific field::') print('Get a specific field::')
print('') print('')
print(' # curl {}/{}/{}'.format(API_URL, plugin, item)) print(f' # curl {API_URL}/{plugin}/{item}')
print(indent_stat(stat_item)) print(indent_stat(stat_item))
print('') print('')
if item and value and stat.get_stats_value(item, value): if item and value and stat.get_stats_value(item, value):
print('Get a specific item when field matches the given value::') print('Get a specific item when field matches the given value::')
print('') 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(indent_stat(json.loads(stat.get_stats_value(item, value))))
print('') print('')
@ -223,7 +218,7 @@ def print_all():
print('') print('')
print('Get all Glances stats::') print('Get all Glances stats::')
print('') 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(' Return a very big dictionary (avoid using this request, performances will be poor)...')
print('') print('')
@ -237,7 +232,7 @@ def print_top(stats):
print('') print('')
print('Get top 2 processes of the processlist plugin::') print('Get top 2 processes of the processlist plugin::')
print('') 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(indent_stat(stats.get_plugin('processlist').get_export()[:2]))
print('') print('')
print('Note: Only work for plugin with a list of items') 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('-' * len(sub_title))
print('Get item description (human readable) for a specific plugin/item::') print('Get item description (human readable) for a specific plugin/item::')
print('') 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(indent_stat(stats.get_plugin('diskio').get_item_info('read_bytes', 'description')))
print('') print('')
print('Note: the description is defined in the fields_description variable of the plugin.') 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('-' * len(sub_title))
print('Get item unit for a specific plugin/item::') print('Get item unit for a specific plugin/item::')
print('') 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(indent_stat(stats.get_plugin('diskio').get_item_info('read_bytes', 'unit')))
print('') print('')
print('Note: the description is defined in the fields_description variable of the plugin.') print('Note: the description is defined in the fields_description variable of the plugin.')
@ -278,22 +273,22 @@ def print_history(stats):
print('') print('')
print('History of a plugin::') print('History of a plugin::')
print('') 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(indent_stat(json.loads(stats.get_plugin('cpu').get_stats_history(nb=3))))
print('') print('')
print('Limit history to last 2 values::') print('Limit history to last 2 values::')
print('') 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(indent_stat(json.loads(stats.get_plugin('cpu').get_stats_history(nb=2))))
print('') print('')
print('History for a specific field::') print('History for a specific field::')
print('') 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(indent_stat(json.loads(stats.get_plugin('cpu').get_stats_history('system'))))
print('') print('')
print('Limit history for a specific field to last 2 values::') print('Limit history for a specific field to last 2 values::')
print('') 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(indent_stat(json.loads(stats.get_plugin('cpu').get_stats_history('system', nb=2))))
print('') print('')
@ -305,17 +300,17 @@ def print_limits(stats):
print('') print('')
print('All limits/thresholds::') print('All limits/thresholds::')
print('') print('')
print(' # curl {}/all/limits'.format(API_URL)) print(f' # curl {API_URL}/all/limits')
print(indent_stat(stats.getAllLimitsAsDict())) print(indent_stat(stats.getAllLimitsAsDict()))
print('') print('')
print('Limits/thresholds for the cpu plugin::') print('Limits/thresholds for the cpu plugin::')
print('') print('')
print(' # curl {}/cpu/limits'.format(API_URL)) print(f' # curl {API_URL}/cpu/limits')
print(indent_stat(stats.get_plugin('cpu').limits)) print(indent_stat(stats.get_plugin('cpu').limits))
print('') print('')
class GlancesStdoutApiDoc(object): class GlancesStdoutApiDoc:
"""This class manages the fields description display.""" """This class manages the fields description display."""
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -14,7 +13,7 @@ import time
from glances.globals import printandflush from glances.globals import printandflush
class GlancesStdoutCsv(object): class GlancesStdoutCsv:
"""This class manages the StdoutCsv display.""" """This class manages the StdoutCsv display."""
separator = ',' separator = ','
@ -53,18 +52,18 @@ class GlancesStdoutCsv(object):
line = '' line = ''
if attribute is not None: if attribute is not None:
line += '{}.{}{}'.format(plugin, attribute, self.separator) line += f'{plugin}.{attribute}{self.separator}'
else: else:
if isinstance(stat, dict): if isinstance(stat, dict):
for k in stat.keys(): for k in stat.keys():
line += '{}.{}{}'.format(plugin, str(k), self.separator) line += f'{plugin}.{str(k)}{self.separator}'
elif isinstance(stat, list): elif isinstance(stat, list):
for i in stat: for i in stat:
if isinstance(i, dict) and 'key' in i: if isinstance(i, dict) and 'key' in i:
for k in i.keys(): for k in i.keys():
line += '{}.{}.{}{}'.format(plugin, str(i[i['key']]), str(k), self.separator) line += '{}.{}.{}{}'.format(plugin, str(i[i['key']]), str(k), self.separator)
else: else:
line += '{}{}'.format(plugin, self.separator) line += f'{plugin}{self.separator}'
return line return line
@ -73,18 +72,18 @@ class GlancesStdoutCsv(object):
line = '' line = ''
if attribute is not None: 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: else:
if isinstance(stat, dict): if isinstance(stat, dict):
for v in stat.values(): for v in stat.values():
line += '{}{}'.format(str(v), self.separator) line += f'{str(v)}{self.separator}'
elif isinstance(stat, list): elif isinstance(stat, list):
for i in stat: for i in stat:
if isinstance(i, dict) and 'key' in i: if isinstance(i, dict) and 'key' in i:
for v in i.values(): for v in i.values():
line += '{}{}'.format(str(v), self.separator) line += f'{str(v)}{self.separator}'
else: else:
line += '{}{}'.format(str(stat), self.separator) line += f'{str(stat)}{self.separator}'
return line return line

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -10,16 +9,16 @@
"""Issue interface class.""" """Issue interface class."""
import os import os
import sys
import platform import platform
import time
import pprint import pprint
import sys
from glances.timer import Counter import time
from glances import __version__, psutil_version
import psutil import psutil
import glances import glances
from glances import __version__, psutil_version
from glances.timer import Counter
TERMINAL_WIDTH = 79 TERMINAL_WIDTH = 79
@ -39,7 +38,7 @@ class colors:
self.NO = '' self.NO = ''
class GlancesStdoutIssue(object): class GlancesStdoutIssue:
"""This class manages the Issue display.""" """This class manages the Issue display."""
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
@ -52,18 +51,14 @@ class GlancesStdoutIssue(object):
def print_version(self): def print_version(self):
sys.stdout.write('=' * TERMINAL_WIDTH + '\n') sys.stdout.write('=' * TERMINAL_WIDTH + '\n')
sys.stdout.write( sys.stdout.write(f'Glances {colors.BLUE + __version__ + colors.NO} ({os.path.realpath(glances.__file__)})\n')
'Glances {} ({})\n'.format(colors.BLUE + __version__ + colors.NO, os.path.realpath(glances.__file__)) 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('Python {} ({})\n'.format(colors.BLUE + platform.python_version() + colors.NO, sys.executable))
sys.stdout.write(
'PsUtil {} ({})\n'.format(colors.BLUE + psutil_version + colors.NO, os.path.realpath(psutil.__file__))
)
sys.stdout.write('=' * TERMINAL_WIDTH + '\n') sys.stdout.write('=' * TERMINAL_WIDTH + '\n')
sys.stdout.flush() sys.stdout.flush()
def print_issue(self, plugin, result, message): 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.write(colors.NO + '\n')
sys.stdout.flush() sys.stdout.flush()
@ -108,9 +103,7 @@ class GlancesStdoutIssue(object):
except Exception as e: except Exception as e:
stat_error = e stat_error = e
if stat_error is None: if stat_error is None:
result = (colors.GREEN + '[OK] ' + colors.BLUE + ' {:.5f}s '.format(counter.get())).rjust( result = (colors.GREEN + '[OK] ' + colors.BLUE + f' {counter.get():.5f}s ').rjust(41 - len(plugin))
41 - len(plugin)
)
if isinstance(stat, list) and len(stat) > 0 and 'key' in stat[0]: if isinstance(stat, list) and len(stat) > 0 and 'key' in stat[0]:
key = 'key={} '.format(stat[0]['key']) key = 'key={} '.format(stat[0]['key'])
stat_output = pprint.pformat([stat[0]], compact=True, width=120, depth=3) stat_output = pprint.pformat([stat[0]], compact=True, width=120, depth=3)
@ -118,9 +111,7 @@ class GlancesStdoutIssue(object):
else: else:
message = '\n' + colors.NO + pprint.pformat(stat, compact=True, width=120, depth=2) message = '\n' + colors.NO + pprint.pformat(stat, compact=True, width=120, depth=2)
else: else:
result = (colors.RED + '[ERROR]' + colors.BLUE + ' {:.5f}s '.format(counter.get())).rjust( result = (colors.RED + '[ERROR]' + colors.BLUE + f' {counter.get():.5f}s ').rjust(41 - len(plugin))
41 - len(plugin)
)
message = colors.NO + str(stat_error)[0 : TERMINAL_WIDTH - 41] message = colors.NO + str(stat_error)[0 : TERMINAL_WIDTH - 41]
# Display the result # Display the result
@ -128,7 +119,7 @@ class GlancesStdoutIssue(object):
# Display total time need to update all plugins # Display total time need to update all plugins
sys.stdout.write('=' * TERMINAL_WIDTH + '\n') 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') sys.stdout.write('=' * TERMINAL_WIDTH + '\n')
# Return True to exit directly (no refresh) # Return True to exit directly (no refresh)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -14,7 +13,7 @@ import time
from glances.globals import printandflush from glances.globals import printandflush
class GlancesStdoutJson(object): class GlancesStdoutJson:
"""This class manages the Stdout JSON display.""" """This class manages the Stdout JSON display."""
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
@ -47,7 +46,7 @@ class GlancesStdoutJson(object):
else: else:
continue continue
# Display stats # Display stats
printandflush('{}: {}'.format(plugin, stat)) printandflush(f'{plugin}: {stat}')
# Wait until next refresh # Wait until next refresh
if duration > 0: if duration > 0:

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -10,14 +9,14 @@
"""Manage unicode message for Glances output.""" """Manage unicode message for Glances output."""
_unicode_message = { _unicode_message = {
'ARROW_LEFT': [u'\u2190', u'<'], 'ARROW_LEFT': ['\u2190', '<'],
'ARROW_RIGHT': [u'\u2192', u'>'], 'ARROW_RIGHT': ['\u2192', '>'],
'ARROW_UP': [u'\u2191', u'^'], 'ARROW_UP': ['\u2191', '^'],
'ARROW_DOWN': [u'\u2193', u'v'], 'ARROW_DOWN': ['\u2193', 'v'],
'CHECK': [u'\u2713', u''], 'CHECK': ['\u2713', ''],
'PROCESS_SELECTOR': [u'>', u'>'], 'PROCESS_SELECTOR': ['>', '>'],
'MEDIUM_LINE': [u'\u23AF', u'-'], 'MEDIUM_LINE': ['\u23af', '-'],
'LOW_LINE': [u'\u2581', u'_'], 'LOW_LINE': ['\u2581', '_'],
} }
@ -25,5 +24,4 @@ def unicode_message(key, args=None):
"""Return the unicode message for the given key.""" """Return the unicode message for the given key."""
if args and hasattr(args, 'disable_unicode') and args.disable_unicode: if args and hasattr(args, 'disable_unicode') and args.disable_unicode:
return _unicode_message[key][1] return _unicode_message[key][1]
else:
return _unicode_message[key][0] return _unicode_message[key][0]

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,19 +8,19 @@
"""Manage password.""" """Manage password."""
import builtins
import getpass import getpass
import hashlib import hashlib
import os import os
import sys import sys
import uuid 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.config import user_config_dir
from glances.globals import b, safe_makedirs, weak_lru_cache
from glances.logger import logger from glances.logger import logger
class GlancesPassword(object): class GlancesPassword:
"""This class contains all the methods relating to password.""" """This class contains all the methods relating to password."""
def __init__(self, username='glances', config=None): def __init__(self, username='glances', config=None):
@ -38,7 +37,6 @@ class GlancesPassword(object):
""" """
if self.config is None: if self.config is None:
return user_config_dir()[0] 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) @weak_lru_cache(maxsize=32)
@ -78,7 +76,7 @@ class GlancesPassword(object):
""" """
if os.path.exists(self.password_file) and not clear: if os.path.exists(self.password_file) and not clear:
# If the password file exist then use it # 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() password = self.load_password()
else: else:
# password_hash is the plain SHA-pbkdf2_hmac password # password_hash is the plain SHA-pbkdf2_hmac password
@ -113,13 +111,11 @@ class GlancesPassword(object):
safe_makedirs(self.password_dir) safe_makedirs(self.password_dir)
# Create/overwrite the password file # 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)) file_pwd.write(b(hashed_password))
def load_password(self): def load_password(self):
"""Load the hashed password from the Glances folder.""" """Load the hashed password from the Glances folder."""
# Read the password file, if it exists # Read the password file, if it exists
with open(self.password_file, 'r') as file_pwd: with builtins.open(self.password_file) as file_pwd:
hashed_password = file_pwd.read() return file_pwd.read()
return hashed_password

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -19,7 +18,7 @@ class GlancesPasswordList(GlancesPassword):
_section = "passwords" _section = "passwords"
def __init__(self, config=None, args=None): def __init__(self, config=None, args=None):
super(GlancesPasswordList, self).__init__() super().__init__()
# password_dict is a dict (JSON compliant) # password_dict is a dict (JSON compliant)
# {'host': 'password', ... } # {'host': 'password', ... }
# Load the configuration file # Load the configuration file
@ -32,14 +31,14 @@ class GlancesPasswordList(GlancesPassword):
if config is None: if config is None:
logger.warning("No configuration file available. Cannot load password list.") logger.warning("No configuration file available. Cannot load password list.")
elif not config.has_section(self._section): 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: 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_dict = dict(config.items(self._section))
# Password list loaded # 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 return password_dict
@ -51,7 +50,7 @@ class GlancesPasswordList(GlancesPassword):
""" """
if host is None: if host is None:
return self._password_dict return self._password_dict
else:
try: try:
return self._password_dict[host] return self._password_dict[host]
except (KeyError, TypeError): except (KeyError, TypeError):

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -101,9 +100,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(args=args, config=config, stats_init_value=[], fields_description=fields_description)
args=args, config=config, stats_init_value=[], fields_description=fields_description
)
# We want to display the stat in the curse interface # We want to display the stat in the curse interface
self.display_curse = True self.display_curse = True
@ -174,7 +171,7 @@ class PluginModel(GlancesPluginModel):
# Top processes # Top processes
top_process = ', '.join(alert['top']) top_process = ', '.join(alert['top'])
if top_process != '': if top_process != '':
msg = ': {}'.format(top_process) msg = f': {top_process}'
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
return ret return ret
@ -183,5 +180,4 @@ class PluginModel(GlancesPluginModel):
"""Compare a with b using the tolerance (if numerical).""" """Compare a with b using the tolerance (if numerical)."""
if str(int(a)).isdigit() and str(int(b)).isdigit(): if str(int(a)).isdigit() and str(int(b)).isdigit():
return abs(a - b) <= max(abs(a), abs(b)) * tolerance return abs(a - b) <= max(abs(a), abs(b)) * tolerance
else:
return a == b return a == b

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,8 +8,8 @@
"""Monitor plugin.""" """Monitor plugin."""
from glances.globals import iteritems
from glances.amps_list import AmpsList as glancesAmpsList from glances.amps_list import AmpsList as glancesAmpsList
from glances.globals import iteritems
from glances.plugins.plugin.model import GlancesPluginModel from glances.plugins.plugin.model import GlancesPluginModel
# Fields description # Fields description
@ -35,9 +34,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(args=args, config=config, stats_init_value=[], fields_description=fields_description)
args=args, config=config, stats_init_value=[], fields_description=fields_description
)
self.args = args self.args = args
self.config = config self.config = config
@ -93,12 +90,10 @@ class PluginModel(GlancesPluginModel):
if nbprocess > 0: if nbprocess > 0:
if int(countmin) <= int(nbprocess) <= int(countmax): if int(countmin) <= int(nbprocess) <= int(countmax):
return 'OK' return 'OK'
else:
return 'WARNING' return 'WARNING'
else:
if int(countmin) == 0: if int(countmin) == 0:
return 'OK' return 'OK'
else:
return 'CRITICAL' return 'CRITICAL'
def msg_curse(self, args=None, max_width=None): def msg_curse(self, args=None, max_width=None):
@ -121,10 +116,10 @@ class PluginModel(GlancesPluginModel):
second_column = '{}'.format(m['count'] if m['regex'] else '') second_column = '{}'.format(m['count'] if m['regex'] else '')
for line in m['result'].split('\n'): for line in m['result'].split('\n'):
# Display first column with the process name... # 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)) ret.append(self.curse_add_line(msg, first_column_style))
# ... and second column with the number of matching processes... # ... 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)) ret.append(self.curse_add_line(msg))
# ... only on the first line # ... only on the first line
first_column = second_column = '' first_column = second_column = ''

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -17,8 +16,8 @@ Supported Cloud API:
import threading import threading
from glances.globals import iteritems, to_ascii from glances.globals import iteritems, to_ascii
from glances.plugins.plugin.model import GlancesPluginModel
from glances.logger import logger from glances.logger import logger
from glances.plugins.plugin.model import GlancesPluginModel
# Import plugin specific dependency # Import plugin specific dependency
try: try:
@ -26,7 +25,7 @@ try:
except ImportError as e: except ImportError as e:
import_error_tag = True import_error_tag = True
# Display debug message if import error # 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: else:
import_error_tag = False import_error_tag = False
@ -44,7 +43,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """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 # We want to display the stat in the curse interface
self.display_curse = True self.display_curse = True
@ -65,7 +64,7 @@ class PluginModel(GlancesPluginModel):
self.OPENSTACK.stop() self.OPENSTACK.stop()
self.OPENSTACKEC2.stop() self.OPENSTACKEC2.stop()
# Call the father class # Call the father class
super(PluginModel, self).exit() super().exit()
@GlancesPluginModel._check_decorator @GlancesPluginModel._check_decorator
@GlancesPluginModel._log_result_decorator @GlancesPluginModel._log_result_decorator
@ -145,7 +144,7 @@ class ThreadOpenStack(threading.Thread):
def __init__(self): def __init__(self):
"""Init the class.""" """Init the class."""
logger.debug("cloud plugin - Create thread for OpenStack metadata") logger.debug("cloud plugin - Create thread for OpenStack metadata")
super(ThreadOpenStack, self).__init__() super().__init__()
# Event needed to stop properly the thread # Event needed to stop properly the thread
self._stopper = threading.Event() self._stopper = threading.Event()
# The class return the stats as a dict # The class return the stats as a dict
@ -161,12 +160,12 @@ class ThreadOpenStack(threading.Thread):
return False return False
for k, v in iteritems(self.OPENSTACK_API_METADATA): 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: try:
# Local request, a timeout of 3 seconds is OK # Local request, a timeout of 3 seconds is OK
r = requests.get(r_url, timeout=3) r = requests.get(r_url, timeout=3)
except Exception as e: 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 break
else: else:
if r.ok: if r.ok:

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -8,14 +7,13 @@
# #
"""Connections plugin.""" """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 import psutil
from glances.globals import nativestr
from glances.logger import logger
from glances.plugins.plugin.model import GlancesPluginModel
# Fields description # Fields description
# description: human readable description # description: human readable description
# short_name: shortname to use un UI # short_name: shortname to use un UI
@ -93,7 +91,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(
args=args, args=args,
config=config, config=config,
# items_history_list=items_history_list, # items_history_list=items_history_list,
@ -122,7 +120,7 @@ class PluginModel(GlancesPluginModel):
try: try:
net_connections = psutil.net_connections(kind="tcp") net_connections = psutil.net_connections(kind="tcp")
except Exception as e: 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') logger.info('Disable connections stats')
stats['net_connections_enabled'] = False stats['net_connections_enabled'] = False
self.stats = stats self.stats = stats
@ -145,10 +143,10 @@ class PluginModel(GlancesPluginModel):
# Grab connections track directly from the /proc file # Grab connections track directly from the /proc file
for i in self.conntrack: for i in self.conntrack:
try: try:
with open(self.conntrack[i], 'r') as f: with open(self.conntrack[i]) as f:
stats[i] = float(f.readline().rstrip("\n")) stats[i] = float(f.readline().rstrip("\n"))
except (IOError, FileNotFoundError) as e: except (OSError, FileNotFoundError) as e:
logger.warning('Can not get network connections track ({})'.format(e)) logger.warning(f'Can not get network connections track ({e})')
logger.info('Disable connections track') logger.info('Disable connections track')
stats['nf_conntrack_enabled'] = False stats['nf_conntrack_enabled'] = False
self.stats = stats self.stats = stats
@ -171,7 +169,7 @@ class PluginModel(GlancesPluginModel):
def update_views(self): def update_views(self):
"""Update stats views.""" """Update stats views."""
# Call the father's method # Call the father's method
super(PluginModel, self).update_views() super().update_views()
# Add specific information # Add specific information
try: try:

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -127,7 +126,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
) )
@ -164,7 +163,6 @@ class PluginModel(GlancesPluginModel):
conf_podman_sock = self.get_conf_value('podman_sock') conf_podman_sock = self.get_conf_value('podman_sock')
if len(conf_podman_sock) == 0: if len(conf_podman_sock) == 0:
return "unix:///run/user/1000/podman/podman.sock" return "unix:///run/user/1000/podman/podman.sock"
else:
return conf_podman_sock[0] return conf_podman_sock[0]
def exit(self): def exit(self):
@ -174,7 +172,7 @@ class PluginModel(GlancesPluginModel):
if self.podman_extension: if self.podman_extension:
self.podman_extension.stop() self.podman_extension.stop()
# Call the father class # Call the father class
super(PluginModel, self).exit() super().exit()
def get_key(self): def get_key(self):
"""Return the key of the list.""" """Return the key of the list."""
@ -189,7 +187,7 @@ class PluginModel(GlancesPluginModel):
try: try:
ret = deepcopy(self.stats) ret = deepcopy(self.stats)
except KeyError as e: except KeyError as e:
logger.debug("docker plugin - Docker export error {}".format(e)) logger.debug(f"docker plugin - Docker export error {e}")
ret = [] ret = []
# Remove fields uses to compute rate # Remove fields uses to compute rate
@ -209,7 +207,6 @@ class PluginModel(GlancesPluginModel):
all_tag = self.get_conf_value('all') all_tag = self.get_conf_value('all')
if len(all_tag) == 0: if len(all_tag) == 0:
return False return False
else:
return all_tag[0].lower() == 'true' return all_tag[0].lower() == 'true'
@GlancesPluginModel._check_decorator @GlancesPluginModel._check_decorator
@ -262,7 +259,7 @@ class PluginModel(GlancesPluginModel):
def update_views(self): def update_views(self):
"""Update stats views.""" """Update stats views."""
# Call the father's method # Call the father's method
super(PluginModel, self).update_views() super().update_views()
if not self.stats: if not self.stats:
return False return False
@ -302,7 +299,7 @@ class PluginModel(GlancesPluginModel):
show_pod_name = True show_pod_name = True
self.views['show_pod_name'] = show_pod_name self.views['show_pod_name'] = show_pod_name
show_engine_name = False 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 show_engine_name = True
self.views['show_engine_name'] = show_engine_name self.views['show_engine_name'] = show_engine_name
@ -321,9 +318,9 @@ class PluginModel(GlancesPluginModel):
# Title # Title
msg = '{}'.format('CONTAINERS') msg = '{}'.format('CONTAINERS')
ret.append(self.curse_add_line(msg, "TITLE")) 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)) 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_add_line(msg))
ret.append(self.curse_new_line()) ret.append(self.curse_new_line())
# Header # Header
@ -404,13 +401,13 @@ class PluginModel(GlancesPluginModel):
unit = 'B' unit = 'B'
try: try:
value = self.auto_unit(int(container['io_rx'])) + unit value = self.auto_unit(int(container['io_rx'])) + unit
msg = '{:>7}'.format(value) msg = f'{value:>7}'
except (KeyError, TypeError): except (KeyError, TypeError):
msg = '{:>7}'.format('_') msg = '{:>7}'.format('_')
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
try: try:
value = self.auto_unit(int(container['io_wx'])) + unit value = self.auto_unit(int(container['io_wx'])) + unit
msg = ' {:<7}'.format(value) msg = f' {value:<7}'
except (KeyError, TypeError): except (KeyError, TypeError):
msg = ' {:<7}'.format('_') msg = ' {:<7}'.format('_')
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
@ -425,13 +422,13 @@ class PluginModel(GlancesPluginModel):
unit = 'b' unit = 'b'
try: try:
value = self.auto_unit(int(container['network_rx'] * to_bit)) + unit value = self.auto_unit(int(container['network_rx'] * to_bit)) + unit
msg = '{:>7}'.format(value) msg = f'{value:>7}'
except (KeyError, TypeError): except (KeyError, TypeError):
msg = '{:>7}'.format('_') msg = '{:>7}'.format('_')
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
try: try:
value = self.auto_unit(int(container['network_tx'] * to_bit)) + unit value = self.auto_unit(int(container['network_tx'] * to_bit)) + unit
msg = ' {:<7}'.format(value) msg = f' {value:<7}'
except (KeyError, TypeError): except (KeyError, TypeError):
msg = ' {:<7}'.format('_') msg = ' {:<7}'.format('_')
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
@ -453,11 +450,10 @@ class PluginModel(GlancesPluginModel):
"""Analyse the container status.""" """Analyse the container status."""
if status == 'running': if status == 'running':
return 'OK' return 'OK'
elif status == 'exited': if status == 'exited':
return 'WARNING' return 'WARNING'
elif status == 'dead': if status == 'dead':
return 'CRITICAL' return 'CRITICAL'
else:
return 'CAREFUL' return 'CAREFUL'

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -8,6 +7,7 @@
# #
"""Docker Extension unit for Glances' Containers plugin.""" """Docker Extension unit for Glances' Containers plugin."""
import time import time
from glances.globals import iterkeys, itervalues, nativestr, pretty_date, replace_special_chars 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) # Docker-py library (optional and Linux-only)
# https://github.com/docker/docker-py # https://github.com/docker/docker-py
try: try:
import requests
import docker import docker
import requests
from dateutil import parser, tz from dateutil import parser, tz
except Exception as e: except Exception as e:
import_docker_error_tag = True import_docker_error_tag = True
# Display debug message if import KeyError # 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: else:
import_docker_error_tag = False import_docker_error_tag = False
@ -46,7 +46,7 @@ class DockerStatsFetcher:
self._streamer = StatsStreamer(stats_iterable, initial_stream_value={}) self._streamer = StatsStreamer(stats_iterable, initial_stream_value={})
def _log_debug(self, msg, exception=None): 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) logger.debug(self._streamer.stats)
def stop(self): def stop(self):
@ -70,13 +70,12 @@ class DockerStatsFetcher:
memory_stats = self._get_memory_stats() memory_stats = self._get_memory_stats()
network_stats = self._get_network_stats() network_stats = self._get_network_stats()
computed_stats = { return {
"io": io_stats or {}, "io": io_stats or {},
"memory": memory_stats or {}, "memory": memory_stats or {},
"network": network_stats or {}, "network": network_stats or {},
"cpu": cpu_stats or {"total": 0.0}, "cpu": cpu_stats or {"total": 0.0},
} }
return computed_stats
@property @property
def time_since_update(self): def time_since_update(self):
@ -229,7 +228,7 @@ class DockerContainersExtension:
# Do not use the timeout option (see issue #1878) # Do not use the timeout option (see issue #1878)
self.client = docker.from_env() self.client = docker.from_env()
except Exception as e: 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 self.client = None
def update_version(self): def update_version(self):
@ -256,7 +255,7 @@ class DockerContainersExtension:
# The Containers/all key of the configuration file should be set to True # The Containers/all key of the configuration file should be set to True
containers = self.client.containers.list(all=all_tag) containers = self.client.containers.list(all=all_tag)
except Exception as e: 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, [] return version_stats, []
# Start new thread for new container # Start new thread for new container
@ -264,14 +263,14 @@ class DockerContainersExtension:
if container.id not in self.stats_fetchers: if container.id not in self.stats_fetchers:
# StatsFetcher did not exist in the internal dict # StatsFetcher did not exist in the internal dict
# Create it, add it to 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) self.stats_fetchers[container.id] = DockerStatsFetcher(container)
# Stop threads for non-existing containers # 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: for container_id in absent_containers:
# Stop the StatsFetcher # 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() self.stats_fetchers[container_id].stop()
# Delete the StatsFetcher from the dict # Delete the StatsFetcher from the dict
del self.stats_fetchers[container_id] del self.stats_fetchers[container_id]

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -7,9 +6,10 @@
# SPDX-License-Identifier: LGPL-3.0-only # SPDX-License-Identifier: LGPL-3.0-only
"""Podman Extension unit for Glances' Containers plugin.""" """Podman Extension unit for Glances' Containers plugin."""
from datetime import datetime 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.logger import logger
from glances.plugins.containers.stats_streamer import StatsStreamer from glances.plugins.containers.stats_streamer import StatsStreamer
@ -20,7 +20,7 @@ try:
except Exception as e: except Exception as e:
import_podman_error_tag = True import_podman_error_tag = True
# Display debug message if import KeyError # 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: else:
import_podman_error_tag = False import_podman_error_tag = False
@ -36,7 +36,7 @@ class PodmanContainerStatsFetcher:
self._streamer = StatsStreamer(stats_iterable, initial_stream_value={}) self._streamer = StatsStreamer(stats_iterable, initial_stream_value={})
def _log_debug(self, msg, exception=None): 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) logger.debug(self._streamer.stats)
def stop(self): def stop(self):
@ -95,7 +95,7 @@ class PodmanPodStatsFetcher:
self._streamer = StatsStreamer(stats_iterable, initial_stream_value={}, sleep_duration=2) self._streamer = StatsStreamer(stats_iterable, initial_stream_value={}, sleep_duration=2)
def _log_debug(self, msg, exception=None): 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) logger.debug(self._streamer.stats)
def stop(self): def stop(self):
@ -234,7 +234,7 @@ class PodmanContainersExtension:
# PodmanClient works lazily, so make a ping to determine if socket is open # PodmanClient works lazily, so make a ping to determine if socket is open
self.client.ping() self.client.ping()
except Exception as e: 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 self.client = None
def update_version(self): def update_version(self):
@ -266,7 +266,7 @@ class PodmanContainersExtension:
if not self.pods_stats_fetcher: if not self.pods_stats_fetcher:
self.pods_stats_fetcher = PodmanPodStatsFetcher(self.client.pods) self.pods_stats_fetcher = PodmanPodStatsFetcher(self.client.pods)
except Exception as e: 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, [] return version_stats, []
# Start new thread for new container # Start new thread for new container
@ -274,14 +274,14 @@ class PodmanContainersExtension:
if container.id not in self.container_stats_fetchers: if container.id not in self.container_stats_fetchers:
# StatsFetcher did not exist in the internal dict # StatsFetcher did not exist in the internal dict
# Create it, add it to 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) self.container_stats_fetchers[container.id] = PodmanContainerStatsFetcher(container)
# Stop threads for non-existing containers # 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: for container_id in absent_containers:
# Stop the StatsFetcher # 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() self.container_stats_fetchers[container_id].stop()
# Delete the StatsFetcher from the dict # Delete the StatsFetcher from the dict
del self.container_stats_fetchers[container_id] del self.container_stats_fetchers[container_id]

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -63,7 +62,7 @@ class StatsStreamer:
break break
except Exception as e: 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() self.stop()
def _pre_update_hook(self): def _pre_update_hook(self):

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,10 +8,10 @@
"""CPU core plugin.""" """CPU core plugin."""
from glances.plugins.plugin.model import GlancesPluginModel
import psutil import psutil
from glances.plugins.plugin.model import GlancesPluginModel
# Fields description # Fields description
fields_description = { fields_description = {
'phys': {'description': 'Number of physical cores (hyper thread CPUs are excluded).', 'unit': 'number'}, '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): def __init__(self, args=None, config=None):
"""Init the plugin.""" """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 # We dot not want to display the stat in the curse interface
# The core number is displayed by the load plugin # The core number is displayed by the load plugin

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,13 +8,13 @@
"""CPU plugin.""" """CPU plugin."""
from glances.globals import LINUX, WINDOWS, SUNOS, iterkeys import psutil
from glances.cpu_percent import cpu_percent 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.core import PluginModel as CorePluginModel
from glances.plugins.plugin.model import GlancesPluginModel from glances.plugins.plugin.model import GlancesPluginModel
import psutil
# Fields description # Fields description
# description: human readable description # description: human readable description
# short_name: shortname to use un UI # short_name: shortname to use un UI
@ -144,7 +143,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the CPU plugin.""" """Init the CPU plugin."""
super(PluginModel, self).__init__( super().__init__(
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description 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): def update_views(self):
"""Update stats views.""" """Update stats views."""
# Call the father's method # Call the father's method
super(PluginModel, self).update_views() super().update_views()
# Add specifics information # Add specifics information
# Alert and log # Alert and log

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -8,14 +7,13 @@
# #
"""Disk I/O plugin.""" """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 import psutil
from glances.globals import nativestr
from glances.logger import logger
from glances.plugins.plugin.model import GlancesPluginModel
# Fields description # Fields description
# description: human readable description # description: human readable description
# short_name: shortname to use un UI # short_name: shortname to use un UI
@ -61,7 +59,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(
args=args, args=args,
config=config, config=config,
items_history_list=items_history_list, items_history_list=items_history_list,
@ -141,7 +139,7 @@ class PluginModel(GlancesPluginModel):
def update_views(self): def update_views(self):
"""Update stats views.""" """Update stats views."""
# Call the father's method # Call the father's method
super(PluginModel, self).update_views() super().update_views()
# Check if the stats should be hidden # Check if the stats should be hidden
self.update_views_hidden() self.update_views_hidden()
@ -171,7 +169,7 @@ class PluginModel(GlancesPluginModel):
name_max_width = max_width - 13 name_max_width = max_width - 13
else: else:
# No max_width defined, return an emptu curse message # 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 return ret
# Header # Header
@ -190,7 +188,7 @@ class PluginModel(GlancesPluginModel):
# Disk list (sorted by name) # Disk list (sorted by name)
for i in self.sorted_stats(): for i in self.sorted_stats():
# Hide stats if never be different from 0 (issue #1787) # 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 continue
# Is there an alias for the disk name ? # Is there an alias for the disk name ?
disk_name = i['alias'] if 'alias' in i else i['disk_name'] disk_name = i['alias'] if 'alias' in i else i['disk_name']
@ -205,13 +203,13 @@ class PluginModel(GlancesPluginModel):
# count # count
txps = self.auto_unit(i.get('read_count_rate_per_sec', None)) txps = self.auto_unit(i.get('read_count_rate_per_sec', None))
rxps = self.auto_unit(i.get('write_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( ret.append(
self.curse_add_line( self.curse_add_line(
msg, self.get_views(item=i[self.get_key()], key='read_count', option='decoration') msg, self.get_views(item=i[self.get_key()], key='read_count', option='decoration')
) )
) )
msg = '{:>7}'.format(rxps) msg = f'{rxps:>7}'
ret.append( ret.append(
self.curse_add_line( self.curse_add_line(
msg, self.get_views(item=i[self.get_key()], key='write_count', option='decoration') msg, self.get_views(item=i[self.get_key()], key='write_count', option='decoration')
@ -221,13 +219,13 @@ class PluginModel(GlancesPluginModel):
# Bitrate # Bitrate
txps = self.auto_unit(i.get('read_bytes_rate_per_sec', None)) txps = self.auto_unit(i.get('read_bytes_rate_per_sec', None))
rxps = self.auto_unit(i.get('write_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( ret.append(
self.curse_add_line( self.curse_add_line(
msg, self.get_views(item=i[self.get_key()], key='read_bytes', option='decoration') msg, self.get_views(item=i[self.get_key()], key='read_bytes', option='decoration')
) )
) )
msg = '{:>7}'.format(rxps) msg = f'{rxps:>7}'
ret.append( ret.append(
self.curse_add_line( self.curse_add_line(
msg, self.get_views(item=i[self.get_key()], key='write_bytes', option='decoration') msg, self.get_views(item=i[self.get_key()], key='write_bytes', option='decoration')

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -8,14 +7,12 @@
# #
"""Folder plugin.""" """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.folder_list import FolderList as glancesFolderList
from glances.globals import nativestr
from glances.logger import logger
from glances.plugins.plugin.model import GlancesPluginModel from glances.plugins.plugin.model import GlancesPluginModel
# Fields description # Fields description
# description: human readable description # description: human readable description
# short_name: shortname to use un UI # short_name: shortname to use un UI
@ -56,9 +53,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(args=args, config=config, stats_init_value=[], fields_description=fields_description)
args=args, config=config, stats_init_value=[], fields_description=fields_description
)
self.args = args self.args = args
self.config = config self.config = config
@ -138,7 +133,7 @@ class PluginModel(GlancesPluginModel):
name_max_width = max_width - 7 name_max_width = max_width - 7
else: else:
# No max_width defined, return an emptu curse message # 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 return ret
# Header # Header

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -8,16 +7,15 @@
# #
"""File system plugin.""" """File system plugin."""
from __future__ import unicode_literals
import operator 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.logger import logger
from glances.plugins.plugin.model import GlancesPluginModel from glances.plugins.plugin.model import GlancesPluginModel
import psutil
# Fields description # Fields description
# description: human readable description # description: human readable description
# short_name: shortname to use un UI # short_name: shortname to use un UI
@ -97,7 +95,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(
args=args, args=args,
config=config, config=config,
items_history_list=items_history_list, items_history_list=items_history_list,
@ -141,7 +139,7 @@ class PluginModel(GlancesPluginModel):
logger.debug("Plugin - fs: PsUtil extended fetch failed") logger.debug("Plugin - fs: PsUtil extended fetch failed")
else: else:
# Discard duplicates (#2299) and add entries matching allowed fs types # 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: for f in all_mounted_fs:
if ( if (
any(f.fstype.find(fs_type) >= 0 for fs_type in allowed_fs_types) 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, 'device_name': fs.device,
'fs_type': fs.fstype, 'fs_type': fs.fstype,
# Manage non breaking space (see issue #1065) # 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, 'size': fs_usage.total,
'used': fs_usage.used, 'used': fs_usage.used,
'free': fs_usage.free, 'free': fs_usage.free,
@ -215,7 +213,6 @@ class PluginModel(GlancesPluginModel):
# Do not take hidden file system into account # Do not take hidden file system into account
if self.is_hide(fs_current['mnt_point']): if self.is_hide(fs_current['mnt_point']):
continue continue
else:
stats.append(fs_current) stats.append(fs_current)
else: else:
# Default behavior # Default behavior
@ -231,7 +228,6 @@ class PluginModel(GlancesPluginModel):
# Do not take hidden file system into account # Do not take hidden file system into account
if self.is_hide(fs_current['mnt_point']) or self.is_hide(fs_current['device_name']): if self.is_hide(fs_current['mnt_point']) or self.is_hide(fs_current['device_name']):
continue continue
else:
stats.append(fs_current) stats.append(fs_current)
# Update the stats # Update the stats
@ -242,7 +238,7 @@ class PluginModel(GlancesPluginModel):
def update_views(self): def update_views(self):
"""Update stats views.""" """Update stats views."""
# Call the father's method # Call the father's method
super(PluginModel, self).update_views() super().update_views()
# Add specifics information # Add specifics information
# Alert # Alert
@ -265,7 +261,7 @@ class PluginModel(GlancesPluginModel):
name_max_width = max_width - 13 name_max_width = max_width - 13
else: else:
# No max_width defined, return an emptu curse message # 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 return ret
# Build the string message # Build the string message

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -16,8 +15,8 @@ Currently supported:
""" """
from glances.globals import to_fahrenheit 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.amd import AmdGPU
from glances.plugins.gpu.cards.nvidia import NvidiaGPU
from glances.plugins.plugin.model import GlancesPluginModel from glances.plugins.plugin.model import GlancesPluginModel
# Fields description # Fields description
@ -67,7 +66,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(
args=args, args=args,
config=config, config=config,
items_history_list=items_history_list, items_history_list=items_history_list,
@ -89,7 +88,7 @@ class PluginModel(GlancesPluginModel):
self.amd.exit() self.amd.exit()
# Call the father exit method # Call the father exit method
super(PluginModel, self).exit() super().exit()
def get_key(self): def get_key(self):
"""Return the key of the list.""" """Return the key of the list."""
@ -150,7 +149,7 @@ class PluginModel(GlancesPluginModel):
def update_views(self): def update_views(self):
"""Update stats views.""" """Update stats views."""
# Call the father's method # Call the father's method
super(PluginModel, self).update_views() super().update_views()
# Add specifics information # Add specifics information
# Alert # Alert
@ -190,7 +189,7 @@ class PluginModel(GlancesPluginModel):
# Header # Header
header = '' header = ''
if len(self.stats) > 1: if len(self.stats) > 1:
header += '{}'.format(len(self.stats)) header += f'{len(self.stats)}'
if same_name: if same_name:
header += ' {}'.format(gpu_stats['name']) header += ' {}'.format(gpu_stats['name'])
else: else:
@ -213,7 +212,7 @@ class PluginModel(GlancesPluginModel):
except TypeError: except TypeError:
mean_proc_msg = '{:>4}'.format('N/A') mean_proc_msg = '{:>4}'.format('N/A')
else: else:
mean_proc_msg = '{:>3.0f}%'.format(mean_proc) mean_proc_msg = f'{mean_proc:>3.0f}%'
if len(self.stats) > 1: if len(self.stats) > 1:
msg = '{:13}'.format('proc mean:') msg = '{:13}'.format('proc mean:')
else: else:
@ -232,7 +231,7 @@ class PluginModel(GlancesPluginModel):
except TypeError: except TypeError:
mean_mem_msg = '{:>4}'.format('N/A') mean_mem_msg = '{:>4}'.format('N/A')
else: else:
mean_mem_msg = '{:>3.0f}%'.format(mean_mem) mean_mem_msg = f'{mean_mem:>3.0f}%'
if len(self.stats) > 1: if len(self.stats) > 1:
msg = '{:13}'.format('mem mean:') msg = '{:13}'.format('mem mean:')
else: else:
@ -255,7 +254,7 @@ class PluginModel(GlancesPluginModel):
if args.fahrenheit: if args.fahrenheit:
mean_temperature = to_fahrenheit(mean_temperature) mean_temperature = to_fahrenheit(mean_temperature)
unit = 'F' 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: if len(self.stats) > 1:
msg = '{:13}'.format('temp mean:') msg = '{:13}'.format('temp mean:')
else: else:
@ -283,7 +282,7 @@ class PluginModel(GlancesPluginModel):
mem_msg = '{:>3.0f}%'.format(gpu_stats['mem']) mem_msg = '{:>3.0f}%'.format(gpu_stats['mem'])
except (ValueError, TypeError): except (ValueError, TypeError):
mem_msg = '{:>4}'.format('N/A') 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)) ret.append(self.curse_add_line(msg))
return ret return ret

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -35,8 +34,9 @@ See: https://wiki.archlinux.org/title/AMDGPU#Manually
# └── 0 # └── 0
# └── amdgpu_pm_info # └── amdgpu_pm_info
import re
import os import os
import re
from typing import Optional
DRM_ROOT_FOLDER: str = '/sys/class/drm' DRM_ROOT_FOLDER: str = '/sys/class/drm'
CARD_REGEX: str = r"^card\d$" CARD_REGEX: str = r"^card\d$"
@ -64,7 +64,7 @@ class AmdGPU:
stats = [] stats = []
for index, device in enumerate(self.device_folders): for index, device in enumerate(self.device_folders):
device_stats = dict() device_stats = {}
# Dictionary key is the GPU_ID # Dictionary key is the GPU_ID
device_stats['key'] = 'gpu_id' device_stats['key'] = 'gpu_id'
# GPU id (for multiple GPU, start at 0) # GPU id (for multiple GPU, start at 0)
@ -104,7 +104,7 @@ def get_device_name(device_folder: str) -> str:
return 'AMD GPU' return 'AMD GPU'
def get_mem(device_folder: str) -> int: def get_mem(device_folder: str) -> Optional[int]:
"""Return the memory consumption in %.""" """Return the memory consumption in %."""
mem_info_vram_total = os.path.join(device_folder, GPU_MEM_TOTAL) mem_info_vram_total = os.path.join(device_folder, GPU_MEM_TOTAL)
mem_info_vram_used = os.path.join(device_folder, GPU_MEM_USED) 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 return None
def get_proc(device_folder: str) -> int: def get_proc(device_folder: str) -> Optional[int]:
"""Return the processor consumption in %.""" """Return the processor consumption in %."""
gpu_busy_percent = os.path.join(device_folder, GPU_PROC_PERCENT) gpu_busy_percent = os.path.join(device_folder, GPU_PROC_PERCENT)
if os.path.isfile(gpu_busy_percent): if os.path.isfile(gpu_busy_percent):
@ -127,7 +127,7 @@ def get_proc(device_folder: str) -> int:
return None 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)""" """Return the processor temperature in °C (mean of all HWMON)"""
temp_input = [] temp_input = []
for root, dirs, _ in os.walk(device_folder): 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())) temp_input.append(int(f.read()))
if len(temp_input) > 0: if len(temp_input) > 0:
return round(sum(temp_input) / len(temp_input) / 1000) 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 the fan speed in %."""
return None return None

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,15 +8,15 @@
"""NVidia Extension unit for Glances' GPU plugin.""" """NVidia Extension unit for Glances' GPU plugin."""
from glances.logger import logger
from glances.globals import nativestr from glances.globals import nativestr
from glances.logger import logger
try: try:
import pynvml import pynvml
except Exception as e: except Exception as e:
nvidia_gpu_enable = False nvidia_gpu_enable = False
# Display debug message if import KeyError # 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: else:
nvidia_gpu_enable = True nvidia_gpu_enable = True
@ -43,14 +42,14 @@ class NvidiaGPU:
try: try:
pynvml.nvmlShutdown() pynvml.nvmlShutdown()
except Exception as e: 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): def get_device_stats(self):
"""Get Nvidia GPU stats.""" """Get Nvidia GPU stats."""
stats = [] stats = []
for index, device_handle in enumerate(self.device_handles): for index, device_handle in enumerate(self.device_handles):
device_stats = dict() device_stats = {}
# Dictionary key is the GPU_ID # Dictionary key is the GPU_ID
device_stats['key'] = 'gpu_id' device_stats['key'] = 'gpu_id'
# GPU id (for multiple GPU, start at 0) # GPU id (for multiple GPU, start at 0)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -12,19 +11,20 @@ Help plugin.
Just a stupid plugin to display the help screen. 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 itertools import chain
from glances import __version__, psutil_version
from glances.globals import iteritems
from glances.plugins.plugin.model import GlancesPluginModel
class PluginModel(GlancesPluginModel): class PluginModel(GlancesPluginModel):
"""Glances help plugin.""" """Glances help plugin."""
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__(args=args, config=config) super().__init__(args=args, config=config)
# Set the config instance # Set the config instance
self.config = config self.config = config
@ -34,11 +34,6 @@ class PluginModel(GlancesPluginModel):
self.display_curse = True self.display_curse = True
# init data dictionary, to preserve insertion order # 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() self.generate_view_data()
@ -51,10 +46,10 @@ class PluginModel(GlancesPluginModel):
def generate_view_data(self): def generate_view_data(self):
"""Generate the views.""" """Generate the views."""
self.view_data['version'] = '{} {}'.format('Glances', __version__) 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: 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: except AttributeError:
pass pass

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -10,20 +9,20 @@
"""IP plugin.""" """IP plugin."""
import threading import threading
from ujson import loads from ujson import loads
from glances.globals import queue, urlopen_auth from glances.globals import queue, urlopen_auth
from glances.logger import logger from glances.logger import logger
from glances.timer import Timer
from glances.timer import getTimeSinceLastUpdate
from glances.plugins.plugin.model import GlancesPluginModel from glances.plugins.plugin.model import GlancesPluginModel
from glances.timer import Timer, getTimeSinceLastUpdate
# Import plugin specific dependency # Import plugin specific dependency
try: try:
import netifaces import netifaces
except ImportError as e: except ImportError as e:
import_error_tag = True 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: else:
import_error_tag = False import_error_tag = False
@ -66,7 +65,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """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 # We want to display the stat in the curse interface
self.display_curse = True self.display_curse = True
@ -104,7 +103,7 @@ class PluginModel(GlancesPluginModel):
try: try:
default_gw = netifaces.gateways()['default'][netifaces.AF_INET] default_gw = netifaces.gateways()['default'][netifaces.AF_INET]
except (KeyError, AttributeError) as e: 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() return self.get_init_value()
else: else:
stats['gateway'] = default_gw[0] stats['gateway'] = default_gw[0]
@ -113,7 +112,7 @@ class PluginModel(GlancesPluginModel):
address = netifaces.ifaddresses(default_gw[1])[netifaces.AF_INET][0]['addr'] address = netifaces.ifaddresses(default_gw[1])[netifaces.AF_INET][0]['addr']
mask = netifaces.ifaddresses(default_gw[1])[netifaces.AF_INET][0]['netmask'] mask = netifaces.ifaddresses(default_gw[1])[netifaces.AF_INET][0]['netmask']
except (KeyError, AttributeError) as e: 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() return self.get_init_value()
else: else:
stats['address'] = address 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_info = PublicIpInfo(self.public_api, self.public_username, self.public_password).get()
self.public_address = self.public_info['ip'] self.public_address = self.public_info['ip']
except (KeyError, AttributeError, TypeError) as e: 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: else:
stats['public_address'] = ( stats['public_address'] = (
self.public_address if not self.args.hide_public_info else self.__hide_ip(self.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('.')) return sum(bin(int(x)).count('1') for x in ip.split('.'))
class PublicIpInfo(object): class PublicIpInfo:
"""Get public IP information from online service.""" """Get public IP information from online service."""
def __init__(self, url, username, password, timeout=2): def __init__(self, url, username, password, timeout=2):
@ -242,11 +241,11 @@ class PublicIpInfo(object):
try: try:
response = urlopen_auth(url, username, password).read() response = urlopen_auth(url, username, password).read()
except Exception as e: 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) queue_target.put(None)
else: else:
try: try:
queue_target.put(loads(response)) queue_target.put(loads(response))
except (ValueError, KeyError) as e: 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) queue_target.put(None)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,14 +8,13 @@
"""IRQ plugin.""" """IRQ plugin."""
import os
import operator import operator
import os
from glances.logger import logger
from glances.globals import LINUX from glances.globals import LINUX
from glances.timer import getTimeSinceLastUpdate from glances.logger import logger
from glances.plugins.plugin.model import GlancesPluginModel from glances.plugins.plugin.model import GlancesPluginModel
from glances.timer import getTimeSinceLastUpdate
# Fields description # Fields description
# description: human readable description # description: human readable description
@ -43,9 +41,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(args=args, config=config, stats_init_value=[], fields_description=fields_description)
args=args, config=config, stats_init_value=[], fields_description=fields_description
)
# We want to display the stat in the curse interface # We want to display the stat in the curse interface
self.display_curse = True self.display_curse = True
@ -87,7 +83,7 @@ class PluginModel(GlancesPluginModel):
def update_views(self): def update_views(self):
"""Update stats views.""" """Update stats views."""
# Call the father's method # Call the father's method
super(PluginModel, self).update_views() super().update_views()
def msg_curse(self, args=None, max_width=None): def msg_curse(self, args=None, max_width=None):
"""Return the dict to display in the curse interface.""" """Return the dict to display in the curse interface."""
@ -104,7 +100,7 @@ class PluginModel(GlancesPluginModel):
name_max_width = max_width - 7 name_max_width = max_width - 7
else: else:
# No max_width defined, return an emptu curse message # 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 return ret
# Build the string message # Build the string message
@ -124,7 +120,7 @@ class PluginModel(GlancesPluginModel):
return ret return ret
class GlancesIRQ(object): class GlancesIRQ:
"""This class manages the IRQ file.""" """This class manages the IRQ file."""
IRQ_FILE = '/proc/interrupts' IRQ_FILE = '/proc/interrupts'
@ -170,7 +166,7 @@ class GlancesIRQ(object):
irq_line = splitted_line[0].replace(':', '') irq_line = splitted_line[0].replace(':', '')
if irq_line.isdigit(): if irq_line.isdigit():
# If the first column is a digit, use the alias (last column) # 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 return irq_line
def __sum(self, line): def __sum(self, line):
@ -217,7 +213,7 @@ class GlancesIRQ(object):
} }
self.stats.append(irq_current) self.stats.append(irq_current)
self.lasts[irq_line] = current_irqs self.lasts[irq_line] = current_irqs
except (OSError, IOError): except OSError:
pass pass
return self.stats return self.stats

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -10,12 +9,13 @@
"""Load plugin.""" """Load plugin."""
import os import os
import psutil import psutil
from glances.globals import iteritems from glances.globals import iteritems
from glances.logger import logger
from glances.plugins.core import PluginModel as CorePluginModel from glances.plugins.core import PluginModel as CorePluginModel
from glances.plugins.plugin.model import GlancesPluginModel from glances.plugins.plugin.model import GlancesPluginModel
from glances.logger import logger
# Fields description # Fields description
fields_description = { fields_description = {
@ -65,7 +65,7 @@ nb_phys_core = 1
try: try:
core = CorePluginModel().update() core = CorePluginModel().update()
except Exception as e: 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: else:
if 'log' in core: if 'log' in core:
nb_log_core = core['log'] nb_log_core = core['log']
@ -81,7 +81,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description 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) stats = self.get_stats_snmp(snmp_oid=snmp_oid)
if stats['min1'] == '': if stats['min1'] == '':
stats = self.get_init_value() return self.get_init_value()
return stats
# Python 3 return a dict like: # Python 3 return a dict like:
# {'min1': "b'0.08'", 'min5': "b'0.12'", 'min15': "b'0.15'"} # {'min1': "b'0.08'", 'min5': "b'0.12'", 'min15': "b'0.15'"}
@ -128,7 +127,7 @@ class PluginModel(GlancesPluginModel):
def update_views(self): def update_views(self):
"""Update stats views.""" """Update stats views."""
# Call the father's method # Call the father's method
super(PluginModel, self).update_views() super().update_views()
# Add specifics information # Add specifics information
try: try:
@ -164,17 +163,17 @@ class PluginModel(GlancesPluginModel):
# Loop over 1min, 5min and 15min load # Loop over 1min, 5min and 15min load
for load_time in ['1', '5', '15']: for load_time in ['1', '5', '15']:
ret.append(self.curse_new_line()) 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)) ret.append(self.curse_add_line(msg))
if args.disable_irix and get_nb_log_core() != 0: if args.disable_irix and get_nb_log_core() != 0:
# Enable Irix mode for load (see issue #1554) # Enable Irix mode for load (see issue #1554)
load_stat = self.stats['min{}'.format(load_time)] / get_nb_log_core() * 100 load_stat = self.stats[f'min{load_time}'] / get_nb_log_core() * 100
msg = '{:>5.1f}%'.format(load_stat) msg = f'{load_stat:>5.1f}%'
else: else:
# Default mode for load # Default mode for load
load_stat = self.stats['min{}'.format(load_time)] load_stat = self.stats[f'min{load_time}']
msg = '{:>6.2f}'.format(load_stat) msg = f'{load_stat:>6.2f}'
ret.append(self.curse_add_line(msg, self.get_views(key='min{}'.format(load_time), option='decoration'))) ret.append(self.curse_add_line(msg, self.get_views(key=f'min{load_time}', option='decoration')))
return ret return ret
@ -205,5 +204,4 @@ def get_load_average(percent: bool = False):
if load_average and percent: if load_average and percent:
return tuple([round(i / get_nb_log_core() * 100, 1) for i in load_average]) return tuple([round(i / get_nb_log_core() * 100, 1) for i in load_average])
else:
return load_average return load_average

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,10 +8,10 @@
"""Virtual memory plugin.""" """Virtual memory plugin."""
from glances.plugins.plugin.model import GlancesPluginModel
import psutil import psutil
from glances.plugins.plugin.model import GlancesPluginModel
# Fields description # Fields description
fields_description = { fields_description = {
'total': {'description': 'Total physical memory available.', 'unit': 'bytes', 'min_symbol': 'K'}, '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): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description 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): def update_views(self):
"""Update stats views.""" """Update stats views."""
# Call the father's method # Call the father's method
super(PluginModel, self).update_views() super().update_views()
# Add specifics information # Add specifics information
# Alert and log # Alert and log

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,12 +8,12 @@
"""Swap memory plugin.""" """Swap memory plugin."""
from glances.globals import iterkeys
from glances.timer import getTimeSinceLastUpdate
from glances.plugins.plugin.model import GlancesPluginModel
import psutil import psutil
from glances.globals import iterkeys
from glances.plugins.plugin.model import GlancesPluginModel
from glances.timer import getTimeSinceLastUpdate
# Fields description # Fields description
fields_description = { fields_description = {
'total': {'description': 'Total swap memory.', 'unit': 'bytes', 'min_symbol': 'K'}, 'total': {'description': 'Total swap memory.', 'unit': 'bytes', 'min_symbol': 'K'},
@ -60,7 +59,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description 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): def update_views(self):
"""Update stats views.""" """Update stats views."""
# Call the father's method # Call the father's method
super(PluginModel, self).update_views() super().update_views()
# Add specifics information # Add specifics information
# Alert and log # Alert and log

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -8,13 +7,12 @@
# #
"""Network plugin.""" """Network plugin."""
from __future__ import unicode_literals
from glances.plugins.plugin.model import GlancesPluginModel
from glances.logger import logger
import psutil import psutil
from glances.logger import logger
from glances.plugins.plugin.model import GlancesPluginModel
# Fields description # Fields description
# description: human readable description # description: human readable description
# short_name: shortname to use un UI # short_name: shortname to use un UI
@ -72,7 +70,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(
args=args, args=args,
config=config, config=config,
items_history_list=items_history_list, items_history_list=items_history_list,
@ -130,7 +128,7 @@ class PluginModel(GlancesPluginModel):
try: try:
net_io_counters = psutil.net_io_counters(pernic=True) net_io_counters = psutil.net_io_counters(pernic=True)
except UnicodeDecodeError as e: 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 return self.stats
# Grab interface's status (issue #765) # Grab interface's status (issue #765)
@ -144,7 +142,7 @@ class PluginModel(GlancesPluginModel):
net_status = psutil.net_if_stats() net_status = psutil.net_if_stats()
except OSError as e: except OSError as e:
# see psutil #797/glances #1106 # 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(): for interface_name, interface_stat in net_io_counters.items():
# Do not take hidden interface into account # Do not take hidden interface into account
@ -180,7 +178,7 @@ class PluginModel(GlancesPluginModel):
def update_views(self): def update_views(self):
"""Update stats views.""" """Update stats views."""
# Call the father's method # Call the father's method
super(PluginModel, self).update_views() super().update_views()
# Check if the stats should be hidden # Check if the stats should be hidden
self.update_views_hidden() self.update_views_hidden()
@ -226,7 +224,7 @@ class PluginModel(GlancesPluginModel):
name_max_width = max_width - 12 name_max_width = max_width - 12
else: else:
# No max_width defined, return an emptu curse message # 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 return ret
# Header # Header
@ -261,7 +259,7 @@ class PluginModel(GlancesPluginModel):
if ('is_up' in i) and (i['is_up'] is False): if ('is_up' in i) and (i['is_up'] is False):
continue continue
# Hide stats if never be different from 0 (issue #1787) # 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 continue
# Format stats # Format stats
# Is there an alias for the interface name ? # Is there an alias for the interface name ?
@ -300,16 +298,16 @@ class PluginModel(GlancesPluginModel):
msg = '{:{width}}'.format(if_name, width=name_max_width) msg = '{:{width}}'.format(if_name, width=name_max_width)
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
if args.network_sum: if args.network_sum:
msg = '{:>14}'.format(ax) msg = f'{ax:>14}'
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
else: else:
msg = '{:>7}'.format(rx) msg = f'{rx:>7}'
ret.append( ret.append(
self.curse_add_line( self.curse_add_line(
msg, self.get_views(item=i[self.get_key()], key='bytes_recv', option='decoration') msg, self.get_views(item=i[self.get_key()], key='bytes_recv', option='decoration')
) )
) )
msg = '{:>7}'.format(tx) msg = f'{tx:>7}'
ret.append( ret.append(
self.curse_add_line( self.curse_add_line(
msg, self.get_views(item=i[self.get_key()], key='bytes_sent', option='decoration') msg, self.get_views(item=i[self.get_key()], key='bytes_sent', option='decoration')

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,8 +8,9 @@
"""Now (current date) plugin.""" """Now (current date) plugin."""
from time import tzname, strftime
import datetime import datetime
from time import strftime, tzname
from glances.plugins.plugin.model import GlancesPluginModel from glances.plugins.plugin.model import GlancesPluginModel
# Fields description # Fields description
@ -37,9 +37,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(args=args, config=config, fields_description=fields_description, stats_init_value={})
args=args, config=config, fields_description=fields_description, stats_init_value={}
)
# We want to display the stat in the curse interface # We want to display the stat in the curse interface
self.display_curse = True self.display_curse = True

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -96,7 +95,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(
args=args, args=args,
config=config, config=config,
items_history_list=items_history_list, items_history_list=items_history_list,
@ -159,7 +158,7 @@ class PluginModel(GlancesPluginModel):
for stat in header: for stat in header:
if stat not in self.stats[0]: if stat not in self.stats[0]:
continue continue
msg = '{:>7}'.format(stat) msg = f'{stat:>7}'
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
# Manage the maximum number of CPU to display (related to enhancement request #2734) # Manage the maximum number of CPU to display (related to enhancement request #2734)
@ -176,9 +175,9 @@ class PluginModel(GlancesPluginModel):
try: try:
cpu_id = cpu[cpu['key']] cpu_id = cpu[cpu['key']]
if cpu_id < 10: if cpu_id < 10:
msg = 'CPU{:1} '.format(cpu_id) msg = f'CPU{cpu_id:1} '
else: else:
msg = '{:4} '.format(cpu_id) msg = f'{cpu_id:4} '
except TypeError: except TypeError:
# TypeError: string indices must be integers (issue #1027) # TypeError: string indices must be integers (issue #1027)
msg = '{:4} '.format('?') msg = '{:4} '.format('?')
@ -187,7 +186,7 @@ class PluginModel(GlancesPluginModel):
if stat not in self.stats[0]: if stat not in self.stats[0]:
continue continue
try: try:
msg = '{:6.1f}%'.format(cpu[stat]) msg = f'{cpu[stat]:6.1f}%'
except TypeError: except TypeError:
msg = '{:>6}%'.format('?') msg = '{:>6}%'.format('?')
ret.append(self.curse_add_line(msg, self.get_alert(cpu[stat], header=stat))) ret.append(self.curse_add_line(msg, self.get_alert(cpu[stat], header=stat)))
@ -204,7 +203,7 @@ class PluginModel(GlancesPluginModel):
[i[stat] for i in percpu_list[0 : self.max_cpu_display]] [i[stat] for i in percpu_list[0 : self.max_cpu_display]]
) )
try: try:
msg = '{:6.1f}%'.format(cpu_stat) msg = f'{cpu_stat:6.1f}%'
except TypeError: except TypeError:
msg = '{:>6}%'.format('?') msg = '{:>6}%'.format('?')
ret.append(self.curse_add_line(msg, self.get_alert(cpu_stat, header=stat))) ret.append(self.curse_add_line(msg, self.get_alert(cpu_stat, header=stat)))

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -13,18 +12,17 @@ I am your father...
...of all Glances model plugins. ...of all Glances model plugins.
""" """
import re
import copy 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.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.history import GlancesHistory
from glances.logger import logger 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.thresholds import glances_thresholds
from glances.timer import Counter, Timer, getTimeSinceLastUpdate from glances.timer import Counter, Timer, getTimeSinceLastUpdate
from glances.outputs.glances_unicode import unicode_message
fields_unit_short = {'percent': '%'} fields_unit_short = {'percent': '%'}
@ -44,7 +42,7 @@ fields_unit_type = {
} }
class GlancesPluginModel(object): class GlancesPluginModel:
"""Main class for Glances plugin model.""" """Main class for Glances plugin model."""
def __init__(self, args=None, config=None, items_history_list=None, stats_init_value={}, fields_description=None): def __init__(self, args=None, config=None, items_history_list=None, stats_init_value={}, fields_description=None):
@ -78,7 +76,7 @@ class GlancesPluginModel(object):
if self.plugin_name.startswith('glances_'): if self.plugin_name.startswith('glances_'):
self.plugin_name = self.plugin_name.split('glances_')[1] 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 # Init the args
self.args = args self.args = args
@ -95,9 +93,9 @@ class GlancesPluginModel(object):
self.stats_history = self.init_stats_history() self.stats_history = self.init_stats_history()
# Init the limits (configuration keys) dictionary # Init the limits (configuration keys) dictionary
self._limits = dict() self._limits = {}
if config is not None: 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) self.load_limits(config=config)
# Init the alias (dictionnary) # Init the alias (dictionnary)
@ -107,7 +105,7 @@ class GlancesPluginModel(object):
self.actions = GlancesActions(args=args) self.actions = GlancesActions(args=args)
# Init the views # Init the views
self.views = dict() self.views = {}
# Hide stats if all the hide_zero_fields has never been != 0 # Hide stats if all the hide_zero_fields has never been != 0
# Default is False, always display stats # Default is False, always display stats
@ -148,11 +146,11 @@ class GlancesPluginModel(object):
def exit(self): def exit(self):
"""Just log an event when Glances exit.""" """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): def get_key(self):
"""Return the key of the list.""" """Return the key of the list."""
return None return
def is_enabled(self, plugin_name=None): def is_enabled(self, plugin_name=None):
"""Return true if plugin is enabled.""" """Return true if plugin is enabled."""
@ -175,14 +173,14 @@ class GlancesPluginModel(object):
"""Init the stats history (dict of GlancesAttribute).""" """Init the stats history (dict of GlancesAttribute)."""
if self.history_enable(): if self.history_enable():
init_list = [a['name'] for a in self.get_items_history_list()] 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() return GlancesHistory()
def reset_stats_history(self): def reset_stats_history(self):
"""Reset the stats history (dict of GlancesAttribute).""" """Reset the stats history (dict of GlancesAttribute)."""
if self.history_enable(): if self.history_enable():
reset_list = [a['name'] for a in self.get_items_history_list()] 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() self.stats_history.reset()
def update_stats_history(self): def update_stats_history(self):
@ -228,10 +226,8 @@ class GlancesPluginModel(object):
s = self.stats_history.get(nb=nb) s = self.stats_history.get(nb=nb)
if item is None: if item is None:
return s return s
else:
if item in s: if item in s:
return s[item] return s[item]
else:
return None return None
def get_export_history(self, item=None): def get_export_history(self, item=None):
@ -288,10 +284,8 @@ class GlancesPluginModel(object):
return sorted( return sorted(
self.stats, self.stats,
key=lambda stat: tuple( key=lambda stat: tuple(
map( int(part) if part.isdigit() else part.lower()
lambda part: int(part) if part.isdigit() else part.lower(), for part in re.split(r"(\d+|\D+)", self.has_alias(stat[key]) or stat[key])
re.split(r"(\d+|\D+)", self.has_alias(stat[key]) or stat[key]),
)
), ),
) )
except TypeError: except TypeError:
@ -299,7 +293,7 @@ class GlancesPluginModel(object):
return sorted( return sorted(
self.stats, self.stats,
key=lambda stat: tuple( 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,13 +405,13 @@ class GlancesPluginModel(object):
""" """
if not isinstance(self.get_raw(), list): if not isinstance(self.get_raw(), list):
return None return None
else:
if (not isinstance(value, int) and not isinstance(value, float)) and value.isdigit(): if (not isinstance(value, int) and not isinstance(value, float)) and value.isdigit():
value = int(value) value = int(value)
try: try:
return {value: [i for i in self.get_raw() if i[item] == value]} return {value: [i for i in self.get_raw() if i[item] == value]}
except (KeyError, ValueError) as e: except (KeyError, ValueError) as e:
logger.error("Cannot get item({})=value({}) ({})".format(item, value, e)) logger.error(f"Cannot get item({item})=value({value}) ({e})")
return None return None
def get_stats_value(self, item, value): def get_stats_value(self, item, value):
@ -428,14 +422,12 @@ class GlancesPluginModel(object):
rsv = self.get_raw_stats_value(item, value) rsv = self.get_raw_stats_value(item, value)
if rsv is None: if rsv is None:
return None return None
else:
return json_dumps(rsv) return json_dumps(rsv)
def get_item_info(self, item, key, default=None): def get_item_info(self, item, key, default=None):
"""Return the item info grabbed into self.fields_description.""" """Return the item info grabbed into self.fields_description."""
if self.fields_description is None or item not in self.fields_description: if self.fields_description is None or item not in self.fields_description:
return default return default
else:
return self.fields_description[item].get(key, default) return self.fields_description[item].get(key, default)
def update_views_hidden(self): def update_views_hidden(self):
@ -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: 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...) # Stats are stored in a list of dict (ex: NETWORK, FS...)
for i in self.get_raw(): 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: for f in self.hide_zero_fields:
self.views[i[self.get_key()]][f]['_zero'] = self.views[i[self.get_key()]][f]['hidden'] self.views[i[self.get_key()]][f]['_zero'] = self.views[i[self.get_key()]][f]['hidden']
for f in self.hide_zero_fields: for f in self.hide_zero_fields:
@ -471,7 +463,7 @@ class GlancesPluginModel(object):
# #
# Stats are stored in a dict (ex: CPU, LOAD...) # Stats are stored in a dict (ex: CPU, LOAD...)
for key in listkeys(self.get_raw()): 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: for f in self.hide_zero_fields:
self.views[f]['_zero'] = self.views[f]['hidden'] self.views[f]['_zero'] = self.views[f]['hidden']
for f in self.hide_zero_fields: for f in self.hide_zero_fields:
@ -536,7 +528,7 @@ class GlancesPluginModel(object):
def reset_views(self): def reset_views(self):
"""Reset the views to input_views.""" """Reset the views to input_views."""
self.views = dict() self.views = {}
def get_views(self, item=None, key=None, option=None): def get_views(self, item=None, key=None, option=None):
"""Return the views object. """Return the views object.
@ -554,13 +546,10 @@ class GlancesPluginModel(object):
if key is None: if key is None:
return item_views return item_views
else:
if option is None: if option is None:
return item_views[key] return item_views[key]
else:
if option in item_views[key]: if option in item_views[key]:
return item_views[key][option] return item_views[key][option]
else:
return 'DEFAULT' return 'DEFAULT'
def get_json_views(self, item=None, key=None, option=None): def get_json_views(self, item=None, key=None, option=None):
@ -590,7 +579,7 @@ class GlancesPluginModel(object):
self._limits[limit] = config.get_float_value(self.plugin_name, level) self._limits[limit] = config.get_float_value(self.plugin_name, level)
except ValueError: except ValueError:
self._limits[limit] = config.get_value(self.plugin_name, level).split(",") 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 return True
@ -621,14 +610,13 @@ class GlancesPluginModel(object):
def set_limits(self, item, value): def set_limits(self, item, value):
"""Set the limits object.""" """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): def get_limits(self, item=None):
"""Return the limits object.""" """Return the limits object."""
if item is None: if item is None:
return self._limits return self._limits
else: return self._limits.get(f'{self.plugin_name}_{item}', None)
return self._limits.get('{}_{}'.format(self.plugin_name, item), None)
def get_stats_action(self): def get_stats_action(self):
"""Return stats for the action. """Return stats for the action.
@ -743,11 +731,10 @@ class GlancesPluginModel(object):
"""Filter the stats to keep only the fields we want (the one defined in fields_description).""" """Filter the stats to keep only the fields we want (the one defined in fields_description)."""
if hasattr(stats, '_asdict'): if hasattr(stats, '_asdict'):
return {k: v for k, v in stats._asdict().items() if k in self.fields_description} 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} 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] return [self.filter_stats(s) for s in stats]
else:
return stats return stats
def manage_threshold(self, stat_name, trigger): def manage_threshold(self, stat_name, trigger):
@ -794,7 +781,6 @@ class GlancesPluginModel(object):
"""Return true if the criticality limit exist for the given stat_name""" """Return true if the criticality limit exist for the given stat_name"""
if stat_name == "": if stat_name == "":
return self.plugin_name + '_' + criticality in self._limits 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=""): def get_limit(self, criticality=None, stat_name=""):
@ -807,7 +793,7 @@ class GlancesPluginModel(object):
# Example: network_wlan0_rx_careful # Example: network_wlan0_rx_careful
if stat_name + '_' + criticality in self._limits: if stat_name + '_' + criticality in self._limits:
return self._limits[stat_name + '_' + criticality] 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] return self._limits[self.plugin_name + '_' + criticality]
# No key found, the raise an error # No key found, the raise an error
@ -841,9 +827,8 @@ class GlancesPluginModel(object):
# Example: network_wlan0_rx_log # Example: network_wlan0_rx_log
if stat_name + '_log' in self._limits: if stat_name + '_log' in self._limits:
return self._limits[stat_name + '_log'][0].lower() == 'true' 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' 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=[]): def get_conf_value(self, value, header="", plugin_name=None, default=[]):
@ -896,14 +881,12 @@ class GlancesPluginModel(object):
"""Return True if the value should be displayed in the UI""" """Return True if the value should be displayed in the UI"""
if self.get_conf_value('show', header=header) != []: if self.get_conf_value('show', header=header) != []:
return self.is_show(value, 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): def read_alias(self):
if self.plugin_name + '_' + 'alias' in self._limits: 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']} return {i.split(':')[0].lower(): i.split(':')[1] for i in self._limits[self.plugin_name + '_' + 'alias']}
else: return {}
return dict()
def has_alias(self, header): def has_alias(self, header):
"""Return the alias name for the relative header it it exists otherwise None.""" """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) value = self.stats.get(key, None)
if width is 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_float = '{:.1f}{}'
msg_template = '{}{}' msg_template = '{}{}'
else: else:
@ -1138,7 +1121,7 @@ class GlancesPluginModel(object):
elif symbol in 'K': elif symbol in 'K':
decimal_precision = 0 decimal_precision = 0
return '{:.{decimal}f}{symbol}'.format(value, decimal=decimal_precision, symbol=symbol) return '{:.{decimal}f}{symbol}'.format(value, decimal=decimal_precision, symbol=symbol)
return '{!s}'.format(number) return f'{number!s}'
def trend_msg(self, trend, significant=1): def trend_msg(self, trend, significant=1):
"""Return the trend message. """Return the trend message.
@ -1184,11 +1167,9 @@ class GlancesPluginModel(object):
counter = Counter() counter = Counter()
ret = fct(*args, **kw) ret = fct(*args, **kw)
duration = counter.get() duration = counter.get()
logger.debug( class_name = args[0].__class__.__name__
"{} {} {} return {} in {} seconds".format( class_module = args[0].__class__.__module__
args[0].__class__.__name__, args[0].__class__.__module__, fct.__name__, ret, duration logger.debug(f"{class_name} {class_module} {fct.__name__} return {ret} in {duration} seconds")
)
)
return ret return ret
return wrapper return wrapper

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,19 +8,19 @@
"""Ports scanner plugin.""" """Ports scanner plugin."""
import numbers
import os import os
import socket
import subprocess import subprocess
import threading import threading
import socket
import time import time
import numbers
from glances.globals import WINDOWS, MACOS, BSD, bool_type from glances.globals import BSD, MACOS, WINDOWS, bool_type
from glances.ports_list import GlancesPortsList
from glances.web_list import GlancesWebList
from glances.timer import Counter
from glances.logger import logger from glances.logger import logger
from glances.plugins.plugin.model import GlancesPluginModel 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: try:
import requests import requests
@ -29,7 +28,7 @@ try:
requests_tag = True requests_tag = True
except ImportError as e: except ImportError as e:
requests_tag = False 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 # Fields description
# description: human readable description # description: human readable description
@ -72,9 +71,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(args=args, config=config, stats_init_value=[], fields_description=fields_description)
args=args, config=config, stats_init_value=[], fields_description=fields_description
)
self.args = args self.args = args
self.config = config self.config = config
@ -95,7 +92,7 @@ class PluginModel(GlancesPluginModel):
if self._thread is not None: if self._thread is not None:
self._thread.stop() self._thread.stop()
# Call the father class # Call the father class
super(PluginModel, self).exit() super().exit()
def get_key(self): def get_key(self):
"""Return the key of the list.""" """Return the key of the list."""
@ -183,7 +180,7 @@ class PluginModel(GlancesPluginModel):
name_max_width = max_width - 7 name_max_width = max_width - 7
else: else:
# No max_width defined, return an emptu curse message # 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 return ret
# Build the string message # Build the string message
@ -199,11 +196,11 @@ class PluginModel(GlancesPluginModel):
status = 'Timeout' status = 'Timeout'
else: else:
# Convert second to ms # 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) msg = '{:{width}}'.format(p['description'][0:name_max_width], width=name_max_width)
ret.append(self.curse_add_line(msg)) 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_add_line(msg, self.get_ports_alert(p, header=p['indice'] + '_rtt')))
ret.append(self.curse_new_line()) ret.append(self.curse_new_line())
elif 'url' in p: elif 'url' in p:
@ -215,7 +212,7 @@ class PluginModel(GlancesPluginModel):
status = 'Scanning' status = 'Scanning'
else: else:
status = p['status'] 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_add_line(msg, self.get_web_alert(p, header=p['indice'] + '_rtt')))
ret.append(self.curse_new_line()) ret.append(self.curse_new_line())
@ -237,8 +234,8 @@ class ThreadScanner(threading.Thread):
def __init__(self, stats): def __init__(self, stats):
"""Init the class.""" """Init the class."""
logger.debug("ports plugin - Create thread for scan list {}".format(stats)) logger.debug(f"ports plugin - Create thread for scan list {stats}")
super(ThreadScanner, self).__init__() super().__init__()
# Event needed to stop properly the thread # Event needed to stop properly the thread
self._stopper = threading.Event() self._stopper = threading.Event()
# The class return the stats as a list of dict # The class return the stats as a list of dict
@ -283,7 +280,7 @@ class ThreadScanner(threading.Thread):
def stop(self, timeout=None): def stop(self, timeout=None):
"""Stop the thread.""" """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() self._stopper.set()
def stopped(self): def stopped(self):
@ -313,7 +310,6 @@ class ThreadScanner(threading.Thread):
"""Scan the port structure (dict) and update the status key.""" """Scan the port structure (dict) and update the status key."""
if int(port['port']) == 0: if int(port['port']) == 0:
return self._port_scan_icmp(port) return self._port_scan_icmp(port)
else:
return self._port_scan_tcp(port) return self._port_scan_tcp(port)
def _resolv_name(self, hostname): def _resolv_name(self, hostname):
@ -322,7 +318,7 @@ class ThreadScanner(threading.Thread):
try: try:
ip = socket.gethostbyname(hostname) ip = socket.gethostbyname(hostname)
except Exception as e: 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 return ip
def _port_scan_icmp(self, port): def _port_scan_icmp(self, port):
@ -380,7 +376,7 @@ class ThreadScanner(threading.Thread):
socket.setdefaulttimeout(port['timeout']) socket.setdefaulttimeout(port['timeout'])
_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) _socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except Exception as e: 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 # Scan port
ip = self._resolv_name(port['host']) ip = self._resolv_name(port['host'])
@ -388,7 +384,7 @@ class ThreadScanner(threading.Thread):
try: try:
ret = _socket.connect_ex((ip, int(port['port']))) ret = _socket.connect_ex((ip, int(port['port'])))
except Exception as e: 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: else:
if ret == 0: if ret == 0:
port['status'] = counter.get() port['status'] = counter.get()

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,8 +8,8 @@
"""Process count plugin.""" """Process count plugin."""
from glances.processes import glances_processes, sort_for_human
from glances.plugins.plugin.model import GlancesPluginModel from glances.plugins.plugin.model import GlancesPluginModel
from glances.processes import glances_processes, sort_for_human
# Fields description # Fields description
# description: human readable description # description: human readable description
@ -58,7 +57,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description 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: if glances_processes.process_filter is not None:
msg = 'Processes filter:' msg = 'Processes filter:'
ret.append(self.curse_add_line(msg, "TITLE")) 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: 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")) ret.append(self.curse_add_line(msg, "FILTER"))
msg = '(\'ENTER\' to edit, \'E\' to reset)' msg = '(\'ENTER\' to edit, \'E\' to reset)'
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
@ -144,7 +143,7 @@ class PluginModel(GlancesPluginModel):
msg = ' {} slp,'.format(self.stats['sleeping']) msg = ' {} slp,'.format(self.stats['sleeping'])
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
msg = ' {} oth '.format(other) msg = f' {other} oth '
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
# Display sort information # Display sort information
@ -156,9 +155,9 @@ class PluginModel(GlancesPluginModel):
if glances_processes.auto_sort: if glances_processes.auto_sort:
msg += ' sorted automatically' msg += ' sorted automatically'
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
msg = ' by {}'.format(sort_human) msg = f' by {sort_human}'
else: else:
msg += ' sorted by {}'.format(sort_human) msg += f' sorted by {sort_human}'
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
# Return the message with decoration # Return the message with decoration

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,15 +8,15 @@
"""Process list plugin.""" """Process list plugin."""
import os
import copy 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.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.outputs.glances_unicode import unicode_message
from glances.plugins.core import PluginModel as CorePluginModel from glances.plugins.core import PluginModel as CorePluginModel
from glances.plugins.plugin.model import GlancesPluginModel from glances.plugins.plugin.model import GlancesPluginModel
from glances.processes import glances_processes, sort_stats
# Fields description # Fields description
# description: human readable description # description: human readable description
@ -155,9 +154,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__( super().__init__(args=args, config=config, fields_description=fields_description, stats_init_value=[])
args=args, config=config, fields_description=fields_description, stats_init_value=[]
)
# We want to display the stat in the curse interface # We want to display the stat in the curse interface
self.display_curse = True self.display_curse = True
@ -341,11 +338,11 @@ class PluginModel(GlancesPluginModel):
else: else:
hours, minutes, seconds = seconds_to_hms(user_system_time) hours, minutes, seconds = seconds_to_hms(user_system_time)
if hours > 99: if hours > 99:
msg = '{:<7}h'.format(hours) msg = f'{hours:<7}h'
elif 0 < hours < 100: elif 0 < hours < 100:
msg = '{}h{}:{}'.format(hours, minutes, seconds) msg = f'{hours}h{minutes}:{seconds}'
else: else:
msg = '{}:{}'.format(minutes, seconds) msg = f'{minutes}:{seconds}'
msg = self.layout_stat['time'].format(msg) msg = self.layout_stat['time'].format(msg)
if hours > 0: if hours > 0:
ret = self.curse_add_line(msg, decoration='CPU_TIME', optional=True) ret = self.curse_add_line(msg, decoration='CPU_TIME', optional=True)
@ -502,7 +499,7 @@ class PluginModel(GlancesPluginModel):
ret.append(self.curse_add_line(msg, decoration=process_decoration, splittable=True)) ret.append(self.curse_add_line(msg, decoration=process_decoration, splittable=True))
except (TypeError, UnicodeEncodeError) as e: except (TypeError, UnicodeEncodeError) as e:
# Avoid crash after running fine for several hours #1335 # 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)) ret.append(self.curse_add_line('', splittable=True))
return ret return ret
@ -643,7 +640,7 @@ class PluginModel(GlancesPluginModel):
# value is a number which goes from 0 to 7. # value is a number which goes from 0 to 7.
# The higher the value, the lower the I/O priority of the process. # The higher the value, the lower the I/O priority of the process.
if hasattr(p['ionice'], 'value') and p['ionice'].value != 0: 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)) ret.append(self.curse_add_line(msg, splittable=True))
# Second line is memory info # Second line is memory info
@ -826,7 +823,7 @@ class PluginModel(GlancesPluginModel):
msg = ' < {}'.format('current') msg = ' < {}'.format('current')
ret.append(self.curse_add_line(msg, optional=True)) ret.append(self.curse_add_line(msg, optional=True))
else: else:
msg = ' < {}'.format(mmm) msg = f' < {mmm}'
ret.append(self.curse_add_line(msg, optional=True)) ret.append(self.curse_add_line(msg, optional=True))
msg = ' (\'M\' to reset)' msg = ' (\'M\' to reset)'
ret.append(self.curse_add_line(msg, optional=True)) ret.append(self.curse_add_line(msg, optional=True))
@ -835,7 +832,6 @@ class PluginModel(GlancesPluginModel):
"""Return the decoration string for the current mmm status.""" """Return the decoration string for the current mmm status."""
if mmm is not None: if mmm is not None:
return 'DEFAULT' return 'DEFAULT'
else:
return 'FILTER' return 'FILTER'
def __mmm_reset(self): def __mmm_reset(self):
@ -902,6 +898,6 @@ class PluginModel(GlancesPluginModel):
"""Return the maximum PID size in number of char.""" """Return the maximum PID size in number of char."""
if self.pid_max is not None: if self.pid_max is not None:
return len(str(self.pid_max)) return len(str(self.pid_max))
else:
# By default return 5 (corresponding to 99999 PID number) # By default return 5 (corresponding to 99999 PID number)
return 5 return 5

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -22,7 +21,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """Init the plugin."""
super(PluginModel, self).__init__(args=args, config=config) super().__init__(args=args, config=config)
self.reset() self.reset()

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -9,15 +8,15 @@
"""Quicklook plugin.""" """Quicklook plugin."""
from glances.logger import logger import psutil
from glances.cpu_percent import cpu_percent 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_bars import Bar
from glances.outputs.glances_sparklines import Sparkline 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 from glances.plugins.plugin.model import GlancesPluginModel
import psutil
# Fields description # Fields description
# description: human readable description # description: human readable description
# short_name: shortname to use un UI # short_name: shortname to use un UI
@ -84,7 +83,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the quicklook plugin.""" """Init the quicklook plugin."""
super(PluginModel, self).__init__( super().__init__(
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description 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 # Define the stats list
self.stats_list = self.get_conf_value('list', default=self.DEFAULT_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): 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 self.stats_list = self.AVAILABLE_STATS_LIST
@GlancesPluginModel._check_decorator @GlancesPluginModel._check_decorator
@ -152,7 +151,7 @@ class PluginModel(GlancesPluginModel):
def update_views(self): def update_views(self):
"""Update stats views.""" """Update stats views."""
# Call the father's method # Call the father's method
super(PluginModel, self).update_views() super().update_views()
# Alert for CPU, MEM and SWAP # Alert for CPU, MEM and SWAP
for key in self.stats_list: for key in self.stats_list:
@ -176,11 +175,11 @@ class PluginModel(GlancesPluginModel):
if not max_width: if not max_width:
# No max_width defined, return an empty message # 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 return ret
# Define the data: Bar (default behavior) or Sparkline # Define the data: Bar (default behavior) or Sparkline
data = dict() data = {}
for key in self.stats_list: for key in self.stats_list:
if self.args.sparkline and self.history_enable() and not self.args.client: if self.args.sparkline and self.history_enable() and not self.args.client:
data[key] = Sparkline(max_width) data[key] = Sparkline(max_width)
@ -221,7 +220,7 @@ class PluginModel(GlancesPluginModel):
else: else:
# Bar only the last value # Bar only the last value
data[key].percent = self.stats[key] 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.extend(self._msg_create_line(msg, data[key], key))
ret.append(self.curse_new_line()) ret.append(self.curse_new_line())
@ -258,9 +257,9 @@ class PluginModel(GlancesPluginModel):
# Bar will only display the last value # Bar will only display the last value
data[key].percent = cpu['total'] data[key].percent = cpu['total']
if cpu_id < 10: if cpu_id < 10:
msg = '{:3}{} '.format(key.upper(), cpu_id) msg = f'{key.upper():3}{cpu_id} '
else: else:
msg = '{:4} '.format(cpu_id) msg = f'{cpu_id:4} '
ret.extend(self._msg_create_line(msg, data[key], key)) ret.extend(self._msg_create_line(msg, data[key], key))
ret.append(self.curse_new_line()) 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( sum_other.percent = sum([i['total'] for i in percpu_list[self.max_cpu_display :]]) / len(
percpu_list[self.max_cpu_display :] 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.extend(self._msg_create_line(msg, sum_other, key))
ret.append(self.curse_new_line()) ret.append(self.curse_new_line())

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# This file is part of Glances. # This file is part of Glances.
# #
@ -18,7 +17,7 @@ try:
from pymdstat import MdStat from pymdstat import MdStat
except ImportError as e: except ImportError as e:
import_error_tag = True 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: else:
import_error_tag = False import_error_tag = False
@ -31,7 +30,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None): def __init__(self, args=None, config=None):
"""Init the plugin.""" """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 # We want to display the stat in the curse interface
self.display_curse = True self.display_curse = True
@ -54,7 +53,7 @@ class PluginModel(GlancesPluginModel):
# mds = MdStat(path='/home/nicolargo/dev/pymdstat/tests/mdstat.10') # mds = MdStat(path='/home/nicolargo/dev/pymdstat/tests/mdstat.10')
stats = mds.get_stats()['arrays'] stats = mds.get_stats()['arrays']
except Exception as e: 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 return self.stats
elif self.input_method == 'snmp': elif self.input_method == 'snmp':
@ -81,7 +80,7 @@ class PluginModel(GlancesPluginModel):
name_max_width = max_width - 12 name_max_width = max_width - 12
else: else:
# No max_width defined, return an emptu curse message # 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 return ret
# Header # Header
@ -108,7 +107,7 @@ class PluginModel(GlancesPluginModel):
# Data: RAID type name | disk used | disk available # 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' 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 # 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) msg = '{:{width}}'.format(full_name, width=name_max_width)
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
if self.stats[array]['type'] == 'raid0' and self.stats[array]['status'] == 'active': 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()) ret.append(self.curse_new_line())
msg = ' {} disk {}: '.format(tree_char, self.stats[array]['components'][component]) msg = ' {} disk {}: '.format(tree_char, self.stats[array]['components'][component])
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
msg = '{}'.format(component) msg = f'{component}'
ret.append(self.curse_add_line(msg)) ret.append(self.curse_add_line(msg))
if self.stats[array]['type'] != 'raid0' and (self.stats[array]['used'] < self.stats[array]['available']): if self.stats[array]['type'] != 'raid0' and (self.stats[array]['used'] < self.stats[array]['available']):
# Display current array configuration # Display current array configuration
@ -161,6 +160,6 @@ class PluginModel(GlancesPluginModel):
return 'CRITICAL' return 'CRITICAL'
if used is None or available is None: if used is None or available is None:
return 'DEFAULT' return 'DEFAULT'
elif used < available: if used < available:
return 'WARNING' return 'WARNING'
return 'OK' return 'OK'

Some files were not shown because too many files have changed in this diff Show More