Replace Bottle by FastAPI #2181

This commit is contained in:
nicolargo 2023-12-16 15:00:04 +01:00
parent 32b33b5883
commit b3828f5497
19 changed files with 204 additions and 226 deletions

View File

@ -102,7 +102,7 @@ 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
semgrep: ## Run semgrep to find bugs and enforce code standards
./venv-dev/bin/semgrep --config=auto --lang python --use-git-ignore ./glances
./venv-dev/bin/semgrep scan --config=auto
profiling: ## How to start the profiling of the Glances software
@echo "Please complete and run: sudo ./venv-dev/bin/py-spy record -o ./docs/_static/glances-flame.svg -d 60 -s --pid <GLANCES PID>"

View File

@ -13,7 +13,6 @@ import time
from datetime import datetime
from glances.processes import glances_processes, sort_stats
from glances.logger import logger
class GlancesEvents(object):
@ -109,8 +108,7 @@ class GlancesEvents(object):
self._create_event(event_state, event_type, event_value, proc_desc)
else:
# Event exist, update it
self._update_event(event_index,
event_state, event_type, event_value, proc_list, proc_desc, peak_time)
self._update_event(event_index, event_state, event_type, event_value, proc_list, proc_desc, peak_time)
return self.len()
@ -130,7 +128,7 @@ class GlancesEvents(object):
time.mktime(datetime.now().timetuple()), # START DATE
-1, # END DATE
event_state, # STATE: WARNING|CRITICAL
event_type, # TYPE: CPU, LOAD, MEM...
event_type, # TYPE: CPU, LOAD, MEM...
event_value, # MAX
event_value, # AVG
event_value, # MIN

View File

@ -36,9 +36,7 @@ class Export(GlancesExport):
# Load the CouchDB configuration file section
# User and Password are mandatory with CouchDB 3.0 and higher
self.export_enable = self.load_conf('couchdb',
mandatories=['host', 'port', 'db',
'user', 'password'])
self.export_enable = self.load_conf('couchdb', mandatories=['host', 'port', 'db', 'user', 'password'])
if not self.export_enable:
sys.exit(2)
@ -51,8 +49,7 @@ class Export(GlancesExport):
return None
# @TODO: https
server_uri = 'http://{}:{}@{}:{}/'.format(self.user, self.password,
self.host, self.port)
server_uri = 'http://{}:{}@{}:{}/'.format(self.user, self.password, self.host, self.port)
try:
s = pycouchdb.Server(server_uri)

View File

@ -36,7 +36,7 @@ class GlancesExport(object):
'processlist',
'psutilversion',
'quicklook',
'version'
'version',
]
def __init__(self, config=None, args=None):

View File

@ -10,7 +10,6 @@
"""Manage the folder list."""
from __future__ import unicode_literals
import os
from glances.timer import Timer
from glances.globals import nativestr, folder_size
@ -132,11 +131,12 @@ class FolderList(object):
# Get folder size
self.__folder_list[i]['size'], self.__folder_list[i]['errno'] = folder_size(self.path(i))
if self.__folder_list[i]['errno'] != 0:
logger.debug('Folder size ({} ~ {}) may not be correct. Error: {}'.format(
self.path(i),
self.__folder_list[i]['size'],
self.__folder_list[i]['errno']))
# Reset the timer
logger.debug(
'Folder size ({} ~ {}) may not be correct. Error: {}'.format(
self.path(i), self.__folder_list[i]['size'], self.__folder_list[i]['errno']
)
)
# Reset the timer
self.timer_folders[i].reset()
# It is no more the first time...

View File

@ -413,8 +413,8 @@ def folder_size(path, errno=0):
def weak_lru_cache(maxsize=128, typed=False):
"""LRU Cache decorator that keeps a weak reference to self
Source: https://stackoverflow.com/a/55990799"""
def wrapper(func):
def wrapper(func):
@functools.lru_cache(maxsize, typed)
def _func(_self, *args, **kwargs):
return func(_self(), *args, **kwargs)

