mirror of
https://github.com/nicolargo/glances.git
synced 2024-11-23 20:45:33 +03:00
Replace Bottle by FastAPI #2181
This commit is contained in:
parent
32b33b5883
commit
b3828f5497
2
Makefile
2
Makefile
@ -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>"
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -36,7 +36,7 @@ class GlancesExport(object):
|
||||
'processlist',
|
||||
'psutilversion',
|
||||
'quicklook',
|
||||
'version'
|
||||
'version',
|
||||
]
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
|
@ -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...
|
||||
|
@ -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)
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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=' '):
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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'])
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user