Test Failed
Push — master ( ee826a...d9056e )
by Nicolas
03:09
created

GlancesRestfulApi._api_item_history()   A

Complexity

Conditions 4

Size

Total Lines 27
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 12
nop 4
dl 0
loc 27
rs 9.8
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# SPDX-FileCopyrightText: 2024 Nicolas Hennion <[email protected]>
6
#
7
# SPDX-License-Identifier: LGPL-3.0-only
8
#
9
10
"""RestFull API interface class."""
11
12
import os
13
import sys
14
import tempfile
15
from io import open
16
import webbrowser
17
from urllib.parse import urljoin
18
19
# Replace typing_extensions by typing when Python 3.8 support will be dropped
20
from typing import Annotated
21
22
from glances import __version__, __apiversion__
23
from glances.password import GlancesPassword
24
from glances.timer import Timer
25
from glances.logger import logger
26
27
# FastAPI import
28
try:
29
    from fastapi import FastAPI, Depends, HTTPException, status, APIRouter, Request
30
    from fastapi.security import HTTPBasic, HTTPBasicCredentials
31
    from fastapi.middleware.cors import CORSMiddleware
32
    from fastapi.middleware.gzip import GZipMiddleware
33
    from fastapi.responses import HTMLResponse, ORJSONResponse
34
    from fastapi.templating import Jinja2Templates
35
    from fastapi.staticfiles import StaticFiles
36
except ImportError:
37
    logger.critical('FastAPI import error. Glances cannot start in web server mode.')
38
    sys.exit(2)
39
40
try:
41
    import uvicorn
42
except ImportError:
43
    logger.critical('Uvicorn import error. Glances cannot start in web server mode.')
44
    sys.exit(2)
45
import contextlib
46
import threading
47
import time
48
49
security = HTTPBasic()
50
51
52
class GlancesUvicornServer(uvicorn.Server):
53
    def install_signal_handlers(self):
54
        pass
55
56
    @contextlib.contextmanager
57
    def run_in_thread(self, timeout=3):
58
        thread = threading.Thread(target=self.run)
59
        thread.start()
60
        try:
61
            chrono = Timer(timeout)
62
            while not self.started and not chrono.finished():
63
                time.sleep(0.5)
64
            # Timeout reached
65
            # Something go wrong...
66
            # The Uvicorn server should be stopped
67
            if not self.started:
68
                self.should_exit = True
69
                thread.join()
70
            yield
71
        finally:
72
            self.should_exit = True
73
            thread.join()
74
75
76
class GlancesRestfulApi(object):
77
    """This class manages the Restful API server."""
78
79
    API_VERSION = __apiversion__
80
81
    def __init__(self, config=None, args=None):
82
        # Init config
83
        self.config = config
84
85
        # Init args
86
        self.args = args
87
88
        # Init stats
89
        # Will be updated within route
90
        self.stats = None
91
92
        # cached_time is the minimum time interval between stats updates
93
        # i.e. HTTP/RESTful calls will not retrieve updated info until the time
94
        # since last update is passed (will retrieve old cached info instead)
95
        self.timer = Timer(0)
96
97
        # Load configuration file
98
        self.load_config(config)
99
100
        # Set the bind URL
101
        self.bind_url = urljoin('http://{}:{}/'.format(self.args.bind_address, self.args.port), self.url_prefix)
102
103
        # FastAPI Init
104
        if self.args.password:
105
            self._app = FastAPI(dependencies=[Depends(self.authentication)])
106
            self._password = GlancesPassword(username=args.username, config=config)
107
108
        else:
109
            self._app = FastAPI()
110
            self._password = None
111
112
        # Change the default root path
113
        if self.url_prefix != '/':
114
            self._app.include_router(APIRouter(prefix=self.url_prefix.rstrip('/')))
115
116
        # Set path for WebUI
117
        webui_root_path = config.get_value(
118
            'outputs', 'webui_root_path', default=os.path.dirname(os.path.realpath(__file__))
119
        )
120
        if webui_root_path == '':
121
            webui_root_path = os.path.dirname(os.path.realpath(__file__))