View File

@ -134,7 +134,7 @@ Examples of use:
'--enable-plugins',
'--enable',
dest='enable_plugin',
help='enable plugin (comma-separated list)'
help='enable plugin (comma-separated list)',
)
parser.add_argument(
'--disable-process',

View File

@ -101,15 +101,7 @@ class _GlancesCurses(object):
_sort_loop = sort_processes_key_list
# Define top menu
_top = [
'quicklook',
'cpu',
'percpu',
'gpu',
'mem',
'memswap',
'load'
]
_top = ['quicklook', 'cpu', 'percpu', 'gpu', 'mem', 'memswap', 'load']
_quicklook_max_width = 68
# Define left sidebar
@ -131,13 +123,7 @@ class _GlancesCurses(object):
_left_sidebar_max_width = 34
# Define right sidebar
_right_sidebar = [
'containers',
'processcount',
'amps',
'processlist',
'alert'
]
_right_sidebar = ['containers', 'processcount', 'amps', 'processlist', 'alert']
def __init__(self, config=None, args=None):
# Init
@ -301,8 +287,7 @@ class _GlancesCurses(object):
if curses.COLORS > 8:
# ex: export TERM=xterm-256color
colors_list = [curses.COLOR_CYAN,
curses.COLOR_YELLOW]
colors_list = [curses.COLOR_CYAN, curses.COLOR_YELLOW]
for i in range(0, 3):
try:
curses.init_pair(i + 9, colors_list[i], -1)
@ -462,9 +447,7 @@ class _GlancesCurses(object):
)
def _handle_sort_key(self, hotkey):
glances_processes.set_sort_key(
self._hotkeys[hotkey]['sort_key'], self._hotkeys[hotkey]['sort_key'] == 'auto'
)
glances_processes.set_sort_key(self._hotkeys[hotkey]['sort_key'], self._hotkeys[hotkey]['sort_key'] == 'auto')
def _handle_enter(self):
self.edit_filter = not self.edit_filter

View File

