mirror of https://github.com/nicolargo/glances.git
Implementation of a JWT token thanks to Jose lib
This commit is contained in:
parent
37d2fe13d0
commit
4181585e18
|
|
@ -37,13 +37,17 @@ click==8.1.8
|
|||
colorama==0.4.6 ; sys_platform == 'win32'
|
||||
# via click
|
||||
cryptography==46.0.3
|
||||
# via pysnmpcrypto
|
||||
# via
|
||||
# pysnmpcrypto
|
||||
# python-jose
|
||||
defusedxml==0.7.1
|
||||
# via glances
|
||||
dnspython==2.8.0
|
||||
# via pymongo
|
||||
docker==7.1.0
|
||||
# via glances
|
||||
ecdsa==0.19.1
|
||||
# via python-jose
|
||||
elastic-transport==9.2.1
|
||||
# via elasticsearch
|
||||
elasticsearch==9.2.1
|
||||
|
|
@ -119,7 +123,10 @@ psycopg-binary==3.3.2 ; implementation_name != 'pypy'
|
|||
pyarrow==22.0.0
|
||||
# via influxdb3-python
|
||||
pyasn1==0.6.1
|
||||
# via pysnmp-lextudio
|
||||
# via
|
||||
# pysnmp-lextudio
|
||||
# python-jose
|
||||
# rsa
|
||||
pycparser==2.23 ; (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') or (implementation_name == 'pypy' and platform_python_implementation == 'PyPy')
|
||||
# via cffi
|
||||
pydantic==2.12.5
|
||||
|
|
@ -155,6 +162,8 @@ python-dateutil==2.9.0.post0
|
|||
# influxdb
|
||||
# influxdb-client
|
||||
# influxdb3-python
|
||||
python-jose==3.5.0
|
||||
# via glances
|
||||
pytz==2025.2
|
||||
# via influxdb
|
||||
pywin32==311 ; sys_platform == 'win32'
|
||||
|
|
@ -174,6 +183,8 @@ requests==2.32.5
|
|||
# influxdb
|
||||
# podman
|
||||
# pysmi-lextudio
|
||||
rsa==4.9.1
|
||||
# via python-jose
|
||||
setuptools==80.9.0
|
||||
# via
|
||||
# influxdb-client
|
||||
|
|
@ -182,6 +193,7 @@ shtab==1.8.0 ; sys_platform != 'win32'
|
|||
# via glances
|
||||
six==1.17.0
|
||||
# via
|
||||
# ecdsa
|
||||
# glances
|
||||
# influxdb
|
||||
# python-dateutil
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ history_size=1200
|
|||
# then configure this folder with the webui_root_path key
|
||||
# Default is folder where glances_restful_api.py is hosted
|
||||
#webui_root_path=
|
||||
#
|
||||
# CORS options
|
||||
# Comma separated list of origins that should be permitted to make cross-origin requests.
|
||||
# Default is *
|
||||
|
|
@ -64,10 +65,18 @@ history_size=1200
|
|||
# Comma separated list of HTTP request headers that should be supported for cross-origin requests.
|
||||
# Default is *
|
||||
#cors_headers=*
|
||||
#
|
||||
# Define SSL files (keyfile_password is optional)
|
||||
#ssl_keyfile_password=kfp
|
||||
#ssl_keyfile=./glances.local+3-key.pem
|
||||
#ssl_certfile=./glances.local+3.pem
|
||||
#
|
||||
# JWT Authentication settings
|
||||
# Secret key for signing JWT tokens (generate with: openssl rand -hex 32)
|
||||
# If not set, a random key is generated per server instance (tokens won't survive restart)
|
||||
#jwt_secret_key=your-secure-secret-key-here
|
||||
# Token expiration time in minutes (default: 60)
|
||||
#jwt_expire_minutes=60
|
||||
|
||||
##############################################################################
|
||||
# Plugins
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ max_processes_display=25
|
|||
# then configure this folder with the webui_root_path key
|
||||
# Default is folder where glances_restful_api.py is hosted
|
||||
#webui_root_path=
|
||||
#
|
||||
# CORS options
|
||||
# Comma separated list of origins that should be permitted to make cross-origin requests.
|
||||
# Default is *
|
||||
|
|
@ -64,10 +65,18 @@ max_processes_display=25
|
|||
# Comma separated list of HTTP request headers that should be supported for cross-origin requests.
|
||||
# Default is *
|
||||
#cors_headers=*
|
||||
#
|
||||
# Define SSL files (keyfile_password is optional)
|
||||
#ssl_keyfile_password=kfp
|
||||
#ssl_keyfile=./glances.local+3-key.pem
|
||||
#ssl_certfile=./glances.local+3.pem
|
||||
#
|
||||
# JWT Authentication settings
|
||||
# Secret key for signing JWT tokens (generate with: openssl rand -hex 32)
|
||||
# If not set, a random key is generated per server instance (tokens won't survive restart)
|
||||
#jwt_secret_key=your-secure-secret-key-here
|
||||
# Token expiration time in minutes (default: 60)
|
||||
#jwt_expire_minutes=60
|
||||
|
||||
##############################################################################
|
||||
# Plugins
|
||||
|
|
|
|||
|
|
@ -8,16 +8,22 @@ anyio==4.12.0
|
|||
# via starlette
|
||||
certifi==2025.11.12
|
||||
# via requests
|
||||
cffi==2.0.0 ; platform_python_implementation != 'PyPy'
|
||||
# via cryptography
|
||||
charset-normalizer==3.4.4
|
||||
# via requests
|
||||
click==8.1.8
|
||||
# via uvicorn
|
||||
colorama==0.4.6 ; sys_platform == 'win32'
|
||||
# via click
|
||||
cryptography==46.0.3
|
||||
# via python-jose
|
||||
defusedxml==0.7.1
|
||||
# via glances
|
||||
docker==7.1.0
|
||||
# via glances
|
||||
ecdsa==0.19.1
|
||||
# via python-jose
|
||||
exceptiongroup==1.2.2 ; python_full_version < '3.11'
|
||||
# via anyio
|
||||
fastapi==0.128.0
|
||||
|
|
@ -38,12 +44,20 @@ podman==5.6.0
|
|||
# via glances
|
||||
psutil==7.2.1
|
||||
# via glances
|
||||
pyasn1==0.6.1
|
||||
# via
|
||||
# python-jose
|
||||
# rsa
|
||||
pycparser==2.23 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy'
|
||||
# via cffi
|
||||
pydantic==2.12.5
|
||||
# via fastapi
|
||||
pydantic-core==2.41.5
|
||||
# via pydantic
|
||||
python-dateutil==2.9.0.post0
|
||||
# via glances
|
||||
python-jose==3.5.0
|
||||
# via glances
|
||||
pywin32==311 ; sys_platform == 'win32'
|
||||
# via docker
|
||||
requests==2.32.5
|
||||
|
|
@ -51,10 +65,13 @@ requests==2.32.5
|
|||
# docker
|
||||
# glances
|
||||
# podman
|
||||
rsa==4.9.1
|
||||
# via python-jose
|
||||
shtab==1.8.0 ; sys_platform != 'win32'
|
||||
# via glances
|
||||
six==1.17.0
|
||||
# via
|
||||
# ecdsa
|
||||
# glances
|
||||
# python-dateutil
|
||||
starlette==0.50.0
|
||||
|
|
@ -64,6 +81,7 @@ tomli==2.0.2 ; python_full_version < '3.11'
|
|||
typing-extensions==4.15.0
|
||||
# via
|
||||
# anyio
|
||||
# cryptography
|
||||
# fastapi
|
||||
# pydantic
|
||||
# pydantic-core
|
||||
|
|
|
|||
|
|
@ -22,25 +22,25 @@ use the following code:
|
|||
>>> gl = api.GlancesAPI()
|
||||
>>> gl.cpu
|
||||
{'cpucore': 16,
|
||||
'ctx_switches': 472872341,
|
||||
'ctx_switches': 541113367,
|
||||
'guest': 0.0,
|
||||
'idle': 93.5,
|
||||
'interrupts': 410386146,
|
||||
'iowait': 0.3,
|
||||
'idle': 93.1,
|
||||
'interrupts': 464358062,
|
||||
'iowait': 0.5,
|
||||
'irq': 0.0,
|
||||
'nice': 0.0,
|
||||
'soft_interrupts': 174928185,
|
||||
'soft_interrupts': 197631605,
|
||||
'steal': 0.0,
|
||||
'syscalls': 0,
|
||||
'system': 4.4,
|
||||
'total': 7.2,
|
||||
'user': 1.7}
|
||||
'system': 4.6,
|
||||
'total': 7.9,
|
||||
'user': 1.9}
|
||||
>>> gl.cpu.get("total")
|
||||
7.2
|
||||
7.9
|
||||
>>> gl.mem.get("used")
|
||||
12810799128
|
||||
10525812760
|
||||
>>> gl.auto_unit(gl.mem.get("used"))
|
||||
11.9G
|
||||
9.80G
|
||||
|
||||
If the stats return a list of items (like network interfaces or processes), you can
|
||||
access them by their name:
|
||||
|
|
@ -51,19 +51,19 @@ access them by their name:
|
|||
['wlp0s20f3']
|
||||
>>> gl.network["wlp0s20f3"]
|
||||
{'alias': None,
|
||||
'bytes_all': 214,
|
||||
'bytes_all_gauge': 15142743463,
|
||||
'bytes_all_rate_per_sec': 606.0,
|
||||
'bytes_recv': 128,
|
||||
'bytes_recv_gauge': 13975900109,
|
||||
'bytes_recv_rate_per_sec': 362.0,
|
||||
'bytes_sent': 86,
|
||||
'bytes_sent_gauge': 1166843354,
|
||||
'bytes_sent_rate_per_sec': 243.0,
|
||||
'bytes_all': 1903,
|
||||
'bytes_all_gauge': 15435196525,
|
||||
'bytes_all_rate_per_sec': 13613.0,
|
||||
'bytes_recv': 1731,
|
||||
'bytes_recv_gauge': 14118055339,
|
||||
'bytes_recv_rate_per_sec': 12382.0,
|
||||
'bytes_sent': 172,
|
||||
'bytes_sent_gauge': 1317141186,
|
||||
'bytes_sent_rate_per_sec': 1230.0,
|
||||
'interface_name': 'wlp0s20f3',
|
||||
'key': 'interface_name',
|
||||
'speed': 0,
|
||||
'time_since_update': 0.3526580333709717}
|
||||
'time_since_update': 0.13979172706604004}
|
||||
|
||||
Init Glances Python API
|
||||
-----------------------
|
||||
|
|
@ -95,32 +95,7 @@ Alert stats:
|
|||
>>> type(gl.alert)
|
||||
<class 'glances.plugins.alert.AlertPlugin'>
|
||||
>>> gl.alert
|
||||
[{'avg': 99.96957776029373,
|
||||
'begin': 1767449522,
|
||||
'count': 2,
|
||||
'desc': '',
|
||||
'end': -1,
|
||||
'global_msg': 'High swap (paging) usage',
|
||||
'max': 99.96957776029373,
|
||||
'min': 99.96957776029373,
|
||||
'sort': 'memory_percent',
|
||||
'state': 'CRITICAL',
|
||||
'sum': 199.93915552058746,
|
||||
'top': ['code', 'code', 'code'],
|
||||
'type': 'MEMSWAP'},
|
||||
{'avg': 77.95894443790266,
|
||||
'begin': 1767449522,
|
||||
'count': 2,
|
||||
'desc': '',
|
||||
'end': -1,
|
||||
'global_msg': 'High swap (paging) usage',
|
||||
'max': 78.00605779103407,
|
||||
'min': 77.91183108477124,
|
||||
'sort': 'memory_percent',
|
||||
'state': 'WARNING',
|
||||
'sum': 155.91788887580532,
|
||||
'top': [],
|
||||
'type': 'MEM'}]
|
||||
[]
|
||||
|
||||
Alert fields description:
|
||||
|
||||
|
|
@ -161,7 +136,7 @@ Ports stats:
|
|||
'port': 0,
|
||||
'refresh': 30,
|
||||
'rtt_warning': None,
|
||||
'status': 0.008168,
|
||||
'status': 0.006093,
|
||||
'timeout': 3}]
|
||||
|
||||
Ports fields description:
|
||||
|
|
@ -202,14 +177,14 @@ Diskio stats:
|
|||
>>> gl.diskio.get("nvme0n1")
|
||||
{'disk_name': 'nvme0n1',
|
||||
'key': 'disk_name',
|
||||
'read_bytes': 29268151808,
|
||||
'read_count': 1162807,
|
||||
'read_bytes': 47595146752,
|
||||
'read_count': 1611927,
|
||||
'read_latency': 0,
|
||||
'read_time': 313717,
|
||||
'write_bytes': 44187964416,
|
||||
'write_count': 3047678,
|
||||
'read_time': 492221,
|
||||
'write_bytes': 47440806912,
|
||||
'write_count': 3244300,
|
||||
'write_latency': 0,
|
||||
'write_time': 2442776}
|
||||
'write_time': 2733363}
|
||||
|
||||
Diskio fields description:
|
||||
|
||||
|
|
@ -294,11 +269,11 @@ Processcount stats:
|
|||
>>> type(gl.processcount)
|
||||
<class 'glances.plugins.processcount.ProcesscountPlugin'>
|
||||
>>> gl.processcount
|
||||
{'pid_max': 0, 'running': 1, 'sleeping': 442, 'thread': 2444, 'total': 597}
|
||||
{'pid_max': 0, 'running': 1, 'sleeping': 412, 'thread': 1984, 'total': 561}
|
||||
>>> gl.processcount.keys()
|
||||
['total', 'running', 'sleeping', 'thread', 'pid_max']
|
||||
>>> gl.processcount.get("total")
|
||||
597
|
||||
561
|
||||
|
||||
Processcount fields description:
|
||||
|
||||
|
|
@ -371,7 +346,7 @@ Percpu stats:
|
|||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 44.0,
|
||||
'idle': 29.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
|
|
@ -380,8 +355,8 @@ Percpu stats:
|
|||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 10.0,
|
||||
'total': 56.0,
|
||||
'user': 0.0}
|
||||
'total': 71.0,
|
||||
'user': 1.0}
|
||||
|
||||
Percpu fields description:
|
||||
|
||||
|
|
@ -471,18 +446,18 @@ Network stats:
|
|||
>>> gl.network.get("wlp0s20f3")
|
||||
{'alias': None,
|
||||
'bytes_all': 0,
|
||||
'bytes_all_gauge': 15142743463,
|
||||
'bytes_all_gauge': 15435196525,
|
||||
'bytes_all_rate_per_sec': 0.0,
|
||||
'bytes_recv': 0,
|
||||
'bytes_recv_gauge': 13975900109,
|
||||
'bytes_recv_gauge': 14118055339,
|
||||
'bytes_recv_rate_per_sec': 0.0,
|
||||
'bytes_sent': 0,
|
||||
'bytes_sent_gauge': 1166843354,
|
||||
'bytes_sent_gauge': 1317141186,
|
||||
'bytes_sent_rate_per_sec': 0.0,
|
||||
'interface_name': 'wlp0s20f3',
|
||||
'key': 'interface_name',
|
||||
'speed': 0,
|
||||
'time_since_update': 0.00287628173828125}
|
||||
'time_since_update': 0.00189208984375}
|
||||
|
||||
Network fields description:
|
||||
|
||||
|
|
@ -523,23 +498,23 @@ Cpu stats:
|
|||
<class 'glances.plugins.cpu.CpuPlugin'>
|
||||
>>> gl.cpu
|
||||
{'cpucore': 16,
|
||||
'ctx_switches': 472872341,
|
||||
'ctx_switches': 541113367,
|
||||
'guest': 0.0,
|
||||
'idle': 93.5,
|
||||
'interrupts': 410386146,
|
||||
'iowait': 0.3,
|
||||
'idle': 93.1,
|
||||
'interrupts': 464358062,
|
||||
'iowait': 0.5,
|
||||
'irq': 0.0,
|
||||
'nice': 0.0,
|
||||
'soft_interrupts': 174928185,
|
||||
'soft_interrupts': 197631605,
|
||||
'steal': 0.0,
|
||||
'syscalls': 0,
|
||||
'system': 4.4,
|
||||
'total': 7.2,
|
||||
'user': 1.7}
|
||||
'system': 4.6,
|
||||
'total': 7.9,
|
||||
'user': 1.9}
|
||||
>>> gl.cpu.keys()
|
||||
['total', 'user', 'nice', 'system', 'idle', 'iowait', 'irq', 'steal', 'guest', 'ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls', 'cpucore']
|
||||
>>> gl.cpu.get("total")
|
||||
7.2
|
||||
7.9
|
||||
|
||||
Cpu fields description:
|
||||
|
||||
|
|
@ -611,7 +586,7 @@ Amps stats:
|
|||
'refresh': 3.0,
|
||||
'regex': True,
|
||||
'result': None,
|
||||
'timer': 0.31592440605163574}
|
||||
'timer': 0.16622281074523926}
|
||||
|
||||
Amps fields description:
|
||||
|
||||
|
|
@ -642,32 +617,32 @@ Processlist stats:
|
|||
>>> gl.processlist
|
||||
Return a dict of dict with key=<pid>
|
||||
>>> gl.processlist.keys()
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 10, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39, 41, 42, 43, 44, 45, 47, 48, 49, 50, 51, 53, 54, 55, 56, 57, 59, 60, 61, 62, 63, 65, 66, 67, 68, 69, 71, 72, 73, 74, 75, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99, 101, 102, 103, 104, 105, 107, 108, 109, 110, 111, 113, 114, 115, 116, 117, 118, 121, 122, 123, 124, 125, 126, 127, 128, 133, 135, 136, 137, 138, 139, 140, 142, 143, 144, 145, 146, 147, 148, 150, 154, 156, 157, 158, 166, 179, 188, 189, 218, 219, 239, 240, 258, 267, 268, 269, 270, 271, 273, 279, 280, 364, 367, 369, 370, 371, 372, 373, 450, 452, 613, 618, 619, 620, 627, 659, 660, 726, 757, 758, 787, 795, 970, 971, 986, 1037, 1040, 1042, 1043, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1056, 1058, 1059, 1064, 1065, 1218, 1219, 1223, 1275, 1277, 1278, 1279, 1320, 1327, 1534, 1537, 1965, 1966, 1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2016, 2017, 2018, 2019, 2020, 2021, 2023, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099, 2100, 2101, 2102, 2103, 2104, 2105, 2117, 2122, 2123, 2124, 2125, 2126, 2127, 2128, 2129, 2130, 2133, 2134, 2135, 2136, 2137, 2138, 2139, 2140, 2141, 2142, 2143, 2144, 2145, 2149, 2603, 2604, 2605, 2606, 2613, 2615, 2744, 2745, 2746, 2750, 2751, 2755, 2762, 2774, 2781, 2788, 2791, 2796, 2799, 2802, 2813, 2820, 2823, 2887, 2904, 2905, 2916, 3031, 3038, 3081, 3181, 3182, 3186, 3191, 3192, 3240, 3382, 3383, 3670, 3671, 3695, 3698, 3807, 3817, 3818, 3822, 3827, 3828, 3846, 3853, 3874, 3901, 3902, 3903, 3904, 3905, 3906, 3907, 3908, 3982, 4194, 4596, 5146, 5166, 5172, 5179, 5189, 5190, 5193, 5195, 5197, 5207, 5263, 5267, 5274, 5318, 5333, 5421, 5425, 5441, 5453, 5457, 5492, 5505, 5509, 5593, 5612, 5620, 5621, 5634, 5657, 5658, 5661, 5664, 5665, 5667, 5669, 5672, 5675, 5678, 5680, 5685, 5687, 5693, 5695, 5700, 5708, 5726, 5734, 5829, 5832, 5833, 5844, 5861, 5871, 5914, 5921, 5931, 5941, 5954, 5957, 6017, 6063, 6087, 6093, 6094, 6123, 6154, 6206, 6244, 6475, 6505, 6552, 6559, 6562, 7227, 8412, 8526, 9631, 9649, 9661, 9731, 9734, 11010, 12121, 12123, 12124, 12138, 12192, 12239, 12278, 12321, 12335, 12336, 12417, 12686, 12726, 12735, 12929, 12931, 12933, 12934, 13048, 13115, 13536, 13665, 36430, 40117, 53755, 53764, 62488, 62637, 72064, 72065, 72120, 72139, 102070, 340569, 388396, 413445, 444052, 444061, 471623, 471714, 471892, 471899, 471919, 471929, 471954, 472557, 472566, 472570, 472785, 472854, 472861, 473314, 481055, 513901, 514243, 524431, 524432, 524447, 524457, 524551, 546233, 546254, 547205, 547220, 547714, 547732, 548091, 548102, 548622, 561234, 564559, 567573, 654061, 661802, 671732, 683980, 686191, 695926, 701770, 705018, 705167, 707900, 708398, 709364, 709585, 710008, 710952, 714751, 714947, 721023, 721204, 726507, 728005, 728006, 728008, 728465, 730137, 731844, 732902, 733166, 733167, 733841, 733865, 734183, 735109, 735551, 736180, 736316, 736396, 736607, 737036, 737183, 737369, 737866, 738311, 738732, 739425, 739889, 740008, 740325, 740438, 741044, 741346, 741856, 742102, 742589, 742967, 743001, 743292, 743314, 743466, 743467, 743468, 743469, 743470, 743471, 743554, 743752, 743753, 743869, 743926, 743942, 744023, 744188, 744189, 744276, 744281, 745624, 745766, 745875, 745878, 745879, 745882]
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 10, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39, 41, 42, 43, 44, 45, 47, 48, 49, 50, 51, 53, 54, 55, 56, 57, 59, 60, 61, 62, 63, 65, 66, 67, 68, 69, 71, 72, 73, 74, 75, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99, 101, 102, 103, 104, 105, 107, 108, 109, 110, 111, 113, 114, 115, 116, 117, 118, 121, 122, 123, 124, 125, 126, 127, 128, 133, 135, 136, 137, 138, 139, 140, 142, 143, 144, 145, 146, 147, 148, 150, 154, 156, 157, 158, 166, 179, 188, 189, 218, 219, 239, 240, 258, 267, 268, 269, 270, 271, 273, 279, 280, 364, 367, 369, 370, 371, 372, 373, 450, 452, 613, 618, 619, 620, 627, 659, 660, 726, 757, 758, 787, 795, 970, 971, 986, 1037, 1040, 1042, 1043, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1056, 1058, 1059, 1064, 1065, 1218, 1219, 1223, 1275, 1277, 1278, 1279, 1320, 1327, 1534, 1537, 1965, 1966, 1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2016, 2017, 2018, 2019, 2020, 2021, 2023, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099, 2100, 2101, 2102, 2103, 2104, 2105, 2117, 2122, 2123, 2124, 2125, 2126, 2127, 2128, 2129, 2130, 2133, 2134, 2135, 2136, 2137, 2138, 2139, 2140, 2141, 2142, 2143, 2144, 2145, 2149, 2603, 2604, 2605, 2606, 2613, 2615, 2744, 2745, 2746, 2750, 2751, 2755, 2762, 2774, 2781, 2788, 2791, 2796, 2799, 2802, 2813, 2820, 2823, 2887, 2904, 2905, 2916, 3031, 3038, 3081, 3181, 3182, 3186, 3191, 3192, 3240, 3382, 3383, 3670, 3671, 3695, 3698, 3807, 3817, 3818, 3822, 3827, 3828, 3846, 3853, 3874, 3901, 3902, 3903, 3904, 3905, 3906, 3907, 3908, 3982, 4194, 4596, 5166, 6562, 36430, 444052, 444061, 481055, 686191, 791450, 797892, 800110, 800150, 800151, 800285, 806441, 808018, 811099, 813104, 813640, 813665, 813669, 813681, 813682, 813683, 813685, 813687, 813689, 813699, 813749, 813753, 813760, 813779, 813795, 813873, 813876, 813892, 813903, 813906, 813949, 813962, 813963, 814046, 814083, 814091, 814092, 814106, 814107, 814128, 814129, 814130, 814133, 814135, 814138, 814140, 814143, 814147, 814150, 814152, 814156, 814158, 814162, 814170, 814177, 814191, 814192, 814284, 814293, 814295, 814306, 814324, 814353, 814356, 814384, 814388, 814397, 814410, 814412, 814429, 814438, 814507, 814547, 814577, 814588, 814592, 814614, 814657, 814874, 814875, 814918, 814987, 814992, 815007, 815154, 815196, 815236, 815252, 815324, 815555, 815952, 815959, 815967, 816178, 816198, 816205, 816444, 816560, 816572, 817391, 817393, 817394, 817439, 817491, 817540, 817578, 817620, 817648, 817649, 817933, 817952, 818000, 818044, 818206, 818210, 818211, 818213, 818223, 818670, 818773, 818940, 818964, 819036, 819054, 819077, 819153, 819443, 820148, 820306, 820835, 823577, 825801, 828210, 828339, 828612, 829135, 829999, 831005, 831221, 834846, 836649, 836885, 837028, 837106, 837195, 838107, 838946, 839073, 839074, 839230, 839243, 842229, 842773, 843197, 843881, 844002, 844988, 845334, 845619, 846865, 847391, 847596, 847599, 847816, 848168, 848169, 848296, 848442, 848637, 848983, 848984, 848985, 848986, 848987, 849482, 849620, 850436, 850804, 851714, 851777, 852971, 853655, 854520, 854521, 854586, 854783, 854845, 854876, 855229, 855232, 855233, 855236]
|
||||
>>> gl.processlist.get("1")
|
||||
{'cmdline': ['/sbin/init', 'splash'],
|
||||
'cpu_percent': 0.0,
|
||||
'cpu_times': {'children_system': 292.72,
|
||||
'children_user': 5149.74,
|
||||
'cpu_times': {'children_system': 11313.22,
|
||||
'children_user': 46981.78,
|
||||
'iowait': 0.0,
|
||||
'system': 9.16,
|
||||
'user': 13.08},
|
||||
'system': 10.51,
|
||||
'user': 14.18},
|
||||
'gids': {'effective': 0, 'real': 0, 'saved': 0},
|
||||
'io_counters': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
'key': 'pid',
|
||||
'memory_info': {'data': 7282688,
|
||||
'dirty': 0,
|
||||
'lib': 0,
|
||||
'rss': 14585856,
|
||||
'shared': 8134656,
|
||||
'rss': 12140544,
|
||||
'shared': 5771264,
|
||||
'text': 45056,
|
||||
'vms': 26632192},
|
||||
'memory_percent': 0.0888145317633538,
|
||||
'memory_percent': 0.07392481666570644,
|
||||
'name': 'systemd',
|
||||
'nice': 0,
|
||||
'num_threads': 1,
|
||||
'pid': 1,
|
||||
'status': 'S',
|
||||
'time_since_update': 0.6452903747558594,
|
||||
'time_since_update': 0.40526819229125977,
|
||||
'username': 'root'}
|
||||
|
||||
Processlist fields description:
|
||||
|
|
@ -752,13 +727,13 @@ Load stats:
|
|||
<class 'glances.plugins.load.LoadPlugin'>
|
||||
>>> gl.load
|
||||
{'cpucore': 16,
|
||||
'min1': 0.869140625,
|
||||
'min15': 1.04248046875,
|
||||
'min5': 1.04052734375}
|
||||
'min1': 0.62451171875,
|
||||
'min15': 0.71826171875,
|
||||
'min5': 0.67919921875}
|
||||
>>> gl.load.keys()
|
||||
['min1', 'min5', 'min15', 'cpucore']
|
||||
>>> gl.load.get("min1")
|
||||
0.869140625
|
||||
0.62451171875
|
||||
|
||||
Load fields description:
|
||||
|
||||
|
|
@ -797,7 +772,7 @@ Sensors stats:
|
|||
'label': 'Ambient',
|
||||
'type': 'temperature_core',
|
||||
'unit': 'C',
|
||||
'value': 34,
|
||||
'value': 36,
|
||||
'warning': 0}
|
||||
|
||||
Sensors fields description:
|
||||
|
|
@ -835,7 +810,7 @@ Uptime stats:
|
|||
>>> type(gl.uptime)
|
||||
<class 'glances.plugins.uptime.UptimePlugin'>
|
||||
>>> gl.uptime
|
||||
'6 days, 22:46:17'
|
||||
'7 days, 18:13:52'
|
||||
|
||||
Uptime limits:
|
||||
|
||||
|
|
@ -854,11 +829,11 @@ Now stats:
|
|||
>>> type(gl.now)
|
||||
<class 'glances.plugins.now.NowPlugin'>
|
||||
>>> gl.now
|
||||
{'custom': '2026-01-03 15:12:02 CET', 'iso': '2026-01-03T15:12:02+01:00'}
|
||||
{'custom': '2026-01-04 10:39:37 CET', 'iso': '2026-01-04T10:39:37+01:00'}
|
||||
>>> gl.now.keys()
|
||||
['iso', 'custom']
|
||||
>>> gl.now.get("iso")
|
||||
'2026-01-03T15:12:02+01:00'
|
||||
'2026-01-04T10:39:37+01:00'
|
||||
|
||||
Now fields description:
|
||||
|
||||
|
|
@ -887,14 +862,14 @@ Fs stats:
|
|||
['/', '/zsfpool']
|
||||
>>> gl.fs.get("/")
|
||||
{'device_name': '/dev/mapper/ubuntu--vg-ubuntu--lv',
|
||||
'free': 582918623232,
|
||||
'free': 582995017728,
|
||||
'fs_type': 'ext4',
|
||||
'key': 'mnt_point',
|
||||
'mnt_point': '/',
|
||||
'options': 'rw,relatime',
|
||||
'percent': 38.8,
|
||||
'size': 1003736440832,
|
||||
'used': 369755312128}
|
||||
'used': 369678917632}
|
||||
|
||||
Fs fields description:
|
||||
|
||||
|
|
@ -934,8 +909,8 @@ Wifi stats:
|
|||
['wlp0s20f3']
|
||||
>>> gl.wifi.get("wlp0s20f3")
|
||||
{'key': 'ssid',
|
||||
'quality_level': -60.0,
|
||||
'quality_link': 50.0,
|
||||
'quality_level': -62.0,
|
||||
'quality_link': 48.0,
|
||||
'ssid': 'wlp0s20f3'}
|
||||
|
||||
Wifi limits:
|
||||
|
|
@ -959,13 +934,9 @@ Ip stats:
|
|||
>>> type(gl.ip)
|
||||
<class 'glances.plugins.ip.IpPlugin'>
|
||||
>>> gl.ip
|
||||
{'address': '192.168.1.26',
|
||||
'mask': '255.255.255.0',
|
||||
'mask_cidr': 24,
|
||||
'public_address': '',
|
||||
'public_info_human': ''}
|
||||
{'address': '192.168.1.26', 'mask': '255.255.255.0', 'mask_cidr': 24}
|
||||
>>> gl.ip.keys()
|
||||
['address', 'mask', 'mask_cidr', 'public_address', 'public_info_human']
|
||||
['address', 'mask', 'mask_cidr']
|
||||
>>> gl.ip.get("address")
|
||||
'192.168.1.26'
|
||||
|
||||
|
|
@ -1067,16 +1038,16 @@ Mem stats:
|
|||
>>> type(gl.mem)
|
||||
<class 'glances.plugins.mem.MemPlugin'>
|
||||
>>> gl.mem
|
||||
{'active': 5529219072,
|
||||
'available': 3612026856,
|
||||
'buffers': 130482176,
|
||||
'cached': 3568499560,
|
||||
'free': 1333854208,
|
||||
'inactive': 7177965568,
|
||||
'percent': 78.0,
|
||||
'shared': 840089600,
|
||||
{'active': 9791209472,
|
||||
'available': 5897013224,
|
||||
'buffers': 257441792,
|
||||
'cached': 5707590504,
|
||||
'free': 704032768,
|
||||
'inactive': 4103938048,
|
||||
'percent': 64.1,
|
||||
'shared': 734093312,
|
||||
'total': 16422825984,
|
||||
'used': 12810799128}
|
||||
'used': 10525812760}
|
||||
>>> gl.mem.keys()
|
||||
['total', 'available', 'percent', 'used', 'free', 'active', 'inactive', 'buffers', 'cached', 'shared']
|
||||
>>> gl.mem.get("total")
|
||||
|
|
@ -1146,19 +1117,19 @@ Quicklook stats:
|
|||
>>> type(gl.quicklook)
|
||||
<class 'glances.plugins.quicklook.QuicklookPlugin'>
|
||||
>>> gl.quicklook
|
||||
{'cpu': 7.2,
|
||||
{'cpu': 7.9,
|
||||
'cpu_hz': 4475000000.0,
|
||||
'cpu_hz_current': 608909250.0,
|
||||
'cpu_hz_current': 1034989000.0,
|
||||
'cpu_log_core': 16,
|
||||
'cpu_name': '13th Gen Intel(R) Core(TM) i7-13620H',
|
||||
'cpu_phys_core': 10,
|
||||
'load': 6.5,
|
||||
'mem': 78.0,
|
||||
'load': 4.5,
|
||||
'mem': 64.1,
|
||||
'percpu': [{'cpu_number': 0,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 44.0,
|
||||
'idle': 29.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
|
|
@ -1167,13 +1138,13 @@ Quicklook stats:
|
|||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 10.0,
|
||||
'total': 56.0,
|
||||
'user': 0.0},
|
||||
'total': 71.0,
|
||||
'user': 1.0},
|
||||
{'cpu_number': 1,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 55.0,
|
||||
'idle': 39.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
|
|
@ -1182,13 +1153,13 @@ Quicklook stats:
|
|||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 0.0,
|
||||
'total': 45.0,
|
||||
'user': 0.0},
|
||||
'total': 61.0,
|
||||
'user': 1.0},
|
||||
{'cpu_number': 2,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 55.0,
|
||||
'idle': 37.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
|
|
@ -1197,13 +1168,13 @@ Quicklook stats:
|
|||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 1.0,
|
||||
'total': 45.0,
|
||||
'user': 1.0},
|
||||
'total': 63.0,
|
||||
'user': 2.0},
|
||||
{'cpu_number': 3,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 55.0,
|
||||
'idle': 41.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
|
|
@ -1211,59 +1182,14 @@ Quicklook stats:
|
|||
'nice': 0.0,
|
||||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 0.0,
|
||||
'total': 45.0,
|
||||
'system': 1.0,
|
||||
'total': 59.0,
|
||||
'user': 0.0},
|
||||
{'cpu_number': 4,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 51.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
'key': 'cpu_number',
|
||||
'nice': 0.0,
|
||||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 3.0,
|
||||
'total': 49.0,
|
||||
'user': 1.0},
|
||||
{'cpu_number': 5,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 55.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
'key': 'cpu_number',
|
||||
'nice': 0.0,
|
||||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 0.0,
|
||||
'total': 45.0,
|
||||
'user': 0.0},
|
||||
{'cpu_number': 6,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 45.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
'key': 'cpu_number',
|
||||
'nice': 0.0,
|
||||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 8.0,
|
||||
'total': 55.0,
|
||||
'user': 3.0},
|
||||
{'cpu_number': 7,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 28.0,
|
||||
'idle': 27.0,
|
||||
'interrupt': None,
|
||||
'iowait': 1.0,
|
||||
'irq': 0.0,
|
||||
|
|
@ -1271,29 +1197,74 @@ Quicklook stats:
|
|||
'nice': 0.0,
|
||||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 15.0,
|
||||
'total': 72.0,
|
||||
'user': 11.0},
|
||||
{'cpu_number': 8,
|
||||
'system': 3.0,
|
||||
'total': 73.0,
|
||||
'user': 7.0},
|
||||
{'cpu_number': 5,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 52.0,
|
||||
'idle': 41.0,
|
||||
'interrupt': None,
|
||||
'iowait': 2.0,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
'key': 'cpu_number',
|
||||
'nice': 0.0,
|
||||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 1.0,
|
||||
'total': 48.0,
|
||||
'system': 0.0,
|
||||
'total': 59.0,
|
||||
'user': 0.0},
|
||||
{'cpu_number': 6,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 34.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
'key': 'cpu_number',
|
||||
'nice': 0.0,
|
||||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 4.0,
|
||||
'total': 66.0,
|
||||
'user': 1.0},
|
||||
{'cpu_number': 7,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 28.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
'key': 'cpu_number',
|
||||
'nice': 0.0,
|
||||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 6.0,
|
||||
'total': 72.0,
|
||||
'user': 5.0},
|
||||
{'cpu_number': 8,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 37.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
'key': 'cpu_number',
|
||||
'nice': 0.0,
|
||||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 2.0,
|
||||
'total': 63.0,
|
||||
'user': 1.0},
|
||||
{'cpu_number': 9,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 55.0,
|
||||
'idle': 40.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
|
|
@ -1302,13 +1273,13 @@ Quicklook stats:
|
|||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 1.0,
|
||||
'total': 45.0,
|
||||
'total': 60.0,
|
||||
'user': 0.0},
|
||||
{'cpu_number': 10,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 54.0,
|
||||
'idle': 37.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
|
|
@ -1316,14 +1287,14 @@ Quicklook stats:
|
|||
'nice': 0.0,
|
||||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 1.0,
|
||||
'total': 46.0,
|
||||
'system': 2.0,
|
||||
'total': 63.0,
|
||||
'user': 1.0},
|
||||
{'cpu_number': 11,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 56.0,
|
||||
'idle': 41.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
|
|
@ -1332,13 +1303,13 @@ Quicklook stats:
|
|||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 0.0,
|
||||
'total': 44.0,
|
||||
'total': 59.0,
|
||||
'user': 0.0},
|
||||
{'cpu_number': 12,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 55.0,
|
||||
'idle': 39.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
|
|
@ -1346,14 +1317,14 @@ Quicklook stats:
|
|||
'nice': 0.0,
|
||||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 0.0,
|
||||
'total': 45.0,
|
||||
'user': 0.0},
|
||||
'system': 1.0,
|
||||
'total': 61.0,
|
||||
'user': 1.0},
|
||||
{'cpu_number': 13,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 55.0,
|
||||
'idle': 40.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
|
|
@ -1361,14 +1332,14 @@ Quicklook stats:
|
|||
'nice': 0.0,
|
||||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 0.0,
|
||||
'total': 45.0,
|
||||
'user': 0.0},
|
||||
'system': 1.0,
|
||||
'total': 60.0,
|
||||
'user': 1.0},
|
||||
{'cpu_number': 14,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 55.0,
|
||||
'idle': 39.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
|
|
@ -1377,13 +1348,13 @@ Quicklook stats:
|
|||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 1.0,
|
||||
'total': 45.0,
|
||||
'user': 0.0},
|
||||
'total': 61.0,
|
||||
'user': 1.0},
|
||||
{'cpu_number': 15,
|
||||
'dpc': None,
|
||||
'guest': 0.0,
|
||||
'guest_nice': 0.0,
|
||||
'idle': 55.0,
|
||||
'idle': 40.0,
|
||||
'interrupt': None,
|
||||
'iowait': 0.0,
|
||||
'irq': 0.0,
|
||||
|
|
@ -1392,9 +1363,9 @@ Quicklook stats:
|
|||
'softirq': 0.0,
|
||||
'steal': 0.0,
|
||||
'system': 1.0,
|
||||
'total': 45.0,
|
||||
'total': 60.0,
|
||||
'user': 1.0}],
|
||||
'swap': 100.0}
|
||||
'swap': 1.9}
|
||||
>>> gl.quicklook.keys()
|
||||
['cpu_name', 'cpu_hz_current', 'cpu_hz', 'cpu', 'percpu', 'mem', 'swap', 'cpu_log_core', 'cpu_phys_core', 'load']
|
||||
>>> gl.quicklook.get("cpu_name")
|
||||
|
|
@ -1444,13 +1415,13 @@ Memswap stats:
|
|||
>>> type(gl.memswap)
|
||||
<class 'glances.plugins.memswap.MemswapPlugin'>
|
||||
>>> gl.memswap
|
||||
{'free': 1306624,
|
||||
'percent': 100.0,
|
||||
'sin': 2306797568,
|
||||
'sout': 7715819520,
|
||||
'time_since_update': 0.7321233749389648,
|
||||
{'free': 4211441664,
|
||||
'percent': 1.9,
|
||||
'sin': 2388013056,
|
||||
'sout': 7785721856,
|
||||
'time_since_update': 0.35303711891174316,
|
||||
'total': 4294963200,
|
||||
'used': 4293656576}
|
||||
'used': 83521536}
|
||||
>>> gl.memswap.keys()
|
||||
['total', 'used', 'free', 'percent', 'sin', 'sout', 'time_since_update']
|
||||
>>> gl.memswap.get("total")
|
||||
|
|
@ -1485,10 +1456,10 @@ Use auto_unit() function to generate a human-readable string with the unit:
|
|||
.. code-block:: python
|
||||
|
||||
>>> gl.mem.get("used")
|
||||
12810799128
|
||||
10525812760
|
||||
|
||||
>>> gl.auto_unit(gl.mem.get("used"))
|
||||
11.9G
|
||||
9.80G
|
||||
|
||||
|
||||
Args:
|
||||
|
|
@ -1514,7 +1485,7 @@ Use bar() function to generate a bar:
|
|||
.. code-block:: python
|
||||
|
||||
>>> gl.bar(gl.mem["percent"])
|
||||
■■■■■■■■■■■■■■□□□□
|
||||
■■■■■■■■■■■□□□□□□□
|
||||
|
||||
|
||||
Args:
|
||||
|
|
@ -1544,7 +1515,7 @@ Use top_process() function to generate a list of top processes sorted by CPU or
|
|||
.. code-block:: python
|
||||
|
||||
>>> gl.top_process()
|
||||
[{'status': 'S', 'memory_info': {'rss': 833155072, 'vms': 1499914432512, 'shared': 37560320, 'text': 148733952, 'lib': 0, 'data': 2573008896, 'dirty': 0}, 'name': 'code', 'nice': 0, 'memory_percent': 5.073152895924881, 'gids': {'real': 1000, 'effective': 1000, 'saved': 1000}, 'num_threads': 22, 'io_counters': [1554405376, 611483648, 1554405376, 611483648, 1], 'pid': 12321, 'cpu_percent': 1.6, 'cpu_times': {'user': 1797.6, 'system': 767.31, 'children_user': 1013.92, 'children_system': 950.58, 'iowait': 0.0}, 'key': 'pid', 'time_since_update': 0.6452903747558594, 'cmdline': ['/proc/self/exe', '--type=utility', '--utility-sub-type=node.mojom.NodeService', '--lang=en-US', '--service-sandbox-type=none', '--no-sandbox', '--dns-result-order=ipv4first', '--experimental-network-inspection', '--inspect-port=0', '--crashpad-handler-pid=12138', '--enable-crash-reporter=864d4bb7-dd20-4851-830f-29e81dd93517,no_channel', '--user-data-dir=/home/nicolargo/.config/Code', '--standard-schemes=vscode-webview,vscode-file', '--secure-schemes=vscode-webview,vscode-file', '--cors-schemes=vscode-webview,vscode-file', '--fetch-schemes=vscode-webview,vscode-file', '--service-worker-schemes=vscode-webview', '--code-cache-schemes=vscode-webview,vscode-file', '--shared-files=v8_context_snapshot_data:100', '--field-trial-handle=3,i,768474601394521582,1148810169323883902,262144', '--enable-features=DocumentPolicyIncludeJSCallStacksInCrashReports,EarlyEstablishGpuChannel,EstablishGpuChannelAsync', '--disable-features=CalculateNativeWinOcclusion,FontationsLinuxSystemFonts,ScreenAIOCREnabled,SpareRendererForSitePerProcess', '--variations-seed-version'], 'username': 'nicolargo'}, {'status': 'S', 'memory_info': {'rss': 610873344, 'vms': 31620141056, 'shared': 180371456, 'text': 708608, 'lib': 0, 'data': 1298669568, 'dirty': 0}, 'name': 'firefox', 'nice': 0, 'memory_percent': 3.71966033492132, 'gids': {'real': 1000, 'effective': 1000, 'saved': 1000}, 'num_threads': 157, 'io_counters': [1705658368, 4228337664, 1705658368, 4228337664, 1], 'pid': 471623, 'cpu_percent': 1.6, 'cpu_times': {'user': 2637.37, 'system': 751.51, 'children_user': 0.13, 'children_system': 1.18, 'iowait': 0.0}, 'key': 'pid', 'time_since_update': 0.6452903747558594, 'cmdline': ['/snap/firefox/7559/usr/lib/firefox/firefox'], 'username': 'nicolargo'}, {'status': 'S', 'memory_info': {'rss': 478920704, 'vms': 4367515648, 'shared': 76931072, 'text': 708608, 'lib': 0, 'data': 692023296, 'dirty': 0}, 'name': 'Isolated Web Co', 'nice': 0, 'memory_percent': 2.9161893602635156, 'gids': {'real': 1000, 'effective': 1000, 'saved': 1000}, 'num_threads': 33, 'io_counters': [38509568, 0, 38509568, 0, 1], 'pid': 472861, 'cpu_percent': 1.6, 'cpu_times': {'user': 925.58, 'system': 94.68, 'children_user': 0.0, 'children_system': 0.0, 'iowait': 0.0}, 'key': 'pid', 'time_since_update': 0.6452903747558594, 'cmdline': ['/snap/firefox/7559/usr/lib/firefox/firefox', '-contentproc', '-isForBrowser', '-prefsHandle', '0:45904', '-prefMapHandle', '1:280269', '-jsInitHandle', '2:223356', '-parentBuildID', '20251217233610', '-sandboxReporter', '3', '-chrootClient', '4', '-ipcHandle', '5', '-initialChannelId', '{b750b1b5-b51f-4cbd-8493-9c918fb23a11}', '-parentPid', '471623', '-crashReporter', '6', '-crashHelper', '7', '-greomni', '/snap/firefox/7559/usr/lib/firefox/omni.ja', '-appomni', '/snap/firefox/7559/usr/lib/firefox/browser/omni.ja', '-appDir', '/snap/firefox/7559/usr/lib/firefox/browser', '11', 'tab'], 'username': 'nicolargo'}]
|
||||
[{'pid': 817540, 'memory_percent': 2.7617802712023183, 'status': 'S', 'num_threads': 25, 'gids': {'real': 1000, 'effective': 1000, 'saved': 1000}, 'io_counters': [23214080, 8192, 23214080, 8192, 1], 'cpu_times': {'user': 501.78, 'system': 42.5, 'children_user': 0.0, 'children_system': 0.0, 'iowait': 0.0}, 'nice': 0, 'cpu_percent': 5.2, 'name': 'code', 'memory_info': {'rss': 453562368, 'vms': 1517460189184, 'shared': 143192064, 'text': 148733952, 'lib': 0, 'data': 1318674432, 'dirty': 0}, 'key': 'pid', 'time_since_update': 0.40526819229125977, 'cmdline': ['/snap/code/211/usr/share/code/code', '--type=zygote', '--no-sandbox'], 'username': 'nicolargo'}, {'pid': 814875, 'memory_percent': 4.359469147986559, 'status': 'S', 'num_threads': 146, 'gids': {'real': 1000, 'effective': 1000, 'saved': 1000}, 'io_counters': [778276864, 563982336, 778276864, 563982336, 1], 'cpu_times': {'user': 154.0, 'system': 52.27, 'children_user': 0.09, 'children_system': 0.41, 'iowait': 0.0}, 'nice': 0, 'cpu_percent': 2.6, 'name': 'firefox', 'memory_info': {'rss': 715948032, 'vms': 22647500800, 'shared': 272883712, 'text': 708608, 'lib': 0, 'data': 1006481408, 'dirty': 0}, 'key': 'pid', 'time_since_update': 0.40526819229125977, 'cmdline': ['/snap/firefox/7559/usr/lib/firefox/firefox'], 'username': 'nicolargo'}, {'pid': 813963, 'memory_percent': 2.5835526261641473, 'status': 'S', 'num_threads': 24, 'gids': {'real': 1000, 'effective': 1000, 'saved': 1000}, 'io_counters': [52350976, 73728, 52350976, 73728, 1], 'cpu_times': {'user': 183.97, 'system': 98.37, 'children_user': 1.44, 'children_system': 1.33, 'iowait': 0.0}, 'nice': 0, 'cpu_percent': 2.6, 'name': 'gnome-shell', 'memory_info': {'rss': 424292352, 'vms': 5059411968, 'shared': 166699008, 'text': 8192, 'lib': 0, 'data': 479715328, 'dirty': 0}, 'key': 'pid', 'time_since_update': 0.40526819229125977, 'cmdline': ['/usr/bin/gnome-shell'], 'username': 'nicolargo'}]
|
||||
|
||||
|
||||
Args:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "GLANCES" "1" "Jan 03, 2026" "4.4.2_dev1" "Glances"
|
||||
.TH "GLANCES" "1" "Jan 04, 2026" "4.4.2_dev1" "Glances"
|
||||
.SH NAME
|
||||
glances \- An eye on your system
|
||||
.SH SYNOPSIS
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2026 Nicolas Hennion <nicolas@nicolargo.com>
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
#
|
||||
|
||||
"""JWT utilities for Glances REST API authentication."""
|
||||
|
||||
import secrets
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from glances.logger import logger
|
||||
|
||||
# JWT library import with fallback
|
||||
try:
|
||||
from jose import JWTError, jwt
|
||||
|
||||
JWT_AVAILABLE = True
|
||||
except ImportError:
|
||||
JWT_AVAILABLE = False
|
||||
JWTError = Exception # Placeholder
|
||||
|
||||
|
||||
class JWTHandler:
|
||||
"""Handle JWT token creation and validation."""
|
||||
|
||||
# Algorithm for JWT signing
|
||||
ALGORITHM = "HS256"
|
||||
|
||||
def __init__(self, secret_key: str | None = None, expire_minutes: int = 60):
|
||||
"""Initialize JWT handler.
|
||||
|
||||
Args:
|
||||
secret_key: Secret key for signing tokens. If None, generates a random key.
|
||||
expire_minutes: Token expiration time in minutes (default: 60)
|
||||
"""
|
||||
if not JWT_AVAILABLE:
|
||||
logger.warning("python-jose library not available. JWT authentication disabled.")
|
||||
self._secret_key = None
|
||||
else:
|
||||
# Use provided key or generate a secure random key
|
||||
self._secret_key = secret_key or secrets.token_urlsafe(32)
|
||||
if secret_key is None:
|
||||
logger.info("JWT secret key generated (valid for this server instance only)")
|
||||
|
||||
self._expire_minutes = expire_minutes
|
||||
|
||||
@property
|
||||
def is_available(self) -> bool:
|
||||
"""Check if JWT functionality is available."""
|
||||
return JWT_AVAILABLE and self._secret_key is not None
|
||||
|
||||
@property
|
||||
def expire_minutes(self) -> int:
|
||||
"""Return the token expiration time in minutes."""
|
||||
return self._expire_minutes
|
||||
|
||||
def create_access_token(self, username: str) -> str:
|
||||
"""Create a JWT access token for the given username.
|
||||
|
||||
Args:
|
||||
username: The username to encode in the token
|
||||
|
||||
Returns:
|
||||
Encoded JWT token string
|
||||
|
||||
Raises:
|
||||
RuntimeError: If JWT is not available
|
||||
"""
|
||||
if not self.is_available:
|
||||
raise RuntimeError("JWT authentication is not available")
|
||||
|
||||
expire = datetime.now(timezone.utc) + timedelta(minutes=self._expire_minutes)
|
||||
to_encode = {
|
||||
"sub": username,
|
||||
"exp": expire,
|
||||
"iat": datetime.now(timezone.utc),
|
||||
"iss": "glances",
|
||||
}
|
||||
return jwt.encode(to_encode, self._secret_key, algorithm=self.ALGORITHM)
|
||||
|
||||
def verify_token(self, token: str) -> str | None:
|
||||
"""Verify a JWT token and extract the username.
|
||||
|
||||
Args:
|
||||
token: The JWT token to verify
|
||||
|
||||
Returns:
|
||||
Username if valid, None otherwise
|
||||
"""
|
||||
if not self.is_available:
|
||||
return None
|
||||
|
||||
try:
|
||||
payload = jwt.decode(token, self._secret_key, algorithms=[self.ALGORITHM])
|
||||
username: str = payload.get("sub")
|
||||
if username is None:
|
||||
return None
|
||||
return username
|
||||
except JWTError as e:
|
||||
logger.debug(f"JWT verification failed: {e}")
|
||||
return None
|
||||
|
|
@ -427,12 +427,13 @@ Examples of use:
|
|||
dest='bind_address',
|
||||
help='bind server to the given IPv4/IPv6 address or hostname',
|
||||
)
|
||||
parser.add_argument('-u', dest='username_used', help='use or define the given username')
|
||||
parser.add_argument(
|
||||
'--username',
|
||||
action='store_true',
|
||||
default=False,
|
||||
dest='username_prompt',
|
||||
help='define a client/server username',
|
||||
help='define or use an username',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--password',
|
||||
|
|
@ -441,7 +442,6 @@ Examples of use:
|
|||
dest='password_prompt',
|
||||
help='define a client/server password',
|
||||
)
|
||||
parser.add_argument('-u', dest='username_used', help='use the given client/server username')
|
||||
parser.add_argument('--snmp-community', default='public', dest='snmp_community', help='SNMP community')
|
||||
parser.add_argument('--snmp-port', default=161, type=int, dest='snmp_port', help='SNMP port')
|
||||
parser.add_argument('--snmp-version', default='2c', dest='snmp_version', help='SNMP version (1, 2c or 3)')
|
||||
|
|
@ -775,12 +775,10 @@ Examples of use:
|
|||
# Every username needs a password
|
||||
args.password_prompt = True
|
||||
# Prompt username
|
||||
if args.server:
|
||||
args.username = self.__get_username(description='Define the Glances server username: ')
|
||||
elif args.webserver:
|
||||
args.username = self.__get_username(description='Define the Glances webserver username: ')
|
||||
if args.server or args.webserver:
|
||||
args.username = self.__get_username(description='Enter new username: ')
|
||||
elif args.client:
|
||||
args.username = self.__get_username(description='Enter the Glances server username: ')
|
||||
args.username = self.__get_username(description='Enter username: ')
|
||||
else:
|
||||
if args.username_used:
|
||||
# A username has been set using the -u option ?
|
||||
|
|
@ -791,21 +789,15 @@ Examples of use:
|
|||
|
||||
if args.password_prompt or args.username_used:
|
||||
# Interactive or file password
|
||||
if args.server:
|
||||
if args.server or args.webserver:
|
||||
args.password = self.__get_password(
|
||||
description=f'Define the Glances server password ({args.username} username): ',
|
||||
confirm=True,
|
||||
username=args.username,
|
||||
)
|
||||
elif args.webserver:
|
||||
args.password = self.__get_password(
|
||||
description=f'Define the Glances webserver password ({args.username} username): ',
|
||||
description='Enter new password: ',
|
||||
confirm=True,
|
||||
username=args.username,
|
||||
)
|
||||
elif args.client:
|
||||
args.password = self.__get_password(
|
||||
description=f'Enter the Glances server password ({args.username} username): ',
|
||||
description='Enter password: ',
|
||||
clear=True,
|
||||
username=args.username,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,15 @@ from glances.events_list import glances_events
|
|||
from glances.globals import json_dumps
|
||||
from glances.logger import logger
|
||||
from glances.password import GlancesPassword
|
||||
|
||||
# JWT import with fallback
|
||||
try:
|
||||
from glances.jwt_utils import JWTHandler
|
||||
|
||||
JWT_AVAILABLE = True
|
||||
except ImportError:
|
||||
JWT_AVAILABLE = False
|
||||
JWTHandler = None
|
||||
from glances.plugins.plugin.dag import get_plugin_dependencies
|
||||
from glances.processes import glances_processes
|
||||
from glances.servers_list import GlancesServersList
|
||||
|
|
@ -33,7 +42,7 @@ try:
|
|||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.middleware.gzip import GZipMiddleware
|
||||
from fastapi.responses import HTMLResponse, JSONResponse
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBasic, HTTPBasicCredentials, HTTPBearer
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
except ImportError as e:
|
||||
|
|
@ -50,7 +59,8 @@ import contextlib
|
|||
import threading
|
||||
import time
|
||||
|
||||
security = HTTPBasic()
|
||||
security = HTTPBasic(auto_error=False)
|
||||
bearer_security = HTTPBearer(auto_error=False)
|
||||
|
||||
|
||||
class GlancesJSONResponse(JSONResponse):
|
||||
|
|
@ -122,13 +132,23 @@ class GlancesRestfulApi:
|
|||
self.bind_url = urljoin(f'{self.protocol}://{self.args.bind_address}:{self.args.port}/', self.url_prefix)
|
||||
|
||||
# FastAPI Init
|
||||
# Note: Authentication is now applied at router level, not app level,
|
||||
# to allow the token endpoint to be unauthenticated
|
||||
self._app = FastAPI(default_response_class=GlancesJSONResponse)
|
||||
if self.args.password:
|
||||
self._app = FastAPI(default_response_class=GlancesJSONResponse, dependencies=[Depends(self.authentication)])
|
||||
self._password = GlancesPassword(username=args.username, config=config)
|
||||
|
||||
# Initialize JWT handler
|
||||
if JWT_AVAILABLE:
|
||||
jwt_secret = config.get_value('outputs', 'jwt_secret_key', default=None)
|
||||
jwt_expire = config.get_int_value('outputs', 'jwt_expire_minutes', default=60)
|
||||
self._jwt_handler = JWTHandler(secret_key=jwt_secret, expire_minutes=jwt_expire)
|
||||
logger.info(f"JWT authentication enabled (token expiration: {jwt_expire} minutes)")
|
||||
else:
|
||||
self._jwt_handler = None
|
||||
logger.info("JWT authentication not available (python-jose not installed)")
|
||||
else:
|
||||
self._app = FastAPI(default_response_class=GlancesJSONResponse)
|
||||
self._password = None
|
||||
self._jwt_handler = None
|
||||
|
||||
# Set path for WebUI
|
||||
webui_root_path = config.get_value(
|
||||
|
|
@ -158,6 +178,9 @@ class GlancesRestfulApi:
|
|||
)
|
||||
|
||||
# FastAPI Define routes
|
||||
# Token endpoint router (no authentication required) - must be added first
|
||||
if self.args.password and self._jwt_handler is not None:
|
||||
self._app.include_router(self._token_router())
|
||||
self._app.include_router(self._router())
|
||||
|
||||
# Enable auto discovering of the service
|
||||
|
|
@ -206,16 +229,36 @@ class GlancesRestfulApi:
|
|||
self.servers_list.update_servers_stats()
|
||||
self.timer = Timer(self.args.cached_time)
|
||||
|
||||
def authentication(self, creds: Annotated[HTTPBasicCredentials, Depends(security)]):
|
||||
"""Check if a username/password combination is valid."""
|
||||
if creds.username == self.args.username:
|
||||
# check_password
|
||||
if self._password.check_password(self.args.password, self._password.get_hash(creds.password)):
|
||||
return creds.username
|
||||
def authentication(
|
||||
self,
|
||||
basic_creds: Annotated[HTTPBasicCredentials | None, Depends(security)] = None,
|
||||
bearer_creds: Annotated[HTTPAuthorizationCredentials | None, Depends(bearer_security)] = None,
|
||||
):
|
||||
"""Check if a username/password combination or JWT token is valid.
|
||||
|
||||
# If the username/password combination is invalid, return an HTTP 401
|
||||
Supports both HTTP Basic Auth and Bearer Token (JWT) authentication.
|
||||
"""
|
||||
# Try JWT Bearer token first
|
||||
if bearer_creds is not None and self._jwt_handler is not None:
|
||||
username = self._jwt_handler.verify_token(bearer_creds.credentials)
|
||||
if username is not None:
|
||||
# Verify the username matches the configured username
|
||||
if username == self.args.username:
|
||||
return username
|
||||
logger.warning(f"JWT token contains unknown username: {username}")
|
||||
|
||||
# Fall back to Basic Auth
|
||||
if basic_creds is not None:
|
||||
if basic_creds.username == self.args.username:
|
||||
if self._password.check_password(self.args.password, self._password.get_hash(basic_creds.password)):
|
||||
return basic_creds.username
|
||||
|
||||
# If no valid authentication provided, return HTTP 401
|
||||
www_authenticate = "Bearer, Basic" if self._jwt_handler and self._jwt_handler.is_available else "Basic"
|
||||
raise HTTPException(
|
||||
status.HTTP_401_UNAUTHORIZED, "Incorrect username or password", {"WWW-Authenticate": "Basic"}
|
||||
status.HTTP_401_UNAUTHORIZED,
|
||||
"Incorrect authentication",
|
||||
{"WWW-Authenticate": www_authenticate},
|
||||
)
|
||||
|
||||
def _logo(self):
|
||||
|
|
@ -228,13 +271,24 @@ class GlancesRestfulApi:
|
|||
\_____|_|\__,_|_| |_|\___\___||___/ {__version__}
|
||||
"""
|
||||
|
||||
def _token_router(self) -> APIRouter:
|
||||
"""Define a router for the token endpoint (no authentication required)."""
|
||||
base_path = f'/api/{self.API_VERSION}'
|
||||
router = APIRouter(prefix=self.url_prefix)
|
||||
# Override global dependencies with empty list to disable authentication for this route
|
||||
router.add_api_route(f'{base_path}/token', self._api_token, methods=['POST'], dependencies=[])
|
||||
return router
|
||||
|
||||
def _router(self) -> APIRouter:
|
||||
"""Define a custom router for Glances path."""
|
||||
base_path = f'/api/{self.API_VERSION}'
|
||||
plugin_path = f"{base_path}/{{plugin}}"
|
||||
|
||||
# Create the main router
|
||||
router = APIRouter(prefix=self.url_prefix)
|
||||
# Create the main router with authentication if password is set
|
||||
if self.args.password:
|
||||
router = APIRouter(prefix=self.url_prefix, dependencies=[Depends(self.authentication)])
|
||||
else:
|
||||
router = APIRouter(prefix=self.url_prefix)
|
||||
|
||||
# REST API route definition
|
||||
# ==========================
|
||||
|
|
@ -426,6 +480,80 @@ class GlancesRestfulApi:
|
|||
glances_events.clean(critical=True)
|
||||
return GlancesJSONResponse({})
|
||||
|
||||
async def _api_token(self, request: Request):
|
||||
"""Glances API RESTful implementation.
|
||||
|
||||
Generate a JWT access token for authenticated users.
|
||||
|
||||
Expected JSON body:
|
||||
{
|
||||
"username": "string",
|
||||
"password": "string"
|
||||
}
|
||||
|
||||
Returns:
|
||||
{
|
||||
"access_token": "string",
|
||||
"token_type": "bearer",
|
||||
"expires_in": int (seconds)
|
||||
}
|
||||
"""
|
||||
# Check if JWT is available
|
||||
if self._jwt_handler is None or not self._jwt_handler.is_available:
|
||||
raise HTTPException(
|
||||
status.HTTP_501_NOT_IMPLEMENTED,
|
||||
"JWT authentication is not available. Install python-jose or check configuration.",
|
||||
)
|
||||
|
||||
# Check if password authentication is enabled
|
||||
if self._password is None:
|
||||
raise HTTPException(
|
||||
status.HTTP_501_NOT_IMPLEMENTED,
|
||||
"Password authentication is not enabled. Start Glances with --password option.",
|
||||
)
|
||||
|
||||
# Parse request body
|
||||
try:
|
||||
body = await request.json()
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, "Invalid JSON body")
|
||||
|
||||
username = body.get('username')
|
||||
password = body.get('password')
|
||||
|
||||
if not username or not password:
|
||||
raise HTTPException(
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
"Missing username or password in request body",
|
||||
)
|
||||
|
||||
# Validate credentials
|
||||
if username != self.args.username:
|
||||
raise HTTPException(
|
||||
status.HTTP_401_UNAUTHORIZED,
|
||||
"Incorrect authentication",
|
||||
{"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
# Check password
|
||||
if not self._password.check_password(self.args.password, self._password.get_hash(password)):
|
||||
raise HTTPException(
|
||||
status.HTTP_401_UNAUTHORIZED,
|
||||
"Incorrect authentication",
|
||||
{"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
# Generate token
|
||||
access_token = self._jwt_handler.create_access_token(username)
|
||||
|
||||
return GlancesJSONResponse(
|
||||
{
|
||||
"access_token": access_token,
|
||||
"token_type": "bearer",
|
||||
"expires_in": self._jwt_handler.expire_minutes * 60,
|
||||
}
|
||||
)
|
||||
|
||||
def _api_help(self):
|
||||
"""Glances API RESTful implementation.
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ Note: The url_prefix should always end with a slash (``/``).
|
|||
For example:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[outputs]
|
||||
url_prefix = /glances/
|
||||
|
||||
|
|
@ -72,6 +73,97 @@ API documentation URL
|
|||
The API documentation is embeded in the server and available at the following URL:
|
||||
``http://localhost:61208/docs#/``.
|
||||
|
||||
Authentication
|
||||
--------------
|
||||
|
||||
Glances API supports both HTTP Basic authentication and JWT (JSON Web Token) Bearer authentication.
|
||||
|
||||
To enable authentication, start Glances with the ``--password`` option.
|
||||
|
||||
To generate a new login/password pair, use the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
glances -w --username
|
||||
> Enter new username: foo
|
||||
> Enter new password: ********
|
||||
> Confirm new password: ********
|
||||
> User 'username' created/updated successfully.
|
||||
|
||||
To reuse an existing login/password pair, start Glances with the ``-u <user>`` option:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
glances -w -u foo
|
||||
|
||||
JWT Token Authentication
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
JWT authentication requires the ``python-jose`` library to be installed.
|
||||
|
||||
**Step 1: Get a JWT Token**
|
||||
|
||||
Request a token by sending your credentials to the token endpoint:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -X POST http://localhost:61208/api/{__apiversion__}/token \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{{"username": "your_username", "password": "your_password"}}'
|
||||
|
||||
This will return a response like:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{{
|
||||
"access_token": "...",
|
||||
"token_type": "bearer",
|
||||
"expires_in": 3600
|
||||
}}
|
||||
|
||||
**Step 2: Use the Token**
|
||||
|
||||
Use the token in the Authorization header with Bearer authentication:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Store the token in a variable
|
||||
TOKEN="your_access_token_here"
|
||||
|
||||
# Access a protected endpoint
|
||||
curl -H "Authorization: Bearer $TOKEN" \\
|
||||
http://localhost:61208/api/{__apiversion__}/cpu
|
||||
|
||||
**Complete Example:**
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Get token and extract access_token
|
||||
TOKEN=$(curl -s -X POST http://localhost:61208/api/{__apiversion__}/token \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{{"username": "glances", "password": "mypassword"}}' \\
|
||||
| grep -o '"access_token":"[^"]*"' \\
|
||||
| cut -d'"' -f4)
|
||||
|
||||
# Use the token to get CPU stats
|
||||
curl -H "Authorization: Bearer $TOKEN" \\
|
||||
http://localhost:61208/api/{__apiversion__}/cpu
|
||||
|
||||
**Configuration:**
|
||||
|
||||
You can configure JWT settings in the Glances configuration file:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[outputs]
|
||||
# JWT secret key (if not set, a random key will be generated)
|
||||
jwt_secret_key = your-secret-key-here
|
||||
# JWT token expiration in minutes (default: 60)
|
||||
jwt_expire_minutes = 60
|
||||
|
||||
**Note:** The token endpoint (``/api/{__apiversion__}/token``) does not require authentication.
|
||||
Protected endpoints support both Bearer token and Basic Auth authentication methods.
|
||||
|
||||
WebUI refresh
|
||||
-------------
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ class GlancesPassword:
|
|||
password_hashed = self.hash_password(password_hash)
|
||||
if confirm:
|
||||
# password_confirm is the clear password (only used to compare)
|
||||
password_confirm = self.get_hash(getpass.getpass('Password (confirm): '))
|
||||
password_confirm = self.get_hash(getpass.getpass('Confirm new password: '))
|
||||
|
||||
if not self.check_password(password_hashed, password_confirm):
|
||||
logger.critical("Sorry, passwords do not match. Exit.")
|
||||
|
|
@ -99,7 +99,7 @@ class GlancesPassword:
|
|||
|
||||
# Save the hashed password to the password file
|
||||
if not clear:
|
||||
save_input = input('Do you want to save the password? [Yes/No]: ')
|
||||
save_input = input(f'Do you want to save the password in {self.password_file} ? [Yes/No]: ')
|
||||
if save_input and save_input[0].upper() == 'Y':
|
||||
self.save_password(password_hashed)
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ snmp = ["pysnmp-lextudio<6.2.0"]
|
|||
sparklines = ["sparklines"]
|
||||
web = [
|
||||
"fastapi>=0.82.0",
|
||||
"python-jose[cryptography]>=3.3.0",
|
||||
"requests",
|
||||
"uvicorn",
|
||||
]
|
||||
|
|
|
|||
Loading…
Reference in New Issue