122
        self.STATIC_PATH = os.path.join(webui_root_path, 'static/public')
123
        self.TEMPLATE_PATH = os.path.join(webui_root_path, 'static/templates')
124
        self._templates = Jinja2Templates(directory=self.TEMPLATE_PATH)
125
126
        # FastAPI Enable CORS
127
        # https://fastapi.tiangolo.com/tutorial/cors/
128
        self._app.add_middleware(
129
            CORSMiddleware,
130
            # allow_origins=["*"],
131
            allow_origins=[self.bind_url],
132
            allow_credentials=True,
133
            allow_methods=["*"],
134
            allow_headers=["*"],
135
        )
136
137
        # FastAPI Enable GZIP compression
138
        # https://fastapi.tiangolo.com/advanced/middleware/
139
        self._app.add_middleware(GZipMiddleware, minimum_size=1000)
140
141
        # FastAPI Define routes
142
        self._app.include_router(self._router())
143
144
    def load_config(self, config):
145
        """Load the outputs section of the configuration file."""
146
        # Limit the number of processes to display in the WebUI
147
        self.url_prefix = '/'
148
        if config is not None and config.has_section('outputs'):
149
            n = config.get_value('outputs', 'max_processes_display', default=None)
150
            logger.debug('Number of processes to display in the WebUI: {}'.format(n))
151
            self.url_prefix = config.get_value('outputs', 'url_prefix', default='/')
152
            logger.debug('URL prefix: {}'.format(self.url_prefix))
153
154
    def __update__(self):
155
        # Never update more than 1 time per cached_time
156
        if self.timer.finished():
157
            self.stats.update()
158
            self.timer = Timer(self.args.cached_time)
159
160
    def app(self):
161
        return self._app()
162
163
    def authentication(self, creds: Annotated[HTTPBasicCredentials, Depends(security)]):
164
        """Check if a username/password combination is valid."""
165
        if creds.username == self.args.username:
166
            # check_password and get_hash are (lru) cached to optimize the requests
167
            if self._password.check_password(self.args.password, self._password.get_hash(creds.password)):
168
                return creds.username
169
170
        # If the username/password combination is invalid, return an HTTP 401
171
        raise HTTPException(
172
            status_code=status.HTTP_401_UNAUTHORIZED,
173
            detail="Incorrect username or password",
174
            headers={"WWW-Authenticate": "Basic"},
175
        )
176
177
    def _router(self):
178
        """Define a custom router for Glances path."""
179
        router = APIRouter()
180
181
        # REST API
182
        router.add_api_route(
183
            '/api/%s/status' % self.API_VERSION,
184
            status_code=status.HTTP_200_OK,
185
            response_class=ORJSONResponse,
186
            endpoint=self._api_status,
187
        )
188
189
        router.add_api_route(
190
            '/api/%s/config' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_config
191
        )
192
        router.add_api_route(
193
            '/api/%s/config/{section}' % self.API_VERSION,
194
            response_class=ORJSONResponse,
195
            endpoint=self._api_config_section,
196
        )
197
        router.add_api_route(
198
            '/api/%s/config/{section}/{item}' % self.API_VERSION,
199
            response_class=ORJSONResponse,
200
            endpoint=self._api_config_section_item,
201
        )
202
203
        router.add_api_route('/api/%s/args' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_args)
204
        router.add_api_route(
205
            '/api/%s/args/{item}' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_args_item
206
        )
207
208
        router.add_api_route(
209
            '/api/%s/pluginslist' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_plugins
210
        )
211
        router.add_api_route('/api/%s/all' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_all)
212
        router.add_api_route(
213
            '/api/%s/all/limits' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_all_limits
214
        )
215
        router.add_api_route(
216
            '/api/%s/all/views' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_all_views
217
        )
218
219
        router.add_api_route('/api/%s/help' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_help)
220
        router.add_api_route('/api/%s/{plugin}' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api)
221
        router.add_api_route(
222
            '/api/%s/{plugin}/history' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_history
223
        )
224
        router.add_api_route(
225
            '/api/%s/{plugin}/history/{nb}' % self.API_VERSION,
226
            response_class=ORJSONResponse,
227
            endpoint=self._api_history,
228
        )