@ -16,6 +16,7 @@ from io import open
import webbrowser
import socket
from urllib.parse import urljoin
# Replace typing_extensions by typing when Python 3.8 support will be dropped
# from typing import Annotated
from typing_extensions import Annotated
@ -72,15 +73,12 @@ 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('http://{}:{}/'.format(self.args.bind_address, self.args.port), self.url_prefix)
# FastAPI Init
if self.args.password:
self._app = FastAPI(dependencies=[Depends(self.authentication)])
self._password = GlancesPassword(username=args.username,
config=config)
self._password = GlancesPassword(username=args.username, config=config)
else:
self._app = FastAPI()
@ -100,9 +98,7 @@ class GlancesRestfulApi(object):
self._app.add_middleware(
CORSMiddleware,
# allow_origins=["*"],
allow_origins=[
self.bind_url
],
allow_origins=[self.bind_url],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
@ -110,8 +106,7 @@ class GlancesRestfulApi(object):
# FastAPI Enable GZIP compression
# https://fastapi.tiangolo.com/advanced/middleware/
self._app.add_middleware(GZipMiddleware,
minimum_size=1000)
self._app.add_middleware(GZipMiddleware, minimum_size=1000)
# FastAPI Define routes
self._app.include_router(self._router())
@ -139,8 +134,7 @@ class GlancesRestfulApi(object):
"""Check if a username/password combination is valid."""
if creds.username == self.args.username:
# check_password and get_hash are (lru) cached to optimize the requests
if self._password.check_password(self.args.password,
self._password.get_hash(creds.password)):
if self._password.check_password(self.args.password, self._password.get_hash(creds.password)):
return creds.username
# If the username/password combination is invalid, return an HTTP 401
@ -155,74 +149,80 @@ class GlancesRestfulApi(object):
router = APIRouter()
# REST API
router.add_api_route('/api/%s/status' % self.API_VERSION,
status_code=status.HTTP_200_OK,
response_class=ORJSONResponse,
endpoint=self._api_status)
router.add_api_route(
'/api/%s/status' % self.API_VERSION,
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)
router.add_api_route('/api/%s/config/{section}' % self.API_VERSION,
response_class=ORJSONResponse,
endpoint=self._api_config_section)
router.add_api_route('/api/%s/config/{section}/{item}' % self.API_VERSION,
response_class=ORJSONResponse,
endpoint=self._api_config_section_item)
router.add_api_route(
'/api/%s/config' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_config
)
router.add_api_route(
'/api/%s/config/{section}' % self.API_VERSION,
response_class=ORJSONResponse,
endpoint=self._api_config_section,
)
router.add_api_route(
'/api/%s/config/{section}/{item}' % self.API_VERSION,
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('/api/%s/args/{item}' % self.API_VERSION,
response_class=ORJSONResponse,
endpoint=self._api_args_item)
router.add_api_route('/api/%s/args' % self.API_VERSION, 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
)
router.add_api_route('/api/%s/pluginslist' % self.API_VERSION,
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('/api/%s/all/limits' % self.API_VERSION,
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)
router.add_api_route(
'/api/%s/pluginslist' % self.API_VERSION, 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(
'/api/%s/all/limits' % self.API_VERSION, 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
)
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('/api/%s/{plugin}/history' % self.API_VERSION,
response_class=ORJSONResponse,
endpoint=self._api_history)
router.add_api_route('/api/%s/{plugin}/history/{nb}' % self.API_VERSION,
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)
router.add_api_route('/api/%s/{plugin}/limits' % self.API_VERSION,
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)
router.add_api_route('/api/%s/{plugin}/{item}' % self.API_VERSION,
response_class=ORJSONResponse,
endpoint=self._api_item)
router.add_api_route('/api/%s/{plugin}/{item}/history' % self.API_VERSION,
response_class=ORJSONResponse,
endpoint=self._api_item_history)
router.add_api_route('/api/%s/{plugin}/{item}/history/{nb}' % self.API_VERSION,
response_class=ORJSONResponse,
endpoint=self._api_item_history)
router.add_api_route('/api/%s/{plugin}/{item}/{value}' % self.API_VERSION,
response_class=ORJSONResponse,
endpoint=self._api_value)
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(
'/api/%s/{plugin}/history' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_history
)
router.add_api_route(
'/api/%s/{plugin}/history/{nb}' % self.API_VERSION,
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
)
router.add_api_route(
'/api/%s/{plugin}/limits' % self.API_VERSION, 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
)
router.add_api_route(
'/api/%s/{plugin}/{item}' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_item
)
router.add_api_route(
'/api/%s/{plugin}/{item}/history' % self.API_VERSION,
response_class=ORJSONResponse,
endpoint=self._api_item_history,
)
router.add_api_route(
'/api/%s/{plugin}/{item}/history/{nb}' % self.API_VERSION,
response_class=ORJSONResponse,
endpoint=self._api_item_history,
)
router.add_api_route(
'/api/%s/{plugin}/{item}/{value}' % self.API_VERSION,
response_class=ORJSONResponse,
endpoint=self._api_value,
)
# Restful API
bindmsg = 'Glances RESTful API Server started on {}api/{}'.format(self.bind_url, self.API_VERSION)
@ -231,14 +231,10 @@ class GlancesRestfulApi(object):
# WEB UI
if not self.args.disable_webui:
# Template for the root index.html file
router.add_api_route('/',
response_class=HTMLResponse,
endpoint=self._index)
router.add_api_route('/', response_class=HTMLResponse, endpoint=self._index)
# Statics files
self._app.mount("/static",
StaticFiles(directory=self.STATIC_PATH),
name="static")
self._app.mount("/static", StaticFiles(directory=self.STATIC_PATH), name="static")
bindmsg = 'Glances Web User Interface started on {}'.format(self.bind_url)
else:
@ -267,10 +263,7 @@ class GlancesRestfulApi(object):
# Run the Web application
try:
uvicorn.run(self._app,
host=self.args.bind_address,
port=self.args.port,
access_log=self.args.debug)
uvicorn.run(self._app, host=self.args.bind_address, port=self.args.port, access_log=self.args.debug)
except socket.error as e:
logger.critical('Error: Can not ran Glances Web server ({})'.format(e))
@ -286,18 +279,19 @@ class GlancesRestfulApi(object):
Note: This function is only called the first time the page is loaded.
"""
refresh_time = request.query_params.get('refresh',
default=max(1, int(self.args.time)))
refresh_time = request.query_params.get('refresh', default=max(1, int(self.args.time)))
# Update the stat
self.__update__()
# Display
return self._templates.TemplateResponse("index.html",
{
"request": request,
"refresh_time": refresh_time,
})
return self._templates.TemplateResponse(
"index.html",
{
"request": request,
"refresh_time": refresh_time,
},
)
def _api_status(self):
"""Glances API RESTful implementation.
@ -318,9 +312,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="Cannot get help view data (%s)" % str(e))
return ORJSONResponse(plist)
@ -356,9 +348,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="Cannot get plugin list (%s)" % str(e))
return ORJSONResponse(plist)
@ -385,9 +375,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="Cannot get stats (%s)" % str(e))
return ORJSONResponse(statval)
@ -403,9 +391,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="Cannot get limits (%s)" % str(e))
return ORJSONResponse(limits)
@ -421,9 +407,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="Cannot get views (%s)" % str(e))
return ORJSONResponse(limits)
@ -438,7 +422,8 @@ 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="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
)
# Update the stat
self.__update__()
@ -448,8 +433,8 @@ class GlancesRestfulApi(object):
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)))
status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get plugin %s (%s)" % (plugin, str(e))
)
return ORJSONResponse(statval)
@ -466,7 +451,8 @@ 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="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
)
# Update the stat
self.__update__()
@ -476,8 +462,8 @@ class GlancesRestfulApi(object):
statval = self.stats.get_plugin(plugin).get_export()
except Exception as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Cannot get plugin %s (%s)" % (plugin, str(e)))
status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get plugin %s (%s)" % (plugin, str(e))
)
if isinstance(statval, list):
statval = statval[:nb]
@ -496,7 +482,8 @@ 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="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
)
# Update the stat
self.__update__()
@ -506,8 +493,8 @@ 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="Cannot get plugin history %s (%s)" % (plugin, str(e))
)
return statval
@ -522,15 +509,16 @@ 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="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
)
try:
# Get the RAW value of the stat limits
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="Cannot get limits for plugin %s (%s)" % (plugin, str(e))
)
return ORJSONResponse(ret)
@ -545,15 +533,16 @@ 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="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
)
try:
# Get the RAW value of the stat views
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="Cannot get views for plugin %s (%s)" % (plugin, str(e))
)
return ORJSONResponse(ret)
@ -568,7 +557,8 @@ 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="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
)
# Update the stat
self.__update__()
@ -579,7 +569,8 @@ 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="Cannot get item %s in plugin %s (%s)" % (item, plugin, str(e)),
)
return ORJSONResponse(ret)
@ -595,7 +586,8 @@ 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="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
)
# Update the stat
self.__update__()
@ -605,8 +597,8 @@ 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="Cannot get history for plugin %s (%s)" % (plugin, str(e))
)
return ORJSONResponse(ret)
@ -621,7 +613,8 @@ 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="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
)
# Update the stat
self.__update__()
@ -632,7 +625,8 @@ 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="Cannot get %s = %s for plugin %s (%s)" % (item, value, plugin, str(e)),
)
return ORJSONResponse(ret)
@ -647,9 +641,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="Cannot get config (%s)" % str(e))
return ORJSONResponse(args_json)
@ -664,16 +656,16 @@ 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)
status_code=status.HTTP_400_BAD_REQUEST, detail="Unknown configuration item %s" % 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="Cannot get config section %s (%s)" % (section, str(e))
)
return ORJSONResponse(ret_section)
@ -688,16 +680,16 @@ 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)
status_code=status.HTTP_400_BAD_REQUEST, detail="Unknown configuration item %s" % 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="Cannot get config section %s (%s)" % (section, str(e))
)
try:
# Get the RAW value of the config' dict item
@ -705,7 +697,8 @@ 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="Cannot get item %s in config section %s (%s)" % (item, section, str(e)),
)
return ORJSONResponse(ret_item)
@ -722,9 +715,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="Cannot get args (%s)" % str(e))
return ORJSONResponse(args_json)
@ -737,9 +728,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="Unknown argument item %s" % item)
try:
# Get the RAW value of the args' dict
@ -747,8 +736,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="Cannot get args item (%s)" % str(e))
return ORJSONResponse(args_json)

