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:
source-code-checks:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Check formatting with Ruff
uses: chartboost/ruff-action@v1
with:
args: 'format --check'
- name: Check linting with Ruff
uses: chartboost/ruff-action@v1
with:
args: 'check'
- name: Static type check
run: |
echo "Skipping static type check for the moment, too much error...";
# pip install pyright
# pyright glances
test-linux:
needs: source-code-checks
# https://github.com/actions/runner-images?tab=readme-ov-file#available-images
runs-on: ubuntu-22.04
strategy:
@ -17,36 +41,22 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Install dependencies
run: |
python -m pip install --upgrade pip
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# Stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=.git,./docs,./glances/outputs/static
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --exclude=.git,./docs,./glances/outputs/static
- name: Static type check
run: |
echo "Skipping static type check for the moment, too much error...";
# pip install pyright
# pyright glances
- name: Unitary tests
run: |
python ./unittest-core.py
- name: Unitary tests
run: |
python ./unittest-core.py
# Error appear with h11, not related to Glances
# Should be tested if correction is done
@ -84,6 +94,7 @@ jobs:
test-macos:
needs: source-code-checks
# https://github.com/actions/runner-images?tab=readme-ov-file#available-images
runs-on: macos-14
strategy:
@ -92,22 +103,22 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Install dependencies
run: |
python -m pip install --upgrade pip
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Unitary tests
run: |
python ./unittest-core.py
- name: Unitary tests
run: |
python ./unittest-core.py
# Error when trying to implement #2749
# pkg: No packages available to install matching 'py-pip' have been found in the repositories