229
        router.add_api_route(
230
            '/api/%s/{plugin}/top/{nb}' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_top
231
        )
232
        router.add_api_route(
233
            '/api/%s/{plugin}/limits' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_limits
234
        )
235
        router.add_api_route(
236
            '/api/%s/{plugin}/views' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_views
237
        )
238
        router.add_api_route(
239
            '/api/%s/{plugin}/{item}' % self.API_VERSION, response_class=ORJSONResponse, endpoint=self._api_item
240
        )
241
        router.add_api_route(
242
            '/api/%s/{plugin}/{item}/history' % self.API_VERSION,
243
            response_class=ORJSONResponse,
244
            endpoint=self._api_item_history,
245
        )
246
        router.add_api_route(
247
            '/api/%s/{plugin}/{item}/history/{nb}' % self.API_VERSION,
248
            response_class=ORJSONResponse,
249
            endpoint=self._api_item_history,
250
        )
251
        router.add_api_route(
252
            '/api/%s/{plugin}/{item}/description' % self.API_VERSION,
253
            response_class=ORJSONResponse,
254
            endpoint=self._api_item_description,
255
        )
256
        router.add_api_route(
257
            '/api/%s/{plugin}/{item}/unit' % self.API_VERSION,
258
            response_class=ORJSONResponse,
259
            endpoint=self._api_item_unit,
260
        )
261
        router.add_api_route(
262
            '/api/%s/{plugin}/{item}/{value}' % self.API_VERSION,
263
            response_class=ORJSONResponse,
264
            endpoint=self._api_value,
265
        )
266
267
        # Restful API
268
        bindmsg = 'Glances RESTful API Server started on {}api/{}'.format(self.bind_url, self.API_VERSION)
269
        logger.info(bindmsg)
270
271
        # WEB UI
272
        if not self.args.disable_webui:
273
            # Template for the root index.html file
274
            router.add_api_route('/', response_class=HTMLResponse, endpoint=self._index)
275
276
            # Statics files
277
            self._app.mount("/static", StaticFiles(directory=self.STATIC_PATH), name="static")
278
279
            logger.info("Get WebUI in {}".format(self.STATIC_PATH))
280
281
            bindmsg = 'Glances Web User Interface started on {}'.format(self.bind_url)
282
        else:
283
            bindmsg = 'The WebUI is disable (--disable-webui)'
284
285
        logger.info(bindmsg)
286
        print(bindmsg)
287
288
        return router
289
290
    def start(self, stats):
291
        """Start the bottle."""
292
        # Init stats
293
        self.stats = stats
294
295
        # Init plugin list
296
        self.plugins_list = self.stats.getPluginsList()
297
298
        if self.args.open_web_browser:
299
            # Implementation of the issue #946
300
            # Try to open the Glances Web UI in the default Web browser if:
301
            # 1) --open-web-browser option is used
302
            # 2) Glances standalone mode is running on Windows OS
303
            webbrowser.open(self.bind_url, new=2, autoraise=1)
304
305
        # Start Uvicorn server
306
        self._start_uvicorn()
307
308
    def _start_uvicorn(self):
309
        # Run the Uvicorn Web server
310
        uvicorn_config = uvicorn.Config(
311
            self._app, host=self.args.bind_address, port=self.args.port, access_log=self.args.debug
312
        )
313
        try:
314
            self.uvicorn_server = GlancesUvicornServer(config=uvicorn_config)
315
        except Exception as e:
316
            logger.critical('Error: Can not ran Glances Web server ({})'.format(e))
317
            self.uvicorn_server = None
318
        else:
319
            with self.uvicorn_server.run_in_thread():
320
                while not self.uvicorn_server.should_exit:
321
                    time.sleep(1)
322
323
    def end(self):
324
        """End the Web server"""
325
        logger.info("Close the Web server")
326
327
    def _index(self, request: Request):
328
        """Return main index.html (/) file.
329
330
        Parameters are available through the request object.
331
        Example: http://localhost:61208/?refresh=5
332
333
        Note: This function is only called the first time the page is loaded.
334
        """