View File

@ -64,7 +64,9 @@ 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=GlancesRestfulApi.API_VERSION)
""".format(
api_version=GlancesRestfulApi.API_VERSION
)
def indent_stat(stat, indent=' '):

View File

@ -13,7 +13,6 @@ from datetime import datetime
from time import tzname
import pytz
from glances.logger import logger
from glances.events import glances_events
from glances.thresholds import glances_thresholds
@ -173,9 +172,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=[])
super(PluginModel, self).__init__(args=args, config=config, stats_init_value=[])
# We want to display the stat in the curse interface
self.display_curse = True
@ -213,8 +210,7 @@ class PluginModel(GlancesPluginModel):
# New line
ret.append(self.curse_new_line())
# Start
msg = str(datetime.fromtimestamp(alert[0],
tz=pytz.timezone(tzname[0] if tzname[0] else 'UTC')))
msg = str(datetime.fromtimestamp(alert[0], tz=pytz.timezone(tzname[0] if tzname[0] else 'UTC')))
ret.append(self.curse_add_line(msg))
# Duration
if alert[1] > 0:

View File

@ -10,7 +10,6 @@
"""Folder plugin."""
from __future__ import unicode_literals
import numbers
from glances.globals import nativestr
from glances.folder_list import FolderList as glancesFolderList
@ -108,7 +107,7 @@ class PluginModel(GlancesPluginModel):
ret.append(self.curse_new_line())
if len(i['path']) > name_max_width:
# Cut path if it is too long
path = '_' + i['path'][-name_max_width + 1:]
path = '_' + i['path'][-name_max_width + 1 :]
else:
path = i['path']
msg = '{:{width}}'.format(nativestr(path), width=name_max_width)

View File

@ -39,10 +39,9 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(PluginModel, self).__init__(args=args,
config=config,
items_history_list=items_history_list,
stats_init_value=[])
super(PluginModel, self).__init__(
args=args, config=config, items_history_list=items_history_list, stats_init_value=[]
)
# Init the Nvidia API
self.init_nvidia()

View File

@ -184,9 +184,33 @@ class PluginModel(GlancesPluginModel):
ret.append(self.curse_new_line())
ret.append(self.curse_add_line('Colors binding:'))
ret.append(self.curse_new_line())
for c in ['DEFAULT', 'UNDERLINE', 'BOLD', 'SORT', 'OK', 'MAX', 'FILTER', 'TITLE', 'PROCESS', 'PROCESS_SELECTED',
'STATUS', 'NICE', 'CPU_TIME', 'CAREFUL', 'WARNING', 'CRITICAL', 'OK_LOG', 'CAREFUL_LOG',
'WARNING_LOG', 'CRITICAL_LOG', 'PASSWORD', 'SELECTED', 'INFO', 'ERROR', 'SEPARATOR']:
for c in [
'DEFAULT',
'UNDERLINE',
'BOLD',
'SORT',
'OK',
'MAX',
'FILTER',
'TITLE',
'PROCESS',
'PROCESS_SELECTED',
'STATUS',
'NICE',
'CPU_TIME',
'CAREFUL',
'WARNING',
'CRITICAL',
'OK_LOG',
'CAREFUL_LOG',
'WARNING_LOG',
'CRITICAL_LOG',
'PASSWORD',
'SELECTED',
'INFO',
'ERROR',
'SEPARATOR',
]:
ret.append(self.curse_add_line(c, decoration=c))
if c == 'CPU_TIME':
ret.append(self.curse_new_line())

View File

@ -115,10 +115,7 @@ class PluginModel(GlancesPluginModel):
def __init__(self, args=None, config=None):
"""Init the plugin."""
super(PluginModel, self).__init__(
args=args,
config=config,
items_history_list=items_history_list,
fields_description=fields_description
args=args, config=config, items_history_list=items_history_list, fields_description=fields_description
)
# We want to display the stat in the curse interface

View File

@ -583,8 +583,7 @@ class PluginModel(GlancesPluginModel):
for k, v in p['memory_info'].items():
ret.append(
self.curse_add_line(
self.auto_unit(v,
low_precision=False),
self.auto_unit(v, low_precision=False),
decoration='INFO',
splittable=True,
)
@ -593,10 +592,7 @@ class PluginModel(GlancesPluginModel):
if 'memory_swap' in p and p['memory_swap'] is not None:
ret.append(
self.curse_add_line(
self.auto_unit(p['memory_swap'],
low_precision=False),
decoration='INFO',
splittable=True
self.auto_unit(p['memory_swap'], low_precision=False), decoration='INFO', splittable=True
)
)
ret.append(self.curse_add_line(' swap ', splittable=True))

View File

@ -482,8 +482,9 @@ class GlancesProcesses(object):
processlist = list(filter(lambda p: not self._filter.is_filtered(p), processlist))
# Save the new processlist and transform all namedtuples to dict
self.processlist = [{k: (v._asdict() if hasattr(v, '_asdict') else v)
for k, v in p.items()} for p in processlist]
self.processlist = [
{k: (v._asdict() if hasattr(v, '_asdict') else v) for k, v in p.items()} for p in processlist
]
# Compute the maximum value for keys in self._max_values_list: CPU, MEM
# Useful to highlight the processes with maximum values

View File

@ -44,10 +44,8 @@ def update_program_dict(program, p):
program['num_threads'] += p['num_threads'] or 0
program['cpu_percent'] += p['cpu_percent'] or 0
program['memory_percent'] += p['memory_percent'] or 0
program['cpu_times'] = dict(Counter(program['cpu_times'] or {}) +
Counter(p['cpu_times'] or {}))
program['memory_info'] = dict(Counter(program['memory_info'] or {}) +
Counter(p['memory_info'] or {}))
program['cpu_times'] = dict(Counter(program['cpu_times'] or {}) + Counter(p['cpu_times'] or {}))
program['memory_info'] = dict(Counter(program['memory_info'] or {}) + Counter(p['memory_info'] or {}))
program['io_counters'] += p['io_counters']
program['childrens'].append(p['pid'])

View File

@ -138,9 +138,10 @@ class GlancesStats(object):
logger.debug("Active plugins list: {}".format(self.getPluginsList()))
def load_additional_plugins(self, args=None, config=None):
""" Load additional plugins if defined """
"""Load additional plugins if defined"""
def get_addl_plugins(self, plugin_path):
""" Get list of additonal plugins """
"""Get list of additonal plugins"""
_plugin_list = []
for plugin in os.listdir(plugin_path):
path = os.path.join(plugin_path, plugin)
@ -175,7 +176,7 @@ class GlancesStats(object):
else:
start_duration.reset()
try:
_mod_loaded = import_module(plugin+'.model')
_mod_loaded = import_module(plugin + '.model')
self._plugins[plugin] = _mod_loaded.PluginModel(args=args, config=config)
logger.debug("Plugin {} started in {} seconds".format(plugin, start_duration.get()))
except Exception as e: