Implement a new API method to limit the number of returned list object #2491

pull/2527/head
nicolargo 2023-08-14 10:30:20 +02:00
parent c5e11cef59
commit ef2f949f04
4 changed files with 79 additions and 20 deletions

View File

@ -2,12 +2,12 @@
#
# This file is part of Glances.
#
# SPDX-FileCopyrightText: 2022 Nicolas Hennion <nicolas@nicolargo.com>
# SPDX-FileCopyrightText: 2023 Nicolas Hennion <nicolas@nicolargo.com>
#
# SPDX-License-Identifier: LGPL-3.0-only
#
"""Web interface class."""
"""RestFull API interface class."""
import os
import sys
@ -152,6 +152,7 @@ class GlancesBottle(object):
self._app.route(
'/api/%s/<plugin>/history/<nb:int>' % self.API_VERSION, method="GET", callback=self._api_history
)
self._app.route('/api/%s/<plugin>/top/<nb:int>' % self.API_VERSION, method="GET", callback=self._api_top)
self._app.route('/api/%s/<plugin>/limits' % self.API_VERSION, method="GET", callback=self._api_limits)
self._app.route('/api/%s/<plugin>/views' % self.API_VERSION, method="GET", callback=self._api_views)
self._app.route('/api/%s/<plugin>/<item>' % self.API_VERSION, method="GET", callback=self._api_item)
@ -393,6 +394,36 @@ class GlancesBottle(object):
return statval
@compress
def _api_top(self, plugin, nb=0):
"""Glances API RESTful implementation.
Return the JSON representation of a given plugin limited to the top nb items.
It is used to reduce the payload of the HTTP response (example: processlist).
HTTP/200 if OK
HTTP/400 if plugin is not found
HTTP/404 if others error
"""
response.content_type = 'application/json; charset=utf-8'
if plugin not in self.plugins_list:
abort(400, "Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list))
# Update the stat
self.__update__()
try:
# Get the value of the stat ID
statval = self.stats.get_plugin(plugin).get_export()
except Exception as e:
abort(404, "Cannot get plugin %s (%s)" % (plugin, str(e)))
if isinstance(statval, list):
return json_dumps(statval[:nb])
else:
return json_dumps(statval)
@compress
def _api_history(self, plugin, nb=0):
"""Glances API RESTful implementation.

View File

@ -163,6 +163,22 @@ def print_all():
print('')
def print_top(stats):
time.sleep(1)
stats.update()
sub_title = 'GET top n items of a specific plugin'
print(sub_title)
print('-' * len(sub_title))
print('')
print('Get top 2 processes of the processlist plugin::')
print('')
print(' # curl {}/processlist/top/2'.format(API_URL))
print(indent_stat(stats.get_plugin('processlist').get_export()[:2]))
print('')
print('Note: Only work for plugin with a list of items')
print('')
def print_history(stats):
time.sleep(1)
stats.update()
@ -248,6 +264,10 @@ class GlancesStdoutApiDoc(object):
# Get all stats
print_all()
# Get top stats (only for plugins with a list of items)
# Example for processlist plugin: get top 2 processes
print_top(stats)
# History
print_history(stats)

View File

@ -47,8 +47,7 @@ WIRELESS_FILE = '/proc/net/wireless'
wireless_file_exists = file_exists(WIRELESS_FILE)
if not nmcli_command_exists and not wireless_file_exists:
logger.debug("Wifi plugin is disabled (no %s command or %s file found)" % ('nmcli',
WIRELESS_FILE))
logger.debug("Wifi plugin is disabled (no %s command or %s file found)" % ('nmcli', WIRELESS_FILE))
class PluginModel(GlancesPluginModel):
@ -121,12 +120,14 @@ class PluginModel(GlancesPluginModel):
# Extract the stats
wifi_stats = wifi_stats.split()
# Add the Wifi link to the list
stats.append({
'key': self.get_key(),
'ssid': wifi_stats[0][:-1],
'signal': float(wifi_stats[3]),
'security': ''
})
stats.append(
{
'key': self.get_key(),
'ssid': wifi_stats[0][:-1],
'signal': float(wifi_stats[3]),
'security': '',
}
)
# Next line
wifi_stats = f.readline()
@ -202,11 +203,11 @@ class PluginModel(GlancesPluginModel):
hotspot_name = i['ssid']
# Cut hotspot_name if it is too long
if len(hotspot_name) > if_name_max_width:
hotspot_name = '_' + hotspot_name[-if_name_max_width - len(i['security']) + 1:]
hotspot_name = '_' + hotspot_name[-if_name_max_width - len(i['security']) + 1 :]
# Add the new hotspot to the message
msg = '{:{width}} {security}'.format(nativestr(hotspot_name),
width=if_name_max_width - len(i['security']) - 1,
security=i['security'])
msg = '{:{width}} {security}'.format(
nativestr(hotspot_name), width=if_name_max_width - len(i['security']) - 1, security=i['security']
)
ret.append(self.curse_add_line(msg))
msg = '{:>7}'.format(
i['signal'],
@ -244,12 +245,7 @@ class ThreadHotspot(threading.Thread):
if len(h) != 4 or h[0] != 'yes':
# Do not process the line if it is not the active hotspot
continue
nmcli_result.append({
'key': 'ssid',
'ssid': h[1],
'signal': -float(h[2]),
'security': h[3]
})
nmcli_result.append({'key': 'ssid', 'ssid': h[1], 'signal': -float(h[2]), 'security': h[3]})
self.thread_stats = nmcli_result
# Wait refresh time until next scan
# Note: nmcli cache the result for x seconds

View File

@ -231,6 +231,18 @@ class TestGlances(unittest.TestCase):
self.assertTrue(req.ok)
self.assertEqual(req.text, "Active")
def test_013_top(self):
"""Values."""
method = "processlist"
request = "%s/%s/top/2" % (URL, method)
print('INFO: [TEST_013] Top nb item of PROCESSLIST')
print(request)
req = self.http_get(request)
self.assertTrue(req.ok)
self.assertIsInstance(req.json(), list)
self.assertEqual(len(req.json()), 2)
def test_999_stop_server(self):
"""Stop the Glances Web Server."""
print('INFO: [TEST_999] Stop the Glances Web Server')