335
        refresh_time = request.query_params.get('refresh', default=max(1, int(self.args.time)))
336
337
        # Update the stat
338
        self.__update__()
339
340
        # Display
341
        return self._templates.TemplateResponse(
342
            "index.html",
343
            {
344
                "request": request,
345
                "refresh_time": refresh_time,
346
            },
347
        )
348
349
    def _api_status(self):
350
        """Glances API RESTful implementation.
351
352
        Return a 200 status code.
353
        This entry point should be used to check the API health.
354
355
        See related issue:  Web server health check endpoint #1988
356
        """
357
358
        return ORJSONResponse({'version': __version__})
359
360
    def _api_help(self):
361
        """Glances API RESTful implementation.
362
363
        Return the help data or 404 error.
364
        """
365
        try:
366
            plist = self.stats.get_plugin("help").get_view_data()
367
        except Exception as e:
368
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get help view data (%s)" % str(e))
369
370
        return ORJSONResponse(plist)
371
372
    def _api_plugins(self):
373
        """Glances API RESTFul implementation.
374
375
        @api {get} /api/%s/pluginslist Get plugins list
376
        @apiVersion 2.0
377
        @apiName pluginslist
378
        @apiGroup plugin
379
380
        @apiSuccess {String[]} Plugins list.
381
382
        @apiSuccessExample Success-Response:
383
            HTTP/1.1 200 OK
384
            [
385
               "load",
386
               "help",
387
               "ip",
388
               "memswap",
389
               "processlist",
390
               ...
391
            ]
392
393
         @apiError Cannot get plugin list.
394
395
         @apiErrorExample Error-Response:
396
            HTTP/1.1 404 Not Found
397
        """
398
        # Update the stat
399
        self.__update__()
400
401
        try:
402
            plist = self.plugins_list
403
        except Exception as e:
404
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get plugin list (%s)" % str(e))
405
406
        return ORJSONResponse(plist)
407
408
    def _api_all(self):
409
        """Glances API RESTful implementation.
410
411
        Return the JSON representation of all the plugins
412
        HTTP/200 if OK
413
        HTTP/400 if plugin is not found
414
        HTTP/404 if others error
415
        """
416
        if self.args.debug:
417
            fname = os.path.join(tempfile.gettempdir(), 'glances-debug.json')
418
            try:
419
                with open(fname) as f:
420
                    return f.read()
421
            except IOError:
422
                logger.debug("Debug file (%s) not found" % fname)
423
424
        # Update the stat
425
        self.__update__()
426
427
        try:
428
            # Get the RAW value of the stat ID
429
            statval = self.stats.getAllAsDict()
430
        except Exception as e:
431
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get stats (%s)" % str(e))
432
433
        return ORJSONResponse(statval)
434
435
    def _api_all_limits(self):
436
        """Glances API RESTful implementation.
437
438
        Return the JSON representation of all the plugins limits
439
        HTTP/200 if OK
440
        HTTP/400 if plugin is not found
441
        HTTP/404 if others error
442
        """
443
        try:
444
            # Get the RAW value of the stat limits
445
            limits = self.stats.getAllLimitsAsDict()
446
        except Exception as e:
447
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get limits (%s)" % str(e))
448
449
        return ORJSONResponse(limits)
450
451
    def _api_all_views(self):
452
        """Glances API RESTful implementation.
453
454
        Return the JSON representation of all the plugins views
455
        HTTP/200 if OK
456
        HTTP/400 if plugin is not found
457
        HTTP/404 if others error
458
        """
459
        try:
460
            # Get the RAW value of the stat view
461
            limits = self.stats.getAllViewsAsDict()
462
        except Exception as e:
463
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get views (%s)" % str(e))
464
465
        return ORJSONResponse(limits)
466
467
    def _api(self, plugin):
468
        """Glances API RESTful implementation.
469
470
        Return the JSON representation of a given plugin
471
        HTTP/200 if OK
472
        HTTP/400 if plugin is not found
473
        HTTP/404 if others error
474
        """
475
        if plugin not in self.plugins_list:
