First step done - Rest server are managed in the current TUI client browser

This commit is contained in:
nicolargo 2024-11-01 14:29:33 +01:00
parent f12de6619b
commit d538b2dd13
5 changed files with 115 additions and 34 deletions

View File

@ -542,14 +542,23 @@ disable=False
# You can also add stats with key, like sensors:value:Ambient (key is case sensitive)
#columns=system:hr_name,load:min5,cpu:total,mem:percent,memswap:percent,sensors:value:Ambient,sensors:value:Composite
# Define the static servers list
# _protocol can be: rpc (default if not defined) or rest
# List is limited to 256 servers max (1 to 256)
server_1_name=localhost
server_1_alias=Local WebUI
server_1_port=61208
server_1_protocol=rest
#server_1_name=localhost
#server_1_alias=My local PC
#server_1_port=61209
#server_1_protocol=rpc
#server_2_name=localhost
#server_2_port=61235
#server_2_protocol=rpc
#server_3_name=192.168.0.17
#server_3_alias=Another PC on my network
#server_3_port=61209
#server_1_protocol=rpc
#server_4_name=pasbon
#server_4_port=61237

View File

@ -42,7 +42,7 @@ class AutoDiscovered:
def __init__(self):
# server_dict is a list of dict (JSON compliant)
# [ {'key': 'zeroconf name', ip': '172.1.2.3', 'port': 61209, 'cpu': 3, 'mem': 34 ...} ... ]
# [ {'key': 'zeroconf name', ip': '172.1.2.3', 'port': 61209, 'protocol': 'rpc', 'cpu': 3, 'mem': 34 ...} ... ]
self._server_list = []
def get_servers_list(self):
@ -60,6 +60,7 @@ class AutoDiscovered:
'name': name.split(':')[0], # Short name
'ip': ip, # IP address seen by the client
'port': port, # TCP port
'protocol': 'rpc', # RPC protocol
'username': 'glances', # Default username
'password': '', # Default password
'status': 'UNKNOWN', # Server status: 'UNKNOWN', 'OFFLINE', 'ONLINE', 'PROTECTED'

View File

@ -9,9 +9,11 @@
"""Manage the Glances client browser (list of Glances server)."""
import threading
import webbrowser
from defusedxml import xmlrpc
from glances import __apiversion__
from glances.autodiscover import GlancesAutoDiscoverServer
from glances.client import GlancesClient, GlancesClientTransport
from glances.globals import json_loads
@ -20,6 +22,15 @@ from glances.outputs.glances_curses_browser import GlancesCursesBrowser
from glances.password_list import GlancesPasswordList as GlancesPassword
from glances.static_list import GlancesStaticServer
try:
import requests
except ImportError as e:
import_requests_error_tag = True
# Display debug message if import error
logger.warning(f"Missing Python Lib ({e}), Client browser will not grab stats from Glances REST server")
else:
import_requests_error_tag = False
# Correct issue #1025 by monkey path the xmlrpc lib
xmlrpc.monkey_patch()
@ -77,14 +88,18 @@ class GlancesClientBrowser:
clear_password = self.password.get_password(server['name'])
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'])
return 'http://{}:{}'.format(server['ip'], server['port'])
uri = 'http://{}:{}@{}:{}'.format(server['username'], server['password'], server['ip'], server['port'])
else:
uri = 'http://{}:{}'.format(server['ip'], server['port'])
return uri
def __update_stats(self, server):
"""Update stats for the given server (picked from the server list)"""
# Get the server URI
uri = self.__get_uri(server)
def __get_key(self, column):
server_key = column.get('plugin') + '_' + column.get('field')
if 'key' in column:
server_key += '_' + column.get('key')
return server_key
def __update_stats_rpc(self, uri, server):
# Try to connect to the server
t = GlancesClientTransport()
t.set_timeout(3)
@ -98,9 +113,7 @@ class GlancesClientBrowser:
# Get the stats
for column in self.static_server.get_columns():
server_key = column.get('plugin') + '_' + column.get('field')
if 'key' in column:
server_key += '_' + column.get('key')
server_key = self.__get_key(column)
try:
# Value
v_json = json_loads(s.getPlugin(column['plugin']))
@ -132,11 +145,55 @@ class GlancesClientBrowser:
return server
def __update_stats_rest(self, uri, server):
try:
requests.get(f'{uri}/status', timeout=3)
except requests.exceptions.RequestException as e:
logger.debug(f"Error while grabbing stats form server ({e})")
server['status'] = 'OFFLINE'
return server
else:
server['status'] = 'ONLINE'
for column in self.static_server.get_columns():
server_key = self.__get_key(column)
try:
r = requests.get(f'{uri}/{column['plugin']}/{column['field']}', timeout=3)
except requests.exceptions.RequestException as e:
logger.debug(f"Error while grabbing stats form server ({e})")
return server
else:
server[server_key] = r.json()[column['field']]
return server
def __update_stats(self, server):
"""Update stats for the given server (picked from the server list)"""
# Get the server URI
uri = self.__get_uri(server)
if server['protocol'].lower() == 'rpc':
self.__update_stats_rpc(uri, server)
elif server['protocol'].lower() == 'rest' and not import_requests_error_tag:
self.__update_stats_rest(f'{uri}/api/{__apiversion__}', server)
return server
def __display_server(self, server):
"""Connect and display the given server"""
# Display the Glances client for the selected server
logger.debug(f"Selected server {server}")
if server['protocol'].lower() == 'rest':
# Display a popup
self.screen.display_popup(
'Open the WebUI {}:{} in a Web Browser'.format(server['name'], server['port']), duration=1
)
# Try to open a Webbrowser
webbrowser.open(self.__get_uri(server), new=2, autoraise=1)
self.screen.active_server = None
return
# Connection can take time
# Display a popup
self.screen.display_popup('Connect to {}:{}'.format(server['name'], server['port']), duration=1)