22
.pre-commit-config.yaml Normal file
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
patches and features.
## Using the issue tracker
The [issue tracker](https://github.com/nicolargo/glances/issues) is
@ -24,7 +23,6 @@ restrictions:
* Please **do not** derail or troll issues. Keep the discussion on topic and
respect the opinions of others.
## Bug reports
A bug is a _demonstrable problem_ that is caused by the code in the repository.
@ -65,19 +63,17 @@ Example:
> causing the bug, and potential solutions (and your opinions on their
> merits).
>
> You can also run Glances in debug mode (-d) and paste/bin the glances.conf file (https://glances.readthedocs.io/en/latest/config.html).
> You can also run Glances in debug mode (-d) and paste/bin the glances.conf file (<https://glances.readthedocs.io/en/latest/config.html>).
>
> Glances 3.2.0 or higher have also a --issue option to run a simple test. Please use it and copy/paste the output.
## Feature requests
Feature requests are welcome. But take a moment to find out whether your idea
fits with the scope and aims of the project. It's up to *you* to make a strong
fits with the scope and aims of the project. It's up to _you* to make a strong
case to convince the project's developers of the merits of this feature. Please
provide as much detail and context as possible.
## Pull requests
Good pull requests—patches, improvements, new features—are a fantastic
@ -133,6 +129,7 @@ included in the project:
5. Test you code using the Makefile:
* make format ==> Format your code thanks to the Ruff linter
* make run ==> Run Glances
* make run-webserver ==> Run a Glances Web Server
* make test ==> Run unit tests

View File

@ -52,6 +52,7 @@ venv-dev-python: ## Install Python 3 venv
venv-dev: venv-python ## Install Python 3 dev dependencies
./venv-dev/bin/pip install -r dev-requirements.txt
./venv-dev/bin/pip install -r doc-requirements.txt
./venv-dev/bin/pre-commit install --hook-type pre-commit
venv-dev-upgrade: ## Upgrade Python 3 dev dependencies
./venv-dev/bin/pip install --upgrade pip
@ -86,15 +87,10 @@ test-min-with-upgrade: venv-min-upgrade ## Upgrade deps and run unit tests in mi
# ===================================================================
format: ## Format the code
@git ls-files 'glances/*.py' | xargs ./venv-dev/bin/python -m autopep8 --in-place --jobs 0 --global-config=.flake8
@git ls-files 'glances/*.py' | xargs ./venv-dev/bin/python -m autoflake --in-place --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys --exclude="compat.py,globals.py"
./venv-dev/bin/python -m black ./glances --exclude outputs/static
./venv-dev/bin/python -m ruff format .
flake8: ## Run flake8 linter.
@git ls-files 'glances/ *.py' | xargs ./venv-dev/bin/python -m flake8 --config=.flake8
ruff: ## Run Ruff (fastest) linter.
./venv-dev/bin/python -m ruff check . --config=./pyproject.toml
lint: ## Lint the code.
./venv-dev/bin/python -m ruff check . --fix
codespell: ## Run codespell to fix common misspellings in text files
./venv-dev/bin/codespell -S .git,./docs/_build,./Glances.egg-info,./venv*,./glances/outputs,*.svg -L hart,bu,te,statics

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
@ -23,11 +22,11 @@ If the *one_line* var is true then the AMP will be displayed in one line.
"""
from glances.globals import u
from glances.timer import Timer
from glances.logger import logger
from glances.timer import Timer
class GlancesAmp(object):
class GlancesAmp:
"""Main class for Glances AMP."""
NAME = '?'
@ -38,7 +37,7 @@ class GlancesAmp(object):
def __init__(self, name=None, args=None):
"""Init AMP class."""
logger.debug("AMP - Init {} version {}".format(self.NAME, self.VERSION))
logger.debug(f"AMP - Init {self.NAME} version {self.VERSION}")
# AMP name (= module name without glances_)
if name is None:
@ -74,7 +73,7 @@ class GlancesAmp(object):
amp_section = 'amp_' + self.amp_name
if hasattr(config, 'has_section') and config.has_section(amp_section):
logger.debug("AMP - {}: Load configuration".format(self.NAME))
logger.debug(f"AMP - {self.NAME}: Load configuration")
for param, _ in config.items(amp_section):
try:
self.configs[param] = config.get_float_value(amp_section, param)
@ -82,9 +81,9 @@ class GlancesAmp(object):
self.configs[param] = config.get_value(amp_section, param).split(',')
if len(self.configs[param]) == 1:
self.configs[param] = self.configs[param][0]
logger.debug("AMP - {}: Load parameter: {} = {}".format(self.NAME, param, self.configs[param]))
logger.debug(f"AMP - {self.NAME}: Load parameter: {param} = {self.configs[param]}")
else:
logger.debug("AMP - {}: Can not find section {} in the configuration file".format(self.NAME, self.amp_name))
logger.debug(f"AMP - {self.NAME}: Can not find section {self.amp_name} in the configuration file")
return False
if self.enable():
@ -92,13 +91,12 @@ class GlancesAmp(object):
for k in ['refresh']:
if k not in self.configs:
logger.warning(
"AMP - {}: Can not find configuration key {} in section {} (the AMP will be disabled)".format(
self.NAME, k, self.amp_name
)
f"AMP - {self.NAME}: Can not find configuration key {k} in section {self.amp_name} "
f"(the AMP will be disabled)"
)
self.configs['enable'] = 'false'
else:
logger.debug("AMP - {} is disabled".format(self.NAME))
logger.debug(f"AMP - {self.NAME} is disabled")
# Init the count to 0
self.configs['count'] = 0
@ -109,16 +107,14 @@ class GlancesAmp(object):
"""Generic method to get the item in the AMP configuration"""
if key in self.configs:
return self.configs[key]
else:
return None
return None
def enable(self):
"""Return True|False if the AMP is enabled in the configuration file (enable=true|false)."""
ret = self.get('enable')
if ret is None:
return False
else:
return ret.lower().startswith('true')
return ret.lower().startswith('true')
def regex(self):
"""Return regular expression used to identified the current application."""
@ -133,8 +129,7 @@ class GlancesAmp(object):
ret = self.get('one_line')
if ret is None:
return False
else:
return ret.lower().startswith('true')
return ret.lower().startswith('true')
def time_until_refresh(self):
"""Return time in seconds until refresh."""
@ -193,5 +188,4 @@ class GlancesAmp(object):
# Call the children update method
if self.should_update():
return self.update(process_list)
else:
return self.result()
return self.result()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
@ -9,13 +8,13 @@
"""Manage the configuration file."""
import os
import sys
import builtins
import multiprocessing
from io import open
import os
import re
import sys
from glances.globals import ConfigParser, NoOptionError, NoSectionError, system_exec, BSD, LINUX, MACOS, SUNOS, WINDOWS
from glances.globals import BSD, LINUX, MACOS, SUNOS, WINDOWS, ConfigParser, NoOptionError, NoSectionError, system_exec
from glances.logger import logger
@ -94,7 +93,7 @@ def default_config_dir():
return [path]
class Config(object):
class Config:
"""This class is used to access/read config file, if it exists.
:param config_dir: the path to search for config file
@ -153,15 +152,15 @@ class Config(object):
def read(self):
"""Read the config file, if it exists. Using defaults otherwise."""
for config_file in self.config_file_paths():
logger.debug('Search glances.conf file in {}'.format(config_file))
logger.debug(f'Search glances.conf file in {config_file}')
if os.path.exists(config_file):
try:
with open(config_file, encoding='utf-8') as f:
with builtins.open(config_file, encoding='utf-8') as f:
self.parser.read_file(f)
self.parser.read(f)
logger.info("Read configuration file '{}'".format(config_file))
logger.info(f"Read configuration file '{config_file}'")
except UnicodeDecodeError as err:
logger.error("Can not read configuration file '{}': {}".format(config_file, err))
logger.error(f"Can not read configuration file '{config_file}': {err}")
sys.exit(1)
# Save the loaded configuration file path (issue #374)
self._loaded_config_file = config_file

View File

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

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
@ -8,7 +7,8 @@
#
"""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_type = "CPU*|LOAD|MEM|MON"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
@ -12,7 +11,7 @@ import re
from glances.logger import logger
class GlancesFilterList(object):
class GlancesFilterList:
"""Manage a lis of GlancesFilter objects
>>> fl = GlancesFilterList()
@ -55,7 +54,7 @@ class GlancesFilterList(object):
return False
class GlancesFilter(object):
class GlancesFilter:
"""Allow Glances to filter processes
>>> f = GlancesFilter()
@ -127,9 +126,9 @@ class GlancesFilter(object):
# Compute the regular expression
try:
self._filter_re = re.compile(self.filter)
logger.debug("Filter regex compilation OK: {}".format(self.filter))
logger.debug(f"Filter regex compilation OK: {self.filter}")
except Exception as e:
logger.error("Cannot compile filter regex: {} ({})".format(self.filter, e))
logger.error(f"Cannot compile filter regex: {self.filter} ({e})")
self._filter = None
self._filter_re = None
self._filter_key = None
@ -156,9 +155,9 @@ class GlancesFilter(object):
if self.filter_key is None:
# Apply filter on command line and process name
return self._is_process_filtered(process, key='name') or self._is_process_filtered(process, key='cmdline')
else:
# Apply filter on <key>
return self._is_process_filtered(process)
# Apply filter on <key>
return self._is_process_filtered(process)
def _is_process_filtered(self, process, key=None):
"""Return True if the process[key] should be filtered according to the current filter"""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
@ -19,7 +18,7 @@ class GlancesPasswordList(GlancesPassword):
_section = "passwords"
def __init__(self, config=None, args=None):
super(GlancesPasswordList, self).__init__()
super().__init__()
# password_dict is a dict (JSON compliant)
# {'host': 'password', ... }
# Load the configuration file
@ -32,14 +31,14 @@ class GlancesPasswordList(GlancesPassword):
if config is None:
logger.warning("No configuration file available. Cannot load password list.")
elif not config.has_section(self._section):
logger.warning("No [%s] section in the configuration file. Cannot load password list." % self._section)
logger.warning(f"No [{self._section}] section in the configuration file. Cannot load password list.")
else:
logger.info("Start reading the [%s] section in the configuration file" % self._section)
logger.info(f"Start reading the [{self._section}] section in the configuration file")
password_dict = dict(config.items(self._section))
# Password list loaded
logger.info("%s password(s) loaded from the configuration file" % len(password_dict))
logger.info(f"{len(password_dict)} password(s) loaded from the configuration file")
return password_dict
@ -51,14 +50,14 @@ class GlancesPasswordList(GlancesPassword):
"""
if host is None:
return self._password_dict
else:
try:
return self._password_dict[host]
except (KeyError, TypeError):
try:
return self._password_dict[host]
return self._password_dict['default']
except (KeyError, TypeError):
try:
return self._password_dict['default']
except (KeyError, TypeError):
return None
return None
def set_password(self, host, password):
"""Set a password for a specific host."""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
@ -7,9 +6,10 @@
# SPDX-License-Identifier: LGPL-3.0-only
"""Podman Extension unit for Glances' Containers plugin."""
from datetime import datetime
from glances.globals import iterkeys, itervalues, nativestr, pretty_date, string_value_to_float, replace_special_chars
from glances.globals import iterkeys, itervalues, nativestr, pretty_date, replace_special_chars, string_value_to_float
from glances.logger import logger
from glances.plugins.containers.stats_streamer import StatsStreamer
@ -20,7 +20,7 @@ try:
except Exception as e:
import_podman_error_tag = True
# Display debug message if import KeyError
logger.warning("Error loading Podman deps Lib. Podman feature in the Containers plugin is disabled ({})".format(e))
logger.warning(f"Error loading Podman deps Lib. Podman feature in the Containers plugin is disabled ({e})")
else:
import_podman_error_tag = False
@ -36,7 +36,7 @@ class PodmanContainerStatsFetcher:
self._streamer = StatsStreamer(stats_iterable, initial_stream_value={})
def _log_debug(self, msg, exception=None):
logger.debug("containers (Podman) ID: {} - {} ({})".format(self._container.id, msg, exception))
logger.debug(f"containers (Podman) ID: {self._container.id} - {msg} ({exception})")
logger.debug(self._streamer.stats)
def stop(self):
@ -95,7 +95,7 @@ class PodmanPodStatsFetcher:
self._streamer = StatsStreamer(stats_iterable, initial_stream_value={}, sleep_duration=2)
def _log_debug(self, msg, exception=None):
logger.debug("containers (Podman): Pod Manager - {} ({})".format(msg, exception))
logger.debug(f"containers (Podman): Pod Manager - {msg} ({exception})")
logger.debug(self._streamer.stats)
def stop(self):
@ -234,7 +234,7 @@ class PodmanContainersExtension:
# PodmanClient works lazily, so make a ping to determine if socket is open
self.client.ping()
except Exception as e:
logger.debug("{} plugin - Can't connect to Podman ({})".format(self.ext_name, e))
logger.debug(f"{self.ext_name} plugin - Can't connect to Podman ({e})")
self.client = None
def update_version(self):
@ -266,7 +266,7 @@ class PodmanContainersExtension:
if not self.pods_stats_fetcher:
self.pods_stats_fetcher = PodmanPodStatsFetcher(self.client.pods)
except Exception as e:
logger.error("{} plugin - Can't get containers list ({})".format(self.ext_name, e))
logger.error(f"{self.ext_name} plugin - Can't get containers list ({e})")
return version_stats, []
# Start new thread for new container
@ -274,14 +274,14 @@ class PodmanContainersExtension:
if container.id not in self.container_stats_fetchers:
# StatsFetcher did not exist in the internal dict
# Create it, add it to the internal dict
logger.debug("{} plugin - Create thread for container {}".format(self.ext_name, container.id[:12]))
logger.debug(f"{self.ext_name} plugin - Create thread for container {container.id[:12]}")
self.container_stats_fetchers[container.id] = PodmanContainerStatsFetcher(container)
# Stop threads for non-existing containers
absent_containers = set(iterkeys(self.container_stats_fetchers)) - set(c.id for c in containers)
absent_containers = set(iterkeys(self.container_stats_fetchers)) - {c.id for c in containers}
for container_id in absent_containers:
# Stop the StatsFetcher
logger.debug("{} plugin - Stop thread for old container {}".format(self.ext_name, container_id[:12]))
logger.debug(f"{self.ext_name} plugin - Stop thread for old container {container_id[:12]}")
self.container_stats_fetchers[container_id].stop()
# Delete the StatsFetcher from the dict
del self.container_stats_fetchers[container_id]

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
@ -63,7 +62,7 @@ class StatsStreamer:
break
except Exception as e:
logger.debug("docker plugin - Exception thrown during run ({})".format(e))
logger.debug(f"docker plugin - Exception thrown during run ({e})")
self.stop()
def _pre_update_hook(self):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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