476
            raise HTTPException(
477
                status_code=status.HTTP_400_BAD_REQUEST,
478
                detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
479
            )
480
481
        # Update the stat
482
        self.__update__()
483
484
        try:
485
            # Get the RAW value of the stat ID
486
            statval = self.stats.get_plugin(plugin).get_raw()
487
        except Exception as e:
488
            raise HTTPException(
489
                status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get plugin %s (%s)" % (plugin, str(e))
490
            )
491
492
        return ORJSONResponse(statval)
493
494
    def _api_top(self, plugin, nb: int = 0):
495
        """Glances API RESTful implementation.
496
497
        Return the JSON representation of a given plugin limited to the top nb items.
498
        It is used to reduce the payload of the HTTP response (example: processlist).
499
500
        HTTP/200 if OK
501
        HTTP/400 if plugin is not found
502
        HTTP/404 if others error
503
        """
504
        if plugin not in self.plugins_list:
505
            raise HTTPException(
506
                status_code=status.HTTP_400_BAD_REQUEST,
507
                detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
508
            )
509
510
        # Update the stat
511
        self.__update__()
512
513
        try:
514
            # Get the RAW value of the stat ID
515
            statval = self.stats.get_plugin(plugin).get_raw()
516
        except Exception as e:
517
            raise HTTPException(
518
                status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get plugin %s (%s)" % (plugin, str(e))
519
            )
520
521
        print(statval)
522
523
        if isinstance(statval, list):
524
            statval = statval[:nb]
525
526
        return ORJSONResponse(statval)
527
528
    def _api_history(self, plugin, nb: int = 0):
529
        """Glances API RESTful implementation.
530
531
        Return the JSON representation of a given plugin history
532
        Limit to the last nb items (all if nb=0)
533
        HTTP/200 if OK
534
        HTTP/400 if plugin is not found
535
        HTTP/404 if others error
536
        """
537
        if plugin not in self.plugins_list:
538
            raise HTTPException(
539
                status_code=status.HTTP_400_BAD_REQUEST,
540
                detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
541
            )
542
543
        # Update the stat
544
        self.__update__()
545
546
        try:
547
            # Get the RAW value of the stat ID
548
            statval = self.stats.get_plugin(plugin).get_raw_history(nb=int(nb))
549
        except Exception as e:
550
            raise HTTPException(
551
                status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get plugin history %s (%s)" % (plugin, str(e))
552
            )
553
554
        return statval
555
556
    def _api_limits(self, plugin):
557
        """Glances API RESTful implementation.
558
559
        Return the JSON limits of a given plugin
560
        HTTP/200 if OK
561
        HTTP/400 if plugin is not found
562
        HTTP/404 if others error
563
        """
564
        if plugin not in self.plugins_list:
565
            raise HTTPException(
566
                status_code=status.HTTP_400_BAD_REQUEST,
567
                detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
568
            )
569
570
        try:
571
            # Get the RAW value of the stat limits
572
            ret = self.stats.get_plugin(plugin).limits
573
        except Exception as e:
574
            raise HTTPException(
575
                status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get limits for plugin %s (%s)" % (plugin, str(e))
576
            )
577
578
        return ORJSONResponse(ret)
579
580
    def _api_views(self, plugin):
581
        """Glances API RESTful implementation.
582
583
        Return the JSON views of a given plugin
584
        HTTP/200 if OK
585
        HTTP/400 if plugin is not found
586
        HTTP/404 if others error
587
        """
588
        if plugin not in self.plugins_list:
589
            raise HTTPException(
590
                status_code=status.HTTP_400_BAD_REQUEST,
591
                detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
592
            )
593
594
        try:
595
            # Get the RAW value of the stat views
596
            ret = self.stats.get_plugin(plugin).get_views()
597
        except Exception as e:
598
            raise HTTPException(
599
                status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get views for plugin %s (%s)" % (plugin, str(e))
600
            )
601
602
        return ORJSONResponse(ret)
603
604
    def _api_item(self, plugin, item):