View File

@ -290,7 +290,7 @@ class GlancesCursesBrowser(_GlancesCurses):
def __build_column_def(self, current_page):
"""Define the column and it size to display in the browser"""
column_def = {'name': 16, 'ip': 15, 'status': 9}
column_def = {'name': 16, 'ip': 15, 'status': 9, 'protocol': 8}
# Add dynamic columns
for server_stat in current_page:

View File

@ -22,7 +22,7 @@ class GlancesStaticServer:
def __init__(self, config=None, args=None):
# server_list is a list of dict (JSON compliant)
# [ {'key': 'zeroconf name', ip': '172.1.2.3', 'port': 61209, 'cpu': 3, 'mem': 34 ...} ... ]
# [ {'key': 'zeroconf name', ip': '172.1.2.3', 'port': 61209, 'protocol': 'rpc', 'cpu': 3, 'mem': 34 ...} ... ]
# Load server list from the Glances configuration file
self._server_list = self.load_server_list(config)
# Load columns to grab/display in the browser mode
@ -39,34 +39,48 @@ class GlancesStaticServer:
else:
logger.info(f"Start reading the [{self._section}] section in the configuration file")
for i in range(1, 256):
# Read the configuration
new_server = {}
postfix = f'server_{str(i)}_'
# Read the server name (mandatory)
for s in ['name', 'port', 'alias']:
for s in ['name', 'port', 'alias', 'protocol']:
new_server[s] = config.get_value(self._section, f'{postfix}{s}')
if new_server['name'] is not None:
# Manage optional information
if new_server['port'] is None:
new_server['port'] = '61209'
new_server['username'] = 'glances'
# By default, try empty (aka no) password
new_server['password'] = ''
try:
new_server['ip'] = gethostbyname(new_server['name'])
except gaierror as e:
logger.error("Cannot get IP address for server {} ({})".format(new_server['name'], e))
continue
new_server['key'] = new_server['name'] + ':' + new_server['port']
# Default status is 'UNKNOWN'
new_server['status'] = 'UNKNOWN'
if new_server['name'] is None:
logger.error(f'Name not define for {postfix}, skip it.')
continue
# Server type is 'STATIC'
new_server['type'] = 'STATIC'
# Type in order to support both RPC and REST servers (see #1121)
if new_server['protocol'] is None:
new_server['protocol'] = 'rpc'
new_server['protocol'] = new_server['protocol'].lower()
if new_server['protocol'] not in ('rpc', 'rest'):
logger.error(f'Unknow protocol for {postfix}, skip it.')
continue
# Add the server to the list
logger.debug("Add server {} to the static list".format(new_server['name']))
server_list.append(new_server)
# Default port
if new_server['port'] is None:
new_server['port'] = '61209' if new_server['type'] == 'rpc' else '61208'
# By default, try empty (aka no) password
new_server['username'] = 'glances'
new_server['password'] = ''
try:
new_server['ip'] = gethostbyname(new_server['name'])
except gaierror as e:
logger.error("Cannot get IP address for server {} ({})".format(new_server['name'], e))
continue
new_server['key'] = new_server['name'] + ':' + new_server['port']
# Default status is 'UNKNOWN'
new_server['status'] = 'UNKNOWN'
# Server type is 'STATIC'
new_server['type'] = 'STATIC'
# Add the server to the list
logger.debug("Add server {} to the static list".format(new_server['name']))
server_list.append(new_server)
# Server list loaded
logger.info(f"{len(server_list)} server(s) loaded from the configuration file")