605
        """Glances API RESTful implementation.
606
607
        Return the JSON representation of the couple plugin/item
608
        HTTP/200 if OK
609
        HTTP/400 if plugin is not found
610
        HTTP/404 if others error
611
        """
612
        if plugin not in self.plugins_list:
613
            raise HTTPException(
614
                status_code=status.HTTP_400_BAD_REQUEST,
615
                detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
616
            )
617
618
        # Update the stat
619
        self.__update__()
620
621
        try:
622
            # Get the RAW value of the stat views
623
            ret = self.stats.get_plugin(plugin).get_raw_stats_item(item)
624
        except Exception as e:
625
            raise HTTPException(
626
                status_code=status.HTTP_404_NOT_FOUND,
627
                detail="Cannot get item %s in plugin %s (%s)" % (item, plugin, str(e)),
628
            )
629
630
        return ORJSONResponse(ret)
631
632
    def _api_item_history(self, plugin, item, nb: int = 0):
633
        """Glances API RESTful implementation.
634
635
        Return the JSON representation of the couple plugin/history of item
636
        HTTP/200 if OK
637
        HTTP/400 if plugin is not found
638
        HTTP/404 if others error
639
640
        """
641
        if plugin not in self.plugins_list:
642
            raise HTTPException(
643
                status_code=status.HTTP_400_BAD_REQUEST,
644
                detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
645
            )
646
647
        # Update the stat
648
        self.__update__()
649
650
        try:
651
            # Get the RAW value of the stat history
652
            ret = self.stats.get_plugin(plugin).get_raw_history(item, nb=nb)
653
        except Exception as e:
654
            raise HTTPException(
655
                status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get history for plugin %s (%s)" % (plugin, str(e))
656
            )
657
        else:
658
            return ORJSONResponse(ret)
659
660 View Code Duplication
    def _api_item_description(self, plugin, item):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
661
        """Glances API RESTful implementation.
662
663
        Return the JSON representation of the couple plugin/item description
664
        HTTP/200 if OK
665
        HTTP/400 if plugin is not found
666
        HTTP/404 if others error
667
        """
668
        if plugin not in self.plugins_list:
669
            raise HTTPException(
670
                status_code=status.HTTP_400_BAD_REQUEST,
671
                detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
672
            )
673
674
        try:
675
            # Get the description
676
            ret = self.stats.get_plugin(plugin).get_item_info(item, 'description')
677
        except Exception as e:
678
            raise HTTPException(
679
                status_code=status.HTTP_404_NOT_FOUND,
680
                detail="Cannot get %s description for plugin %s (%s)" % (item, plugin, str(e)),
681
            )
682
        else:
683
            return ORJSONResponse(ret)
684
685 View Code Duplication
    def _api_item_unit(self, plugin, item):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
686
        """Glances API RESTful implementation.
687
688
        Return the JSON representation of the couple plugin/item unit
689
        HTTP/200 if OK
690
        HTTP/400 if plugin is not found
691
        HTTP/404 if others error
692
        """
693
        if plugin not in self.plugins_list:
694
            raise HTTPException(
695
                status_code=status.HTTP_400_BAD_REQUEST,
696
                detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
697
            )
698
699
        try:
700
            # Get the unit
701
            ret = self.stats.get_plugin(plugin).get_item_info(item, 'unit')
702
        except Exception as e:
703
            raise HTTPException(
704
                status_code=status.HTTP_404_NOT_FOUND,
705
                detail="Cannot get %s unit for plugin %s (%s)" % (item, plugin, str(e)),
706
            )
707
        else:
708
            return ORJSONResponse(ret)
709
710
    def _api_value(self, plugin, item, value):
711
        """Glances API RESTful implementation.
712
713
        Return the process stats (dict) for the given item=value
714
        HTTP/200 if OK
715
        HTTP/400 if plugin is not found
716
        HTTP/404 if others error
717
        """
718
        if plugin not in self.plugins_list:
719
            raise HTTPException(
720
                status_code=status.HTTP_400_BAD_REQUEST,
721
                detail="Unknown plugin %s (available plugins: %s)" % (plugin, self.plugins_list),
722
            )
723
724
        # Update the stat
725
        self.__update__()
726
727
        try:
728
            # Get the RAW value
729
            ret = self.stats.get_plugin(plugin).get_raw_stats_value(item, value)
730
        except Exception as e:
731
            raise HTTPException(
732
                status_code=status.HTTP_404_NOT_FOUND,
733
                detail="Cannot get %s = %s for plugin %s (%s)" % (item, value, plugin, str(e)),
734
            )
735
        else:
736
            return ORJSONResponse(ret)
737
738
    def _api_config(self):
739
        """Glances API RESTful implementation.
740
741
        Return the JSON representation of the Glances configuration file
742
        HTTP/200 if OK
743
        HTTP/404 if others error
744
        """
745
        try:
746
            # Get the RAW value of the config' dict
747
            args_json = self.config.as_dict()
748
        except Exception as e:
749
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get config (%s)" % str(e))
750
        else:
751
            return ORJSONResponse(args_json)
752
753
    def _api_config_section(self, section):
754
        """Glances API RESTful implementation.
755
756
        Return the JSON representation of the Glances configuration section
757
        HTTP/200 if OK
758
        HTTP/400 if item is not found
759
        HTTP/404 if others error
760
        """
761
        config_dict = self.config.as_dict()
762
        if section not in config_dict:
763
            raise HTTPException(
764
                status_code=status.HTTP_400_BAD_REQUEST, detail="Unknown configuration item %s" % section
765
            )
766
767
        try:
768
            # Get the RAW value of the config' dict
769
            ret_section = config_dict[section]
770
        except Exception as e:
771
            raise HTTPException(
772
                status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get config section %s (%s)" % (section, str(e))
773
            )
774
775
        return ORJSONResponse(ret_section)
776
777
    def _api_config_section_item(self, section, item):
778
        """Glances API RESTful implementation.
779
780
        Return the JSON representation of the Glances configuration section/item
781
        HTTP/200 if OK
782
        HTTP/400 if item is not found
783
        HTTP/404 if others error
784
        """
785
        config_dict = self.config.as_dict()
786
        if section not in config_dict:
787
            raise HTTPException(
788
                status_code=status.HTTP_400_BAD_REQUEST, detail="Unknown configuration item %s" % section
789
            )
790
791
        try:
792
            # Get the RAW value of the config' dict section
793
            ret_section = config_dict[section]
794
        except Exception as e:
795
            raise HTTPException(
796
                status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get config section %s (%s)" % (section, str(e))
797
            )
798
799
        try:
800
            # Get the RAW value of the config' dict item
801
            ret_item = ret_section[item]
802
        except Exception as e:
803
            raise HTTPException(
804
                status_code=status.HTTP_404_NOT_FOUND,
805
                detail="Cannot get item %s in config section %s (%s)" % (item, section, str(e)),
806
            )
807
808
        return ORJSONResponse(ret_item)
809
810
    def _api_args(self):
811
        """Glances API RESTful implementation.
812
813
        Return the JSON representation of the Glances command line arguments
814
        HTTP/200 if OK
815
        HTTP/404 if others error
816
        """
817
        try:
818
            # Get the RAW value of the args' dict
819
            # Use vars to convert namespace to dict
820
            # Source: https://docs.python.org/%s/library/functions.html#vars
821
            args_json = vars(self.args)
822
        except Exception as e:
823
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get args (%s)" % str(e))
824
825
        return ORJSONResponse(args_json)
826
827
    def _api_args_item(self, item):
828
        """Glances API RESTful implementation.
829
830
        Return the JSON representation of the Glances command line arguments item
831
        HTTP/200 if OK
832
        HTTP/400 if item is not found
833
        HTTP/404 if others error
834
        """
835
        if item not in self.args:
836
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Unknown argument item %s" % item)
837
838
        try:
839
            # Get the RAW value of the args' dict
840
            # Use vars to convert namespace to dict
841
            # Source: https://docs.python.org/%s/library/functions.html#vars
842
            args_json = vars(self.args)[item]
843
        except Exception as e:
844
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cannot get args item (%s)" % str(e))
845
846
        return ORJSONResponse(args_json)
847