Compare commits
581 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
17266144c8 | |
|
|
7da3111b76 | |
|
|
131d0aec8d | |
|
|
3f060c684b | |
|
|
37d2fe13d0 | |
|
|
0d8352c9c7 | |
|
|
ad9d52b5b0 | |
|
|
22f7be01cb | |
|
|
326c2fef51 | |
|
|
a3195463a0 | |
|
|
84324615c5 | |
|
|
fa9bb2cdab | |
|
|
d97c5cd5b9 | |
|
|
420305a24a | |
|
|
603f1ee8a4 | |
|
|
62e0b784bd | |
|
|
1e25b4b130 | |
|
|
8b76cd458e | |
|
|
5cbbe91e1f | |
|
|
862ac41b58 | |
|
|
c8b2a90f3b | |
|
|
6e844f3c8b | |
|
|
91279b9144 | |
|
|
7160cd8ae4 | |
|
|
4bd36a34a1 | |
|
|
6cedd16753 | |
|
|
12083f33fc | |
|
|
b7c6cce373 | |
|
|
ba932e72bd | |
|
|
5d9593b393 | |
|
|
5886321156 | |
|
|
f72b3e226e | |
|
|
b9a89006a2 | |
|
|
1353b22445 | |
|
|
f058360b52 | |
|
|
f3b3b42e29 | |
|
|
be199ef044 | |
|
|
15d252a40b | |
|
|
0b141ff92d | |
|
|
60f304c313 | |
|
|
29d7a15dab | |
|
|
f837614a69 | |
|
|
3247b0dc33 | |
|
|
507079491a | |
|
|
d5ce8a7461 | |
|
|
f587b281c0 | |
|
|
e74dcc0060 | |
|
|
59d54b8e2b | |
|
|
184e490660 | |
|
|
cd1f9348c2 | |
|
|
68b5215e94 | |
|
|
928e577d8c | |
|
|
1b6d63fd2f | |
|
|
fafdef615d | |
|
|
8ab188bc04 | |
|
|
cd9360f637 | |
|
|
be5a92c7e6 | |
|
|
9907e7242f | |
|
|
9b5cc01b06 | |
|
|
89753198b3 | |
|
|
8408a35100 | |
|
|
bd246fe966 | |
|
|
7b22761c75 | |
|
|
9d4bc76304 | |
|
|
74771d0949 | |
|
|
2a508d7641 | |
|
|
41db9c86f9 | |
|
|
eb21408b72 | |
|
|
4f48d33709 | |
|
|
2800e81cb0 | |
|
|
bc629d12b2 | |
|
|
ec11dff2cd | |
|
|
71b0d4b606 | |
|
|
d009335ba0 | |
|
|
f84baaa8df | |
|
|
bbb733d6cb | |
|
|
73f45dd9a8 | |
|
|
394f94fb27 | |
|
|
2146673841 | |
|
|
fcc5061d8c | |
|
|
f2641c80e2 | |
|
|
b9c451ce7a | |
|
|
ca2d729211 | |
|
|
50818213b1 | |
|
|
868aa0f4b1 | |
|
|
f7ab7efdd4 | |
|
|
9797fc795e | |
|
|
fc2e2c71af | |
|
|
45bdcc92c7 | |
|
|
1cf7683c1d | |
|
|
1b3ee3e035 | |
|
|
3de1a25305 | |
|
|
1567c2db49 | |
|
|
1171d0b4d3 | |
|
|
df684c64c2 | |
|
|
c70321c3fb | |
|
|
93120b9779 | |
|
|
89c8fc125c | |
|
|
11aac6ca4d | |
|
|
e7bd8d4768 | |
|
|
e660520eb2 | |
|
|
0ae24bade0 | |
|
|
728b36a8c9 | |
|
|
e2a1606479 | |
|
|
8c9822ed26 | |
|
|
c4f6c04eb4 | |
|
|
7c13d53cb8 | |
|
|
ccb00ec252 | |
|
|
ab3b386e10 | |
|
|
5cbd9e030a | |
|
|
c89effd602 | |
|
|
ab68fcbe28 | |
|
|
df4b312bec | |
|
|
25da171479 | |
|
|
34c3c11725 | |
|
|
de0b1ef2da | |
|
|
c0dad78189 | |
|
|
d9abfca7fb | |
|
|
cb377055f2 | |
|
|
b2e3ea283d | |
|
|
a74b1867d7 | |
|
|
afff3e2ba6 | |
|
|
fb17066692 | |
|
|
e5e476d255 | |
|
|
ea10748e07 | |
|
|
7c19d01c21 | |
|
|
8bb2a8a91b | |
|
|
b75d1f24c3 | |
|
|
5efa3a01a3 | |
|
|
e11cb80492 | |
|
|
e2d37ee482 | |
|
|
930745b5f3 | |
|
|
ba1b898a3e | |
|
|
9de127cce9 | |
|
|
55ff04437f | |
|
|
ba6be768d0 | |
|
|
4e669d0bc7 | |
|
|
3e7a49eb3b | |
|
|
f1bd18a3a5 | |
|
|
ef5ef9eb8a | |
|
|
89a10d9633 | |
|
|
6692e00a0e | |
|
|
a1edfc735a | |
|
|
5101512320 | |
|
|
244f13634f | |
|
|
7e3116a4e1 | |
|
|
579049cec5 | |
|
|
3646e011cf | |
|
|
ff5e33d856 | |
|
|
de6fae584e | |
|
|
1c6a9e1412 | |
|
|
a7ba827cc5 | |
|
|
c523d20648 | |
|
|
ef36eb9ab6 | |
|
|
f38ff27faa | |
|
|
f87783689b | |
|
|
28098f1555 | |
|
|
0cec111764 | |
|
|
54068209b3 | |
|
|
bdd66c56d3 | |
|
|
10111cc7f6 | |
|
|
02752f90ea | |
|
|
d9d4507bd4 | |
|
|
133f97efd8 | |
|
|
c9fc7d4bb4 | |
|
|
7f34578cea | |
|
|
7b827954e0 | |
|
|
3a3ae4eddb | |
|
|
aef78c3a10 | |
|
|
db13c8380b | |
|
|
7f2f69985c | |
|
|
6361217258 | |
|
|
7edbab35d8 | |
|
|
067eb918ad | |
|
|
c8ed977c9d | |
|
|
1ac760c55b | |
|
|
c2e038d244 | |
|
|
02fe3aa12e | |
|
|
f3a76caa64 | |
|
|
729532713f | |
|
|
2b1d6f4309 | |
|
|
6dd33e3317 | |
|
|
1114a9ab87 | |
|
|
4361df8f84 | |
|
|
1bc8df0a13 | |
|
|
1b93758b65 | |
|
|
8fa0677538 | |
|
|
074a01efc2 | |
|
|
227dedb41b | |
|
|
bcd48844b5 | |
|
|
f01008f430 | |
|
|
a41a67edf7 | |
|
|
6fc887839e | |
|
|
8c70a55488 | |
|
|
8a748d15e3 | |
|
|
bcfb3675a6 | |
|
|
e3b016e6f3 | |
|
|
a64474f43f | |
|
|
c2b59d0a74 | |
|
|
d6a820d2bb | |
|
|
ee50f4e8e2 | |
|
|
eead34b0d0 | |
|
|
29e3947577 | |
|
|
bb0116a198 | |
|
|
349c33fd27 | |
|
|
52c5042e37 | |
|
|
853b71998c | |
|
|
03d4d6cf65 | |
|
|
d02aed76a6 | |
|
|
97ec09dfc3 | |
|
|
0c2b9e5e8e | |
|
|
0134487f5e | |
|
|
f1328a84e9 | |
|
|
547d98599a | |
|
|
031d5d6485 | |
|
|
62c6c9df4e | |
|
|
6302389073 | |
|
|
83d5886a35 | |
|
|
350ed125d3 | |
|
|
7382089129 | |
|
|
2254f899b6 | |
|
|
d03295a977 | |
|
|
9f1eb9496a | |
|
|
d28f3056a7 | |
|
|
e2c65c6687 | |
|
|
e721b66488 | |
|
|
26a0c3ef87 | |
|
|
0bf79775b6 | |
|
|
c970932e96 | |
|
|
38c7bfccba | |
|
|
48674ee71f | |
|
|
6539979498 | |
|
|
3652dc339f | |
|
|
da700c8cfc | |
|
|
420cf2bdde | |
|
|
ce27d795db | |
|
|
f53dbe748e | |
|
|
3931457495 | |
|
|
d0137830c2 | |
|
|
48296a5de9 | |
|
|
6339393271 | |
|
|
2b59ad71fe | |
|
|
ff6dcd5cb0 | |
|
|
b26dac4dd5 | |
|
|
b6db99cbb8 | |
|
|
86e198310d | |
|
|
55c3b9c0ab | |
|
|
9eaef1da36 | |
|
|
de9d46297c | |
|
|
36c9658912 | |
|
|
afdd09d648 | |
|
|
c8e67d07c1 | |
|
|
63f0aacb7c | |
|
|
2c1abd666e | |
|
|
9834e204c4 | |
|
|
39c1b9e13c | |
|
|
6631b37d67 | |
|
|
95620bf584 | |
|
|
fcdd792722 | |
|
|
3de48a4f56 | |
|
|
17eca1f6e6 | |
|
|
3db5f13d8c | |
|
|
11bab0fb5e | |
|
|
ef45c6def2 | |
|
|
32e17004b6 | |
|
|
be29f069d6 | |
|
|
d5d38af356 | |
|
|
0abc39f7df | |
|
|
8f240afec1 | |
|
|
8c8120fb90 | |
|
|
54216c6580 | |
|
|
605f3a9da3 | |
|
|
3ad154b4f8 | |
|
|
af082ee2e0 | |
|
|
ba68e3d8f4 | |
|
|
48641b5d1a | |
|
|
fa82d8ed1c | |
|
|
81a16075bd | |
|
|
05080765cc | |
|
|
580af978f4 | |
|
|
d99d3684c4 | |
|
|
26a41d0c21 | |
|
|
435b250a84 | |
|
|
a31b60b1ab | |
|
|
cb8003c1df | |
|
|
973dd8978e | |
|
|
06c6f8408e | |
|
|
948bc90b58 | |
|
|
a7683caa71 | |
|
|
3f5c1f478a | |
|
|
f65ae16ea2 | |
|
|
e88f33ca05 | |
|
|
2a3046796b | |
|
|
89e9ad1284 | |
|
|
dbba6e0673 | |
|
|
1daac0e6d0 | |
|
|
f173c8d6b4 | |
|
|
5f3e1de01e | |
|
|
9a58715a2f | |
|
|
afaab7190b | |
|
|
c63f2404aa | |
|
|
ae31d29c75 | |
|
|
04fee963cc | |
|
|
f1ae3bb535 | |
|
|
f5ec34cdf9 | |
|
|
11890f31b1 | |
|
|
c1c0d4038f | |
|
|
2cf92f93a9 | |
|
|
89118e53de | |
|
|
1e8ea943ba | |
|
|
6ffcaf4a94 | |
|
|
cba28407cb | |
|
|
463d0af666 | |
|
|
3eb09d7fc5 | |
|
|
202289599d | |
|
|
3a1e188d58 | |
|
|
3d263bc7d4 | |
|
|
365a670c12 | |
|
|
660b94e99d | |
|
|
65393b0771 | |
|
|
45c3c489a0 | |
|
|
73a01d4c52 | |
|
|
218831ecb9 | |
|
|
e3bbd0dc2a | |
|
|
b340b5fc30 | |
|
|
fca11995cb | |
|
|
76b070f3de | |
|
|
5b2b7325fc | |
|
|
94ae566eb7 | |
|
|
a9c3770851 | |
|
|
4c31611f2f | |
|
|
e46a38f141 | |
|
|
c6a48bf424 | |
|
|
bf2c6d1bda | |
|
|
455888e0fa | |
|
|
0db81b71c6 | |
|
|
c1d67556c7 | |
|
|
e46eb765ff | |
|
|
82a829d0c4 | |
|
|
155452e223 | |
|
|
15a94f04da | |
|
|
813785ff82 | |
|
|
28e82eb237 | |
|
|
1268b58bbc | |
|
|
1434d3ad5c | |
|
|
28ae053e4b | |
|
|
cd71b45089 | |
|
|
1365d600a3 | |
|
|
7c13ae17fa | |
|
|
b888dc55e8 | |
|
|
ed2e1cdefb | |
|
|
505a90f8da | |
|
|
cac2e0b884 | |
|
|
df93b5bf4c | |
|
|
e277908118 | |
|
|
ea12008e10 | |
|
|
bcf3d42097 | |
|
|
0ffb08dd2a | |
|
|
75b7998185 | |
|
|
f45900b450 | |
|
|
96660cae16 | |
|
|
83a5847ff1 | |
|
|
51166fe1df | |
|
|
1375573a46 | |
|
|
239d5f7db6 | |
|
|
81150246e5 | |
|
|
39ac435218 | |
|
|
016799cbfa | |
|
|
719094edf7 | |
|
|
c343351072 | |
|
|
d79c74df25 | |
|
|
3488760ae5 | |
|
|
7c0dee3a63 | |
|
|
a5b2a97e6c | |
|
|
b57b26e1b5 | |
|
|
31834a366f | |
|
|
aea1b67862 | |
|
|
e28bf6496f | |
|
|
a78e778b36 | |
|
|
91902f0ffa | |
|
|
f747bdfa73 | |
|
|
e4cdc14cd0 | |
|
|
dc165930b4 | |
|
|
03f23a582d | |
|
|
1e92365c5e | |
|
|
a3e55145b8 | |
|
|
72a1466e9f | |
|
|
c612899612 | |
|
|
b86b201e6a | |
|
|
c59931cb80 | |
|
|
bfda524b4e | |
|
|
0bbb20a127 | |
|
|
54e57317be | |
|
|
c473060740 | |
|
|
4e8b1f5508 | |
|
|
82e59dbfda | |
|
|
fb81a2a5f7 | |
|
|
50c440f739 | |
|
|
9db02dc7ec | |
|
|
ae5eb61307 | |
|
|
080be4d845 | |
|
|
c738f482e9 | |
|
|
a550afbd25 | |
|
|
941baba5b4 | |
|
|
91c132fe08 | |
|
|
009212a04d | |
|
|
43626d9d69 | |
|
|
ec8365817f | |
|
|
bdef92da4f | |
|
|
16dee18c52 | |
|
|
d2de1424fb | |
|
|
daf6964f14 | |
|
|
cfc34e7cd4 | |
|
|
0d2f5e4ba3 | |
|
|
7de582fdb0 | |
|
|
1378eb80ba | |
|
|
fe59850c28 | |
|
|
558ebbaec7 | |
|
|
9e2504b056 | |
|
|
b4d5de96fc | |
|
|
0e181d9059 | |
|
|
25b550834a | |
|
|
b30a29897e | |
|
|
451b1a2294 | |
|
|
b23a4f6a3d | |
|
|
415abe2284 | |
|
|
7f5f2fd2b1 | |
|
|
49aa4e21ab | |
|
|
11a1aa6250 | |
|
|
a1eb6cbacf | |
|
|
343c933e15 | |
|
|
e8b6fd7641 | |
|
|
5d4596ed41 | |
|
|
0d542550a4 | |
|
|
ba67e55a0f | |
|
|
4cf6cf8ac2 | |
|
|
3eb67a32bb | |
|
|
fe48f7df94 | |
|
|
ca65324976 | |
|
|
d563c68b4c | |
|
|
d978d545ed | |
|
|
4f743287b6 | |
|
|
12eecf47f6 | |
|
|
7b8724244a | |
|
|
fcbad49771 | |
|
|
4b15e6693c | |
|
|
6db3672c50 | |
|
|
b56b231ad8 | |
|
|
eec89ccd90 | |
|
|
95ff4506d1 | |
|
|
4e5eff834a | |
|
|
4f03be737b | |
|
|
f799911058 | |
|
|
0dbd4c58d4 | |
|
|
ecb3e6e0ea | |
|
|
4ef03ddac2 | |
|
|
ed400246fd | |
|
|
efa636b7a7 | |
|
|
a3556a4004 | |
|
|
4dd312229f | |
|
|
36e0b46903 | |
|
|
b31aa7234e | |
|
|
f854ea31d8 | |
|
|
1f8e63ec3d | |
|
|
0e288cdc97 | |
|
|
6ed7bee526 | |
|
|
5a661a295c | |
|
|
9e29d8aa45 | |
|
|
a6de55bfca | |
|
|
fe5edd6e60 | |
|
|
71c1c34ddd | |
|
|
eff75ebeed | |
|
|
07daacd35f | |
|
|
7440f6df9b | |
|
|
d6802b77d7 | |
|
|
3462a48081 | |
|
|
c5acae327b | |
|
|
73663ef5c7 | |
|
|
003ba392c0 | |
|
|
b28d793d72 | |
|
|
1c6485fda6 | |
|
|
649562ca37 | |
|
|
17e43c1271 | |
|
|
c1a772d7f1 | |
|
|
72e1511dc4 | |
|
|
3278027281 | |
|
|
ad5e7f8479 | |
|
|
3f124d3596 | |
|
|
24eb6cf21a | |
|
|
52bd26f03e | |
|
|
75afbe16c9 | |
|
|
6af2aa092b | |
|
|
95d38a1392 | |
|
|
dde335696a | |
|
|
ef83c404ff | |
|
|
445d20dcad | |
|
|
e732ba37be | |
|
|
e118d7d171 | |
|
|
b414bafc94 | |
|
|
8e23bd661f | |
|
|
c8d70fb343 | |
|
|
21f0fd0eb4 | |
|
|
5f31106ac1 | |
|
|
6e466c7d14 | |
|
|
1be8d528ed | |
|
|
1e7e0c056b | |
|
|
ebd8f766b0 | |
|
|
270ff52363 | |
|
|
f933d9f1ef | |
|
|
c69a5a9307 | |
|
|
b747c87796 | |
|
|
f77e039f21 | |
|
|
1901fcad05 | |
|
|
2ef24c7b2d | |
|
|
1c93368561 | |
|
|
55258748ff | |
|
|
133b97bcaf | |
|
|
840670ea77 | |
|
|
5e1a08af79 | |
|
|
4531aac37f | |
|
|
829ae981b5 | |
|
|
8d1039d4ac | |
|
|
2cbf7a7fba | |
|
|
0f10ffc245 | |
|
|
f9250c9985 | |
|
|
bdccb0b196 | |
|
|
7218a22002 | |
|
|
1feb1ec6f8 | |
|
|
4b21d0a18f | |
|
|
bead0ae13d | |
|
|
1c7a824821 | |
|
|
48543cab8f | |
|
|
01b4ca1cf5 | |
|
|
de90b4fd54 | |
|
|
a91b187069 | |
|
|
eeb7db8e82 | |
|
|
d22f3e16fe | |
|
|
7cc4e86639 | |
|
|
3cdededa78 | |
|
|
3016717208 | |
|
|
9fd1efce21 | |
|
|
4db6aaeb3d | |
|
|
d4202339d9 | |
|
|
551ec80be6 | |
|
|
4c9685f0bf | |
|
|
90ebe27f7e | |
|
|
0907700843 | |
|
|
ca2b7a9907 | |
|
|
c1beee40a2 | |
|
|
acf54a508e | |
|
|
50aa49a497 | |
|
|
17553fac8f | |
|
|
060457c2ae | |
|
|
bf54140b87 | |
|
|
18d6beebe1 | |
|
|
bc1211a672 | |
|
|
9cd51fc060 | |
|
|
041a9852a9 | |
|
|
aa8f3abab3 | |
|
|
96aa6987ab | |
|
|
f29af964ef | |
|
|
aee1ea6acd | |
|
|
6f93c1bad9 | |
|
|
20d08c0cb6 | |
|
|
4d7300ab96 | |
|
|
0b62a9f785 | |
|
|
cb22a0c56a | |
|
|
fcbcbbfb4d | |
|
|
c5954c9d99 | |
|
|
ad0be97653 | |
|
|
6ccd4430e9 | |
|
|
b06b0d5c7c | |
|
|
3a96cfd0cc | |
|
|
584fbba311 | |
|
|
fc4dc38028 | |
|
|
aa428b51c9 | |
|
|
3ba5946974 | |
|
|
b7cdf66eb0 | |
|
|
6a265be9d5 | |
|
|
f1054c5eea | |
|
|
4c3e8f9448 |
|
|
@ -8,10 +8,8 @@
|
|||
!/glances/outputs/static
|
||||
|
||||
# Include Requirements files
|
||||
!/requirements.txt
|
||||
!/all-requirements.txt
|
||||
!/docker-requirements.txt
|
||||
!/webui-requirements.txt
|
||||
!/optional-requirements.txt
|
||||
|
||||
# Include Config file
|
||||
!/docker-compose/glances.conf
|
||||
|
|
@ -19,3 +17,6 @@
|
|||
|
||||
# Include Binary file
|
||||
!/docker-bin.sh
|
||||
|
||||
# Include TOML file
|
||||
!/pyproject.toml
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ jobs:
|
|||
if: github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.13"
|
||||
- name: Install pypa/build
|
||||
|
|
@ -45,7 +45,7 @@ jobs:
|
|||
id-token: write
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
|
|
@ -54,6 +54,7 @@ jobs:
|
|||
with:
|
||||
skip-existing: true
|
||||
attestations: false
|
||||
print-hash: true
|
||||
|
||||
pypi_test:
|
||||
name: Publish Python 🐍 distribution 📦 to TestPyPI
|
||||
|
|
@ -69,7 +70,7 @@ jobs:
|
|||
id-token: write
|
||||
steps:
|
||||
- name: Download all the dists
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: python-package-distributions
|
||||
path: dist/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ name: build_docker
|
|||
|
||||
env:
|
||||
DEFAULT_DOCKER_IMAGE: nicolargo/glances
|
||||
PUSH_BRANCH: ${{ 'refs/heads/develop' == github.ref || 'refs/heads/master' == github.ref || startsWith(github.ref, 'refs/tags/v') }}
|
||||
PUSH_BRANCH: ${{ 'refs/heads/develop' == github.ref || startsWith(github.ref, 'refs/tags/v') }}
|
||||
# Alpine image platform: https://hub.docker.com/_/alpine
|
||||
# linux/arm/v6,linux/arm/v7 do not work (timeout during the build)
|
||||
DOCKER_PLATFORMS: linux/amd64,linux/arm64/v8
|
||||
|
|
@ -36,15 +36,11 @@ jobs:
|
|||
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
VERSION=${GITHUB_REF#refs/tags/v}
|
||||
TAG_ARRAY="[{ \"target\": \"minimal\", \"tag\": \"${VERSION}\" },"
|
||||
TAG_ARRAY="$TAG_ARRAY { \"target\": \"full\", \"tag\": \"${VERSION}-full\" }]"
|
||||
TAG_ARRAY="$TAG_ARRAY { \"target\": \"minimal\", \"tag\": \"latest\" },"
|
||||
TAG_ARRAY="$TAG_ARRAY { \"target\": \"full\", \"tag\": \"${VERSION}-full\" },"
|
||||
TAG_ARRAY="$TAG_ARRAY { \"target\": \"full\", \"tag\": \"latest-full\" }]"
|
||||
elif [[ $GITHUB_REF == refs/heads/develop ]]; then
|
||||
TAG_ARRAY="[{ \"target\": \"dev\", \"tag\": \"dev\" }]"
|
||||
elif [[ $GITHUB_REF == refs/heads/master ]]; then
|
||||
TAG_ARRAY="[{ \"target\": \"minimal\", \"tag\": \"latest\" },"
|
||||
TAG_ARRAY="$TAG_ARRAY { \"target\": \"full\", \"tag\": \"latest-full\" }]"
|
||||
elif [[ $GITHUB_REF == refs/heads/main ]]; then
|
||||
TAG_ARRAY="[{ \"target\": \"minimal\", \"tag\": \"latest\" },"
|
||||
TAG_ARRAY="$TAG_ARRAY { \"target\": \"full\", \"tag\": \"latest-full\" }]"
|
||||
else
|
||||
TAG_ARRAY="[]"
|
||||
fi
|
||||
|
|
@ -63,7 +59,7 @@ jobs:
|
|||
tag: ${{ fromJson(needs.create_docker_images_list.outputs.tags) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Retrieve Repository Docker metadata
|
||||
id: docker_meta
|
||||
|
|
@ -92,7 +88,7 @@ jobs:
|
|||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Build and push image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
push: ${{ env.PUSH_BRANCH == 'true' }}
|
||||
tags: "${{ env.DEFAULT_DOCKER_IMAGE }}:${{ matrix.os != 'alpine' && format('{0}-', matrix.os) || '' }}${{ matrix.tag.tag }}"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Run Trivy vulnerability scanner in repo mode
|
||||
uses: aquasecurity/trivy-action@master
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ jobs:
|
|||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
days-before-issue-stale: 90
|
||||
days-before-issue-close: -1
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ on:
|
|||
jobs:
|
||||
|
||||
source-code-checks:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Check formatting with Ruff
|
||||
uses: chartboost/ruff-action@v1
|
||||
|
|
@ -37,14 +37,16 @@ jobs:
|
|||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
||||
# Python EOL version are note tested
|
||||
# Multiple Python version only tested for Linux
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
|
|
@ -58,26 +60,22 @@ jobs:
|
|||
run: |
|
||||
python -m pytest ./tests/test_core.py
|
||||
|
||||
# Error appear with h11, not related to Glances
|
||||
# Should be tested if correction is done
|
||||
# Installed c:\hostedtoolcache\windows\python\3.9.13\x64\lib\site-packages\exceptiongroup-1.2.1-py3.9.egg
|
||||
# error: h11 0.14.0 is installed but h11<0.13,>=0.11 is required by {'httpcore'}
|
||||
# Error: Process completed with exit code 1.
|
||||
test-windows:
|
||||
|
||||
needs: source-code-checks
|
||||
# https://github.com/actions/runner-images?tab=readme-ov-file#available-images
|
||||
runs-on: windows-2022
|
||||
runs-on: windows-2025
|
||||
strategy:
|
||||
matrix:
|
||||
# Windows-curses not available for Python 3.13 for the moment
|
||||
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
||||
# Windows-curses not available for Python 3.14 for the moment
|
||||
# See https://github.com/zephyrproject-rtos/windows-curses/issues/76
|
||||
python-version: ["3.13"]
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
|
|
@ -96,18 +94,18 @@ jobs:
|
|||
|
||||
needs: source-code-checks
|
||||
# https://github.com/actions/runner-images?tab=readme-ov-file#available-images
|
||||
runs-on: macos-14
|
||||
runs-on: macos-15
|
||||
strategy:
|
||||
matrix:
|
||||
# Only test the latest stable version
|
||||
python-version: ["3.13"]
|
||||
python-version: ["3.14"]
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ jobs:
|
|||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Glances will be build with Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ local.properties
|
|||
.classpath
|
||||
.settings/
|
||||
.loadpath
|
||||
.ipynb_checkpoints/
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
|
@ -63,7 +64,14 @@ bower_components/
|
|||
/*_source.tar.bz2
|
||||
|
||||
# Virtual env
|
||||
/venv*/
|
||||
.venv-uv/
|
||||
.venv/
|
||||
uv.lock
|
||||
.python-version
|
||||
|
||||
# Test
|
||||
.coverage
|
||||
tests-data/issues/*/config/
|
||||
|
||||
# Local SSL certificates
|
||||
glances.local*.pem
|
||||
|
|
|
|||
|
|
@ -1,22 +1,107 @@
|
|||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
- repo: https://github.com/gitleaks/gitleaks
|
||||
rev: v8.24.2
|
||||
hooks:
|
||||
- id: check-ast
|
||||
- id: check-docstring-first
|
||||
- id: check-json
|
||||
- id: check-merge-conflict
|
||||
- id: check-shebang-scripts-are-executable
|
||||
- id: check-toml
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
- id: detect-private-key
|
||||
- id: mixed-line-ending
|
||||
- id: requirements-txt-fixer
|
||||
- id: gitleaks
|
||||
name: "🔒 security · Detect hardcoded secrets"
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.4.4
|
||||
rev: v0.14.10
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
- id: ruff
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
- id: ruff-format
|
||||
name: "🐍 python · Formatter with Ruff"
|
||||
types_or: [ python, pyi ]
|
||||
args: [ --config, './pyproject.toml' ]
|
||||
- id: ruff-check
|
||||
name: "🐍 python · Linter with Ruff"
|
||||
types_or: [ python, pyi ]
|
||||
args: [ --fix, --exit-non-zero-on-fix, --config, './pyproject.toml' ]
|
||||
|
||||
# - repo: https://github.com/RobertCraigie/pyright-python
|
||||
# rev: v1.1.391
|
||||
# hooks:
|
||||
# - id: pyright
|
||||
# name: "🐍 python · Check types"
|
||||
|
||||
# - repo: https://github.com/biomejs/pre-commit
|
||||
# rev: "v2.3.7"
|
||||
# hooks:
|
||||
# - id: biome-check
|
||||
# name: "🟨 javascript · Lint, format, and safe fixes with Biome"
|
||||
|
||||
- repo: https://github.com/python-jsonschema/check-jsonschema
|
||||
rev: 0.35.0
|
||||
hooks:
|
||||
- id: check-github-workflows
|
||||
name: "🐙 github-actions · Validate gh workflow files"
|
||||
args: ["--verbose"]
|
||||
|
||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||
rev: v0.11.0.1
|
||||
hooks:
|
||||
- id: shellcheck
|
||||
name: "🐚 shell · Lint shell scripts"
|
||||
|
||||
- repo: https://github.com/openstack/bashate
|
||||
rev: 2.1.1
|
||||
hooks:
|
||||
- id: bashate
|
||||
name: "🐚 shell · Check shell script code style"
|
||||
entry: bashate --error . --ignore=E006
|
||||
|
||||
# - repo: https://github.com/mrtazz/checkmake.git
|
||||
# rev: 0.2.2
|
||||
# hooks:
|
||||
# - id: checkmake
|
||||
# name: "🐮 Makefile · Lint Makefile"
|
||||
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: check-executables-have-shebangs
|
||||
name: "📁 filesystem/⚙️ exec · Verify shebang presence"
|
||||
- id: check-shebang-scripts-are-executable
|
||||
name: "📁 filesystem/⚙️ exec · Verify script permissions"
|
||||
- id: check-case-conflict
|
||||
name: "📁 filesystem/📝 names · Check case sensitivity"
|
||||
- id: destroyed-symlinks
|
||||
name: "📁 filesystem/🔗 symlink · Detect broken symlinks"
|
||||
- id: check-merge-conflict
|
||||
name: "🌳 git · Detect conflict markers"
|
||||
- id: forbid-new-submodules
|
||||
name: "🌳 git · Prevent submodule creation"
|
||||
- id: no-commit-to-branch
|
||||
name: "🌳 git · Protect main branches"
|
||||
args: ["--branch", "main", "--branch", "master"]
|
||||
- id: check-added-large-files
|
||||
name: "🌳 git · Block large file commits"
|
||||
args: ['--maxkb=5000']
|
||||
- id: check-ast
|
||||
name: "🐍 python/🔍 quality · Validate Python AST"
|
||||
- id: check-docstring-first
|
||||
name: "🐍 python/📝 style · Enforce docstring at top"
|
||||
- id: check-json
|
||||
name: "📄 formats/json · Validate JSON files"
|
||||
- id: check-shebang-scripts-are-executable
|
||||
name: "📁 filesystem/⚙️ exec · Ensure scripts are executable"
|
||||
- id: check-toml
|
||||
name: "📄 formats/toml · Validate TOML files"
|
||||
- id: check-yaml
|
||||
name: "📄 formats/yaml · Validate YAML syntax"
|
||||
- id: debug-statements
|
||||
name: "🐍 python/🪲 debug · Detect debug statements"
|
||||
- id: detect-private-key
|
||||
name: "🔐 security · Detect private keys"
|
||||
- id: mixed-line-ending
|
||||
name: "📄 text/↩️ newline · Normalize line endings"
|
||||
- id: requirements-txt-fixer
|
||||
name: "🐍 python/📦 deps · Sort requirements.txt"
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: find-duplicate-lines
|
||||
name: "❗️local script · Find duplicate lines at the end of file"
|
||||
entry: bash tests-data/tools/find-duplicate-lines.sh
|
||||
language: system
|
||||
types: [python]
|
||||
pass_filenames: false
|
||||
|
|
|
|||
|
|
@ -31,4 +31,4 @@ sphinx:
|
|||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: doc-requirements.txt
|
||||
- requirements: dev-requirements.txt
|
||||
3
AUTHORS
|
|
@ -13,6 +13,9 @@ PGP Public key: gpg --keyserver pgp.mit.edu --recv-keys 0xba43c11f2c8b4347
|
|||
RazCrimson (maintainer of the Glances project)
|
||||
https://github.com/RazCrimson
|
||||
|
||||
Ariel Otibili (aka) ariel-anieli (for the huge work on code quality)
|
||||
https://github.com/ariel-anieli
|
||||
|
||||
Alessio Sergi (aka) Al3hex (thanks you for the great job on this project)
|
||||
https://twitter.com/al3hex
|
||||
https://github.com/asergi
|
||||
|
|
|
|||
|
|
@ -3,9 +3,12 @@ include CONTRIBUTING.md
|
|||
include COPYING
|
||||
include NEWS.rst
|
||||
include README.rst
|
||||
include README-pypi.rst
|
||||
include SECURITY.md
|
||||
include conf/glances.conf
|
||||
include conf/fetch-templates/*.jinja
|
||||
include requirements.txt
|
||||
include all-requirements.txt
|
||||
recursive-include docs *
|
||||
recursive-include glances *.py
|
||||
recursive-include glances/outputs/static *
|
||||
|
|
|
|||
253
Makefile
|
|
@ -1,17 +1,6 @@
|
|||
PORT ?= 8008
|
||||
venv_full:= venv/bin
|
||||
venv_min := venv-min/bin
|
||||
CONF := conf/glances.conf
|
||||
PIP := $(venv_full)/pip
|
||||
PYTHON := $(venv_full)/python
|
||||
PYTEST := $(venv_full)/python -m pytest
|
||||
LASTTAG = $(shell git describe --tags --abbrev=0)
|
||||
|
||||
VENV_TYPES := full min
|
||||
VENV_PYTHON := $(VENV_TYPES:%=venv-%-python)
|
||||
VENV_UPG := $(VENV_TYPES:%=venv-%-upgrade)
|
||||
VENV_DEPS := $(VENV_TYPES:%=venv-%)
|
||||
VENV_INST_UPG := $(VENV_DEPS) $(VENV_UPG)
|
||||
PORT ?= 8008
|
||||
CONF := conf/glances.conf
|
||||
LASTTAG = $(shell git describe --tags --abbrev=0)
|
||||
|
||||
IMAGES_TYPES := full minimal
|
||||
DISTROS := alpine ubuntu
|
||||
|
|
@ -26,109 +15,155 @@ PODMAN_SOCK ?= /run/user/$(shell id -u)/podman/podman.sock
|
|||
DOCKER_SOCK ?= /var/run/docker.sock
|
||||
DOCKER_SOCKS := -v $(PODMAN_SOCK):$(PODMAN_SOCK):ro -v $(DOCKER_SOCK):$(DOCKER_SOCK):ro
|
||||
DOCKER_OPTS := --rm -e TZ="${TZ}" -e GLANCES_OPT="" --pid host --network host
|
||||
UV_RUN := .venv-uv/bin/uv
|
||||
|
||||
# if the command is only `make`, the default tasks will be the printing of the help.
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
.PHONY: help test docs docs-server venv venv-min
|
||||
.PHONY: help test docs docs-server venv requirements profiling docker all clean all test
|
||||
|
||||
help: ## List all make commands available
|
||||
@grep -E '^[\.a-zA-Z_%-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
|
||||
awk -F ":" '{print $1}' | \
|
||||
grep -v % | \
|
||||
sed 's/\\//g' | \
|
||||
sort | \
|
||||
grep -v % | sed 's/\\//g' | sort | \
|
||||
awk 'BEGIN {FS = ":[^:]*?##"}; {printf "\033[1;34mmake %-50s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
# ===================================================================
|
||||
# Virtualenv
|
||||
# ===================================================================
|
||||
|
||||
venv-%-upgrade: UPGRADE = --upgrade
|
||||
# install-uv: ## Instructions to install the UV tool
|
||||
# @echo "Install the UV tool (https://astral.sh/uv/)"
|
||||
# @echo "Please install the UV tool manually"
|
||||
# @echo "For example with: curl -LsSf https://astral.sh/uv/install.sh | sh"
|
||||
# @echo "Or via a package manager of your distribution"
|
||||
# @echo "For example for Snap: snap install astral-uv"
|
||||
|
||||
define DEFINE_VARS_FOR_TYPE
|
||||
venv-$(TYPE) venv-$(TYPE)-upgrade: VIRTUAL_ENV = $(venv_$(TYPE))
|
||||
endef
|
||||
install-uv: ## Install UV tool in a specific virtualenv
|
||||
python3 -m venv .venv-uv
|
||||
.venv-uv/bin/pip install uv
|
||||
|
||||
$(foreach TYPE,$(VENV_TYPES),$(eval $(DEFINE_VARS_FOR_TYPE)))
|
||||
upgrade-uv: ## Upgrade the UV tool
|
||||
.venv-uv/bin/pip install --upgrade pip
|
||||
.venv-uv/bin/pip install --upgrade uv
|
||||
|
||||
$(VENV_PYTHON): venv-%-python:
|
||||
virtualenv -p python3 $(if $(filter full,$*),venv,venv-$*)
|
||||
venv: ## Create the virtualenv with all dependencies
|
||||
$(UV_RUN) sync --all-extras --no-group dev
|
||||
|
||||
$(VENV_INST_UPG): venv-%:
|
||||
$(if $(UPGRADE),$(VIRTUAL_ENV)/pip install --upgrade pip,)
|
||||
$(foreach REQ,$(REQS), $(VIRTUAL_ENV)/pip install $(UPGRADE) -r $(REQ);)
|
||||
$(if $(PRE_COMMIT),$(VIRTUAL_ENV)/pre-commit install --hook-type pre-commit,)
|
||||
venv-upgrade venv-switch-to-full: ## Upgrade the virtualenv with all dependencies
|
||||
$(UV_RUN) sync --upgrade --all-extras
|
||||
|
||||
venv-python: $(VENV_PYTHON) ## Install all Python 3 venv
|
||||
venv: $(VENV_DEPS) ## Install all Python 3 dependencies
|
||||
venv-upgrade: $(VENV_UPG) ## Upgrade all Python 3 dependencies
|
||||
venv-min: ## Create the virtualenv with minimal dependencies
|
||||
$(UV_RUN) sync
|
||||
|
||||
# For full installation (with optional dependencies)
|
||||
venv-upgrade-min venv-switch-to-min: ## Upgrade the virtualenv with minimal dependencies
|
||||
$(UV_RUN) sync --upgrade
|
||||
|
||||
venv-full venv-full-upgrade: REQS = requirements.txt optional-requirements.txt dev-requirements.txt doc-requirements.txt
|
||||
venv-clean: ## Remove the virtualenv
|
||||
rm -rf .venv
|
||||
|
||||
venv-full-python: ## Install Python 3 venv
|
||||
venv-full: venv-python ## Install Python 3 run-time
|
||||
venv-full-upgrade: ## Upgrade Python 3 run-time dependencies
|
||||
venv-full: PRE_COMMIT = 1
|
||||
venv-dev: ## Create the virtualenv with dev dependencies
|
||||
$(UV_RUN) sync --dev --all-extras
|
||||
$(UV_RUN) run pre-commit install --hook-type pre-commit
|
||||
|
||||
# For minimal installation (without optional dependencies)
|
||||
# ===================================================================
|
||||
# Requirements
|
||||
#
|
||||
# Note: the --no-hashes option should be used because pip (in CI) has
|
||||
# issues with hashes.
|
||||
# ===================================================================
|
||||
|
||||
venv-min venv-min-upgrade: REQS = requirements.txt dev-requirements.txt doc-requirements.txt
|
||||
requirements-min: ## Generate the requirements.txt files (minimal dependencies)
|
||||
$(UV_RUN) export --no-emit-workspace --no-hashes --no-group dev --output-file requirements.txt
|
||||
|
||||
venv-min-python: ## Install Python 3 venv minimal
|
||||
venv-min: venv-min-python ## Install Python 3 minimal run-time dependencies
|
||||
venv-min-upgrade: ## Upgrade Python 3 minimal run-time dependencies
|
||||
requirements-all: ## Generate the all-requirements.txt files (all dependencies)
|
||||
$(UV_RUN) export --no-emit-workspace --no-hashes --all-extras --no-group dev --output-file all-requirements.txt
|
||||
|
||||
requirements-docker: ## Generate the docker-requirements.txt files (Docker specific dependencies)
|
||||
$(UV_RUN) export --no-emit-workspace --no-hashes --no-group dev --extra containers --extra web --output-file docker-requirements.txt
|
||||
|
||||
requirements-dev: ## Generate the dev-requirements.txt files (dev dependencies)
|
||||
$(UV_RUN) export --no-hashes --only-dev --output-file dev-requirements.txt
|
||||
|
||||
requirements: requirements-min requirements-all requirements-dev requirements-docker ## Generate all the requirements files
|
||||
|
||||
requirements-upgrade: venv-upgrade requirements ## Upgrade the virtualenv and regenerate all the requirements files
|
||||
|
||||
# ===================================================================
|
||||
# Tests
|
||||
# ===================================================================
|
||||
|
||||
test: ## Run All unit tests
|
||||
$(PYTEST)
|
||||
$(UV_RUN) run pytest
|
||||
|
||||
test-core: ## Run Core unit tests
|
||||
$(PYTEST) tests/test_core.py
|
||||
$(UV_RUN) run pytest tests/test_core.py
|
||||
|
||||
test-api: ## Run API unit tests
|
||||
$(UV_RUN) run pytest tests/test_api.py
|
||||
|
||||
test-memoryleak: ## Run Memory-leak unit tests
|
||||
$(PYTEST) tests/test_memoryleak.py
|
||||
$(UV_RUN) run pytest tests/test_memoryleak.py
|
||||
|
||||
test-perf: ## Run Perf unit tests
|
||||
$(PYTEST) tests/test_perf.py
|
||||
$(UV_RUN) run pytest tests/test_perf.py
|
||||
|
||||
test-restful: ## Run Restful API unit tests
|
||||
$(PYTEST) tests/test_restful.py
|
||||
$(UV_RUN) run pytest tests/test_restful.py
|
||||
|
||||
test-webui: ## Run WebUI unit tests
|
||||
$(PYTEST) tests/test_webui.py
|
||||
$(UV_RUN) run pytest tests/test_webui.py
|
||||
|
||||
test-xmlrpc: ## Run XMLRPC API unit tests
|
||||
$(PYTEST) tests/test_xmlrpc.py
|
||||
$(UV_RUN) run pytest tests/test_xmlrpc.py
|
||||
|
||||
test-with-upgrade: venv-upgrade test ## Upgrade deps and run unit tests
|
||||
|
||||
test-min: ## Run core unit tests in minimal environment
|
||||
$(venv_min)/python -m pytest tests/test_core.py
|
||||
test-export-csv: ## Run interface tests with CSV
|
||||
/bin/bash ./tests/test_export_csv.sh
|
||||
|
||||
test-min-with-upgrade: venv-min-upgrade ## Upgrade deps and run unit tests in minimal environment
|
||||
$(venv_min)/python -m pytest tests/test_core.py
|
||||
test-export-json: ## Run interface tests with JSON
|
||||
/bin/bash ./tests/test_export_json.sh
|
||||
|
||||
test-export-influxdb-v1: ## Run interface tests with InfluxDB version 1 (Legacy)
|
||||
/bin/bash ./tests/test_export_influxdb_v1.sh
|
||||
|
||||
test-export-influxdb-v3: ## Run interface tests with InfluxDB version 3 (Core)
|
||||
/bin/bash ./tests/test_export_influxdb_v3.sh
|
||||
|
||||
test-export-timescaledb: ## Run interface tests with TimescaleDB
|
||||
/bin/bash ./tests/test_export_timescaledb.sh
|
||||
|
||||
test-export-nats: ## Run interface tests with NATS
|
||||
/bin/bash ./tests/test_export_nats.sh
|
||||
|
||||
test-exports: test-export-csv test-export-json test-export-influxdb-v1 test-export-influxdb-v3 test-export-timescaledb test-export-nats ## Tests all exports
|
||||
|
||||
# ===================================================================
|
||||
# Linters, profilers and cyber security
|
||||
# ===================================================================
|
||||
|
||||
pre-commit: ## Run pre-commit hooks
|
||||
$(UV_RUN) run pre-commit run --all-files
|
||||
|
||||
find-duplicate-lines: ## Search for duplicate lines in files
|
||||
/bin/bash tests-data/tools/find-duplicate-lines.sh
|
||||
|
||||
format: ## Format the code
|
||||
$(venv_full)/python -m ruff format .
|
||||
$(UV_RUN) run ruff format .
|
||||
|
||||
lint: ## Lint the code.
|
||||
$(venv_full)/python -m ruff check . --fix
|
||||
$(UV_RUN) run ruff check . --fix
|
||||
|
||||
lint-readme: ## Lint the main README.rst file
|
||||
$(UV_RUN) run rstcheck README.rst
|
||||
$(UV_RUN) run rstcheck README-pypi.rst
|
||||
|
||||
codespell: ## Run codespell to fix common misspellings in text files
|
||||
$(venv_full)/codespell -S .git,./docs/_build,./Glances.egg-info,./venv*,./glances/outputs,*.svg -L hart,bu,te,statics -w
|
||||
$(UV_RUN) run codespell -S .git,./docs/_build,./Glances.egg-info,./venv*,./glances/outputs,*.svg -L hart,bu,te,statics -w
|
||||
|
||||
semgrep: ## Run semgrep to find bugs and enforce code standards
|
||||
$(venv_full)/semgrep scan --config=auto
|
||||
$(UV_RUN) run semgrep scan --config=auto
|
||||
|
||||
profiling-%: SLEEP = 3
|
||||
profiling-%: TIMES = 30
|
||||
|
|
@ -142,27 +177,27 @@ endef
|
|||
profiling-gprof: CPROF = glances.cprof
|
||||
profiling-gprof: ## Callgraph profiling (need "apt install graphviz")
|
||||
$(DISPLAY-BANNER)
|
||||
$(PYTHON) -m cProfile -o $(CPROF) run-venv.py -C $(CONF) --stop-after $(TIMES)
|
||||
$(venv_full)/gprof2dot -f pstats $(CPROF) | dot -Tsvg -o $(OUT_DIR)/glances-cgraph.svg
|
||||
$(UV_RUN) run python -m cProfile -o $(CPROF) run-venv.py -C $(CONF) --stop-after $(TIMES)
|
||||
$(UV_RUN) run gprof2dot -f pstats $(CPROF) | dot -Tsvg -o $(OUT_DIR)/glances-cgraph.svg
|
||||
rm -f $(CPROF)
|
||||
|
||||
profiling-pyinstrument: ## PyInstrument profiling
|
||||
$(DISPLAY-BANNER)
|
||||
$(PIP) install pyinstrument
|
||||
$(PYTHON) -m pyinstrument -r html -o $(OUT_DIR)/glances-pyinstrument.html -m glances -C $(CONF) --stop-after $(TIMES)
|
||||
$(UV_RUN) add pyinstrument
|
||||
$(UV_RUN) run pyinstrument -r html -o $(OUT_DIR)/glances-pyinstrument.html -m glances -C $(CONF) --stop-after $(TIMES)
|
||||
|
||||
profiling-pyspy: ## Flame profiling
|
||||
$(DISPLAY-BANNER)
|
||||
$(venv_full)/py-spy record -o $(OUT_DIR)/glances-flame.svg -d 60 -s -- $(PYTHON) run-venv.py -C $(CONF) --stop-after $(TIMES)
|
||||
$(UV_RUN) run py-spy record -o $(OUT_DIR)/glances-flame.svg -d 60 -s -- .venv-uv/bin/uvrun python run-venv.py -C $(CONF) --stop-after $(TIMES)
|
||||
|
||||
profiling: profiling-gprof profiling-pyinstrument profiling-pyspy ## Profiling of the Glances software
|
||||
|
||||
trace-malloc: ## Trace the malloc() calls
|
||||
@echo "Malloc test is running, please wait ~30 secondes..."
|
||||
$(PYTHON) -m glances -C $(CONF) --trace-malloc --stop-after 15 --quiet
|
||||
$(UV_RUN) run python -m glances -C $(CONF) --trace-malloc --stop-after 15 --quiet
|
||||
|
||||
memory-leak: ## Profile memory leaks
|
||||
$(PYTHON) -m glances -C $(CONF) --memory-leak
|
||||
$(UV_RUN) run python -m glances -C $(CONF) --memory-leak
|
||||
|
||||
memory-profiling: TIMES = 2400
|
||||
memory-profiling: PROFILE = mprofile_*.dat
|
||||
|
|
@ -171,29 +206,37 @@ memory-profiling: ## Profile memory usage
|
|||
@echo "It's a very long test (~4 hours)..."
|
||||
rm -f $(PROFILE)
|
||||
@echo "1/2 - Start memory profiling with the history option enable"
|
||||
$(venv_full)/mprof run -T 1 -C run-venv.py -C $(CONF) --stop-after $(TIMES) --quiet
|
||||
$(venv_full)/mprof plot --output $(OUT_DIR)/glances-memory-profiling-with-history.png
|
||||
$(UV_RUN) run mprof run -T 1 -C run-venv.py -C $(CONF) --stop-after $(TIMES) --quiet
|
||||
$(UV_RUN) run mprof plot --output $(OUT_DIR)/glances-memory-profiling-with-history.png
|
||||
rm -f $(PROFILE)
|
||||
@echo "2/2 - Start memory profiling with the history option disable"
|
||||
$(venv_full)/mprof run -T 1 -C run-venv.py -C $(CONF) --disable-history --stop-after $(TIMES) --quiet
|
||||
$(venv_full)/mprof plot --output $(OUT_DIR)/glances-memory-profiling-without-history.png
|
||||
$(UV_RUN) run mprof run -T 1 -C run-venv.py -C $(CONF) --disable-history --stop-after $(TIMES) --quiet
|
||||
$(UV_RUN) run mprof plot --output $(OUT_DIR)/glances-memory-profiling-without-history.png
|
||||
rm -f $(PROFILE)
|
||||
|
||||
# Trivy installation: https://aquasecurity.github.io/trivy/latest/getting-started/installation/
|
||||
trivy: ## Run Trivy to find vulnerabilities in container images
|
||||
trivy fs .
|
||||
trivy: ## Run Trivy to find vulnerabilities
|
||||
$(UV_RUN) run trivy fs ./glances/
|
||||
|
||||
bandit: ## Run Bandit to find vulnerabilities
|
||||
$(UV_RUN) run bandit glances -r
|
||||
|
||||
# ===================================================================
|
||||
# Docs
|
||||
# ===================================================================
|
||||
|
||||
docs: ## Create the documentation
|
||||
$(PYTHON) -m glances -C $(CONF) --api-doc > ./docs/api.rst
|
||||
$(UV_RUN) run python -m glances -C $(CONF) --api-doc > ./docs/api/python.rst
|
||||
$(UV_RUN) run python ./generate_openapi.py
|
||||
$(UV_RUN) run python -m glances -C $(CONF) --api-restful-doc > ./docs/api/restful.rst
|
||||
cd docs && ./build.sh && cd ..
|
||||
|
||||
docs-server: docs ## Start a Web server to serve the documentation
|
||||
(sleep 2 && sensible-browser "http://localhost:$(PORT)") &
|
||||
cd docs/_build/html/ && ../../../venv/bin/python -m http.server $(PORT)
|
||||
cd docs/_build/html/ && .venv-uv/bin/uvrun python -m http.server $(PORT)
|
||||
|
||||
docs-jupyter: ## Start Jupyter Notebook
|
||||
$(UV_RUN) run --with jupyter jupyter lab
|
||||
|
||||
release-note: ## Generate release note
|
||||
git --no-pager log $(LASTTAG)..HEAD --first-parent --pretty=format:"* %s"
|
||||
|
|
@ -210,23 +253,28 @@ install: ## Open a Web Browser to the installation procedure
|
|||
|
||||
webui webui%: DIR = glances/outputs/static/
|
||||
|
||||
webui: ## Build the Web UI
|
||||
$(PYTHON) -c 'import json; from glances.outputs.glances_curses import _GlancesCurses; print(json.dumps({ "leftMenu": [p for p in _GlancesCurses._left_sidebar if p != "now"]}, indent=4))' > ./glances/outputs/static/js/uiconfig.json
|
||||
webui-gen-config: ## Generate the Web UI config file
|
||||
$(UV_RUN) run python ./generate_webui_conf.py > ./glances/outputs/static/js/uiconfig.json
|
||||
|
||||
webui: webui-gen-config ## Build the Web UI
|
||||
cd $(DIR) && npm ci && npm run build
|
||||
|
||||
webui-audit: ## Audit the Web UI
|
||||
cd $(DIR) && npm audit
|
||||
|
||||
webui-audit-fix: ## Fix audit the Web UI
|
||||
webui-audit-fix: webui-gen-config ## Fix audit the Web UI
|
||||
cd $(DIR) && npm audit fix && npm ci && npm run build
|
||||
|
||||
webui-update: webui-gen-config ## Update JS dependencies
|
||||
cd $(DIR) && npm update --save && npm ci && npm run build
|
||||
|
||||
# ===================================================================
|
||||
# Packaging
|
||||
# ===================================================================
|
||||
|
||||
flatpak: venv-upgrade ## Generate FlatPack JSON file
|
||||
git clone https://github.com/flatpak/flatpak-builder-tools.git
|
||||
$(PYTHON) ./flatpak-builder-tools/pip/flatpak-pip-generator glances
|
||||
$(UV_RUN) run python ./flatpak-builder-tools/pip/flatpak-pip-generator glances
|
||||
rm -rf ./flatpak-builder-tools
|
||||
@echo "Now follow: https://github.com/flathub/flathub/wiki/App-Submission"
|
||||
|
||||
|
|
@ -260,33 +308,33 @@ docker-ubuntu-full: ## Generate local docker image (Ubuntu full)
|
|||
docker-ubuntu-minimal: ## Generate local docker image (Ubuntu minimal)
|
||||
docker-ubuntu-dev: ## Generate local docker image (Ubuntu dev)
|
||||
|
||||
trivy-docker: ## Run Trivy to find vulnerabilities in Docker images
|
||||
$(UV_RUN) run trivy image glances:local-alpine-full
|
||||
$(UV_RUN) run trivy image glances:local-alpine-minimal
|
||||
$(UV_RUN) run trivy image glances:local-ubuntu-full
|
||||
$(UV_RUN) run trivy image glances:local-ubuntu-minimal
|
||||
|
||||
# ===================================================================
|
||||
# Run
|
||||
# ===================================================================
|
||||
|
||||
run: ## Start Glances in console mode (also called standalone)
|
||||
$(PYTHON) -m glances -C $(CONF)
|
||||
$(UV_RUN) run python -m glances -C $(CONF)
|
||||
|
||||
run-debug: ## Start Glances in debug console mode (also called standalone)
|
||||
$(PYTHON) -m glances -C $(CONF) -d
|
||||
$(UV_RUN) run python -m glances -C $(CONF) -d
|
||||
|
||||
run-local-conf: ## Start Glances in console mode with the system conf file
|
||||
$(PYTHON) -m glances
|
||||
$(UV_RUN) run python -m glances
|
||||
|
||||
run-local-conf-hide-public: ## Start Glances in console mode with the system conf file and hide public information
|
||||
$(PYTHON) -m glances --hide-public-info
|
||||
|
||||
run-min: ## Start minimal Glances in console mode (also called standalone)
|
||||
$(venv_min)/python -m glances -C $(CONF)
|
||||
|
||||
run-min-debug: ## Start minimal Glances in debug console mode (also called standalone)
|
||||
$(venv_min)/python -m glances -C $(CONF) -d
|
||||
|
||||
run-min-local-conf: ## Start minimal Glances in console mode with the system conf file
|
||||
$(venv_min)/python -m glances
|
||||
$(UV_RUN) run python -m glances --hide-public-info
|
||||
|
||||
run-like-htop: ## Start Glances with the same features than Htop
|
||||
$(venv_min)/python -m glances --disable-plugin network,ports,wifi,connections,diskio,fs,irq,folders,raid,smart,sensors,vms,containers,ip,amps --disable-left-sidebar
|
||||
$(UV_RUN) run python -m glances --disable-plugin network,ports,wifi,connections,diskio,fs,irq,folders,raid,smart,sensors,vms,containers,ip,amps --disable-left-sidebar
|
||||
|
||||
run-fetch: ## Start Glances in fetch mode
|
||||
$(UV_RUN) run python -m glances --fetch
|
||||
|
||||
$(DOCKER_RUNTIMES): run-docker-%:
|
||||
$(DOCKER_RUN) $(DOCKER_OPTS) $(DOCKER_SOCKS) -it glances:local-$*
|
||||
|
|
@ -298,32 +346,35 @@ run-docker-ubuntu-minimal: ## Start Glances Ubuntu Docker minimal in console mod
|
|||
run-docker-ubuntu-full: ## Start Glances Ubuntu Docker full in console mode
|
||||
run-docker-ubuntu-dev: ## Start Glances Ubuntu Docker dev in console mode
|
||||
|
||||
generate-ssl: ## Generate local and sel signed SSL certificates for dev (need mkcert)
|
||||
mkcert glances.local localhost 120.0.0.1 0.0.0.0
|
||||
|
||||
run-webserver: ## Start Glances in Web server mode
|
||||
$(PYTHON) -m glances -C $(CONF) -w
|
||||
$(UV_RUN) run python -m glances -C $(CONF) -w
|
||||
|
||||
run-webserver-local-conf: ## Start Glances in Web server mode with the system conf file
|
||||
$(PYTHON) -m glances -w
|
||||
$(UV_RUN) run python -m glances -w
|
||||
|
||||
run-webserver-local-conf-hide-public: ## Start Glances in Web server mode with the system conf file and hide public info
|
||||
$(PYTHON) -m glances -w --hide-public-info
|
||||
$(UV_RUN) run python -m glances -w --hide-public-info
|
||||
|
||||
run-restapiserver: ## Start Glances in REST API server mode
|
||||
$(PYTHON) -m glances -C $(CONF) -w --disable-webui
|
||||
$(UV_RUN) run python -m glances -C $(CONF) -w --disable-webui
|
||||
|
||||
run-server: ## Start Glances in server mode (RPC)
|
||||
$(PYTHON) -m glances -C $(CONF) -s
|
||||
$(UV_RUN) run python -m glances -C $(CONF) -s
|
||||
|
||||
run-client: ## Start Glances in client mode (RPC)
|
||||
$(PYTHON) -m glances -C $(CONF) -c localhost
|
||||
$(UV_RUN) run python -m glances -C $(CONF) -c localhost
|
||||
|
||||
run-browser: ## Start Glances in browser mode (RPC)
|
||||
$(PYTHON) -m glances -C $(CONF) --browser
|
||||
$(UV_RUN) run python -m glances -C $(CONF) --browser
|
||||
|
||||
run-web-browser: ## Start Web Central Browser
|
||||
$(PYTHON) -m glances -C $(CONF) -w --browser
|
||||
$(UV_RUN) run python -m glances -C $(CONF) -w --browser
|
||||
|
||||
run-issue: ## Start Glances in issue mode
|
||||
$(PYTHON) -m glances -C $(CONF) --issue
|
||||
$(UV_RUN) run python -m glances -C $(CONF) --issue
|
||||
|
||||
run-multipass: ## Install and start Glances in a VM (only available on Ubuntu with multipass already installed)
|
||||
multipass launch -n glances-on-lts lts
|
||||
|
|
@ -333,4 +384,4 @@ run-multipass: ## Install and start Glances in a VM (only available on Ubuntu wi
|
|||
multipass delete glances-on-lts
|
||||
|
||||
show-version: ## Show Glances version number
|
||||
$(PYTHON) -m glances -C $(CONF) -V
|
||||
$(UV_RUN) run python -m glances -C $(CONF) -V
|
||||
|
|
|
|||
162
NEWS.rst
|
|
@ -1,6 +1,150 @@
|
|||
==============================================================================
|
||||
Glances ChangeLog
|
||||
============================================================================
|
||||
==============================================================================
|
||||
|
||||
=============
|
||||
Version 4.4.1
|
||||
=============
|
||||
|
||||
Bug corrected:
|
||||
|
||||
* Restful API issue after a while (stats are no more updated) #3333
|
||||
|
||||
=============
|
||||
Version 4.4.0
|
||||
=============
|
||||
|
||||
Breaking changes:
|
||||
|
||||
* A new Python API is now available to use Glances as a Python lib in your hown development #3237
|
||||
* In the process list, the long command line is now truncated by default. Use the arrow keys to show the full command line. SHIFT + arrow keys are used to switch between column sorts (TUI).
|
||||
* Prometheus export format is now more user friendly (see detail in #3283)
|
||||
|
||||
Enhancements:
|
||||
|
||||
* Make a Glances API in order to use Glances as a Python lib #3237
|
||||
* Add a new --fetch (neofetch like) option to display a snapshot of the current system status #3281
|
||||
* Show used port in container section #2054
|
||||
* Show long command line with arrow key #1553
|
||||
* Sensors plugin refresh by default every 10 seconds
|
||||
* Do not call update if a call is done to a specific plugin through the API #3033
|
||||
* [UI] Process virtual memory display can be disable by configuration #3299
|
||||
* Choose between used or available in the mem plugin #3288
|
||||
* [Experimental] Add export to DuckDB database #3205
|
||||
* Add Disk I/O Latency stats #1070
|
||||
* Filter fields to export #3258
|
||||
* Remove .keys() from loops over dicts #3253
|
||||
* Remove iterator helpers #3252
|
||||
|
||||
Bug corrected:
|
||||
|
||||
* [MACOS] Glances not showing Processes on MacOS #3100
|
||||
* Last dev build broke Homepage API calls ? only 1 widget still working #3322
|
||||
* Cloud plugin always generate communication with 169.254.169.254, even if the plugin is disabled #3316
|
||||
* API response delay (3+ minutes) when VMs are running #3317
|
||||
* [WINDOWS] Glances do not display CPU stat correctly #3155
|
||||
* Glances hangs if network device (NFS) is no available #3290
|
||||
* Fix prometheus export format #3283
|
||||
* Issue #3279 zfs cache and memory math issues #3289
|
||||
* [MACOS] Glances crashes when I try to filter #3266
|
||||
* Glances hang when killing process with muliple CTRL-C #3264
|
||||
* Issues after disabling system and processcount plugins #3248
|
||||
* Headers missing from predefined fields in TUI browser machine list #3250
|
||||
* Add another check for the famous Netifaces issue - Related to #3219
|
||||
* Key error 'type' in server_list_static.py (load_server_list) #3247
|
||||
|
||||
Continious integration and documentation:
|
||||
|
||||
* Glances now use uv for the dev environment #3025
|
||||
* Glances is compatible with Python 3.14 #3319
|
||||
* Glances provides requirements files with specific versions for each release
|
||||
* Requirements files are now generated dynamically with the make requirements or requirements-upgrade target
|
||||
* Add duplicate line check in pre-commit (strange behavor with some VScode extension)
|
||||
* Solve issue with multiprocessing exception with Snap package
|
||||
* Add a test script for identify CPU consumption of sensor plugin
|
||||
* Refactor port to take into account netifaces2
|
||||
* Correct issue with Chrome driver in WebUI unit test
|
||||
* Upgrade export test with InfluxDB 1.12
|
||||
* Fix typo of --export-process-filter help message #3314
|
||||
* In the outdated feature, catch error message if Pypi server not reachable
|
||||
* Add unit test for auto_unit
|
||||
* Label error in docs #3286
|
||||
* Put WebUI conf generator in a dedicated script
|
||||
* Refactor the Makefile to generate WebUI config file for all webui targets
|
||||
* Update sensors documentation #3275
|
||||
* Update docker compose env quote #3273
|
||||
* Update docker-compose.yml #3249
|
||||
* Update API doc generation
|
||||
* Update README with nice icons #3236
|
||||
* Add documentation for WebUI test
|
||||
|
||||
Thanks to all contributors and bug reporters !
|
||||
|
||||
Special thanks to:
|
||||
- Adi
|
||||
- Bennett Kanuka
|
||||
- Tim Potter
|
||||
- Ariel Otilibili
|
||||
- Boris Okassa
|
||||
- Lawrence
|
||||
- Shohei YOSHIDA
|
||||
- jmwallach
|
||||
- korn3r
|
||||
|
||||
=============
|
||||
Version 4.3.3
|
||||
=============
|
||||
|
||||
Bug corrected:
|
||||
|
||||
* Something in 4.3.2 broke the home assistant add-on for Glances #3238
|
||||
|
||||
Thanks to the FastAPI and Home Assistant community for the support.
|
||||
|
||||
=============
|
||||
Version 4.3.2
|
||||
=============
|
||||
|
||||
Enhancements:
|
||||
|
||||
* Add stats about running VMS (qemu/libvirt/kvm support through virsh) #1531
|
||||
* Add support for InfluxDB 3 Core #3182
|
||||
* (postgre)SQL export support / TimeScaleDB #2814
|
||||
* CSV column name now include the plugin name - Related to #2394
|
||||
* Make all results from amps plugins exportable #2394
|
||||
* Make --stdout (csv and json) compliant with client/server mode #3235
|
||||
* API history endpoints shows times without timezone #3218
|
||||
* FR: Sort Sensors my name in proper number order #3132
|
||||
* In the FS module, do not display threshold for volume mounted in 'ro' (read-only) #3143
|
||||
* Add a new field in the process list to identifie Zombie process #3178
|
||||
* Update plugin containers display and order #3186
|
||||
* Implement a basic memory cache with TTL for API call (set to ~1 second) #3202
|
||||
* Add container inactive_file & limit to InfluxDB2 export #3206
|
||||
|
||||
Bug corrected:
|
||||
|
||||
* [GPU] AMD Plugin: Operation not permitted #3125
|
||||
* Container memory stats not displayed #3142
|
||||
* [WEBUI] Irix mode (per core instead of per CPU percentage) not togglable #3158
|
||||
* Related to iteritems, itervalues, and iterkeys are not more needed in Python 3 #3181
|
||||
* Glances Central Browser should use name instead of IP adress for redirection #3103
|
||||
* Glances breaks if Podman container is started while it is running #3199
|
||||
|
||||
Continious integration and documentation:
|
||||
|
||||
* Add a new option --print-completion to generate shell tab completion - #3111
|
||||
* Improve Restful API documentation embeded in FastAPI #2632
|
||||
* Upgrade JS libs #3147
|
||||
* Improve unittest for CSV export #3150
|
||||
* Improve unittest for InfluxDB plugin #3149
|
||||
* Code refactoring - Rename plugin class to <Plugin name>Plugin instead of PluginModel #3169
|
||||
* Refactor code to limit the complexity of update_views method in plugins #3171
|
||||
|
||||
Thanks to all contributors and bug reporters !
|
||||
|
||||
Special thanks to:
|
||||
- Ariel Otilibili
|
||||
- kenrmayfield
|
||||
|
||||
=============
|
||||
Version 4.3.1
|
||||
|
|
@ -369,7 +513,7 @@ See release note in Wiki format: https://github.com/nicolargo/glances/wiki/Glanc
|
|||
**BREAKING CHANGES:**
|
||||
|
||||
* The minimal Python version is 3.8
|
||||
* The Glances API version 3 is replaced by the version 4. So Restfull API URL is now /api/4/ #2610
|
||||
* The Glances API version 3 is replaced by the version 4. So Restful API URL is now /api/4/ #2610
|
||||
* Alias definition change in the configuration file #1735
|
||||
|
||||
Glances version 3.x and lower:
|
||||
|
|
@ -394,9 +538,9 @@ Minimal requirements for Glances version 4 are:
|
|||
* packaging
|
||||
* ujson
|
||||
* pydantic
|
||||
* fastapi (for WebUI / RestFull API)
|
||||
* uvicorn (for WebUI / RestFull API)
|
||||
* jinja2 (for WebUI / RestFull API)
|
||||
* fastapi (for WebUI / RestFul API)
|
||||
* uvicorn (for WebUI / RestFul API)
|
||||
* jinja2 (for WebUI / RestFul API)
|
||||
|
||||
Majors changes between Glances version 3 and version 4:
|
||||
|
||||
|
|
@ -456,7 +600,7 @@ Bug corrected:
|
|||
CI and documentation:
|
||||
|
||||
* New logo for Glances version 4.0 #2713
|
||||
* Update api.rst documentation #2496
|
||||
* Update api-restful.rst documentation #2496
|
||||
* Change Renovate config #2729
|
||||
* Docker compose password unrecognized arguments when applying docs #2698
|
||||
* Docker includes OS Release Volume mount info #2473
|
||||
|
|
@ -834,7 +978,7 @@ Bugs corrected:
|
|||
* Threading.Event.isSet is deprecated in Python 3.10 #2017
|
||||
* Fix code scanning alert - Clear-text logging of sensitive information security #2006
|
||||
* The gpu temperature unit are displayed incorrectly in web ui bug #2002
|
||||
* Doc for 'alert' Restfull/JSON API response documentation #1994
|
||||
* Doc for 'alert' Restful/JSON API response documentation #1994
|
||||
* Show the spinning state of a disk documentation #1993
|
||||
* Web server status check endpoint enhancement #1988
|
||||
* --time parameter being ignored for client/server mode bug #1978
|
||||
|
|
@ -929,7 +1073,7 @@ Bugs corrected:
|
|||
* [3.2.0/3.2.1] keybinding not working anymore #1904
|
||||
* InfluxDB/InfluxDB2 Export object has no attribute hostname #1899
|
||||
|
||||
Documentation: The "make docs" generate RestFull/API documentation file.
|
||||
Documentation: The "make docs" generate RestFul/API documentation file.
|
||||
|
||||
===============
|
||||
Version 3.2.1
|
||||
|
|
@ -1956,7 +2100,7 @@ Version 2.1
|
|||
* Add Glances log message (in the /tmp/glances.log file)
|
||||
The default log level is INFO, you can switch to the DEBUG mode using the -d option on the command line.
|
||||
* Add RESTful API to the Web server mode
|
||||
RESTful API doc: https://github.com/nicolargo/glances/wiki/The-Glances-RESTFULL-JSON-API
|
||||
RESTful API doc: https://github.com/nicolargo/glances/wiki/The-Glances-RESTFUL-JSON-API
|
||||
* Improve SNMP fallback mode for Cisco IOS, VMware ESXi
|
||||
* Add --theme-white feature to optimize display for white background
|
||||
* Experimental history feature (--enable-history option on the command line)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,385 @@
|
|||
Glances 🌟
|
||||
==========
|
||||
|
||||
**Glances** is an open-source system cross-platform monitoring tool.
|
||||
It allows real-time monitoring of various aspects of your system such as
|
||||
CPU, memory, disk, network usage etc. It also allows monitoring of running processes,
|
||||
logged in users, temperatures, voltages, fan speeds etc.
|
||||
It also supports container monitoring, it supports different container management
|
||||
systems such as Docker, LXC. The information is presented in an easy to read dashboard
|
||||
and can also be used for remote monitoring of systems via a web interface or command
|
||||
line interface. It is easy to install and use and can be customized to show only
|
||||
the information that you are interested in.
|
||||
|
||||
In client/server mode, remote monitoring could be done via terminal,
|
||||
Web interface or API (XML-RPC and RESTful).
|
||||
Stats can also be exported to files or external time/value databases, CSV or direct
|
||||
output to STDOUT.
|
||||
|
||||
Glances is written in Python and uses libraries to grab information from
|
||||
your system. It is based on an open architecture where developers can
|
||||
add new plugins or exports modules.
|
||||
|
||||
Usage 👋
|
||||
========
|
||||
|
||||
For the standalone mode, just run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances
|
||||
|
||||
.. image:: https://github.com/nicolargo/glances/raw/refs/heads/master/docs/_static/glances-responsive-webdesign.png
|
||||
|
||||
For the Web server mode, run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances -w
|
||||
|
||||
and enter the URL ``http://<ip>:61208`` in your favorite web browser.
|
||||
|
||||
In this mode, a HTTP/Restful API is exposed, see document `RestfulApi`_ for more details.
|
||||
|
||||
.. image:: https://github.com/nicolargo/glances/raw/refs/heads/master/docs/_static/screenshot-web.png
|
||||
|
||||
For the client/server mode (remote monitoring through XML-RPC), run the following command on the server:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances -s
|
||||
|
||||
and this one on the client:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances -c <ip>
|
||||
|
||||
You can also detect and display all Glances servers available on your
|
||||
network (or defined in the configuration file) in TUI:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --browser
|
||||
|
||||
or WebUI:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances -w --browser
|
||||
|
||||
It possible to display raw stats on stdout:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --stdout cpu.user,mem.used,load
|
||||
cpu.user: 30.7
|
||||
mem.used: 3278204928
|
||||
load: {'cpucore': 4, 'min1': 0.21, 'min5': 0.4, 'min15': 0.27}
|
||||
cpu.user: 3.4
|
||||
mem.used: 3275251712
|
||||
load: {'cpucore': 4, 'min1': 0.19, 'min5': 0.39, 'min15': 0.27}
|
||||
...
|
||||
|
||||
or in a CSV format thanks to the stdout-csv option:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --stdout-csv now,cpu.user,mem.used,load
|
||||
now,cpu.user,mem.used,load.cpucore,load.min1,load.min5,load.min15
|
||||
2018-12-08 22:04:20 CEST,7.3,5948149760,4,1.04,0.99,1.04
|
||||
2018-12-08 22:04:23 CEST,5.4,5949136896,4,1.04,0.99,1.04
|
||||
...
|
||||
|
||||
or in a JSON format thanks to the stdout-json option (attribute not supported in this mode in order to have a real JSON object in output):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --stdout-json cpu,mem
|
||||
cpu: {"total": 29.0, "user": 24.7, "nice": 0.0, "system": 3.8, "idle": 71.4, "iowait": 0.0, "irq": 0.0, "softirq": 0.0, "steal": 0.0, "guest": 0.0, "guest_nice": 0.0, "time_since_update": 1, "cpucore": 4, "ctx_switches": 0, "interrupts": 0, "soft_interrupts": 0, "syscalls": 0}
|
||||
mem: {"total": 7837949952, "available": 2919079936, "percent": 62.8, "used": 4918870016, "free": 2919079936, "active": 2841214976, "inactive": 3340550144, "buffers": 546799616, "cached": 3068141568, "shared": 788156416}
|
||||
...
|
||||
|
||||
Last but not least, you can use the fetch mode to get a quick look of a machine:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --fetch
|
||||
|
||||
Results look like this:
|
||||
|
||||
.. image:: https://github.com/nicolargo/glances/raw/refs/heads/master/docs/_static/screenshot-fetch.png
|
||||
|
||||
Use Glances as a Python library 📚
|
||||
==================================
|
||||
|
||||
You can access the Glances API by importing the `glances.api` module and creating an
|
||||
instance of the `GlancesAPI` class. This instance provides access to all Glances plugins
|
||||
and their fields. For example, to access the CPU plugin and its total field, you can
|
||||
use the following code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> from glances import api
|
||||
>>> gl = api.GlancesAPI()
|
||||
>>> gl.cpu
|
||||
{'cpucore': 16,
|
||||
'ctx_switches': 1214157811,
|
||||
'guest': 0.0,
|
||||
'idle': 91.4,
|
||||
'interrupts': 991768733,
|
||||
'iowait': 0.3,
|
||||
'irq': 0.0,
|
||||
'nice': 0.0,
|
||||
'soft_interrupts': 423297898,
|
||||
'steal': 0.0,
|
||||
'syscalls': 0,
|
||||
'system': 5.4,
|
||||
'total': 7.3,
|
||||
'user': 3.0}
|
||||
>>> gl.cpu["total"]
|
||||
7.3
|
||||
>>> gl.mem["used"]
|
||||
12498582144
|
||||
>>> gl.auto_unit(gl.mem["used"])
|
||||
11.6G
|
||||
|
||||
If the stats return a list of items (like network interfaces or processes), you can
|
||||
access them by their name:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> gl.network.keys()
|
||||
['wlp0s20f3', 'veth33b370c', 'veth19c7711']
|
||||
>>> gl.network["wlp0s20f3"]
|
||||
{'alias': None,
|
||||
'bytes_all': 362,
|
||||
'bytes_all_gauge': 9242285709,
|
||||
'bytes_all_rate_per_sec': 1032.0,
|
||||
'bytes_recv': 210,
|
||||
'bytes_recv_gauge': 7420522678,
|
||||
'bytes_recv_rate_per_sec': 599.0,
|
||||
'bytes_sent': 152,
|
||||
'bytes_sent_gauge': 1821763031,
|
||||
'bytes_sent_rate_per_sec': 433.0,
|
||||
'interface_name': 'wlp0s20f3',
|
||||
'key': 'interface_name',
|
||||
'speed': 0,
|
||||
'time_since_update': 0.3504955768585205}
|
||||
|
||||
For a complete example of how to use Glances as a library, have a look to the `PythonApi`_.
|
||||
|
||||
Documentation 📜
|
||||
================
|
||||
|
||||
For complete documentation have a look at the readthedocs_ website.
|
||||
|
||||
If you have any question (after RTFM! and the `FAQ`_), please post it on the official Reddit `forum`_ or in GitHub `Discussions`_.
|
||||
|
||||
Gateway to other services 🌐
|
||||
============================
|
||||
|
||||
Glances can export stats to:
|
||||
|
||||
- ``CSV`` file
|
||||
- ``JSON`` file
|
||||
- ``InfluxDB`` server
|
||||
- ``Cassandra`` server
|
||||
- ``CouchDB`` server
|
||||
- ``OpenTSDB`` server
|
||||
- ``Prometheus`` server
|
||||
- ``StatsD`` server
|
||||
- ``ElasticSearch`` server
|
||||
- ``PostgreSQL/TimeScale`` server
|
||||
- ``RabbitMQ/ActiveMQ`` broker
|
||||
- ``ZeroMQ`` broker
|
||||
- ``Kafka`` broker
|
||||
- ``Riemann`` server
|
||||
- ``Graphite`` server
|
||||
- ``RESTful`` endpoint
|
||||
|
||||
Installation 🚀
|
||||
===============
|
||||
|
||||
There are several methods to test/install Glances on your system. Choose your weapon!
|
||||
|
||||
PyPI: Pip, the standard way
|
||||
---------------------------
|
||||
|
||||
Glances is on ``PyPI``. By using PyPI, you will be using the latest stable version.
|
||||
|
||||
To install Glances, simply use the ``pip`` command line.
|
||||
|
||||
Warning: on modern Linux operating systems, you may have an externally-managed-environment
|
||||
error message when you try to use ``pip``. In this case, go to the the PipX section below.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install --user glances
|
||||
|
||||
*Note*: Python headers are required to install `psutil`_, a Glances
|
||||
dependency. For example, on Debian/Ubuntu **the simplest** is
|
||||
``apt install python3-psutil`` or alternatively need to install first
|
||||
the *python-dev* package and gcc (*python-devel* on Fedora/CentOS/RHEL).
|
||||
For Windows, just install psutil from the binary installation file.
|
||||
|
||||
By default, Glances is installed **without** the Web interface dependencies.
|
||||
To install it, use the following command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install --user 'glances[web]'
|
||||
|
||||
For a full installation (with all features, see features list bellow):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install --user 'glances[all]'
|
||||
|
||||
Features list:
|
||||
|
||||
- all: install dependencies for all features
|
||||
- action: install dependencies for action feature
|
||||
- browser: install dependencies for Glances centram browser
|
||||
- cloud: install dependencies for cloud plugin
|
||||
- containers: install dependencies for container plugin
|
||||
- export: install dependencies for all exports modules
|
||||
- gpu: install dependencies for GPU plugin
|
||||
- graph: install dependencies for graph export
|
||||
- ip: install dependencies for IP public option
|
||||
- raid: install dependencies for RAID plugin
|
||||
- sensors: install dependencies for sensors plugin
|
||||
- smart: install dependencies for smart plugin
|
||||
- snmp: install dependencies for SNMP
|
||||
- sparklines: install dependencies for sparklines option
|
||||
- web: install dependencies for Webserver (WebUI) and Web API
|
||||
- wifi: install dependencies for Wifi plugin
|
||||
|
||||
To upgrade Glances to the latest version:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install --user --upgrade glances
|
||||
|
||||
The current develop branch is published to the test.pypi.org package index.
|
||||
If you want to test the develop version (could be instable), enter:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install --user -i https://test.pypi.org/simple/ Glances
|
||||
|
||||
PyPI: PipX, the alternative way
|
||||
-------------------------------
|
||||
|
||||
Install PipX on your system (apt install pipx on Ubuntu).
|
||||
|
||||
Install Glances (with all features):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pipx install 'glances[all]'
|
||||
|
||||
The glances script will be installed in the ~/.local/bin folder.
|
||||
|
||||
Shell tab completion 🔍
|
||||
=======================
|
||||
|
||||
Glances 4.3.2 and higher includes shell tab autocompletion thanks to the --print-completion option.
|
||||
|
||||
For example, on a Linux operating system with bash shell:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ mkdir -p ${XDG_DATA_HOME:="$HOME/.local/share"}/bash-completion
|
||||
$ glances --print-completion bash > ${XDG_DATA_HOME:="$HOME/.local/share"}/bash-completion/glances
|
||||
$ source ${XDG_DATA_HOME:="$HOME/.local/share"}/bash-completion/glances
|
||||
|
||||
Following shells are supported: bash, zsh and tcsh.
|
||||
|
||||
Requirements 🧩
|
||||
===============
|
||||
|
||||
Glances is developed in Python. A minimal Python version 3.10 or higher
|
||||
should be installed on your system.
|
||||
|
||||
*Note for Python 2 users*
|
||||
|
||||
Glances version 4 or higher do not support Python 2 (and Python 3 < 3.10).
|
||||
Please uses Glances version 3.4.x if you need Python 2 support.
|
||||
|
||||
Dependencies:
|
||||
|
||||
- ``psutil`` (better with latest version)
|
||||
- ``defusedxml`` (in order to monkey patch xmlrpc)
|
||||
- ``packaging`` (for the version comparison)
|
||||
- ``windows-curses`` (Windows Curses implementation) [Windows-only]
|
||||
- ``shtab`` (Shell autocompletion) [All but Windows]
|
||||
- ``jinja2`` (for fetch mode and templating)
|
||||
|
||||
Extra dependencies:
|
||||
|
||||
- ``batinfo`` (for battery monitoring)
|
||||
- ``bernhard`` (for the Riemann export module)
|
||||
- ``cassandra-driver`` (for the Cassandra export module)
|
||||
- ``chevron`` (for the action script feature)
|
||||
- ``docker`` (for the Containers Docker monitoring support)
|
||||
- ``elasticsearch`` (for the Elastic Search export module)
|
||||
- ``FastAPI`` and ``Uvicorn`` (for Web server mode)
|
||||
- ``graphitesender`` (For the Graphite export module)
|
||||
- ``hddtemp`` (for HDD temperature monitoring support) [Linux-only]
|
||||
- ``influxdb`` (for the InfluxDB version 1 export module)
|
||||
- ``influxdb-client`` (for the InfluxDB version 2 export module)
|
||||
- ``kafka-python`` (for the Kafka export module)
|
||||
- ``nvidia-ml-py`` (for the GPU plugin)
|
||||
- ``pycouchdb`` (for the CouchDB export module)
|
||||
- ``pika`` (for the RabbitMQ/ActiveMQ export module)
|
||||
- ``podman`` (for the Containers Podman monitoring support)
|
||||
- ``potsdb`` (for the OpenTSDB export module)
|
||||
- ``prometheus_client`` (for the Prometheus export module)
|
||||
- ``psycopg[binary]`` (for the PostgreSQL/TimeScale export module)
|
||||
- ``pygal`` (for the graph export module)
|
||||
- ``pymdstat`` (for RAID support) [Linux-only]
|
||||
- ``pymongo`` (for the MongoDB export module)
|
||||
- ``pysnmp-lextudio`` (for SNMP support)
|
||||
- ``pySMART.smartx`` (for HDD Smart support) [Linux-only]
|
||||
- ``pyzmq`` (for the ZeroMQ export module)
|
||||
- ``requests`` (for the Ports, Cloud plugins and RESTful export module)
|
||||
- ``sparklines`` (for the Quick Plugin sparklines option)
|
||||
- ``statsd`` (for the StatsD export module)
|
||||
- ``wifi`` (for the wifi plugin) [Linux-only]
|
||||
- ``zeroconf`` (for the autodiscover mode)
|
||||
|
||||
Project sponsorship 🙌
|
||||
======================
|
||||
|
||||
You can help me to achieve my goals of improving this open-source project
|
||||
or just say "thank you" by:
|
||||
|
||||
- sponsor me using one-time or monthly tier Github sponsors_ page
|
||||
- send me some pieces of bitcoin: 185KN9FCix3svJYp7JQM7hRMfSKyeaJR4X
|
||||
- buy me a gift on my wishlist_ page
|
||||
|
||||
Any and all contributions are greatly appreciated.
|
||||
|
||||
Authors and Contributors 🔥
|
||||
===========================
|
||||
|
||||
Nicolas Hennion (@nicolargo) <nicolas@nicolargo.com>
|
||||
|
||||
.. image:: https://img.shields.io/twitter/url/https/twitter.com/cloudposse.svg?style=social&label=Follow%20%40nicolargo
|
||||
:target: https://twitter.com/nicolargo
|
||||
|
||||
License 📜
|
||||
==========
|
||||
|
||||
Glances is distributed under the LGPL version 3 license. See ``COPYING`` for more details.
|
||||
|
||||
.. _psutil: https://github.com/giampaolo/psutil
|
||||
.. _readthedocs: https://glances.readthedocs.io/
|
||||
.. _forum: https://www.reddit.com/r/glances/
|
||||
.. _sponsors: https://github.com/sponsors/nicolargo
|
||||
.. _wishlist: https://www.amazon.fr/hz/wishlist/ls/BWAAQKWFR3FI?ref_=wl_share
|
||||
.. _PythonApi: https://glances.readthedocs.io/en/develop/api/python.html
|
||||
.. _RestfulApi: https://glances.readthedocs.io/en/develop/api/restful.html
|
||||
.. _FAQ: https://github.com/nicolargo/glances/blob/develop/docs/faq.rst
|
||||
.. _Discussions: https://github.com/nicolargo/glances/discussions
|
||||
529
README.rst
|
|
@ -1,10 +1,18 @@
|
|||
===============================
|
||||
Glances - An eye on your system
|
||||
===============================
|
||||
.. raw:: html
|
||||
|
||||
<div align="center">
|
||||
|
||||
.. image:: ./docs/_static/glances-responsive-webdesign.png
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<h1>Glances</h1>
|
||||
|
||||
An Eye on your System
|
||||
|
||||
| |pypi| |test| |contributors| |quality|
|
||||
| |starts| |docker| |pypistat| |ossrank|
|
||||
| |sponsors| |twitter|
|
||||
| |starts| |docker| |pypistat| |sponsors|
|
||||
| |reddit|
|
||||
|
||||
.. |pypi| image:: https://img.shields.io/pypi/v/glances.svg
|
||||
:target: https://pypi.python.org/pypi/Glances
|
||||
|
|
@ -21,10 +29,6 @@ Glances - An eye on your system
|
|||
:target: https://pepy.tech/project/glances
|
||||
:alt: Pypi downloads
|
||||
|
||||
.. |ossrank| image:: https://shields.io/endpoint?url=https://ossrank.com/shield/3689
|
||||
:target: https://ossrank.com/p/3689
|
||||
:alt: OSSRank
|
||||
|
||||
.. |test| image:: https://github.com/nicolargo/glances/actions/workflows/ci.yml/badge.svg?branch=develop
|
||||
:target: https://github.com/nicolargo/glances/actions
|
||||
:alt: Linux tests (GitHub Actions)
|
||||
|
|
@ -41,12 +45,20 @@ Glances - An eye on your system
|
|||
:target: https://github.com/sponsors/nicolargo
|
||||
:alt: Sponsors
|
||||
|
||||
.. |twitter| image:: https://img.shields.io/twitter/url/https/twitter.com/cloudposse.svg?style=social&label=Follow%20%40nicolargo
|
||||
.. |twitter| image:: https://img.shields.io/badge/X-000000?style=for-the-badge&logo=x&logoColor=white
|
||||
:target: https://twitter.com/nicolargo
|
||||
:alt: @nicolargo
|
||||
|
||||
Summary
|
||||
=======
|
||||
.. |reddit| image:: https://img.shields.io/badge/Reddit-FF4500?style=for-the-badge&logo=reddit&logoColor=white
|
||||
:target: https://www.reddit.com/r/glances/
|
||||
:alt: @reddit
|
||||
|
||||
.. raw:: html
|
||||
|
||||
</div>
|
||||
|
||||
Summary 🌟
|
||||
==========
|
||||
|
||||
**Glances** is an open-source system cross-platform monitoring tool.
|
||||
It allows real-time monitoring of various aspects of your system such as
|
||||
|
|
@ -58,102 +70,199 @@ and can also be used for remote monitoring of systems via a web interface or com
|
|||
line interface. It is easy to install and use and can be customized to show only
|
||||
the information that you are interested in.
|
||||
|
||||
.. image:: https://raw.githubusercontent.com/nicolargo/glances/develop/docs/_static/glances-summary.png
|
||||
|
||||
In client/server mode, remote monitoring could be done via terminal,
|
||||
Web interface or API (XML-RPC and RESTful).
|
||||
Stats can also be exported to files or external time/value databases, CSV or direct
|
||||
output to STDOUT.
|
||||
|
||||
.. image:: https://raw.githubusercontent.com/nicolargo/glances/develop/docs/_static/glances-responsive-webdesign.png
|
||||
|
||||
Glances is written in Python and uses libraries to grab information from
|
||||
your system. It is based on an open architecture where developers can
|
||||
add new plugins or exports modules.
|
||||
|
||||
Project sponsorship
|
||||
===================
|
||||
Usage 👋
|
||||
========
|
||||
|
||||
You can help me to achieve my goals of improving this open-source project
|
||||
or just say "thank you" by:
|
||||
For the standalone mode, just run:
|
||||
|
||||
- sponsor me using one-time or monthly tier Github sponsors_ page
|
||||
- send me some pieces of bitcoin: 185KN9FCix3svJYp7JQM7hRMfSKyeaJR4X
|
||||
- buy me a gift on my wishlist_ page
|
||||
.. code-block:: console
|
||||
|
||||
Any and all contributions are greatly appreciated.
|
||||
$ glances
|
||||
|
||||
Requirements
|
||||
============
|
||||
.. image:: ./docs/_static/glances-summary.png
|
||||
|
||||
Glances is developed in Python. A minimal Python version 3.9 or higher
|
||||
should be installed on your system.
|
||||
For the Web server mode, run:
|
||||
|
||||
*Note for Python 2 users*
|
||||
.. code-block:: console
|
||||
|
||||
Glances version 4 or higher do not support Python 2 (and Python 3 < 3.9).
|
||||
Please uses Glances version 3.4.x if you need Python 2 support.
|
||||
$ glances -w
|
||||
|
||||
Dependencies:
|
||||
and enter the URL ``http://<ip>:61208`` in your favorite web browser.
|
||||
|
||||
- ``psutil`` (better with latest version)
|
||||
- ``defusedxml`` (in order to monkey patch xmlrpc)
|
||||
- ``packaging`` (for the version comparison)
|
||||
- ``windows-curses`` (Windows Curses implementation) [Windows-only]
|
||||
In this mode, a HTTP/Restful API is exposed, see document `RestfulApi`_ for more details.
|
||||
|
||||
Optional dependencies:
|
||||
.. image:: ./docs/_static/screenshot-web.png
|
||||
|
||||
- ``batinfo`` (for battery monitoring)
|
||||
- ``bernhard`` (for the Riemann export module)
|
||||
- ``cassandra-driver`` (for the Cassandra export module)
|
||||
- ``chevron`` (for the action script feature)
|
||||
- ``docker`` (for the Containers Docker monitoring support)
|
||||
- ``elasticsearch`` (for the Elastic Search export module)
|
||||
- ``FastAPI`` and ``Uvicorn`` (for Web server mode)
|
||||
- ``graphitesender`` (For the Graphite export module)
|
||||
- ``hddtemp`` (for HDD temperature monitoring support) [Linux-only]
|
||||
- ``influxdb`` (for the InfluxDB version 1 export module)
|
||||
- ``influxdb-client`` (for the InfluxDB version 2 export module)
|
||||
- ``jinja2`` (for templating, used under the hood by FastAPI)
|
||||
- ``kafka-python`` (for the Kafka export module)
|
||||
- ``netifaces2`` (for the IP plugin)
|
||||
- ``nvidia-ml-py`` (for the GPU plugin)
|
||||
- ``pycouchdb`` (for the CouchDB export module)
|
||||
- ``pika`` (for the RabbitMQ/ActiveMQ export module)
|
||||
- ``podman`` (for the Containers Podman monitoring support)
|
||||
- ``potsdb`` (for the OpenTSDB export module)
|
||||
- ``prometheus_client`` (for the Prometheus export module)
|
||||
- ``pygal`` (for the graph export module)
|
||||
- ``pymdstat`` (for RAID support) [Linux-only]
|
||||
- ``pymongo`` (for the MongoDB export module)
|
||||
- ``pysnmp-lextudio`` (for SNMP support)
|
||||
- ``pySMART.smartx`` (for HDD Smart support) [Linux-only]
|
||||
- ``pyzmq`` (for the ZeroMQ export module)
|
||||
- ``requests`` (for the Ports, Cloud plugins and RESTful export module)
|
||||
- ``sparklines`` (for the Quick Plugin sparklines option)
|
||||
- ``statsd`` (for the StatsD export module)
|
||||
- ``wifi`` (for the wifi plugin) [Linux-only]
|
||||
- ``zeroconf`` (for the autodiscover mode)
|
||||
For the client/server mode (remote monitoring through XML-RPC), run the following command on the server:
|
||||
|
||||
Installation
|
||||
============
|
||||
.. code-block:: console
|
||||
|
||||
$ glances -s
|
||||
|
||||
and this one on the client:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances -c <ip>
|
||||
|
||||
You can also detect and display all Glances servers available on your
|
||||
network (or defined in the configuration file) in TUI:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --browser
|
||||
|
||||
or WebUI:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances -w --browser
|
||||
|
||||
It possible to display raw stats on stdout:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --stdout cpu.user,mem.used,load
|
||||
cpu.user: 30.7
|
||||
mem.used: 3278204928
|
||||
load: {'cpucore': 4, 'min1': 0.21, 'min5': 0.4, 'min15': 0.27}
|
||||
cpu.user: 3.4
|
||||
mem.used: 3275251712
|
||||
load: {'cpucore': 4, 'min1': 0.19, 'min5': 0.39, 'min15': 0.27}
|
||||
...
|
||||
|
||||
or in a CSV format thanks to the stdout-csv option:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --stdout-csv now,cpu.user,mem.used,load
|
||||
now,cpu.user,mem.used,load.cpucore,load.min1,load.min5,load.min15
|
||||
2018-12-08 22:04:20 CEST,7.3,5948149760,4,1.04,0.99,1.04
|
||||
2018-12-08 22:04:23 CEST,5.4,5949136896,4,1.04,0.99,1.04
|
||||
...
|
||||
|
||||
or in a JSON format thanks to the stdout-json option (attribute not supported in this mode in order to have a real JSON object in output):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --stdout-json cpu,mem
|
||||
cpu: {"total": 29.0, "user": 24.7, "nice": 0.0, "system": 3.8, "idle": 71.4, "iowait": 0.0, "irq": 0.0, "softirq": 0.0, "steal": 0.0, "guest": 0.0, "guest_nice": 0.0, "time_since_update": 1, "cpucore": 4, "ctx_switches": 0, "interrupts": 0, "soft_interrupts": 0, "syscalls": 0}
|
||||
mem: {"total": 7837949952, "available": 2919079936, "percent": 62.8, "used": 4918870016, "free": 2919079936, "active": 2841214976, "inactive": 3340550144, "buffers": 546799616, "cached": 3068141568, "shared": 788156416}
|
||||
...
|
||||
|
||||
Last but not least, you can use the fetch mode to get a quick look of a machine:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --fetch
|
||||
|
||||
Results look like this:
|
||||
|
||||
.. image:: ./docs/_static/screenshot-fetch.png
|
||||
|
||||
Use Glances as a Python library 📚
|
||||
==================================
|
||||
|
||||
You can access the Glances API by importing the `glances.api` module and creating an
|
||||
instance of the `GlancesAPI` class. This instance provides access to all Glances plugins
|
||||
and their fields. For example, to access the CPU plugin and its total field, you can
|
||||
use the following code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> from glances import api
|
||||
>>> gl = api.GlancesAPI()
|
||||
>>> gl.cpu
|
||||
{'cpucore': 16,
|
||||
'ctx_switches': 1214157811,
|
||||
'guest': 0.0,
|
||||
'idle': 91.4,
|
||||
'interrupts': 991768733,
|
||||
'iowait': 0.3,
|
||||
'irq': 0.0,
|
||||
'nice': 0.0,
|
||||
'soft_interrupts': 423297898,
|
||||
'steal': 0.0,
|
||||
'syscalls': 0,
|
||||
'system': 5.4,
|
||||
'total': 7.3,
|
||||
'user': 3.0}
|
||||
>>> gl.cpu.get("total")
|
||||
7.3
|
||||
>>> gl.mem.get("used")
|
||||
12498582144
|
||||
>>> gl.auto_unit(gl.mem.get("used"))
|
||||
11.6G
|
||||
|
||||
If the stats return a list of items (like network interfaces or processes), you can
|
||||
access them by their name:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> gl.network.keys()
|
||||
['wlp0s20f3', 'veth33b370c', 'veth19c7711']
|
||||
>>> gl.network.get("wlp0s20f3")
|
||||
{'alias': None,
|
||||
'bytes_all': 362,
|
||||
'bytes_all_gauge': 9242285709,
|
||||
'bytes_all_rate_per_sec': 1032.0,
|
||||
'bytes_recv': 210,
|
||||
'bytes_recv_gauge': 7420522678,
|
||||
'bytes_recv_rate_per_sec': 599.0,
|
||||
'bytes_sent': 152,
|
||||
'bytes_sent_gauge': 1821763031,
|
||||
'bytes_sent_rate_per_sec': 433.0,
|
||||
'interface_name': 'wlp0s20f3',
|
||||
'key': 'interface_name',
|
||||
'speed': 0,
|
||||
'time_since_update': 0.3504955768585205}
|
||||
|
||||
For a complete example of how to use Glances as a library, have a look to the `PythonApi`_.
|
||||
|
||||
Documentation 📜
|
||||
================
|
||||
|
||||
For complete documentation have a look at the readthedocs_ website.
|
||||
|
||||
If you have any question (after RTFM! and the `FAQ`_), please post it on the official Reddit `forum`_ or in GitHub `Discussions`_.
|
||||
|
||||
Gateway to other services 🌐
|
||||
============================
|
||||
|
||||
Glances can export stats to:
|
||||
|
||||
- files: ``CSV`` and ``JSON``
|
||||
- databases: ``InfluxDB``, ``ElasticSearch``, ``PostgreSQL/TimeScale``, ``Cassandra``, ``CouchDB``, ``OpenTSDB``, ``Prometheus``, ``StatsD``, ``Riemann`` and ``Graphite``
|
||||
- brokers: ``RabbitMQ/ActiveMQ``, ``NATS``, ``ZeroMQ`` and ``Kafka``
|
||||
- others: ``RESTful`` endpoint
|
||||
|
||||
Installation 🚀
|
||||
===============
|
||||
|
||||
There are several methods to test/install Glances on your system. Choose your weapon!
|
||||
|
||||
PyPI: Pip, the standard way
|
||||
---------------------------
|
||||
|
||||
Glances is on ``PyPI``. By using PyPI, you will be using the latest
|
||||
stable version.
|
||||
Glances is on ``PyPI``. By using PyPI, you will be using the latest stable version.
|
||||
|
||||
To install Glances, simply use the ``pip`` command line.
|
||||
|
||||
Warning: on modern Linux operating systems, you may have an externally-managed-environment
|
||||
error message when you try to use ``pip``. In this case, go to the the PipX section below.
|
||||
To install Glances, simply use the ``pip`` command line in an virtual environment.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install --user glances
|
||||
cd ~
|
||||
python3 -m venv ~/.venv
|
||||
source ~/.venv/bin/activate
|
||||
pip install glances
|
||||
|
||||
*Note*: Python headers are required to install `psutil`_, a Glances
|
||||
dependency. For example, on Debian/Ubuntu **the simplest** is
|
||||
|
|
@ -162,17 +271,18 @@ the *python-dev* package and gcc (*python-devel* on Fedora/CentOS/RHEL).
|
|||
For Windows, just install psutil from the binary installation file.
|
||||
|
||||
By default, Glances is installed **without** the Web interface dependencies.
|
||||
|
||||
To install it, use the following command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install --user 'glances[web]'
|
||||
pip install 'glances[web]'
|
||||
|
||||
For a full installation (with all features, see features list bellow):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install --user 'glances[all]'
|
||||
pip install 'glances[all]'
|
||||
|
||||
Features list:
|
||||
|
||||
|
|
@ -197,21 +307,18 @@ To upgrade Glances to the latest version:
|
|||
|
||||
.. code-block:: console
|
||||
|
||||
pip install --user --upgrade glances
|
||||
|
||||
The current develop branch is published to the test.pypi.org package index.
|
||||
If you want to test the develop version (could be instable), enter:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pip install --user -i https://test.pypi.org/simple/ Glances
|
||||
pip install --upgrade glances
|
||||
|
||||
PyPI: PipX, the alternative way
|
||||
-------------------------------
|
||||
|
||||
Install PipX on your system (apt install pipx on Ubuntu).
|
||||
Install PipX on your system. For example on Ubuntu/Debian:
|
||||
|
||||
Install Glances (with all features):
|
||||
.. code-block:: console
|
||||
|
||||
sudo apt install pipx
|
||||
|
||||
Then install Glances (with all features):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
|
|
@ -219,18 +326,18 @@ Install Glances (with all features):
|
|||
|
||||
The glances script will be installed in the ~/.local/bin folder.
|
||||
|
||||
To upgrade Glances to the latest version:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pipx upgrade glances
|
||||
|
||||
Docker: the cloudy way
|
||||
----------------------
|
||||
|
||||
Glances Docker images are available. You can use it to monitor your
|
||||
server and all your containers !
|
||||
|
||||
Get the Glances container:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
docker pull nicolargo/glances:latest-full
|
||||
|
||||
The following tags are available:
|
||||
|
||||
- *latest-full* for a full Alpine Glances image (latest release) with all dependencies
|
||||
|
|
@ -275,13 +382,32 @@ Run the container in *Web server mode*:
|
|||
|
||||
For a full list of options, see the Glances `Docker`_ documentation page.
|
||||
|
||||
It is also possible to use a simple Docker compose file (see in ./docker-compose/docker-compose.yml):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
cd ./docker-compose
|
||||
docker-compose up
|
||||
|
||||
It will start a Glances server with WebUI.
|
||||
|
||||
Brew: The missing package manager
|
||||
---------------------------------
|
||||
|
||||
For Linux and Mac OS, it is also possible to install Glances with `Brew`_:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
brew install glances
|
||||
|
||||
GNU/Linux package
|
||||
-----------------
|
||||
|
||||
`Glances` is available on many Linux distributions, so you should be
|
||||
able to install it using your favorite package manager. Be aware that
|
||||
when you use this method the operating system `package`_ for `Glances`
|
||||
may not be the latest version and only basics plugins are enabled.
|
||||
able to install it using your favorite package manager. Nevetheless,
|
||||
i do not recommend it. Be aware that when you use this method the operating
|
||||
system `package`_ for `Glances`may not be the latest version and only basics
|
||||
plugins are enabled.
|
||||
|
||||
Note: The Debian package (and all other Debian-based distributions) do
|
||||
not include anymore the JS statics files used by the Web interface
|
||||
|
|
@ -292,13 +418,30 @@ higher, the path to the statics file is configurable (see ``issue2612``).
|
|||
FreeBSD
|
||||
-------
|
||||
|
||||
To install the binary package:
|
||||
On FreeBSD, package name depends on the Python version.
|
||||
|
||||
Check for Python version:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# pkg install py39-glances
|
||||
# python --version
|
||||
|
||||
To install Glances from ports:
|
||||
Install the Glances package:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# pkg install pyXY-glances
|
||||
|
||||
Where X and Y are the Major and Minor Values of your Python System.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# Example for Python 3.11.3: pkg install py311-glances
|
||||
|
||||
**NOTE:** Check Glances Binary Package Version for your System Architecture.
|
||||
You must have the Correct Python Version Installed which corresponds to the Glances Binary Package.
|
||||
|
||||
To install Glances from Ports:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
|
|
@ -308,9 +451,7 @@ To install Glances from ports:
|
|||
macOS
|
||||
-----
|
||||
|
||||
If you do not want to use the glancesautoinstall script, follow this procedure.
|
||||
|
||||
macOS users can install Glances using ``Homebrew`` or ``MacPorts``.
|
||||
MacOS users can install Glances using ``Homebrew`` or ``MacPorts``.
|
||||
|
||||
Homebrew
|
||||
````````
|
||||
|
|
@ -330,11 +471,7 @@ Windows
|
|||
-------
|
||||
|
||||
Install `Python`_ for Windows (Python 3.4+ ship with pip) and
|
||||
then run the following command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ pip install glances
|
||||
follow the Glances Pip install procedure.
|
||||
|
||||
Android
|
||||
-------
|
||||
|
|
@ -388,94 +525,77 @@ Ansible
|
|||
|
||||
A Glances ``Ansible`` role is available: https://galaxy.ansible.com/zaxos/glances-ansible-role/
|
||||
|
||||
Usage
|
||||
=====
|
||||
Shell tab completion 🔍
|
||||
=======================
|
||||
|
||||
For the standalone mode, just run:
|
||||
Glances 4.3.2 and higher includes shell tab autocompletion thanks to the --print-completion option.
|
||||
|
||||
For example, on a Linux operating system with bash shell:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances
|
||||
$ mkdir -p ${XDG_DATA_HOME:="$HOME/.local/share"}/bash-completion
|
||||
$ glances --print-completion bash > ${XDG_DATA_HOME:="$HOME/.local/share"}/bash-completion/glances
|
||||
$ source ${XDG_DATA_HOME:="$HOME/.local/share"}/bash-completion/glances
|
||||
|
||||
For the Web server mode, run:
|
||||
Following shells are supported: bash, zsh and tcsh.
|
||||
|
||||
.. code-block:: console
|
||||
Requirements 🧩
|
||||
===============
|
||||
|
||||
$ glances -w
|
||||
Glances is developed in Python. A minimal Python version 3.10 or higher
|
||||
should be installed on your system.
|
||||
|
||||
and enter the URL ``http://<ip>:61208`` in your favorite web browser.
|
||||
*Note for Python 2 users*
|
||||
|
||||
For the client/server mode, run:
|
||||
Glances version 4 or higher do not support Python 2 (and Python 3 < 3.10).
|
||||
Please uses Glances version 3.4.x if you need Python 2 support.
|
||||
|
||||
.. code-block:: console
|
||||
Dependencies:
|
||||
|
||||
$ glances -s
|
||||
- ``psutil`` (better with latest version)
|
||||
- ``defusedxml`` (in order to monkey patch xmlrpc)
|
||||
- ``packaging`` (for the version comparison)
|
||||
- ``windows-curses`` (Windows Curses implementation) [Windows-only]
|
||||
- ``shtab`` (Shell autocompletion) [All but Windows]
|
||||
- ``jinja2`` (for fetch mode and templating)
|
||||
|
||||
on the server side and run:
|
||||
Extra dependencies:
|
||||
|
||||
.. code-block:: console
|
||||
- ``batinfo`` (for battery monitoring)
|
||||
- ``bernhard`` (for the Riemann export module)
|
||||
- ``cassandra-driver`` (for the Cassandra export module)
|
||||
- ``chevron`` (for the action script feature)
|
||||
- ``docker`` (for the Containers Docker monitoring support)
|
||||
- ``elasticsearch`` (for the Elastic Search export module)
|
||||
- ``FastAPI`` and ``Uvicorn`` (for Web server mode)
|
||||
- ``graphitesender`` (For the Graphite export module)
|
||||
- ``hddtemp`` (for HDD temperature monitoring support) [Linux-only]
|
||||
- ``influxdb`` (for the InfluxDB version 1 export module)
|
||||
- ``influxdb-client`` (for the InfluxDB version 2 export module)
|
||||
- ``kafka-python`` (for the Kafka export module)
|
||||
- ``nats-py`` (for the NATS export module)
|
||||
- ``nvidia-ml-py`` (for the GPU plugin)
|
||||
- ``pycouchdb`` (for the CouchDB export module)
|
||||
- ``pika`` (for the RabbitMQ/ActiveMQ export module)
|
||||
- ``podman`` (for the Containers Podman monitoring support)
|
||||
- ``potsdb`` (for the OpenTSDB export module)
|
||||
- ``prometheus_client`` (for the Prometheus export module)
|
||||
- ``psycopg[binary]`` (for the PostgreSQL/TimeScale export module)
|
||||
- ``pygal`` (for the graph export module)
|
||||
- ``pymdstat`` (for RAID support) [Linux-only]
|
||||
- ``pymongo`` (for the MongoDB export module)
|
||||
- ``pysnmp-lextudio`` (for SNMP support)
|
||||
- ``pySMART.smartx`` (for HDD Smart support) [Linux-only]
|
||||
- ``pyzmq`` (for the ZeroMQ export module)
|
||||
- ``requests`` (for the Ports, Cloud plugins and RESTful export module)
|
||||
- ``sparklines`` (for the Quick Plugin sparklines option)
|
||||
- ``statsd`` (for the StatsD export module)
|
||||
- ``wifi`` (for the wifi plugin) [Linux-only]
|
||||
- ``zeroconf`` (for the autodiscover mode)
|
||||
|
||||
$ glances -c <ip>
|
||||
|
||||
on the client one.
|
||||
|
||||
You can also detect and display all Glances servers available on your
|
||||
network or defined in the configuration file:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --browser
|
||||
|
||||
You can also display raw stats on stdout:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --stdout cpu.user,mem.used,load
|
||||
cpu.user: 30.7
|
||||
mem.used: 3278204928
|
||||
load: {'cpucore': 4, 'min1': 0.21, 'min5': 0.4, 'min15': 0.27}
|
||||
cpu.user: 3.4
|
||||
mem.used: 3275251712
|
||||
load: {'cpucore': 4, 'min1': 0.19, 'min5': 0.39, 'min15': 0.27}
|
||||
...
|
||||
|
||||
or in a CSV format thanks to the stdout-csv option:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --stdout-csv now,cpu.user,mem.used,load
|
||||
now,cpu.user,mem.used,load.cpucore,load.min1,load.min5,load.min15
|
||||
2018-12-08 22:04:20 CEST,7.3,5948149760,4,1.04,0.99,1.04
|
||||
2018-12-08 22:04:23 CEST,5.4,5949136896,4,1.04,0.99,1.04
|
||||
...
|
||||
|
||||
or in a JSON format thanks to the stdout-json option (attribute not supported in this mode in order to have a real JSON object in output):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --stdout-json cpu,mem
|
||||
cpu: {"total": 29.0, "user": 24.7, "nice": 0.0, "system": 3.8, "idle": 71.4, "iowait": 0.0, "irq": 0.0, "softirq": 0.0, "steal": 0.0, "guest": 0.0, "guest_nice": 0.0, "time_since_update": 1, "cpucore": 4, "ctx_switches": 0, "interrupts": 0, "soft_interrupts": 0, "syscalls": 0}
|
||||
mem: {"total": 7837949952, "available": 2919079936, "percent": 62.8, "used": 4918870016, "free": 2919079936, "active": 2841214976, "inactive": 3340550144, "buffers": 546799616, "cached": 3068141568, "shared": 788156416}
|
||||
...
|
||||
|
||||
and RTFM, always.
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
For complete documentation have a look at the readthedocs_ website.
|
||||
|
||||
If you have any question (after RTFM!), please post it on the official Q&A `forum`_.
|
||||
|
||||
Gateway to other services
|
||||
=========================
|
||||
|
||||
Glances can export stats to: ``CSV`` file, ``JSON`` file, ``InfluxDB``, ``Cassandra``, ``CouchDB``,
|
||||
``OpenTSDB``, ``Prometheus``, ``StatsD``, ``ElasticSearch``, ``RabbitMQ/ActiveMQ``,
|
||||
``ZeroMQ``, ``Kafka``, ``Riemann``, ``Graphite`` and ``RESTful`` server.
|
||||
|
||||
How to contribute ?
|
||||
===================
|
||||
How to contribute ? 🤝
|
||||
======================
|
||||
|
||||
If you want to contribute to the Glances project, read this `wiki`_ page.
|
||||
|
||||
|
|
@ -484,30 +604,53 @@ There is also a chat dedicated to the Glances developers:
|
|||
.. image:: https://badges.gitter.im/Join%20Chat.svg
|
||||
:target: https://gitter.im/nicolargo/glances?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
|
||||
Author
|
||||
======
|
||||
Project sponsorship 🙌
|
||||
======================
|
||||
|
||||
You can help me to achieve my goals of improving this open-source project
|
||||
or just say "thank you" by:
|
||||
|
||||
- sponsor me using one-time or monthly tier Github sponsors_ page
|
||||
- send me some pieces of bitcoin: 185KN9FCix3svJYp7JQM7hRMfSKyeaJR4X
|
||||
- buy me a gift on my wishlist_ page
|
||||
|
||||
Any and all contributions are greatly appreciated.
|
||||
|
||||
Authors and Contributors 🔥
|
||||
===========================
|
||||
|
||||
Nicolas Hennion (@nicolargo) <nicolas@nicolargo.com>
|
||||
|
||||
.. image:: https://img.shields.io/twitter/url/https/twitter.com/cloudposse.svg?style=social&label=Follow%20%40nicolargo
|
||||
:target: https://twitter.com/nicolargo
|
||||
|
||||
License
|
||||
=======
|
||||
License 📜
|
||||
==========
|
||||
|
||||
Glances is distributed under the LGPL version 3 license. See ``COPYING`` for more details.
|
||||
|
||||
More stars ! 🌟
|
||||
===============
|
||||
|
||||
Please give us a star on `GitHub`_ if you like this project.
|
||||
|
||||
.. image:: https://api.star-history.com/svg?repos=nicolargo/glances&type=Date
|
||||
:target: https://www.star-history.com/#nicolargo/glances&Date
|
||||
:alt: Star history
|
||||
|
||||
.. _psutil: https://github.com/giampaolo/psutil
|
||||
.. _glancesautoinstall: https://github.com/nicolargo/glancesautoinstall
|
||||
.. _Brew: https://formulae.brew.sh/formula/glances
|
||||
.. _Python: https://www.python.org/getit/
|
||||
.. _Termux: https://play.google.com/store/apps/details?id=com.termux
|
||||
.. _readthedocs: https://glances.readthedocs.io/
|
||||
.. _forum: https://groups.google.com/forum/?hl=en#!forum/glances-users
|
||||
.. _forum: https://www.reddit.com/r/glances/
|
||||
.. _wiki: https://github.com/nicolargo/glances/wiki/How-to-contribute-to-Glances-%3F
|
||||
.. _package: https://repology.org/project/glances/versions
|
||||
.. _sponsors: https://github.com/sponsors/nicolargo
|
||||
.. _wishlist: https://www.amazon.fr/hz/wishlist/ls/BWAAQKWFR3FI?ref_=wl_share
|
||||
.. _issue2021: https://github.com/nicolargo/glances/issues/2021
|
||||
.. _issue2021comment: https://github.com/nicolargo/glances/issues/2021#issuecomment-1197831157
|
||||
.. _issue2612: https://github.com/nicolargo/glances/issues/2612
|
||||
.. _Docker: https://github.com/nicolargo/glances/blob/develop/docs/docker.rst
|
||||
.. _Docker: https://github.com/nicolargo/glances/blob/master/docs/docker.rst
|
||||
.. _GitHub: https://github.com/nicolargo/glances
|
||||
.. _PythonApi: https://glances.readthedocs.io/en/develop/api/python.html
|
||||
.. _RestfulApi: https://glances.readthedocs.io/en/develop/api/restful.html
|
||||
.. _FAQ: https://github.com/nicolargo/glances/blob/develop/docs/faq.rst
|
||||
.. _Discussions: https://github.com/nicolargo/glances/discussions
|
||||
|
|
|
|||
|
|
@ -0,0 +1,237 @@
|
|||
# This file was autogenerated by uv via the following command:
|
||||
# uv export --no-emit-workspace --no-hashes --all-extras --no-group dev --output-file all-requirements.txt
|
||||
annotated-doc==0.0.4
|
||||
# via fastapi
|
||||
annotated-types==0.7.0
|
||||
# via pydantic
|
||||
anyio==4.12.0
|
||||
# via
|
||||
# elasticsearch
|
||||
# starlette
|
||||
batinfo==0.4.2 ; sys_platform == 'linux'
|
||||
# via glances
|
||||
bernhard==0.2.6
|
||||
# via glances
|
||||
cassandra-driver==3.29.3
|
||||
# via glances
|
||||
certifi==2025.11.12
|
||||
# via
|
||||
# elastic-transport
|
||||
# influxdb-client
|
||||
# influxdb3-python
|
||||
# requests
|
||||
cffi==2.0.0 ; implementation_name == 'pypy' or platform_python_implementation != 'PyPy'
|
||||
# via
|
||||
# cryptography
|
||||
# pyzmq
|
||||
chardet==5.2.0
|
||||
# via pysmart
|
||||
charset-normalizer==3.4.4
|
||||
# via requests
|
||||
chevron==0.14.0
|
||||
# via glances
|
||||
click==8.1.8
|
||||
# via
|
||||
# geomet
|
||||
# uvicorn
|
||||
colorama==0.4.6 ; sys_platform == 'win32'
|
||||
# via click
|
||||
cryptography==46.0.3
|
||||
# via pysnmpcrypto
|
||||
defusedxml==0.7.1
|
||||
# via glances
|
||||
dnspython==2.8.0
|
||||
# via pymongo
|
||||
docker==7.1.0
|
||||
# via glances
|
||||
elastic-transport==9.2.1
|
||||
# via elasticsearch
|
||||
elasticsearch==9.2.1
|
||||
# via glances
|
||||
exceptiongroup==1.2.2 ; python_full_version < '3.11'
|
||||
# via anyio
|
||||
fastapi==0.128.0
|
||||
# via glances
|
||||
geomet==1.1.0
|
||||
# via cassandra-driver
|
||||
graphitesender==0.11.2
|
||||
# via glances
|
||||
h11==0.16.0
|
||||
# via uvicorn
|
||||
humanfriendly==10.0
|
||||
# via pysmart
|
||||
ibm-cloud-sdk-core==3.24.2
|
||||
# via ibmcloudant
|
||||
ibmcloudant==0.11.2
|
||||
# via glances
|
||||
idna==3.11
|
||||
# via
|
||||
# anyio
|
||||
# requests
|
||||
ifaddr==0.2.0
|
||||
# via zeroconf
|
||||
importlib-metadata==8.7.1
|
||||
# via pygal
|
||||
influxdb==5.3.2
|
||||
# via glances
|
||||
influxdb-client==1.49.0
|
||||
# via glances
|
||||
influxdb3-python==0.16.0
|
||||
# via glances
|
||||
jinja2==3.1.6
|
||||
# via
|
||||
# glances
|
||||
# pysmi-lextudio
|
||||
kafka-python==2.3.0
|
||||
# via glances
|
||||
markupsafe==3.0.3
|
||||
# via jinja2
|
||||
msgpack==1.1.2
|
||||
# via influxdb
|
||||
nats-py==2.12.0
|
||||
# via glances
|
||||
nvidia-ml-py==13.590.44
|
||||
# via glances
|
||||
packaging==25.0
|
||||
# via glances
|
||||
paho-mqtt==2.1.0
|
||||
# via glances
|
||||
pbkdf2==1.3
|
||||
# via wifi
|
||||
pika==1.3.2
|
||||
# via glances
|
||||
ply==3.11
|
||||
# via pysmi-lextudio
|
||||
podman==5.6.0
|
||||
# via glances
|
||||
potsdb==1.0.3
|
||||
# via glances
|
||||
prometheus-client==0.23.1
|
||||
# via glances
|
||||
protobuf==6.33.2
|
||||
# via bernhard
|
||||
psutil==7.2.1
|
||||
# via glances
|
||||
psycopg==3.3.2
|
||||
# via glances
|
||||
psycopg-binary==3.3.2 ; implementation_name != 'pypy'
|
||||
# via psycopg
|
||||
pyarrow==22.0.0
|
||||
# via influxdb3-python
|
||||
pyasn1==0.6.1
|
||||
# via pysnmp-lextudio
|
||||
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
|
||||
# via fastapi
|
||||
pydantic-core==2.41.5
|
||||
# via pydantic
|
||||
pygal==3.1.0
|
||||
# via glances
|
||||
pyjwt==2.10.1
|
||||
# via
|
||||
# ibm-cloud-sdk-core
|
||||
# ibmcloudant
|
||||
pymdstat==0.4.3
|
||||
# via glances
|
||||
pymongo==4.15.5
|
||||
# via glances
|
||||
pyreadline3==3.5.4 ; sys_platform == 'win32'
|
||||
# via humanfriendly
|
||||
pysmart==1.4.2
|
||||
# via glances
|
||||
pysmi-lextudio==1.4.3
|
||||
# via pysnmp-lextudio
|
||||
pysnmp-lextudio==6.1.2
|
||||
# via glances
|
||||
pysnmpcrypto==0.0.4
|
||||
# via pysnmp-lextudio
|
||||
python-dateutil==2.9.0.post0
|
||||
# via
|
||||
# elasticsearch
|
||||
# glances
|
||||
# ibm-cloud-sdk-core
|
||||
# ibmcloudant
|
||||
# influxdb
|
||||
# influxdb-client
|
||||
# influxdb3-python
|
||||
pytz==2025.2
|
||||
# via influxdb
|
||||
pywin32==311 ; sys_platform == 'win32'
|
||||
# via docker
|
||||
pyzmq==27.1.0
|
||||
# via glances
|
||||
reactivex==4.1.0
|
||||
# via
|
||||
# influxdb-client
|
||||
# influxdb3-python
|
||||
requests==2.32.5
|
||||
# via
|
||||
# docker
|
||||
# glances
|
||||
# ibm-cloud-sdk-core
|
||||
# ibmcloudant
|
||||
# influxdb
|
||||
# podman
|
||||
# pysmi-lextudio
|
||||
setuptools==80.9.0
|
||||
# via
|
||||
# influxdb-client
|
||||
# wifi
|
||||
shtab==1.8.0 ; sys_platform != 'win32'
|
||||
# via glances
|
||||
six==1.17.0
|
||||
# via
|
||||
# glances
|
||||
# influxdb
|
||||
# python-dateutil
|
||||
sniffio==1.3.1
|
||||
# via
|
||||
# elastic-transport
|
||||
# elasticsearch
|
||||
sparklines==0.7.0
|
||||
# via glances
|
||||
starlette==0.50.0
|
||||
# via fastapi
|
||||
statsd==4.0.1
|
||||
# via glances
|
||||
termcolor==3.3.0
|
||||
# via sparklines
|
||||
tomli==2.0.2 ; python_full_version < '3.11'
|
||||
# via podman
|
||||
typing-extensions==4.15.0
|
||||
# via
|
||||
# anyio
|
||||
# cryptography
|
||||
# elasticsearch
|
||||
# fastapi
|
||||
# psycopg
|
||||
# pydantic
|
||||
# pydantic-core
|
||||
# reactivex
|
||||
# starlette
|
||||
# typing-inspection
|
||||
# uvicorn
|
||||
typing-inspection==0.4.2
|
||||
# via pydantic
|
||||
tzdata==2025.3 ; sys_platform == 'win32'
|
||||
# via psycopg
|
||||
urllib3==2.6.2
|
||||
# via
|
||||
# docker
|
||||
# elastic-transport
|
||||
# ibm-cloud-sdk-core
|
||||
# influxdb-client
|
||||
# influxdb3-python
|
||||
# podman
|
||||
# requests
|
||||
uvicorn==0.40.0
|
||||
# via glances
|
||||
wifi==0.3.8
|
||||
# via glances
|
||||
windows-curses==2.4.1 ; sys_platform == 'win32'
|
||||
# via glances
|
||||
zeroconf==0.148.0
|
||||
# via glances
|
||||
zipp==3.23.0
|
||||
# via importlib-metadata
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
✨ {{ gl.system['hostname'] }}{{ ' - ' + gl.ip['address'] if gl.ip['address'] else '' }}
|
||||
⚙️ {{ gl.system['hr_name'] }} | Uptime: {{ gl.uptime }}
|
||||
|
||||
💡 LOAD {{ '%0.2f'| format(gl.load['min1']) }} {{ '%0.2f'| format(gl.load['min5']) }} {{ '%0.2f'| format(gl.load['min15']) }}
|
||||
⚡ CPU {{ gl.bar(gl.cpu['total']) }} {{ gl.cpu['total'] }}% of {{ gl.core['log'] }} cores
|
||||
🧠 MEM {{ gl.bar(gl.mem['percent']) }} {{ gl.mem['percent'] }}% ({{ gl.auto_unit(gl.mem['used']) }} {{ gl.auto_unit(gl.mem['total']) }})
|
||||
{% for fs in gl.fs.keys() %}💾 {% if loop.index == 1 %}DISK{% else %} {% endif %} {{ gl.bar(gl.fs[fs]['percent']) }} {{ gl.fs[fs]['percent'] }}% ({{ gl.auto_unit(gl.fs[fs]['used']) }} {{ gl.auto_unit(gl.fs[fs]['size']) }}) for {{ fs }}
|
||||
{% endfor %}{% for net in gl.network.keys() %}📡 {% if loop.index == 1 %}NET{% else %} {% endif %} ↓ {{ gl.auto_unit(gl.network[net]['bytes_recv_rate_per_sec']) }}b/s ↑ {{ gl.auto_unit(gl.network[net]['bytes_sent_rate_per_sec']) }}b/s for {{ net }}
|
||||
{% endfor %}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
_____ _
|
||||
/ ____| |
|
||||
| | __| | __ _ _ __ ___ ___ ___
|
||||
| | |_ | |/ _` | '_ \ / __/ _ \/ __|
|
||||
| |__| | | (_| | | | | (_| __/\__
|
||||
\_____|_|\__,_|_| |_|\___\___||___/
|
||||
|
||||
|
||||
✨ {{ gl.system['hostname'] }}{{ ' - ' + gl.ip['address'] if gl.ip['address'] else '' }}
|
||||
⚙️ {{ gl.system['hr_name'] }} | Uptime: {{ gl.uptime }}
|
||||
|
||||
💡 LOAD {{ '%0.2f'| format(gl.load['min1']) }} {{ '%0.2f'| format(gl.load['min5']) }} {{ '%0.2f'| format(gl.load['min15']) }}
|
||||
⚡ CPU {{ gl.bar(gl.cpu['total']) }} {{ gl.cpu['total'] }}% of {{ gl.core['log'] }} cores
|
||||
🧠 MEM {{ gl.bar(gl.mem['percent']) }} {{ gl.mem['percent'] }}% ({{ gl.auto_unit(gl.mem['used']) }} {{ gl.auto_unit(gl.mem['total']) }})
|
||||
{% for fs in gl.fs.keys() %}💾 {% if loop.index == 1 %}DISK{% else %} {% endif %} {{ gl.bar(gl.fs[fs]['percent']) }} {{ gl.fs[fs]['percent'] }}% ({{ gl.auto_unit(gl.fs[fs]['used']) }} {{ gl.auto_unit(gl.fs[fs]['size']) }}) for {{ fs }}
|
||||
{% endfor %}{% for net in gl.network.keys() %}📡 {% if loop.index == 1 %}NET{% else %} {% endif %} ↓ {{ gl.auto_unit(gl.network[net]['bytes_recv_rate_per_sec']) }}b/s ↑ {{ gl.auto_unit(gl.network[net]['bytes_sent_rate_per_sec']) }}b/s for {{ net }}
|
||||
{% endfor %}
|
||||
🔥 TOP PROCESS by CPU
|
||||
{% for process in gl.top_process() %}{{ loop.index }}️⃣ {{ process['name'][:20] }}{{ ' ' * (20 - process['name'][:20] | length) }} ⚡ {{ process['cpu_percent'] }}% CPU{{ ' ' * (8 - (gl.auto_unit(process['cpu_percent']) | length)) }} 🧠 {{ gl.auto_unit(process['memory_info']['rss']) }}B MEM
|
||||
{% endfor %}
|
||||
🔥 TOP PROCESS by MEM
|
||||
{% for process in gl.top_process(sorted_by='memory_percent', sorted_by_secondary='cpu_percent') %}{{ loop.index }}️⃣ {{ process['name'][:20] }}{{ ' ' * (20 - process['name'][:20] | length) }} 🧠 {{ gl.auto_unit(process['memory_info']['rss']) }}B MEM{{ ' ' * (7 - (gl.auto_unit(process['memory_info']['rss']) | length)) }} ⚡ {{ process['cpu_percent'] }}% CPU
|
||||
{% endfor %}
|
||||
|
|
@ -49,7 +49,7 @@ history_size=1200
|
|||
# You can download it in a specific folder
|
||||
# thanks to https://github.com/nicolargo/glances/issues/2021
|
||||
# then configure this folder with the webui_root_path key
|
||||
# Default is folder where glances_restfull_api.py is hosted
|
||||
# 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.
|
||||
|
|
@ -64,6 +64,10 @@ 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
|
||||
|
||||
##############################################################################
|
||||
# Plugins
|
||||
|
|
@ -127,7 +131,7 @@ user_careful=50
|
|||
user_warning=70
|
||||
user_critical=90
|
||||
user_log=False
|
||||
#user_critical_action=echo {{user}} {{value}} {{max}} > /tmp/cpu.alert
|
||||
#user_critical_action=echo "{{time}} User CPU {{user}} higher than {{critical}}" > /tmp/cpu.alert
|
||||
#
|
||||
system_careful=50
|
||||
system_warning=70
|
||||
|
|
@ -181,12 +185,14 @@ temperature_critical=80
|
|||
|
||||
[mem]
|
||||
disable=False
|
||||
# Display available memory instead of used memory
|
||||
#available=True
|
||||
# Define RAM thresholds in %
|
||||
# Default values if not defined: 50/70/90
|
||||
careful=50
|
||||
#careful_action_repeat=echo {{percent}} >> /tmp/memory.alert
|
||||
warning=70
|
||||
critical=90
|
||||
#critical_action_repeat=echo "{{time}} {{percent}} higher than {{critical}}"" >> /tmp/memory.alert
|
||||
|
||||
[memswap]
|
||||
disable=False
|
||||
|
|
@ -195,6 +201,7 @@ disable=False
|
|||
careful=50
|
||||
warning=70
|
||||
critical=90
|
||||
#warning_action=echo "{{time}} {{percent}} higher than {{warning}}"" > /tmp/memory.alert
|
||||
|
||||
[load]
|
||||
disable=False
|
||||
|
|
@ -241,8 +248,9 @@ hide_zero=False
|
|||
#wlan0_tx_warning=900000
|
||||
#wlan0_tx_critical=1000000
|
||||
#wlan0_tx_log=True
|
||||
#wlan0_rx_critical_action=echo "{{time}} {{interface_name}} RX {{bytes_recv_rate_per_sec}}Bps" > /tmp/network.alert
|
||||
# Alias for network interface name
|
||||
#alias=wlp2s0:WIFI
|
||||
#alias=wlp0s20f3:WIFI
|
||||
|
||||
[ip]
|
||||
# Disable display of private IP address
|
||||
|
|
@ -300,15 +308,32 @@ hide_zero=False
|
|||
#show=sda.*
|
||||
# Alias for sda1 and sdb1
|
||||
#alias=sda1:SystemDisk,sdb1:DataDisk
|
||||
# Set thresholds (in bytes per second) for a given disk name (rx = read / tx = write)
|
||||
# Default latency thresholds (in ms) (rx = read / tx = write)
|
||||
rx_latency_careful=10
|
||||
rx_latency_warning=20
|
||||
rx_latency_critical=50
|
||||
tx_latency_careful=10
|
||||
tx_latency_warning=20
|
||||
tx_latency_critical=50
|
||||
# Set latency thresholds (latency in ms) for a given disk name (rx = read / tx = write)
|
||||
# dm-0_rx_latency_careful=10
|
||||
# dm-0_rx_latency_warning=20
|
||||
# dm-0_rx_latency_critical=50
|
||||
# dm-0_rx_latency_log=False
|
||||
# dm-0_tx_latency_careful=10
|
||||
# dm-0_tx_latency_warning=20
|
||||
# dm-0_tx_latency_critical=50
|
||||
# dm-0_tx_latency_log=False
|
||||
# There is no default bitrate thresholds for disk (because it is not possible to know the disk speed)
|
||||
# Set bitrate thresholds (in bytes per second) for a given disk name (rx = read / tx = write)
|
||||
#dm-0_rx_careful=4000000000
|
||||
#dm-0_rx_warning=5000000000
|
||||
#dm-0_rx_critical=6000000000
|
||||
#dm-0_rx_log=True
|
||||
#dm-0_rx_log=False
|
||||
#dm-0_tx_careful=700000000
|
||||
#dm-0_tx_warning=900000000
|
||||
#dm-0_tx_critical=1000000000
|
||||
#dm-0_tx_log=True
|
||||
#dm-0_tx_log=False
|
||||
|
||||
[fs]
|
||||
disable=False
|
||||
|
|
@ -318,15 +343,19 @@ hide=/boot.*,.*/snap.*
|
|||
#show=/,/srv
|
||||
# Define filesystem space thresholds in %
|
||||
# Default values if not defined: 50/70/90
|
||||
# It is also possible to define per mount point value
|
||||
# Example: /_careful=40
|
||||
careful=50
|
||||
warning=70
|
||||
critical=90
|
||||
# It is also possible to define per mount point value
|
||||
# Example: /_careful=40
|
||||
#/_careful=1
|
||||
#/_warning=5
|
||||
#/_critical=10
|
||||
#/_critical_action=echo "{{time}} {{mnt_point}} filesystem space {{percent}}% higher than {{critical}}%" > /tmp/fs.alert
|
||||
# Allow additional file system types (comma-separated FS type)
|
||||
#allow=shm
|
||||
# Alias for root file system
|
||||
#alias=/:Root,/zsfpool:ZSF
|
||||
#alias=/:Root,/zfspool:ZFS
|
||||
|
||||
[irq]
|
||||
# Documentation: https://glances.readthedocs.io/en/latest/aoa/irq.html
|
||||
|
|
@ -373,6 +402,8 @@ disable=True
|
|||
#hide=.*Hide_this_driver.*
|
||||
# Define the list of sensors to show (comma-separated regexp)
|
||||
#show=.*Drive_Temperature.*
|
||||
# List of attributes to hide (comma separated)
|
||||
#hide_attributes=Self-tests,Errors
|
||||
|
||||
[hddtemp]
|
||||
disable=False
|
||||
|
|
@ -384,8 +415,8 @@ port=7634
|
|||
# Documentation: https://glances.readthedocs.io/en/latest/aoa/sensors.html
|
||||
disable=False
|
||||
# Set the refresh multiplicator for the sensors
|
||||
# By default refresh every Glances refresh * 3 (increase to reduce CPU consumption)
|
||||
#refresh=3
|
||||
# By default refresh every Glances refresh * 5 (increase to reduce CPU consumption)
|
||||
#refresh=5
|
||||
# Hide some sensors (comma separated list of regexp)
|
||||
hide=unknown.*
|
||||
# Show only the following sensors (comma separated list of regexp)
|
||||
|
|
@ -393,10 +424,11 @@ hide=unknown.*
|
|||
# Sensors core thresholds (in Celsius...)
|
||||
# By default values are grabbed from the system
|
||||
# Overwrite thresholds for a specific sensor
|
||||
#temperature_core_Ambient_careful=45
|
||||
#temperature_core_Ambient_warning=65
|
||||
#temperature_core_Ambient_critical=80
|
||||
#temperature_core_Ambient_log=False
|
||||
# temperature_core_Ambient_careful=40
|
||||
# temperature_core_Ambient_warning=60
|
||||
# temperature_core_Ambient_critical=85
|
||||
# temperature_core_Ambient_log=True
|
||||
# temperature_core_Ambient_critical_action=echo "{{time}} {{label}} temperature {{value}}{{unit}} higher than {{critical}}{{unit}}" > /tmp/temperature.alert
|
||||
# Overwrite thresholds for a specific type of sensor
|
||||
#temperature_core_careful=45
|
||||
#temperature_core_warning=65
|
||||
|
|
@ -431,6 +463,8 @@ disable=False
|
|||
# Stats that can be disabled: cpu_percent,memory_info,memory_percent,username,cpu_times,num_threads,nice,status,io_counters,cmdline
|
||||
# Stats that can not be disable: pid,name
|
||||
#disable_stats=cpu_percent,memory_info,memory_percent,username,cpu_times,num_threads,nice,status,io_counters,cmdline
|
||||
# Disable display of virtual memory
|
||||
#disable_virtual_memory=True
|
||||
# Define CPU/MEM (per process) thresholds in %
|
||||
# Default values if not defined: 50/70/90
|
||||
cpu_careful=50
|
||||
|
|
@ -447,13 +481,20 @@ mem_critical=90
|
|||
nice_warning=-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19
|
||||
#
|
||||
# Nice: Example 2, low priority processes escalate from careful to critical
|
||||
#nice_ok=O
|
||||
#nice_careful=1,2,3,4,5,6,7,8,9
|
||||
#nice_warning=10,11,12,13,14
|
||||
#nice_critical=15,16,17,18,19
|
||||
#
|
||||
# Status: define threshold regarding the process status (first letter of process status)
|
||||
# R: Running, S: Sleeping, Z: Zombie (complete list here https://psutil.readthedocs.io/en/latest/#process-status-constants)
|
||||
status_ok=R,W,P,I
|
||||
status_critical=Z,D
|
||||
# Define the list of processes to export using:
|
||||
# a comma-separated list of Glances filter
|
||||
#export=.*firefox.*,pid:1234
|
||||
# Define a list of process to focus on (comma-separated list of Glances filter)
|
||||
#focus=.*firefox.*,.*python.*
|
||||
|
||||
[ports]
|
||||
disable=False
|
||||
|
|
@ -505,7 +546,8 @@ port_default_gateway=True
|
|||
disable=True
|
||||
# Define the maximum VMs size name (default is 20 chars)
|
||||
max_name_size=20
|
||||
# By default, Glances only display running VMs with states: 'Running', 'Starting' or 'Restarting'
|
||||
# By default, Glances only display running VMs with states:
|
||||
# 'Running', 'Paused', 'Starting' or 'Restarting'
|
||||
# Set the following key to True to display all VMs regarding their states
|
||||
all=False
|
||||
|
||||
|
|
@ -520,8 +562,8 @@ disable=False
|
|||
# Define the maximum docker size name (default is 20 chars)
|
||||
max_name_size=20
|
||||
# List of stats to disable (not display)
|
||||
# Following stats can be disabled: name,status,uptime,cpu,mem,diskio,networkio,command
|
||||
; disable_stats=diskio,networkio
|
||||
# Following stats can be disabled: name,status,uptime,cpu,mem,diskio,networkio,ports,command
|
||||
disable_stats=command
|
||||
# Thresholds for CPU and MEM (in %)
|
||||
; cpu_careful=50
|
||||
; cpu_warning=70
|
||||
|
|
@ -599,6 +641,11 @@ disable=False
|
|||
# Exports
|
||||
##############################################################################
|
||||
|
||||
[export]
|
||||
# Common section for all exporters
|
||||
# Do not export following fields (comma separated list of regex)
|
||||
#exclude_fields=.*_critical,.*_careful,.*_warning,.*\.key$
|
||||
|
||||
[graph]
|
||||
# Configuration for the --export graph option
|
||||
# Set the path where the graph (.svg files) will be created
|
||||
|
|
@ -617,7 +664,7 @@ style=DarkStyle
|
|||
[influxdb]
|
||||
# !!!
|
||||
# Will be DEPRECATED in future release.
|
||||
# Please have a look on the new influxdb2 export module (compatible with InfluxDB 1.8.x and 2.x)
|
||||
# Please have a look on the new influxdb3 export module
|
||||
# !!!
|
||||
# Configuration for the --export influxdb option
|
||||
# https://influxdb.com/
|
||||
|
|
@ -646,7 +693,28 @@ port=8086
|
|||
protocol=http
|
||||
org=nicolargo
|
||||
bucket=glances
|
||||
token=EjFUTWe8U-MIseEAkaVIgVnej_TrnbdvEcRkaB1imstW7gapSqy6_6-8XD-yd51V0zUUpDy-kAdVD1purDLuxA==
|
||||
token=PUT_YOUR_INFLUXDB2_TOKEN_HERE
|
||||
# Set the interval between two exports (in seconds)
|
||||
# If the interval is set to 0, the Glances refresh time is used (default behavor)
|
||||
#interval=0
|
||||
# Prefix will be added for all measurement name
|
||||
# Ex: prefix=foo
|
||||
# => foo.cpu
|
||||
# => foo.mem
|
||||
# You can also use dynamic values
|
||||
#prefix=foo
|
||||
# Following tags will be added for all measurements
|
||||
# You can also use dynamic values.
|
||||
# Note: hostname and name (for process) are always added as a tag
|
||||
#tags=foo:bar,spam:eggs,domain:`domainname`
|
||||
|
||||
[influxdb3]
|
||||
# Configuration for the --export influxdb3 option
|
||||
# https://influxdb.com/
|
||||
host=http://localhost:8181
|
||||
org=nicolargo
|
||||
database=glances
|
||||
token=PUT_YOUR_INFLUXDB3_TOKEN_HERE
|
||||
# Set the interval between two exports (in seconds)
|
||||
# If the interval is set to 0, the Glances refresh time is used (default behavor)
|
||||
#interval=0
|
||||
|
|
@ -812,6 +880,26 @@ prefix=glances
|
|||
# By default, system_name = FQDN
|
||||
#system_name=mycomputer
|
||||
|
||||
[timescaledb]
|
||||
# Configuration for the --export timescaledb option
|
||||
# https://www.timescale.com/
|
||||
host=localhost
|
||||
port=5432
|
||||
db=glances
|
||||
user=postgres
|
||||
password=password
|
||||
# Overwrite device name (default is the FQDN)
|
||||
# Most of the time, you should not overwrite this value
|
||||
#hostname=mycomputer
|
||||
|
||||
[nats]
|
||||
# Configuration for the --export nats option
|
||||
# https://nats.io/
|
||||
# Host is a separated list of NATS nodes
|
||||
host=nats://localhost:4222
|
||||
# Prefix for the subjects (default is 'glances')
|
||||
prefix=glances
|
||||
|
||||
##############################################################################
|
||||
# AMPS
|
||||
# * enable: Enable (true) or disable (false) the AMP
|
||||
|
|
|
|||
|
|
@ -1,18 +1,395 @@
|
|||
codespell
|
||||
coverage
|
||||
fonttools>=4.43.0 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
gprof2dot
|
||||
matplotlib
|
||||
memory-profiler
|
||||
numpy>=1.22.2 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
pillow>=10.0.1 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
pre-commit
|
||||
py-spy
|
||||
pyright
|
||||
pytest
|
||||
requirements-parser
|
||||
ruff
|
||||
selenium
|
||||
semgrep; platform_system == 'Linux'
|
||||
setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
webdriver-manager
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv export --no-hashes --only-dev --output-file dev-requirements.txt
|
||||
alabaster==1.0.0
|
||||
# via sphinx
|
||||
annotated-types==0.7.0
|
||||
# via pydantic
|
||||
anyio==4.12.0
|
||||
# via
|
||||
# httpx
|
||||
# mcp
|
||||
# sse-starlette
|
||||
# starlette
|
||||
attrs==25.4.0
|
||||
# via
|
||||
# glom
|
||||
# jsonschema
|
||||
# outcome
|
||||
# referencing
|
||||
# reuse
|
||||
# semgrep
|
||||
# trio
|
||||
babel==2.17.0
|
||||
# via sphinx
|
||||
boltons==21.0.0
|
||||
# via
|
||||
# face
|
||||
# glom
|
||||
# semgrep
|
||||
boolean-py==5.0
|
||||
# via license-expression
|
||||
bracex==2.6
|
||||
# via wcmatch
|
||||
certifi==2025.11.12
|
||||
# via
|
||||
# httpcore
|
||||
# httpx
|
||||
# requests
|
||||
# selenium
|
||||
cffi==2.0.0 ; (implementation_name != 'pypy' and os_name == 'nt') or platform_python_implementation != 'PyPy'
|
||||
# via
|
||||
# cryptography
|
||||
# trio
|
||||
cfgv==3.5.0
|
||||
# via pre-commit
|
||||
charset-normalizer==3.4.4
|
||||
# via
|
||||
# python-debian
|
||||
# requests
|
||||
click==8.1.8
|
||||
# via
|
||||
# click-option-group
|
||||
# reuse
|
||||
# semgrep
|
||||
# typer
|
||||
# uvicorn
|
||||
click-option-group==0.5.9
|
||||
# via semgrep
|
||||
codespell==2.4.1
|
||||
colorama==0.4.6
|
||||
# via
|
||||
# click
|
||||
# pytest
|
||||
# semgrep
|
||||
# sphinx
|
||||
contourpy==1.3.2 ; python_full_version < '3.11'
|
||||
# via matplotlib
|
||||
contourpy==1.3.3 ; python_full_version >= '3.11'
|
||||
# via matplotlib
|
||||
cryptography==46.0.3
|
||||
# via pyjwt
|
||||
cycler==0.12.1
|
||||
# via matplotlib
|
||||
distlib==0.4.0
|
||||
# via virtualenv
|
||||
docutils==0.21.2
|
||||
# via
|
||||
# rstcheck-core
|
||||
# sphinx
|
||||
# sphinx-rtd-theme
|
||||
exceptiongroup==1.2.2
|
||||
# via
|
||||
# anyio
|
||||
# pytest
|
||||
# semgrep
|
||||
# trio
|
||||
# trio-websocket
|
||||
face==24.0.0
|
||||
# via glom
|
||||
filelock==3.20.2
|
||||
# via virtualenv
|
||||
fonttools==4.61.1
|
||||
# via matplotlib
|
||||
glom==22.1.0
|
||||
# via semgrep
|
||||
googleapis-common-protos==1.72.0
|
||||
# via opentelemetry-exporter-otlp-proto-http
|
||||
gprof2dot==2025.4.14
|
||||
h11==0.16.0
|
||||
# via
|
||||
# httpcore
|
||||
# uvicorn
|
||||
# wsproto
|
||||
httpcore==1.0.9
|
||||
# via httpx
|
||||
httpx==0.28.1
|
||||
# via mcp
|
||||
httpx-sse==0.4.3
|
||||
# via mcp
|
||||
identify==2.6.15
|
||||
# via pre-commit
|
||||
idna==3.11
|
||||
# via
|
||||
# anyio
|
||||
# httpx
|
||||
# requests
|
||||
# trio
|
||||
imagesize==1.4.1
|
||||
# via sphinx
|
||||
importlib-metadata==8.7.1
|
||||
# via opentelemetry-api
|
||||
iniconfig==2.3.0
|
||||
# via pytest
|
||||
jinja2==3.1.6
|
||||
# via
|
||||
# reuse
|
||||
# sphinx
|
||||
jsonschema==4.25.1
|
||||
# via
|
||||
# mcp
|
||||
# semgrep
|
||||
jsonschema-specifications==2025.9.1
|
||||
# via jsonschema
|
||||
kiwisolver==1.4.9
|
||||
# via matplotlib
|
||||
license-expression==30.4.4
|
||||
# via reuse
|
||||
markdown-it-py==4.0.0
|
||||
# via rich
|
||||
markupsafe==3.0.3
|
||||
# via jinja2
|
||||
matplotlib==3.10.8
|
||||
mcp==1.23.3
|
||||
# via semgrep
|
||||
mdurl==0.1.2
|
||||
# via markdown-it-py
|
||||
memory-profiler==0.61.0
|
||||
nodeenv==1.10.0
|
||||
# via
|
||||
# pre-commit
|
||||
# pyright
|
||||
numpy==2.2.6 ; python_full_version < '3.11'
|
||||
# via
|
||||
# contourpy
|
||||
# matplotlib
|
||||
numpy==2.4.0 ; python_full_version >= '3.11'
|
||||
# via
|
||||
# contourpy
|
||||
# matplotlib
|
||||
opentelemetry-api==1.37.0
|
||||
# via
|
||||
# opentelemetry-exporter-otlp-proto-http
|
||||
# opentelemetry-instrumentation
|
||||
# opentelemetry-instrumentation-requests
|
||||
# opentelemetry-sdk
|
||||
# opentelemetry-semantic-conventions
|
||||
# semgrep
|
||||
opentelemetry-exporter-otlp-proto-common==1.37.0
|
||||
# via opentelemetry-exporter-otlp-proto-http
|
||||
opentelemetry-exporter-otlp-proto-http==1.37.0
|
||||
# via semgrep
|
||||
opentelemetry-instrumentation==0.58b0
|
||||
# via opentelemetry-instrumentation-requests
|
||||
opentelemetry-instrumentation-requests==0.58b0
|
||||
# via semgrep
|
||||
opentelemetry-proto==1.37.0
|
||||
# via
|
||||
# opentelemetry-exporter-otlp-proto-common
|
||||
# opentelemetry-exporter-otlp-proto-http
|
||||
opentelemetry-sdk==1.37.0
|
||||
# via
|
||||
# opentelemetry-exporter-otlp-proto-http
|
||||
# semgrep
|
||||
opentelemetry-semantic-conventions==0.58b0
|
||||
# via
|
||||
# opentelemetry-instrumentation
|
||||
# opentelemetry-instrumentation-requests
|
||||
# opentelemetry-sdk
|
||||
opentelemetry-util-http==0.58b0
|
||||
# via opentelemetry-instrumentation-requests
|
||||
outcome==1.3.0.post0
|
||||
# via
|
||||
# trio
|
||||
# trio-websocket
|
||||
packaging==25.0
|
||||
# via
|
||||
# matplotlib
|
||||
# opentelemetry-instrumentation
|
||||
# pytest
|
||||
# requirements-parser
|
||||
# semgrep
|
||||
# sphinx
|
||||
# webdriver-manager
|
||||
peewee==3.18.3
|
||||
# via semgrep
|
||||
pillow==12.1.0
|
||||
# via matplotlib
|
||||
platformdirs==4.5.1
|
||||
# via virtualenv
|
||||
pluggy==1.6.0
|
||||
# via pytest
|
||||
pre-commit==4.5.1
|
||||
protobuf==6.33.2
|
||||
# via
|
||||
# googleapis-common-protos
|
||||
# opentelemetry-proto
|
||||
psutil==7.2.1
|
||||
# via memory-profiler
|
||||
py-spy==0.4.1
|
||||
pycparser==2.23 ; (implementation_name != 'PyPy' and implementation_name != 'pypy' and os_name == 'nt') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy')
|
||||
# via cffi
|
||||
pydantic==2.12.5
|
||||
# via
|
||||
# mcp
|
||||
# pydantic-settings
|
||||
# rstcheck-core
|
||||
pydantic-core==2.41.5
|
||||
# via pydantic
|
||||
pydantic-settings==2.12.0
|
||||
# via mcp
|
||||
pygments==2.19.2
|
||||
# via
|
||||
# pytest
|
||||
# rich
|
||||
# sphinx
|
||||
pyinstrument==5.1.1
|
||||
pyjwt==2.10.1
|
||||
# via mcp
|
||||
pyparsing==3.3.1
|
||||
# via matplotlib
|
||||
pyright==1.1.407
|
||||
pysocks==1.7.1
|
||||
# via urllib3
|
||||
pytest==9.0.2
|
||||
python-dateutil==2.9.0.post0
|
||||
# via matplotlib
|
||||
python-debian==1.0.1
|
||||
# via reuse
|
||||
python-dotenv==1.2.1
|
||||
# via
|
||||
# pydantic-settings
|
||||
# webdriver-manager
|
||||
python-magic==0.4.27
|
||||
# via reuse
|
||||
python-multipart==0.0.21
|
||||
# via mcp
|
||||
pywin32==311 ; sys_platform == 'win32'
|
||||
# via
|
||||
# mcp
|
||||
# semgrep
|
||||
pyyaml==6.0.3
|
||||
# via pre-commit
|
||||
referencing==0.37.0
|
||||
# via
|
||||
# jsonschema
|
||||
# jsonschema-specifications
|
||||
requests==2.32.5
|
||||
# via
|
||||
# opentelemetry-exporter-otlp-proto-http
|
||||
# semgrep
|
||||
# sphinx
|
||||
# webdriver-manager
|
||||
requirements-parser==0.13.0
|
||||
reuse==6.2.0
|
||||
rich==13.5.3
|
||||
# via
|
||||
# semgrep
|
||||
# typer
|
||||
roman-numerals==4.1.0 ; python_full_version >= '3.11'
|
||||
# via roman-numerals-py
|
||||
roman-numerals-py==4.1.0 ; python_full_version >= '3.11'
|
||||
# via sphinx
|
||||
rpds-py==0.30.0
|
||||
# via
|
||||
# jsonschema
|
||||
# referencing
|
||||
rstcheck==6.2.5
|
||||
rstcheck-core==1.2.2
|
||||
# via rstcheck
|
||||
ruamel-yaml==0.19.1
|
||||
# via semgrep
|
||||
ruamel-yaml-clib==0.2.14
|
||||
# via semgrep
|
||||
ruff==0.14.10
|
||||
selenium==4.39.0
|
||||
semgrep==1.146.0
|
||||
setuptools==80.9.0
|
||||
shellingham==1.5.4
|
||||
# via typer
|
||||
six==1.17.0
|
||||
# via python-dateutil
|
||||
sniffio==1.3.1
|
||||
# via trio
|
||||
snowballstemmer==3.0.1
|
||||
# via sphinx
|
||||
sortedcontainers==2.4.0
|
||||
# via trio
|
||||
sphinx==8.1.3 ; python_full_version < '3.11'
|
||||
# via
|
||||
# sphinx-rtd-theme
|
||||
# sphinxcontrib-jquery
|
||||
sphinx==8.2.3 ; python_full_version >= '3.11'
|
||||
# via
|
||||
# sphinx-rtd-theme
|
||||
# sphinxcontrib-jquery
|
||||
sphinx-rtd-theme==3.0.2
|
||||
sphinxcontrib-applehelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-devhelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-htmlhelp==2.1.0
|
||||
# via sphinx
|
||||
sphinxcontrib-jquery==4.1
|
||||
# via sphinx-rtd-theme
|
||||
sphinxcontrib-jsmath==1.0.1
|
||||
# via sphinx
|
||||
sphinxcontrib-qthelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-serializinghtml==2.0.0
|
||||
# via sphinx
|
||||
sse-starlette==3.1.2
|
||||
# via mcp
|
||||
starlette==0.50.0
|
||||
# via
|
||||
# mcp
|
||||
# sse-starlette
|
||||
tomli==2.0.2
|
||||
# via
|
||||
# pytest
|
||||
# semgrep
|
||||
# sphinx
|
||||
tomlkit==0.13.3
|
||||
# via reuse
|
||||
trio==0.32.0
|
||||
# via
|
||||
# selenium
|
||||
# trio-websocket
|
||||
trio-websocket==0.12.2
|
||||
# via selenium
|
||||
typer==0.21.0
|
||||
# via rstcheck
|
||||
typing-extensions==4.15.0
|
||||
# via
|
||||
# anyio
|
||||
# cryptography
|
||||
# mcp
|
||||
# opentelemetry-api
|
||||
# opentelemetry-exporter-otlp-proto-http
|
||||
# opentelemetry-sdk
|
||||
# opentelemetry-semantic-conventions
|
||||
# pydantic
|
||||
# pydantic-core
|
||||
# pyright
|
||||
# referencing
|
||||
# selenium
|
||||
# semgrep
|
||||
# starlette
|
||||
# typer
|
||||
# typing-inspection
|
||||
# uvicorn
|
||||
# virtualenv
|
||||
typing-inspection==0.4.2
|
||||
# via
|
||||
# mcp
|
||||
# pydantic
|
||||
# pydantic-settings
|
||||
urllib3==2.6.2
|
||||
# via
|
||||
# requests
|
||||
# selenium
|
||||
# semgrep
|
||||
uvicorn==0.40.0 ; sys_platform != 'emscripten'
|
||||
# via mcp
|
||||
virtualenv==20.35.4
|
||||
# via pre-commit
|
||||
wcmatch==8.5.2
|
||||
# via semgrep
|
||||
webdriver-manager==4.0.2
|
||||
websocket-client==1.9.0
|
||||
# via selenium
|
||||
wrapt==1.17.3
|
||||
# via opentelemetry-instrumentation
|
||||
wsproto==1.3.2
|
||||
# via trio-websocket
|
||||
zipp==3.23.0
|
||||
# via importlib-metadata
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
psutil
|
||||
defusedxml
|
||||
orjson
|
||||
reuse
|
||||
setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
sphinx
|
||||
sphinx_rtd_theme
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
FROM glances:local-alpine-minimal as glancesminimal
|
||||
COPY glances.conf /glances/conf/glances.conf
|
||||
CMD python -m glances -C /glances/conf/glances.conf $GLANCES_OPT
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
version: "3.9"
|
||||
services:
|
||||
reverse-proxy:
|
||||
image: traefik
|
||||
command: --api --docker
|
||||
ports:
|
||||
- "80:80"
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
whoami:
|
||||
image: emilevauge/whoami
|
||||
labels:
|
||||
- "traefik.frontend.rule=Host:whoami.docker.localhost"
|
||||
|
||||
monitoring:
|
||||
image: nicolargo/glances:dev
|
||||
restart: unless-stopped
|
||||
pid: host
|
||||
privileged: true
|
||||
network_mode: "host"
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
- "/run/user/1000/podman/podman.sock:/run/user/1000/podman/podman.sock:ro"
|
||||
- "./glances.conf:/glances/conf/glances.conf"
|
||||
environment:
|
||||
- TZ=${TZ}
|
||||
- "GLANCES_OPT=-C /glances/conf/glances.conf -w"
|
||||
# Uncomment for GPU compatibility (Nvidia) inside the container
|
||||
# deploy:
|
||||
# resources:
|
||||
# reservations:
|
||||
# devices:
|
||||
# - driver: nvidia
|
||||
# count: 1
|
||||
# capabilities: [gpu]
|
||||
labels:
|
||||
- "traefik.port=61208"
|
||||
- "traefik.frontend.rule=Host:glances.docker.localhost"
|
||||
|
|
@ -1,20 +1,41 @@
|
|||
version: '3.9'
|
||||
services:
|
||||
glances:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: Dockerfile
|
||||
# See all images tags here: https://hub.docker.com/r/nicolargo/glances/tags
|
||||
image: nicolargo/glances:latest-full
|
||||
restart: always
|
||||
|
||||
pid: "host"
|
||||
privileged: true
|
||||
network_mode: "host"
|
||||
|
||||
read_only: true
|
||||
privileged: false
|
||||
# Uncomment next line for SATA or NVME smartctl monitoring
|
||||
# cap_add:
|
||||
# Uncomment next line for SATA smartctl monitoring
|
||||
# - SYS_RAWIO
|
||||
# Uncomment next line for NVME smartctl monitoring
|
||||
# - SYS_ADMIN
|
||||
# devices:
|
||||
# - "/dev/nvme0"
|
||||
|
||||
volumes:
|
||||
- "/:/rootfs:ro"
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
- "/run/user/1000/podman/podman.sock:/run/user/1000/podman/podman.sock:ro"
|
||||
- "./glances.conf:/glances/conf/glances.conf"
|
||||
# Uncomment for proper distro information in upper panel.
|
||||
# # Works only for distros that do have this file (most of distros do).
|
||||
# - "/etc/os-release:/etc/os-release:ro"
|
||||
|
||||
tmpfs:
|
||||
- /tmp
|
||||
|
||||
environment:
|
||||
- TZ=${TZ}
|
||||
- GLANCES_OPT=-C /glances/conf/glances.conf -w
|
||||
# Please set to your local timezone (or use local ${TZ} environment variable if set on your host)
|
||||
- TZ=Europe/Paris
|
||||
- GLANCES_OPT=-C /glances/conf/glances.conf -w --enable-plugin smart
|
||||
- PYTHONPYCACHEPREFIX=/tmp/py_caches
|
||||
|
||||
# # Uncomment for GPU compatibility (Nvidia) inside the container
|
||||
# deploy:
|
||||
# resources:
|
||||
|
|
@ -23,10 +44,11 @@ services:
|
|||
# - driver: nvidia
|
||||
# count: 1
|
||||
# capabilities: [gpu]
|
||||
# # Uncomment to protect Glances WebUI by a login/password (add --password to GLANCES_OPT)
|
||||
# secrets:
|
||||
# - source: glances_password
|
||||
# target: /root/.config/glances/<login>.pwd
|
||||
|
||||
# Uncomment to protect Glances WebUI by a login/password (add --password to GLANCES_OPT)
|
||||
# secrets:
|
||||
# - source: glances_password
|
||||
# target: /root/.config/glances/<login>.pwd
|
||||
|
||||
# secrets:
|
||||
# glances_password:
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ check_update=False
|
|||
# Default is 1200 values (~1h with the default refresh rate)
|
||||
history_size=1200
|
||||
# Set the way Glances should display the date (default is %Y-%m-%d %H:%M:%S %Z)
|
||||
# strftime_format=%Y-%m-%d %H:%M:%S %Z
|
||||
#strftime_format=%Y-%m-%d %H:%M:%S %Z
|
||||
# Define external directory for loading additional plugins
|
||||
# The layout follows the glances standard for plugin definitions
|
||||
#plugin_dir=/home/user/dev/plugins
|
||||
|
|
@ -49,7 +49,7 @@ max_processes_display=25
|
|||
# You can download it in a specific folder
|
||||
# thanks to https://github.com/nicolargo/glances/issues/2021
|
||||
# then configure this folder with the webui_root_path key
|
||||
# Default is folder where glances_restfull_api.py is hosted
|
||||
# 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.
|
||||
|
|
@ -64,6 +64,10 @@ 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
|
||||
|
||||
##############################################################################
|
||||
# Plugins
|
||||
|
|
@ -127,7 +131,7 @@ user_careful=50
|
|||
user_warning=70
|
||||
user_critical=90
|
||||
user_log=False
|
||||
#user_critical_action=echo {{user}} {{value}} {{max}} > /tmp/cpu.alert
|
||||
#user_critical_action=echo "{{time}} User CPU {{user}} higher than {{critical}}" > /tmp/cpu.alert
|
||||
#
|
||||
system_careful=50
|
||||
system_warning=70
|
||||
|
|
@ -181,12 +185,14 @@ temperature_critical=80
|
|||
|
||||
[mem]
|
||||
disable=False
|
||||
# Display available memory instead of used memory
|
||||
#available=True
|
||||
# Define RAM thresholds in %
|
||||
# Default values if not defined: 50/70/90
|
||||
careful=50
|
||||
#careful_action_repeat=echo {{percent}} >> /tmp/memory.alert
|
||||
warning=70
|
||||
critical=90
|
||||
#critical_action_repeat=echo "{{time}} {{percent}} higher than {{critical}}"" >> /tmp/memory.alert
|
||||
|
||||
[memswap]
|
||||
disable=False
|
||||
|
|
@ -195,6 +201,7 @@ disable=False
|
|||
careful=50
|
||||
warning=70
|
||||
critical=90
|
||||
#warning_action=echo "{{time}} {{percent}} higher than {{warning}}"" > /tmp/memory.alert
|
||||
|
||||
[load]
|
||||
disable=False
|
||||
|
|
@ -241,8 +248,9 @@ hide_zero=False
|
|||
#wlan0_tx_warning=900000
|
||||
#wlan0_tx_critical=1000000
|
||||
#wlan0_tx_log=True
|
||||
#wlan0_rx_critical_action=echo "{{time}} {{interface_name}} RX {{bytes_recv_rate_per_sec}}Bps" > /tmp/network.alert
|
||||
# Alias for network interface name
|
||||
#alias=wlp2s0:WIFI
|
||||
#alias=wlp0s20f3:WIFI
|
||||
|
||||
[ip]
|
||||
# Disable display of private IP address
|
||||
|
|
@ -300,15 +308,32 @@ hide_zero=False
|
|||
#show=sda.*
|
||||
# Alias for sda1 and sdb1
|
||||
#alias=sda1:SystemDisk,sdb1:DataDisk
|
||||
# Set thresholds (in bytes per second) for a given disk name (rx = read / tx = write)
|
||||
# Default latency thresholds (in ms) (rx = read / tx = write)
|
||||
rx_latency_careful=10
|
||||
rx_latency_warning=20
|
||||
rx_latency_critical=50
|
||||
tx_latency_careful=10
|
||||
tx_latency_warning=20
|
||||
tx_latency_critical=50
|
||||
# Set latency thresholds (latency in ms) for a given disk name (rx = read / tx = write)
|
||||
# dm-0_rx_latency_careful=10
|
||||
# dm-0_rx_latency_warning=20
|
||||
# dm-0_rx_latency_critical=50
|
||||
# dm-0_rx_latency_log=False
|
||||
# dm-0_tx_latency_careful=10
|
||||
# dm-0_tx_latency_warning=20
|
||||
# dm-0_tx_latency_critical=50
|
||||
# dm-0_tx_latency_log=False
|
||||
# There is no default bitrate thresholds for disk (because it is not possible to know the disk speed)
|
||||
# Set bitrate thresholds (in bytes per second) for a given disk name (rx = read / tx = write)
|
||||
#dm-0_rx_careful=4000000000
|
||||
#dm-0_rx_warning=5000000000
|
||||
#dm-0_rx_critical=6000000000
|
||||
#dm-0_rx_log=True
|
||||
#dm-0_rx_log=False
|
||||
#dm-0_tx_careful=700000000
|
||||
#dm-0_tx_warning=900000000
|
||||
#dm-0_tx_critical=1000000000
|
||||
#dm-0_tx_log=True
|
||||
#dm-0_tx_log=False
|
||||
|
||||
[fs]
|
||||
disable=False
|
||||
|
|
@ -318,11 +343,15 @@ hide=/boot.*,.*/snap.*
|
|||
#show=/,/srv
|
||||
# Define filesystem space thresholds in %
|
||||
# Default values if not defined: 50/70/90
|
||||
# It is also possible to define per mount point value
|
||||
# Example: /_careful=40
|
||||
careful=50
|
||||
warning=70
|
||||
critical=90
|
||||
# It is also possible to define per mount point value
|
||||
# Example: /_careful=40
|
||||
#/_careful=1
|
||||
#/_warning=5
|
||||
#/_critical=10
|
||||
#/_critical_action=echo "{{time}} {{mnt_point}} filesystem space {{percent}}% higher than {{critical}}%" > /tmp/fs.alert
|
||||
# Allow additional file system types (comma-separated FS type)
|
||||
#allow=shm
|
||||
# Alias for root file system
|
||||
|
|
@ -373,6 +402,8 @@ disable=True
|
|||
#hide=.*Hide_this_driver.*
|
||||
# Define the list of sensors to show (comma-separated regexp)
|
||||
#show=.*Drive_Temperature.*
|
||||
# List of attributes to hide (comma separated)
|
||||
#hide_attributes=Self-tests,Errors
|
||||
|
||||
[hddtemp]
|
||||
disable=False
|
||||
|
|
@ -384,8 +415,8 @@ port=7634
|
|||
# Documentation: https://glances.readthedocs.io/en/latest/aoa/sensors.html
|
||||
disable=False
|
||||
# Set the refresh multiplicator for the sensors
|
||||
# By default refresh every Glances refresh * 3 (increase to reduce CPU consumption)
|
||||
#refresh=3
|
||||
# By default refresh every Glances refresh * 5 (increase to reduce CPU consumption)
|
||||
#refresh=5
|
||||
# Hide some sensors (comma separated list of regexp)
|
||||
hide=unknown.*
|
||||
# Show only the following sensors (comma separated list of regexp)
|
||||
|
|
@ -393,10 +424,11 @@ hide=unknown.*
|
|||
# Sensors core thresholds (in Celsius...)
|
||||
# By default values are grabbed from the system
|
||||
# Overwrite thresholds for a specific sensor
|
||||
#temperature_core_Ambient_careful=45
|
||||
#temperature_core_Ambient_warning=65
|
||||
#temperature_core_Ambient_critical=80
|
||||
#temperature_core_Ambient_log=False
|
||||
# temperature_core_Ambient_careful=40
|
||||
# temperature_core_Ambient_warning=60
|
||||
# temperature_core_Ambient_critical=85
|
||||
# temperature_core_Ambient_log=True
|
||||
# temperature_core_Ambient_critical_action=echo "{{time}} {{label}} temperature {{value}}{{unit}} higher than {{critical}}{{unit}}" > /tmp/temperature.alert
|
||||
# Overwrite thresholds for a specific type of sensor
|
||||
#temperature_core_careful=45
|
||||
#temperature_core_warning=65
|
||||
|
|
@ -431,6 +463,8 @@ disable=False
|
|||
# Stats that can be disabled: cpu_percent,memory_info,memory_percent,username,cpu_times,num_threads,nice,status,io_counters,cmdline
|
||||
# Stats that can not be disable: pid,name
|
||||
#disable_stats=cpu_percent,memory_info,memory_percent,username,cpu_times,num_threads,nice,status,io_counters,cmdline
|
||||
# Disable display of virtual memory
|
||||
#disable_virtual_memory=True
|
||||
# Define CPU/MEM (per process) thresholds in %
|
||||
# Default values if not defined: 50/70/90
|
||||
cpu_careful=50
|
||||
|
|
@ -447,13 +481,20 @@ mem_critical=90
|
|||
nice_warning=-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19
|
||||
#
|
||||
# Nice: Example 2, low priority processes escalate from careful to critical
|
||||
#nice_ok=O
|
||||
#nice_careful=1,2,3,4,5,6,7,8,9
|
||||
#nice_warning=10,11,12,13,14
|
||||
#nice_critical=15,16,17,18,19
|
||||
#
|
||||
# Status: define threshold regarding the process status (first letter of process status)
|
||||
# R: Running, S: Sleeping, Z: Zombie (complete list here https://psutil.readthedocs.io/en/latest/#process-status-constants)
|
||||
status_ok=R,W,P,I
|
||||
status_critical=Z,D
|
||||
# Define the list of processes to export using:
|
||||
# a comma-separated list of Glances filter
|
||||
#export=.*firefox.*,pid:1234
|
||||
# Define a list of process to focus on (comma-separated list of Glances filter)
|
||||
#focus=.*firefox.*,.*python.*
|
||||
|
||||
[ports]
|
||||
disable=False
|
||||
|
|
@ -505,7 +546,8 @@ port_default_gateway=False
|
|||
disable=True
|
||||
# Define the maximum VMs size name (default is 20 chars)
|
||||
max_name_size=20
|
||||
# By default, Glances only display running VMs with states: 'Running', 'Starting' or 'Restarting'
|
||||
# By default, Glances only display running VMs with states:
|
||||
# 'Running', 'Paused', 'Starting' or 'Restarting'
|
||||
# Set the following key to True to display all VMs regarding their states
|
||||
all=False
|
||||
|
||||
|
|
@ -520,8 +562,8 @@ disable=False
|
|||
# Define the maximum docker size name (default is 20 chars)
|
||||
max_name_size=20
|
||||
# List of stats to disable (not display)
|
||||
# Following stats can be disabled: name,status,uptime,cpu,mem,diskio,networkio,command
|
||||
; disable_stats=diskio,networkio
|
||||
# Following stats can be disabled: name,status,uptime,cpu,mem,diskio,networkio,ports,command
|
||||
disable_stats=command
|
||||
# Thresholds for CPU and MEM (in %)
|
||||
; cpu_careful=50
|
||||
; cpu_warning=70
|
||||
|
|
@ -599,6 +641,11 @@ disable=False
|
|||
# Exports
|
||||
##############################################################################
|
||||
|
||||
[export]
|
||||
# Common section for all exporters
|
||||
# Do not export following fields (comma separated list of regex)
|
||||
#exclude_fields=.*_critical,.*_careful,.*_warning,.*\.key$
|
||||
|
||||
[graph]
|
||||
# Configuration for the --export graph option
|
||||
# Set the path where the graph (.svg files) will be created
|
||||
|
|
@ -617,7 +664,7 @@ style=DarkStyle
|
|||
[influxdb]
|
||||
# !!!
|
||||
# Will be DEPRECATED in future release.
|
||||
# Please have a look on the new influxdb2 export module (compatible with InfluxDB 1.8.x and 2.x)
|
||||
# Please have a look on the new influxdb3 export module
|
||||
# !!!
|
||||
# Configuration for the --export influxdb option
|
||||
# https://influxdb.com/
|
||||
|
|
@ -646,7 +693,28 @@ port=8086
|
|||
protocol=http
|
||||
org=nicolargo
|
||||
bucket=glances
|
||||
token=EjFUTWe8U-MIseEAkaVIgVnej_TrnbdvEcRkaB1imstW7gapSqy6_6-8XD-yd51V0zUUpDy-kAdVD1purDLuxA==
|
||||
token=PUT_YOUR_INFLUXDB2_TOKEN_HERE
|
||||
# Set the interval between two exports (in seconds)
|
||||
# If the interval is set to 0, the Glances refresh time is used (default behavor)
|
||||
#interval=0
|
||||
# Prefix will be added for all measurement name
|
||||
# Ex: prefix=foo
|
||||
# => foo.cpu
|
||||
# => foo.mem
|
||||
# You can also use dynamic values
|
||||
#prefix=foo
|
||||
# Following tags will be added for all measurements
|
||||
# You can also use dynamic values.
|
||||
# Note: hostname and name (for process) are always added as a tag
|
||||
#tags=foo:bar,spam:eggs,domain:`domainname`
|
||||
|
||||
[influxdb3]
|
||||
# Configuration for the --export influxdb3 option
|
||||
# https://influxdb.com/
|
||||
host=http://localhost:8181
|
||||
org=nicolargo
|
||||
database=glances
|
||||
token=PUT_YOUR_INFLUXDB3_TOKEN_HERE
|
||||
# Set the interval between two exports (in seconds)
|
||||
# If the interval is set to 0, the Glances refresh time is used (default behavor)
|
||||
#interval=0
|
||||
|
|
@ -812,6 +880,26 @@ prefix=glances
|
|||
# By default, system_name = FQDN
|
||||
#system_name=mycomputer
|
||||
|
||||
[timescaledb]
|
||||
# Configuration for the --export timescaledb option
|
||||
# https://www.timescale.com/
|
||||
host=localhost
|
||||
port=5432
|
||||
db=glances
|
||||
user=postgres
|
||||
password=password
|
||||
# Overwrite device name (default is the FQDN)
|
||||
# Most of the time, you should not overwrite this value
|
||||
#hostname=mycomputer
|
||||
|
||||
[nats]
|
||||
# Configuration for the --export nats option
|
||||
# https://nats.io/
|
||||
# Host is a separated list of NATS nodes
|
||||
host=nats://localhost:4222
|
||||
# Prefix for the subjects (default is 'glances')
|
||||
prefix=glances
|
||||
|
||||
##############################################################################
|
||||
# AMPS
|
||||
# * enable: Enable (true) or disable (false) the AMP
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@
|
|||
# WARNING: the Alpine image version and Python version should be set.
|
||||
# Alpine 3.18 tag is a link to the latest 3.18.x version.
|
||||
# Be aware that if you change the Alpine version, you may have to change the Python version.
|
||||
ARG IMAGE_VERSION=3.21
|
||||
ARG IMAGE_VERSION=3.23
|
||||
ARG PYTHON_VERSION=3.12
|
||||
|
||||
##############################################################################
|
||||
# Base layer to be used for building dependencies and the release images
|
||||
FROM alpine:${IMAGE_VERSION} as base
|
||||
FROM alpine:${IMAGE_VERSION} AS base
|
||||
|
||||
# Upgrade the system
|
||||
RUN apk update \
|
||||
|
|
@ -34,7 +34,7 @@ RUN apk add --no-cache \
|
|||
# BUILD Stages
|
||||
##############################################################################
|
||||
# BUILD: Base image shared by all build images
|
||||
FROM base as build
|
||||
FROM base AS build
|
||||
ARG PYTHON_VERSION
|
||||
|
||||
RUN apk add --no-cache \
|
||||
|
|
@ -61,26 +61,21 @@ RUN apk add --no-cache \
|
|||
RUN python${PYTHON_VERSION} -m venv venv-build
|
||||
RUN /venv-build/bin/python${PYTHON_VERSION} -m pip install --upgrade pip
|
||||
|
||||
RUN python${PYTHON_VERSION} -m venv venv-build
|
||||
RUN /venv-build/bin/python${PYTHON_VERSION} -m pip install --upgrade pip
|
||||
|
||||
RUN python${PYTHON_VERSION} -m venv --without-pip venv
|
||||
|
||||
COPY requirements.txt docker-requirements.txt webui-requirements.txt optional-requirements.txt ./
|
||||
COPY pyproject.toml docker-requirements.txt all-requirements.txt ./
|
||||
|
||||
##############################################################################
|
||||
# BUILD: Install the minimal image deps
|
||||
FROM build as buildMinimal
|
||||
FROM build AS buildminimal
|
||||
ARG PYTHON_VERSION
|
||||
|
||||
RUN /venv-build/bin/python${PYTHON_VERSION} -m pip install --target="/venv/lib/python${PYTHON_VERSION}/site-packages" \
|
||||
-r requirements.txt \
|
||||
-r docker-requirements.txt \
|
||||
-r webui-requirements.txt
|
||||
-r docker-requirements.txt
|
||||
|
||||
##############################################################################
|
||||
# BUILD: Install all the deps
|
||||
FROM build as buildFull
|
||||
FROM build AS buildfull
|
||||
ARG PYTHON_VERSION
|
||||
|
||||
# Required for optional dependency cassandra-driver
|
||||
|
|
@ -89,14 +84,13 @@ ARG CASS_DRIVER_NO_CYTHON=1
|
|||
ARG CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||
|
||||
RUN /venv-build/bin/python${PYTHON_VERSION} -m pip install --target="/venv/lib/python${PYTHON_VERSION}/site-packages" \
|
||||
-r requirements.txt \
|
||||
-r optional-requirements.txt
|
||||
-r all-requirements.txt
|
||||
|
||||
##############################################################################
|
||||
# RELEASE Stages
|
||||
##############################################################################
|
||||
# Base image shared by all releases
|
||||
FROM base as release
|
||||
FROM base AS release
|
||||
ARG PYTHON_VERSION
|
||||
|
||||
# Copy source code and config file
|
||||
|
|
@ -108,40 +102,48 @@ COPY docker-bin.sh /usr/local/bin/glances
|
|||
RUN chmod a+x /usr/local/bin/glances
|
||||
ENV PATH="/venv/bin:$PATH"
|
||||
|
||||
# Copy binary and update PATH
|
||||
COPY docker-bin.sh /usr/local/bin/glances
|
||||
RUN chmod a+x /usr/local/bin/glances
|
||||
ENV PATH="/venv/bin:$PATH"
|
||||
|
||||
# EXPOSE PORT (XMLRPC / WebUI)
|
||||
EXPOSE 61209 61208
|
||||
|
||||
# Add glances user
|
||||
# RUN addgroup -g 1000 glances && \
|
||||
# adduser -D -u 1000 -G glances glances && \
|
||||
# chown -R glances:glances /app
|
||||
|
||||
# Define default command.
|
||||
WORKDIR /app
|
||||
CMD /venv/bin/python3 -m glances $GLANCES_OPT
|
||||
ENV PYTHON_VERSION=${PYTHON_VERSION}
|
||||
CMD ["/bin/sh", "-c", "/venv/bin/python${PYTHON_VERSION} -m glances ${GLANCES_OPT}"]
|
||||
|
||||
################################################################################
|
||||
# RELEASE: minimal
|
||||
FROM release as minimal
|
||||
FROM release AS minimal
|
||||
|
||||
COPY --from=buildMinimal /venv /venv
|
||||
COPY --from=buildminimal /venv /venv
|
||||
|
||||
# USER glances
|
||||
|
||||
################################################################################
|
||||
# RELEASE: full
|
||||
FROM release as full
|
||||
FROM release AS full
|
||||
|
||||
RUN apk add --no-cache libzmq
|
||||
|
||||
COPY --from=buildFull /venv /venv
|
||||
COPY --from=buildfull /venv /venv
|
||||
|
||||
# USER glances
|
||||
|
||||
################################################################################
|
||||
# RELEASE: dev - to be compatible with CI
|
||||
FROM full as dev
|
||||
FROM full AS dev
|
||||
|
||||
# Add the specific logger configuration file for Docker dev
|
||||
# All logs will be forwarded to stdout
|
||||
COPY ./docker-files/docker-logger.json /app
|
||||
ENV LOG_CFG=/app/docker-logger.json
|
||||
|
||||
# USER glances
|
||||
|
||||
WORKDIR /app
|
||||
CMD /venv/bin/python3 -m glances $GLANCES_OPT
|
||||
ENV PYTHON_VERSION=${PYTHON_VERSION}
|
||||
CMD ["/bin/sh", "-c", "/venv/bin/python${PYTHON_VERSION} -m glances ${GLANCES_OPT}"]
|
||||
|
|
|
|||
|
|
@ -1,22 +1,24 @@
|
|||
{
|
||||
"version": 1,
|
||||
"disable_existing_loggers": "False",
|
||||
"root": {"level": "INFO", "handlers": ["console"]},
|
||||
"formatters": {
|
||||
"standard": {"format": "%(asctime)s -- %(levelname)s -- %(message)s"},
|
||||
"short": {"format": "%(levelname)s -- %(message)s"},
|
||||
"long": {"format": "%(asctime)s -- %(levelname)s -- %(message)s (%(funcName)s in %(filename)s)"},
|
||||
"free": {"format": "%(message)s"}
|
||||
},
|
||||
"handlers": {
|
||||
"console": {"class": "logging.StreamHandler", "formatter": "standard"}
|
||||
},
|
||||
"loggers": {
|
||||
"debug": {"handlers": ["console"], "level": "DEBUG"},
|
||||
"verbose": {"handlers": ["console"], "level": "INFO"},
|
||||
"standard": {"handlers": ["console"], "level": "INFO"},
|
||||
"requests": {"handlers": ["console"], "level": "ERROR"},
|
||||
"elasticsearch": {"handlers": ["console"], "level": "ERROR"},
|
||||
"elasticsearch.trace": {"handlers": ["console"], "level": "ERROR"}
|
||||
}
|
||||
}
|
||||
"version": 1,
|
||||
"disable_existing_loggers": "False",
|
||||
"root": { "level": "INFO", "handlers": ["console"] },
|
||||
"formatters": {
|
||||
"standard": { "format": "%(asctime)s -- %(levelname)s -- %(message)s" },
|
||||
"short": { "format": "%(levelname)s -- %(message)s" },
|
||||
"long": {
|
||||
"format": "%(asctime)s -- %(levelname)s -- %(message)s (%(funcName)s in %(filename)s)"
|
||||
},
|
||||
"free": { "format": "%(message)s" }
|
||||
},
|
||||
"handlers": {
|
||||
"console": { "class": "logging.StreamHandler", "formatter": "standard" }
|
||||
},
|
||||
"loggers": {
|
||||
"debug": { "handlers": ["console"], "level": "DEBUG" },
|
||||
"verbose": { "handlers": ["console"], "level": "INFO" },
|
||||
"standard": { "handlers": ["console"], "level": "INFO" },
|
||||
"requests": { "handlers": ["console"], "level": "ERROR" },
|
||||
"elasticsearch": { "handlers": ["console"], "level": "ERROR" },
|
||||
"elasticsearch.trace": { "handlers": ["console"], "level": "ERROR" }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ ARG PYTHON_VERSION=3.12
|
|||
|
||||
##############################################################################
|
||||
# Base layer to be used for building dependencies and the release images
|
||||
FROM ubuntu:${IMAGE_VERSION} as base
|
||||
FROM ubuntu:${IMAGE_VERSION} AS base
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update \
|
||||
|
|
@ -32,7 +32,7 @@ RUN apt-get update \
|
|||
# BUILD Stages
|
||||
##############################################################################
|
||||
# BUILD: Base image shared by all build images
|
||||
FROM base as build
|
||||
FROM base AS build
|
||||
ARG PYTHON_VERSION
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
|
|
@ -55,32 +55,29 @@ RUN apt-get clean \
|
|||
|
||||
RUN python3 -m venv --without-pip venv
|
||||
|
||||
COPY requirements.txt docker-requirements.txt webui-requirements.txt optional-requirements.txt ./
|
||||
COPY pyproject.toml docker-requirements.txt all-requirements.txt ./
|
||||
|
||||
##############################################################################
|
||||
# BUILD: Install the minimal image deps
|
||||
FROM build as buildMinimal
|
||||
FROM build AS buildminimal
|
||||
ARG PYTHON_VERSION
|
||||
|
||||
RUN python3 -m pip install --target="/venv/lib/python${PYTHON_VERSION}/site-packages" \
|
||||
-r requirements.txt \
|
||||
-r docker-requirements.txt \
|
||||
-r webui-requirements.txt
|
||||
-r docker-requirements.txt
|
||||
|
||||
##############################################################################
|
||||
# BUILD: Install all the deps
|
||||
FROM build as buildFull
|
||||
FROM build AS buildfull
|
||||
ARG PYTHON_VERSION
|
||||
|
||||
RUN python3 -m pip install --target="/venv/lib/python${PYTHON_VERSION}/site-packages" \
|
||||
-r requirements.txt \
|
||||
-r optional-requirements.txt
|
||||
-r all-requirements.txt
|
||||
|
||||
##############################################################################
|
||||
# RELEASE Stages
|
||||
##############################################################################
|
||||
# Base image shared by all releases
|
||||
FROM base as release
|
||||
FROM base AS release
|
||||
ARG PYTHON_VERSION
|
||||
|
||||
# Copy Glances source code and config file
|
||||
|
|
@ -92,28 +89,34 @@ COPY docker-bin.sh /usr/local/bin/glances
|
|||
RUN chmod a+x /usr/local/bin/glances
|
||||
ENV PATH="/venv/bin:$PATH"
|
||||
|
||||
# Copy binary and update PATH
|
||||
COPY docker-bin.sh /usr/local/bin/glances
|
||||
RUN chmod a+x /usr/local/bin/glances
|
||||
ENV PATH="/venv/bin:$PATH"
|
||||
|
||||
# EXPOSE PORT (XMLRPC / WebUI)
|
||||
EXPOSE 61209 61208
|
||||
|
||||
# Add glances user
|
||||
# NOTE: If used, the Glances Docker plugin do not work...
|
||||
# UID and GUID 1000 are already configured for the ubuntu user
|
||||
# Create anew one with UID and GUID 1001
|
||||
# RUN groupadd -g 1001 glances && \
|
||||
# useradd -u 1001 -g glances glances && \
|
||||
# chown -R glances:glances /app
|
||||
|
||||
# Define default command.
|
||||
WORKDIR /app
|
||||
CMD /venv/bin/python3 -m glances $GLANCES_OPT
|
||||
ENV PYTHON_VERSION=${PYTHON_VERSION}
|
||||
CMD ["/bin/sh", "-c", "/venv/bin/python${PYTHON_VERSION} -m glances ${GLANCES_OPT}"]
|
||||
|
||||
################################################################################
|
||||
# RELEASE: minimal
|
||||
FROM release as minimal
|
||||
FROM release AS minimal
|
||||
ARG PYTHON_VERSION
|
||||
|
||||
COPY --from=buildMinimal /venv /venv
|
||||
|
||||
# USER glances
|
||||
|
||||
################################################################################
|
||||
# RELEASE: full
|
||||
FROM release as full
|
||||
FROM release AS full
|
||||
ARG PYTHON_VERSION
|
||||
|
||||
RUN apt-get update \
|
||||
|
|
@ -121,11 +124,13 @@ RUN apt-get update \
|
|||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=buildFull /venv /venv
|
||||
COPY --from=buildfull /venv /venv
|
||||
|
||||
# USER glances
|
||||
|
||||
################################################################################
|
||||
# RELEASE: dev - to be compatible with CI
|
||||
FROM full as dev
|
||||
FROM full AS dev
|
||||
ARG PYTHON_VERSION
|
||||
|
||||
# Add the specific logger configuration file for Docker dev
|
||||
|
|
@ -133,5 +138,8 @@ ARG PYTHON_VERSION
|
|||
COPY ./docker-files/docker-logger.json /app
|
||||
ENV LOG_CFG=/app/docker-logger.json
|
||||
|
||||
# USER glances
|
||||
|
||||
WORKDIR /app
|
||||
CMD /venv/bin/python3 -m glances $GLANCES_OPT
|
||||
ENV PYTHON_VERSION=${PYTHON_VERSION}
|
||||
CMD ["/bin/sh", "-c", "/venv/bin/python${PYTHON_VERSION} -m glances ${GLANCES_OPT}"]
|
||||
|
|
|
|||
|
|
@ -1,10 +1,83 @@
|
|||
# install with base requirements file
|
||||
-r requirements.txt
|
||||
|
||||
docker>=6.1.1
|
||||
orjson # JSON Serialization speedup
|
||||
podman
|
||||
python-dateutil
|
||||
requests
|
||||
six
|
||||
urllib3
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv export --no-emit-workspace --no-hashes --no-group dev --extra containers --extra web --output-file docker-requirements.txt
|
||||
annotated-doc==0.0.4
|
||||
# via fastapi
|
||||
annotated-types==0.7.0
|
||||
# via pydantic
|
||||
anyio==4.12.0
|
||||
# via starlette
|
||||
certifi==2025.11.12
|
||||
# via requests
|
||||
charset-normalizer==3.4.4
|
||||
# via requests
|
||||
click==8.1.8
|
||||
# via uvicorn
|
||||
colorama==0.4.6 ; sys_platform == 'win32'
|
||||
# via click
|
||||
defusedxml==0.7.1
|
||||
# via glances
|
||||
docker==7.1.0
|
||||
# via glances
|
||||
exceptiongroup==1.2.2 ; python_full_version < '3.11'
|
||||
# via anyio
|
||||
fastapi==0.128.0
|
||||
# via glances
|
||||
h11==0.16.0
|
||||
# via uvicorn
|
||||
idna==3.11
|
||||
# via
|
||||
# anyio
|
||||
# requests
|
||||
jinja2==3.1.6
|
||||
# via glances
|
||||
markupsafe==3.0.3
|
||||
# via jinja2
|
||||
packaging==25.0
|
||||
# via glances
|
||||
podman==5.6.0
|
||||
# via glances
|
||||
psutil==7.2.1
|
||||
# via glances
|
||||
pydantic==2.12.5
|
||||
# via fastapi
|
||||
pydantic-core==2.41.5
|
||||
# via pydantic
|
||||
python-dateutil==2.9.0.post0
|
||||
# via glances
|
||||
pywin32==311 ; sys_platform == 'win32'
|
||||
# via docker
|
||||
requests==2.32.5
|
||||
# via
|
||||
# docker
|
||||
# glances
|
||||
# podman
|
||||
shtab==1.8.0 ; sys_platform != 'win32'
|
||||
# via glances
|
||||
six==1.17.0
|
||||
# via
|
||||
# glances
|
||||
# python-dateutil
|
||||
starlette==0.50.0
|
||||
# via fastapi
|
||||
tomli==2.0.2 ; python_full_version < '3.11'
|
||||
# via podman
|
||||
typing-extensions==4.15.0
|
||||
# via
|
||||
# anyio
|
||||
# fastapi
|
||||
# pydantic
|
||||
# pydantic-core
|
||||
# starlette
|
||||
# typing-inspection
|
||||
# uvicorn
|
||||
typing-inspection==0.4.2
|
||||
# via pydantic
|
||||
urllib3==2.6.2
|
||||
# via
|
||||
# docker
|
||||
# podman
|
||||
# requests
|
||||
uvicorn==0.40.0
|
||||
# via glances
|
||||
windows-curses==2.4.1 ; sys_platform == 'win32'
|
||||
# via glances
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = ../venv/bin/sphinx-build
|
||||
SPHINXBUILD = ../.venv/bin/sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 292 KiB After Width: | Height: | Size: 203 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
|
@ -3,7 +3,7 @@
|
|||
Actions
|
||||
=======
|
||||
|
||||
Glances can trigger actions on events.
|
||||
Glances can trigger actions on events for warning and critical thresholds.
|
||||
|
||||
By ``action``, we mean all shell command line. For example, if you want
|
||||
to execute the ``foo.py`` script if the last 5 minutes load are critical
|
||||
|
|
@ -18,6 +18,13 @@ then add the ``_action`` line to the Glances configuration file:
|
|||
All the stats are available in the command line through the use of the
|
||||
`Mustache`_ syntax. `Chevron`_ is required to render the mustache's template syntax.
|
||||
|
||||
Additionaly to the stats of the current plugin, the following variables are
|
||||
also available:
|
||||
- ``{{time}}``: current time in ISO format
|
||||
- ``{{critical}}``: critical threshold value
|
||||
- ``{{warning}}``: warning threshold value
|
||||
- ``{{careful}}``: careful threshold value
|
||||
|
||||
Another example would be to create a log file
|
||||
containing used vs total disk space if a space trigger warning is
|
||||
reached:
|
||||
|
|
@ -26,7 +33,7 @@ reached:
|
|||
|
||||
[fs]
|
||||
warning=70
|
||||
warning_action=echo {{mnt_point}} {{used}}/{{size}} > /tmp/fs.alert
|
||||
warning_action=echo "{{time}} {{mnt_point}} {{used}}/{{size}}" > /tmp/fs.alert
|
||||
|
||||
A last example would be to create a log file containing the total user disk
|
||||
space usage for a device and notify by email each time a space trigger
|
||||
|
|
@ -36,13 +43,11 @@ critical is reached:
|
|||
|
||||
[fs]
|
||||
critical=90
|
||||
critical_action_repeat=echo {{device_name}} {{percent}} > /tmp/fs.alert && python /etc/glances/actions.d/fs-critical.py
|
||||
|
||||
critical_action_repeat=echo "{{time}} {{device_name}} {{percent}}" > /tmp/fs.alert && python /etc/glances/actions.d/fs-critical.py
|
||||
|
||||
.. note::
|
||||
Use && as separator for multiple commands
|
||||
|
||||
|
||||
Within ``/etc/glances/actions.d/fs-critical.py``:
|
||||
|
||||
.. code-block:: python
|
||||
|
|
@ -63,7 +68,7 @@ Within ``/etc/glances/actions.d/fs-critical.py``:
|
|||
.. note::
|
||||
|
||||
You can use all the stats for the current plugin. See
|
||||
https://github.com/nicolargo/glances/wiki/The-Glances-RESTFULL-JSON-API
|
||||
https://github.com/nicolargo/glances/wiki/The-Glances-RESTFUL-JSON-API
|
||||
for the stats list.
|
||||
|
||||
It is also possible to repeat action until the end of the alert.
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ under the ``[containers]`` section:
|
|||
# Define the maximum containers size name (default is 20 chars)
|
||||
max_name_size=20
|
||||
# List of stats to disable (not display)
|
||||
# Following stats can be disabled: name,status,uptime,cpu,mem,diskio,networkio,command
|
||||
disable_stats=diskio,networkio
|
||||
# Following stats can be disabled: name,status,uptime,cpu,mem,diskio,networkio,ports,command
|
||||
disable_stats=command
|
||||
# Global containers' thresholds for CPU and MEM (in %)
|
||||
cpu_careful=50
|
||||
cpu_warning=70
|
||||
|
|
|
|||
|
|
@ -5,21 +5,16 @@ Disk I/O
|
|||
|
||||
.. image:: ../_static/diskio.png
|
||||
|
||||
Glances displays the disk I/O throughput. The unit is adapted
|
||||
dynamically.
|
||||
|
||||
You can display:
|
||||
|
||||
Glances displays the disk I/O throughput, count and mean latency:
|
||||
- bytes per second (default behavior / Bytes/s, KBytes/s, MBytes/s, etc)
|
||||
- requests per second (using --diskio-iops option or *B* hotkey)
|
||||
- mean latency (using --diskio-latency option or *L* hotkey)
|
||||
|
||||
There is no alert on this information.
|
||||
|
||||
It's possible to define:
|
||||
It's also possible to define:
|
||||
|
||||
- a list of disk to show (white list)
|
||||
- a list of disks to hide
|
||||
- aliases for disk name
|
||||
- aliases for disk name (use \ to espace special characters)
|
||||
|
||||
under the ``[diskio]`` section in the configuration file.
|
||||
|
||||
|
|
@ -42,13 +37,20 @@ Filtering is based on regular expression. Please be sure that your regular
|
|||
expression works as expected. You can use an online tool like `regex101`_ in
|
||||
order to test your regular expression.
|
||||
|
||||
It is also possible to define thesholds for bytes read and write per second:
|
||||
It is also possible to define thesholds for latency and bytes read and write per second:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[diskio]
|
||||
# Alias for sda1 and sdb1
|
||||
#alias=sda1:SystemDisk,sdb1:DataDisk
|
||||
# Default latency thresholds (in ms) (rx = read / tx = write)
|
||||
rx_latency_careful=10
|
||||
rx_latency_warning=20
|
||||
rx_latency_critical=50
|
||||
tx_latency_careful=10
|
||||
tx_latency_warning=20
|
||||
tx_latency_critical=50
|
||||
# Set thresholds (in bytes per second) for a given disk name (rx = read / tx = write)
|
||||
dm-0_rx_careful=4000000000
|
||||
dm-0_rx_warning=5000000000
|
||||
|
|
|
|||
|
|
@ -35,6 +35,11 @@ system:
|
|||
[fs]
|
||||
allow=shm
|
||||
|
||||
With the above configuration key, it is also possible to monitor NFS
|
||||
mount points (allow=nfs). Be aware that this can slow down the
|
||||
performance of the plugin if the NFS server is not reachable. In this
|
||||
case, the plugin will wait for a 2 seconds timeout.
|
||||
|
||||
Also, you can hide mount points using regular expressions.
|
||||
|
||||
To hide all mount points starting with /boot and /snap:
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ Legend:
|
|||
hddtemp
|
||||
ps
|
||||
containers
|
||||
vms
|
||||
amps
|
||||
events
|
||||
actions
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ Stats description:
|
|||
is in RAM.
|
||||
- **inactive**: (UNIX): memory that is marked as not used.
|
||||
- **buffers**: (Linux, BSD): cache for things like file system metadata.
|
||||
- **cached**: (Linux, BSD): cache for various things.
|
||||
- **cached**: (Linux, BSD): cache for various things (including ZFS cache).
|
||||
|
||||
Additional stats available in through the API:
|
||||
|
||||
|
|
@ -41,6 +41,10 @@ Additional stats available in through the API:
|
|||
- **shared**: (BSD): memory that may be simultaneously accessed by multiple
|
||||
processes.
|
||||
|
||||
It is possible to display the available memory instead of the used memory
|
||||
by setting the ``available`` option to ``True`` in the configuration file
|
||||
under the ``[mem]`` section.
|
||||
|
||||
A character is also displayed just after the MEM header and shows the
|
||||
trend value:
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Additionally, you can define:
|
|||
- automatically hide interfaces not up
|
||||
- automatically hide interfaces without IP address
|
||||
- per-interface limit values
|
||||
- aliases for interface name
|
||||
- aliases for interface name (use \ to espace special characters)
|
||||
|
||||
The configuration should be done in the ``[network]`` section of the
|
||||
Glances configuration file.
|
||||
|
|
@ -72,7 +72,7 @@ can also be used to set a threshold higher than zero.
|
|||
|
||||
.. code-block:: ini
|
||||
|
||||
[diskio]
|
||||
[network]
|
||||
hide_zero=True
|
||||
hide_threshold_bytes=0
|
||||
|
||||
|
|
|
|||
|
|
@ -102,7 +102,8 @@ Columns display
|
|||
``CPU%`` % of CPU used by the process
|
||||
|
||||
If Irix/Solaris mode is off ('0' key), the value
|
||||
is divided by logical core number
|
||||
is divided by logical core number (the column
|
||||
name became CPUi)
|
||||
``MEM%`` % of MEM used by the process (RES divided by
|
||||
the total RAM you have)
|
||||
``VIRT`` Virtual Memory Size
|
||||
|
|
@ -148,12 +149,24 @@ Columns display
|
|||
pressing on the ``'/'`` key
|
||||
========================= ==============================================
|
||||
|
||||
Disable display of virtual memory
|
||||
---------------------------------
|
||||
|
||||
It's possible to disable the display of the VIRT column (virtual memory) by adding the
|
||||
``disable_virtual_memory=True`` option in the ``[processlist]`` section of the configuration
|
||||
file (glances.conf):
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[processlist]
|
||||
disable_virtual_memory=True
|
||||
|
||||
Process filtering
|
||||
-----------------
|
||||
|
||||
It's possible to filter the processes list using the ``ENTER`` key.
|
||||
|
||||
Filter syntax is the following (examples):
|
||||
Glances filter syntax is the following (examples):
|
||||
|
||||
- ``python``: Filter processes name or command line starting with
|
||||
*python* (regexp)
|
||||
|
|
@ -162,6 +175,25 @@ Filter syntax is the following (examples):
|
|||
- ``username:nicolargo``: Processes of nicolargo user (key:regexp)
|
||||
- ``cmdline:\/usr\/bin.*``: Processes starting by */usr/bin*
|
||||
|
||||
Process focus
|
||||
-------------
|
||||
|
||||
It's also possible to select a processes list to focus on.
|
||||
|
||||
A list of Glances filters (see upper) can be define from the command line:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
glances --process-focus .*python.*,.*firefox.*
|
||||
|
||||
|
||||
or the glances.conf file:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[processlist]
|
||||
focus=.*python.*,.*firefox.*
|
||||
|
||||
Extended info
|
||||
-------------
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ thresholds (default behavor).
|
|||
#temperature_core_careful=45
|
||||
#temperature_core_warning=65
|
||||
#temperature_core_critical=80
|
||||
#alias=temp1:Motherboard 0,core 0:CPU Core 0
|
||||
|
||||
.. note 1::
|
||||
The support for multiple batteries is only available if
|
||||
|
|
|
|||
|
|
@ -23,25 +23,32 @@ How to read the information:
|
|||
.. warning::
|
||||
This plugin needs administrator rights. Please run Glances as root/admin.
|
||||
|
||||
Also, you can hide sensors using regular expressions.
|
||||
Also, you can hide driver using regular expressions.
|
||||
|
||||
To hide sensors you should use the hide option:
|
||||
To hide device you should use the hide option:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[smart]
|
||||
hide=.*Hide_this_driver.*
|
||||
hide=.*Hide_this_device.*
|
||||
|
||||
It is also possible to configure a white list of devices to display.
|
||||
Example to only show .*Drive_Temperature.* sensors:
|
||||
Example to show only the specified drive:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[smart]
|
||||
show=.*Drive_Temperature.*
|
||||
show=.*Show_this_device.*
|
||||
|
||||
Filtering is based on regular expression. Please be sure that your regular
|
||||
expression works as expected. You can use an online tool like `regex101`_ in
|
||||
order to test your regular expression.
|
||||
|
||||
.. _regex101: https://regex101.com/
|
||||
.. _regex101: https://regex101.com/
|
||||
|
||||
You can also hide attributes, for example Self-tests, Errors, etc. Use a comma separated list.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[smart]
|
||||
hide_attributes=attribute_name1,attribute_name2
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
.. _vms:
|
||||
|
||||
VMs
|
||||
===
|
||||
|
||||
Glances ``vms`` plugin is designed to display stats about VMs ran on the host.
|
||||
|
||||
It's actually support two engines: `Multipass` and `Virsh`.
|
||||
|
||||
No Python dependency is needed but Multipass and Virsh binary should be available:
|
||||
- multipass should be executable from /snap/bin/multipass
|
||||
- virsh should be executable from /usr/bin/virsh
|
||||
|
||||
Note: CPU information is not availble for Multipass VM. Load is not available for Virsh VM.
|
||||
|
||||
Configuration file options:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[vms]
|
||||
disable=True
|
||||
# Define the maximum VMs size name (default is 20 chars)
|
||||
max_name_size=20
|
||||
# By default, Glances only display running VMs with states:
|
||||
# 'Running', 'Paused', 'Starting' or 'Restarting'
|
||||
# Set the following key to True to display all VMs regarding their states
|
||||
all=False
|
||||
|
||||
You can use all the variables ({{foo}}) available in the containers plugin.
|
||||
|
||||
Filtering (for hide or show) is based on regular expression. Please be sure that your regular
|
||||
expression works as expected. You can use an online tool like `regex101`_ in
|
||||
order to test your regular expression.
|
||||
|
||||
.. _Multipass: https://canonical.com/multipass
|
||||
.. _Virsh: https://www.libvirt.org/manpages/virsh.html
|
||||
|
|
@ -1,2 +1,5 @@
|
|||
#!/bin/sh
|
||||
|
||||
make clean
|
||||
make html
|
||||
LC_ALL=C make man
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ Command-Line Options
|
|||
|
||||
enable debug mode
|
||||
|
||||
.. option:: --print-completion
|
||||
|
||||
generate shell tab completion scripts for Glances CLI
|
||||
|
||||
.. option:: -C CONF_FILE, --config CONF_FILE
|
||||
|
||||
path to the configuration file
|
||||
|
|
@ -348,7 +352,7 @@ The following commands (key pressed) are supported while in Glances:
|
|||
Show/hide RAID plugin
|
||||
|
||||
``s``
|
||||
Show/hide sensors stats
|
||||
Show/hide sensors plugin
|
||||
|
||||
``S``
|
||||
Enable/disable spark lines
|
||||
|
|
@ -365,6 +369,9 @@ The following commands (key pressed) are supported while in Glances:
|
|||
``U``
|
||||
View cumulative network I/O
|
||||
|
||||
``V``
|
||||
Show/hide VMS plugin
|
||||
|
||||
``w``
|
||||
Delete finished warning log messages
|
||||
|
||||
|
|
@ -409,12 +416,18 @@ The following commands (key pressed) are supported while in Glances:
|
|||
``F5`` or ``CTRL-R``
|
||||
Refresh user interface
|
||||
|
||||
``LEFT``
|
||||
``SHIFT-LEFT``
|
||||
Navigation left through the process sort
|
||||
|
||||
``RIGHT``
|
||||
``SHIFT-RIGHT``
|
||||
Navigation right through the process sort
|
||||
|
||||
``LEFT``
|
||||
Navigation left through the process name
|
||||
|
||||
``RIGHT``
|
||||
Navigation right through the process name
|
||||
|
||||
``UP``
|
||||
Up in the processes list
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ than a second one concerning the user interface:
|
|||
# You can download it in a specific folder
|
||||
# thanks to https://github.com/nicolargo/glances/issues/2021
|
||||
# then configure this folder with the webui_root_path key
|
||||
# Default is folder where glances_restfull_api.py is hosted
|
||||
# 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.
|
||||
|
|
@ -95,6 +95,10 @@ than a second one concerning the user interface:
|
|||
# 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=./glances.local+3-key.pem
|
||||
#ssl_keyfile_password=kfp
|
||||
#ssl_certfile=./glances.local+3.pem
|
||||
|
||||
Each plugin, export module, and application monitoring process (AMP) can
|
||||
have a section. Below is an example for the CPU plugin:
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ and make it visible to your container by adding it to ``docker-compose.yml`` as
|
|||
image: nicolargo/glances:latest
|
||||
restart: always
|
||||
environment:
|
||||
- GLANCES_OPT="-w --password"
|
||||
- "GLANCES_OPT=-w --password"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
# Uncomment the below line if you want glances to display host OS detail instead of container's
|
||||
|
|
|
|||
45
docs/faq.rst
|
|
@ -3,8 +3,51 @@
|
|||
F.A.Q
|
||||
=====
|
||||
|
||||
*Any encoding issue ?*
|
||||
Any encoding issue ?
|
||||
--------------------
|
||||
|
||||
Try to run Glances with the following command line:
|
||||
|
||||
LANG=en_US.UTF-8 LC_ALL= glances
|
||||
|
||||
Container memory stats not displayed ?
|
||||
--------------------------------------
|
||||
|
||||
On ARM64, Docker needs to be configured to allow access to the memory stats.
|
||||
|
||||
Edit the /boot/firmware/cmdline.txt and add the following configuration key:
|
||||
|
||||
cgroup_enable=memory
|
||||
|
||||
Netifaces issue ?
|
||||
-----------------
|
||||
|
||||
Previously, Glances uses Netifaces to get network interfaces information.
|
||||
|
||||
Now, Glances uses Netifaces2.
|
||||
|
||||
Please uninstall Netifaces and install Netifaces2 instead.
|
||||
|
||||
Extra note: Glances 4.5 or higher do not use Netifaces/Netifaces2 anymore.
|
||||
|
||||
On Debian/Ubuntu Operating Systems, Webserver display a blank screen ?
|
||||
----------------------------------------------------------------------
|
||||
|
||||
For some reason, the Glances Debian/Ubuntu packages do not include the Web UI static files.
|
||||
|
||||
Please read: https://github.com/nicolargo/glances/issues/2021 for workaround and more information.
|
||||
|
||||
Glances said that my computer has no free memory, is it normal ?
|
||||
----------------------------------------------------------------
|
||||
|
||||
On Linux, Glances shows by default the free memory.
|
||||
|
||||
Free memory can be low, it's a "normal" behavior because Linux uses free memory for disk caching
|
||||
to improve performance. More information can be found here: https://linuxatemyram.com/.
|
||||
|
||||
If you want to display the "available" memory instead of the "free" memory, you can uses the
|
||||
the following configuration key in the Glances configuration file:
|
||||
|
||||
[mem]
|
||||
# Display available memory instead of used memory
|
||||
available=True
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
.. _fetch:
|
||||
|
||||
Fetch
|
||||
=====
|
||||
|
||||
The fetch mode is used to get and share a quick look of a machine using the
|
||||
``fetch`` option. In this mode, current stats are displayed on the console in
|
||||
a fancy way.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --fetch
|
||||
|
||||
Results look like this:
|
||||
|
||||
.. image:: _static/screenshot-fetch.png
|
||||
|
||||
It is also possible to use a custom template with the ``--fetch-template </path/to/template.jinja>`` option.
|
||||
|
||||
Some examples are provided in the ``conf/fetch-templates/`` directory. Please feel free to
|
||||
customize them or create your own template (contribution via PR are welcome).
|
||||
|
||||
The format of the template is based on the Jinja2 templating engine and can use all the stats
|
||||
available in Glances through the ``gl`` variable (an instance of the :ref:`Glances Python API<api>`).
|
||||
|
||||
For example, the default template is define as:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✨ {{ gl.system['hostname'] }}{{ ' - ' + gl.ip['address'] if gl.ip['address'] else '' }}
|
||||
⚙️ {{ gl.system['hr_name'] }} | Uptime: {{ gl.uptime }}
|
||||
|
||||
💡 LOAD {{ '%0.2f'| format(gl.load['min1']) }} {{ '%0.2f'| format(gl.load['min5']) }} {{ '%0.2f'| format(gl.load['min15']) }}
|
||||
⚡ CPU {{ gl.bar(gl.cpu['total']) }} {{ gl.cpu['total'] }}% of {{ gl.core['log'] }} cores
|
||||
🧠 MEM {{ gl.bar(gl.mem['percent']) }} {{ gl.mem['percent'] }}% ({{ gl.auto_unit(gl.mem['used']) }} {{ gl.auto_unit(gl.mem['total']) }})
|
||||
{% for fs in gl.fs.keys() %}💾 {% if loop.index == 1 %}DISK{% else %} {% endif %} {{ gl.bar(gl.fs[fs]['percent']) }} {{ gl.fs[fs]['percent'] }}% ({{ gl.auto_unit(gl.fs[fs]['used']) }} {{ gl.auto_unit(gl.fs[fs]['size']) }}) for {{ fs }}
|
||||
{% endfor %}{% for net in gl.network.keys() %}📡 {% if loop.index == 1 %}NET{% else %} {% endif %} ↓ {{ gl.auto_unit(gl.network[net]['bytes_recv_rate_per_sec']) }}b/s ↑ {{ gl.auto_unit(gl.network[net]['bytes_sent_rate_per_sec']) }}b/s for {{ net }}
|
||||
{% endfor %}
|
||||
🔥 TOP PROCESS by CPU
|
||||
{% for process in gl.top_process() %}{{ loop.index }}️⃣ {{ process['name'][:20] }}{{ ' ' * (20 - process['name'][:20] | length) }} ⚡ {{ process['cpu_percent'] }}% CPU{{ ' ' * (8 - (gl.auto_unit(process['cpu_percent']) | length)) }} 🧠 {{ gl.auto_unit(process['memory_info']['rss']) }}B MEM
|
||||
{% endfor %}
|
||||
🔥 TOP PROCESS by MEM
|
||||
{% for process in gl.top_process(sorted_by='memory_percent', sorted_by_secondary='cpu_percent') %}{{ loop.index }}️⃣ {{ process['name'][:20] }}{{ ' ' * (20 - process['name'][:20] | length) }} 🧠 {{ gl.auto_unit(process['memory_info']['rss']) }}B MEM{{ ' ' * (7 - (gl.auto_unit(process['memory_info']['rss']) | length)) }} ⚡ {{ process['cpu_percent'] }}% CPU
|
||||
{% endfor %}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
.. _duckdb:
|
||||
|
||||
DuckDB
|
||||
===========
|
||||
|
||||
DuckDB is an in-process SQL OLAP database management system.
|
||||
|
||||
You can export statistics to a ``DuckDB`` server.
|
||||
|
||||
The connection should be defined in the Glances configuration file as
|
||||
following:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[duckdb]
|
||||
# database defines where data are stored, can be one of:
|
||||
# /path/to/glances.db (see https://duckdb.org/docs/stable/clients/python/dbapi#file-based-connection)
|
||||
# :memory:glances (see https://duckdb.org/docs/stable/clients/python/dbapi#in-memory-connection)
|
||||
# Or anyone else supported by the API (see https://duckdb.org/docs/stable/clients/python/dbapi)
|
||||
database=/tmp/glances.db
|
||||
|
||||
and run Glances with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --export duckdb
|
||||
|
||||
Data model
|
||||
-----------
|
||||
|
||||
The data model is composed of one table per Glances plugin.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> import duckdb
|
||||
>>> db = duckdb.connect(database='/tmp/glances.db', read_only=True)
|
||||
|
||||
>>> db.sql("SELECT * from cpu")
|
||||
┌─────────────────────┬─────────────────┬────────┬────────┬────────┬───┬────────────────────┬─────────────────────┬──────────────────────┬──────────────────────┬──────────────────────┐
|
||||
│ time │ hostname_id │ total │ user │ nice │ … │ cpu_iowait_warning │ cpu_iowait_critical │ cpu_ctx_switches_c… │ cpu_ctx_switches_w… │ cpu_ctx_switches_c… │
|
||||
│ time with time zone │ varchar │ double │ double │ double │ │ double │ double │ double │ double │ double │
|
||||
├─────────────────────┼─────────────────┼────────┼────────┼────────┼───┼────────────────────┼─────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┤
|
||||
│ 11:50:25+00 │ nicolargo-xps15 │ 8.0 │ 5.6 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:50:27+00 │ nicolargo-xps15 │ 4.3 │ 3.2 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:50:29+00 │ nicolargo-xps15 │ 4.3 │ 3.2 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:50:31+00 │ nicolargo-xps15 │ 14.9 │ 15.7 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:50:33+00 │ nicolargo-xps15 │ 14.9 │ 15.7 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:50:35+00 │ nicolargo-xps15 │ 8.2 │ 7.8 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:50:37+00 │ nicolargo-xps15 │ 8.2 │ 7.8 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:50:39+00 │ nicolargo-xps15 │ 12.7 │ 10.3 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:50:41+00 │ nicolargo-xps15 │ 12.7 │ 10.3 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:50:43+00 │ nicolargo-xps15 │ 12.2 │ 10.3 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
|
||||
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
|
||||
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
|
||||
│ 11:51:29+00 │ nicolargo-xps15 │ 10.1 │ 7.4 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:51:32+00 │ nicolargo-xps15 │ 10.1 │ 7.4 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:51:34+00 │ nicolargo-xps15 │ 6.6 │ 4.9 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:51:36+00 │ nicolargo-xps15 │ 6.6 │ 4.9 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:51:38+00 │ nicolargo-xps15 │ 9.9 │ 7.5 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:51:40+00 │ nicolargo-xps15 │ 9.9 │ 7.5 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:51:42+00 │ nicolargo-xps15 │ 4.0 │ 3.1 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:51:44+00 │ nicolargo-xps15 │ 4.0 │ 3.1 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:51:46+00 │ nicolargo-xps15 │ 11.1 │ 8.8 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
│ 11:51:48+00 │ nicolargo-xps15 │ 11.1 │ 8.8 │ 0.0 │ … │ 5.625 │ 6.25 │ 640000.0 │ 720000.0 │ 800000.0 │
|
||||
├─────────────────────┴─────────────────┴────────┴────────┴────────┴───┴────────────────────┴─────────────────────┴──────────────────────┴──────────────────────┴──────────────────────┤
|
||||
│ 41 rows (20 shown) 47 columns (10 shown) │
|
||||
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
>>> db.sql("SELECT * from cpu").fetchall()[0]
|
||||
(datetime.time(11, 50, 25, tzinfo=datetime.timezone.utc), 'nicolargo-xps15', 8.0, 5.6, 0.0, 2.3, 91.9, 0.1, 0.0, 0.0, 0.0, 0, 0, 0, 0, 16, 2.4103684425354004, 90724823, 0, 63323797, 0, 30704572, 0, 0, 0, 1200.0, 65.0, 75.0, 85.0, True, 50.0, 70.0, 90.0, True, 50.0, 70.0, 90.0, True, 50.0, 70.0, 90.0, 5.0, 5.625, 6.25, 640000.0, 720000.0, 800000.0)
|
||||
|
||||
|
||||
>>> db.sql("SELECT * from network")
|
||||
┌─────────────────────┬─────────────────┬────────────────┬────────────┬────────────┬───┬─────────────────────┬────────────────┬────────────────────┬────────────────────┬───────────────────┐
|
||||
│ time │ hostname_id │ key_id │ bytes_sent │ bytes_recv │ … │ network_tx_critical │ network_hide │ network_hide_no_up │ network_hide_no_ip │ network_hide_zero │
|
||||
│ time with time zone │ varchar │ varchar │ int64 │ int64 │ │ double │ varchar │ boolean │ boolean │ boolean │
|
||||
├─────────────────────┼─────────────────┼────────────────┼────────────┼────────────┼───┼─────────────────────┼────────────────┼────────────────────┼────────────────────┼───────────────────┤
|
||||
│ 11:50:25+00 │ nicolargo-xps15 │ interface_name │ 407761 │ 32730 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:50:27+00 │ nicolargo-xps15 │ interface_name │ 2877 │ 4857 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:50:29+00 │ nicolargo-xps15 │ interface_name │ 44504 │ 32555 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:50:31+00 │ nicolargo-xps15 │ interface_name │ 1092285 │ 48600 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:50:33+00 │ nicolargo-xps15 │ interface_name │ 150119 │ 43805 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:50:35+00 │ nicolargo-xps15 │ interface_name │ 34424 │ 14825 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:50:37+00 │ nicolargo-xps15 │ interface_name │ 19382 │ 33614 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:50:39+00 │ nicolargo-xps15 │ interface_name │ 53060 │ 39780 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:50:41+00 │ nicolargo-xps15 │ interface_name │ 371914 │ 78626 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:50:43+00 │ nicolargo-xps15 │ interface_name │ 82356 │ 60612 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
|
||||
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
|
||||
│ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │ · │
|
||||
│ 11:51:29+00 │ nicolargo-xps15 │ interface_name │ 3766 │ 9977 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:51:32+00 │ nicolargo-xps15 │ interface_name │ 188036 │ 18668 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:51:34+00 │ nicolargo-xps15 │ interface_name │ 543 │ 2451 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:51:36+00 │ nicolargo-xps15 │ interface_name │ 8247 │ 7275 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:51:38+00 │ nicolargo-xps15 │ interface_name │ 7252 │ 986 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:51:40+00 │ nicolargo-xps15 │ interface_name │ 172 │ 132 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:51:42+00 │ nicolargo-xps15 │ interface_name │ 8080 │ 6640 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:51:44+00 │ nicolargo-xps15 │ interface_name │ 19660 │ 17830 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:51:46+00 │ nicolargo-xps15 │ interface_name │ 1007030 │ 84170 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
│ 11:51:48+00 │ nicolargo-xps15 │ interface_name │ 128947 │ 18087 │ … │ 90.0 │ [docker.*, lo] │ true │ true │ true │
|
||||
├─────────────────────┴─────────────────┴────────────────┴────────────┴────────────┴───┴─────────────────────┴────────────────┴────────────────────┴────────────────────┴───────────────────┤
|
||||
│ 41 rows (20 shown) 28 columns (10 shown) │
|
||||
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
||||
.. _duckdb: https://duckdb.org/
|
||||
|
||||
|
|
@ -3,8 +3,24 @@
|
|||
Gateway To Other Services
|
||||
=========================
|
||||
|
||||
Glances can exports stats to a CSV file. Also, it can act as a gateway
|
||||
to providing stats to multiple services (see list below).
|
||||
Glances can exports stats in files or to other services like databases, message queues, etc.
|
||||
|
||||
Each exporter has its own configuration options, which can be set in the Glances
|
||||
configuration file (`glances.conf`).
|
||||
|
||||
A common options section is also available:
|
||||
|
||||
is the `exclude_fields` option, which allows you to specify
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[export]
|
||||
# Common section for all exporters
|
||||
# Do not export following fields (comma separated list of regex)
|
||||
exclude_fields=.*_critical,.*_careful,.*_warning,.*\.key$
|
||||
|
||||
|
||||
This section describes the available exporters and how to configure them:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
|
@ -14,15 +30,18 @@ to providing stats to multiple services (see list below).
|
|||
couchdb
|
||||
elastic
|
||||
graph
|
||||
graphite
|
||||
influxdb
|
||||
json
|
||||
kafka
|
||||
mqtt
|
||||
mongodb
|
||||
nats
|
||||
opentsdb
|
||||
prometheus
|
||||
rabbitmq
|
||||
restful
|
||||
riemann
|
||||
statsd
|
||||
timescaledb
|
||||
zeromq
|
||||
|
|
|
|||
|
|
@ -17,19 +17,19 @@ Glances InfluxDB data model:
|
|||
+---------------+-----------------------+-----------------------+
|
||||
| Measurement | Fields | Tags |
|
||||
+===============+=======================+=======================+
|
||||
| cpu | user | hostname |
|
||||
| cpu | user | hostname |
|
||||
| | system | |
|
||||
| | iowait... | |
|
||||
+---------------+-----------------------+-----------------------+
|
||||
| network | read_bytes | hostname |
|
||||
| | write_bytes | disk_name |
|
||||
| | time_since_update... | |
|
||||
| | | |
|
||||
| | | |
|
||||
+---------------+-----------------------+-----------------------+
|
||||
| diskio | rx | hostname |
|
||||
| | tx | interface_name |
|
||||
| | time_since_update... | |
|
||||
| | | |
|
||||
| | | |
|
||||
+---------------+-----------------------+-----------------------+
|
||||
| docker | cpu_percent | hostname |
|
||||
| | memory_usage... | name |
|
||||
|
|
@ -78,7 +78,7 @@ configuration file (no limit on columns number).
|
|||
Note: if you want to use SSL, please set 'protocol=https'.
|
||||
|
||||
|
||||
InfluxDB v2 (from InfluxDB v1.8.x/Flux and InfluxDB v2.x)
|
||||
InfluxDB v2 (from InfluxDB v1.8.x/Flux and InfluxDB <v3.x)
|
||||
---------------------------------------------------------
|
||||
|
||||
Note: The InfluxDB v2 client (https://pypi.org/project/influxdb-client/)
|
||||
|
|
@ -90,12 +90,14 @@ following:
|
|||
.. code-block:: ini
|
||||
|
||||
[influxdb2]
|
||||
# Configuration for the --export influxdb2 option
|
||||
# https://influxdb.com/
|
||||
host=localhost
|
||||
port=8086
|
||||
protocol=http
|
||||
org=nicolargo
|
||||
bucket=glances
|
||||
token=EjFUTWe8U-MIseEAkaVIgVnej_TrnbdvEcRkaB1imstW7gapSqy6_6-8XD-yd51V0zUUpDy-kAdVD1purDLuxA==
|
||||
token=PUT_YOUR_INFLUXDB2_TOKEN_HERE
|
||||
# Set the interval between two exports (in seconds)
|
||||
# If the interval is set to 0, the Glances refresh time is used (default behavor)
|
||||
#interval=0
|
||||
|
|
@ -107,7 +109,7 @@ following:
|
|||
#prefix=foo
|
||||
# Following tags will be added for all measurements
|
||||
# You can also use dynamic values.
|
||||
# Note: hostname is always added as a tag
|
||||
# Note: hostname and name (for process) are always added as a tag
|
||||
#tags=foo:bar,spam:eggs,domain:`domainname`
|
||||
|
||||
and run Glances with:
|
||||
|
|
@ -118,6 +120,46 @@ and run Glances with:
|
|||
|
||||
Note: if you want to use SSL, please set 'protocol=https'.
|
||||
|
||||
InfluxDB v3 (for InfluxDB 3.x)
|
||||
------------------------------
|
||||
|
||||
Note: The InfluxDB v3 client (https://pypi.org/project/influxdb3-python/)
|
||||
is only available for Python 3.8 or higher.
|
||||
|
||||
The connection should be defined in the Glances configuration file as
|
||||
following:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[influxdb3]
|
||||
# Configuration for the --export influxdb3 option
|
||||
# https://influxdb.com/
|
||||
host=http://localhost:8181
|
||||
org=nicolargo
|
||||
database=glances
|
||||
token=PUT_YOUR_INFLUXDB3_TOKEN_HERE
|
||||
# Set the interval between two exports (in seconds)
|
||||
# If the interval is set to 0, the Glances refresh time is used (default behavor)
|
||||
#interval=0
|
||||
# Prefix will be added for all measurement name
|
||||
# Ex: prefix=foo
|
||||
# => foo.cpu
|
||||
# => foo.mem
|
||||
# You can also use dynamic values
|
||||
#prefix=foo
|
||||
# Following tags will be added for all measurements
|
||||
# You can also use dynamic values.
|
||||
# Note: hostname and name (for process) are always added as a tag
|
||||
#tags=foo:bar,spam:eggs,domain:`domainname`
|
||||
|
||||
and run Glances with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --export influxdb3
|
||||
|
||||
Note: if you want to use SSL, please set host with 'https' scheme instead of 'http'.
|
||||
|
||||
Grafana
|
||||
-------
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
.. _nats:
|
||||
|
||||
NATS
|
||||
====
|
||||
|
||||
NATS is a message broker.
|
||||
|
||||
You can export statistics to a ``NATS`` server.
|
||||
|
||||
The connection should be defined in the Glances configuration file as
|
||||
following:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[nats]
|
||||
host=nats://localhost:4222
|
||||
prefix=glances
|
||||
|
||||
and run Glances with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --export nats
|
||||
|
||||
Data model
|
||||
-----------
|
||||
|
||||
Glances stats are published as JSON messagesto the following subjects:
|
||||
|
||||
<prefix>.<plugin>
|
||||
|
||||
Example:
|
||||
|
||||
CPU stats are published to glances.cpu
|
||||
|
||||
So a simple Python client will subscribe to this subject with:
|
||||
|
||||
|
||||
import asyncio
|
||||
|
||||
import nats
|
||||
|
||||
|
||||
async def main():
|
||||
nc = nats.NATS()
|
||||
|
||||
await nc.connect(servers=["nats://localhost:4222"])
|
||||
|
||||
future = asyncio.Future()
|
||||
|
||||
async def cb(msg):
|
||||
nonlocal future
|
||||
future.set_result(msg)
|
||||
|
||||
await nc.subscribe("glances.cpu", cb=cb)
|
||||
|
||||
# Wait for message to come in
|
||||
print("Waiting (max 30 seconds) for a message on 'glances' subject...")
|
||||
msg = await asyncio.wait_for(future, 30)
|
||||
print(msg.subject, msg.data)
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
|
||||
To subscribe to all Glannces stats use wildcard:
|
||||
|
||||
await nc.subscribe("glances.*", cb=cb)
|
||||
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
.. _timescale:
|
||||
|
||||
TimeScaleDB
|
||||
===========
|
||||
|
||||
TimescaleDB is a time-series database built on top of PostgreSQL.
|
||||
|
||||
You can export statistics to a ``TimescaleDB`` server.
|
||||
|
||||
The connection should be defined in the Glances configuration file as
|
||||
following:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[timescaledb]
|
||||
host=localhost
|
||||
port=5432
|
||||
db=glances
|
||||
user=postgres
|
||||
password=password
|
||||
|
||||
and run Glances with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --export timescaledb
|
||||
|
||||
Data model
|
||||
-----------
|
||||
|
||||
Each plugin will create an `hypertable`_ in the TimescaleDB database.
|
||||
|
||||
Tables are partitionned by time (using the ``time`` column).
|
||||
|
||||
Tables are segmented by hostname (in order to have multiple host stored in the Glances database).
|
||||
|
||||
For plugin with a key (example network where the key is the interface name), the key will
|
||||
be added as a column in the table (named key_id) and added to the timescaledb.segmentby option.
|
||||
|
||||
Current limitations
|
||||
-------------------
|
||||
|
||||
Sensors, Fs and DiskIO plugins are not supported by the TimescaleDB exporter.
|
||||
|
||||
In the cpu plugin, the user field is exported as user_cpu (user_percpu in the percpu plugin)
|
||||
because user is a reserved keyword in PostgreSQL.
|
||||
|
||||
.. _hypertable: https://docs.tigerdata.com/use-timescale/latest/hypertables/
|
||||
|
|
@ -11,12 +11,11 @@ information depending on the terminal size.
|
|||
It can also work in client/server mode. Remote monitoring can be
|
||||
done via terminal, Web interface, or API (XMLRPC and RESTful).
|
||||
|
||||
Glances is written in Python and uses the `psutil`_ library to get
|
||||
information from your system.
|
||||
Stats can also be exported to :ref:`files or external databases<gw>`.
|
||||
|
||||
Stats can also be exported to external time/value databases.
|
||||
|
||||
.. _psutil: https://github.com/giampaolo/psutil
|
||||
It is also possible to use it in your own Python scripts thanks to
|
||||
the :ref:`Glances API<api>` or in any other application through
|
||||
the :ref:`RESTful API<api_restful>`.
|
||||
|
||||
Table of Contents
|
||||
=================
|
||||
|
|
@ -30,7 +29,11 @@ Table of Contents
|
|||
config
|
||||
aoa/index
|
||||
gw/index
|
||||
api
|
||||
api/python
|
||||
api/restful
|
||||
docker
|
||||
faq
|
||||
support
|
||||
|
||||
.. _psutil: https://github.com/giampaolo/psutil
|
||||
|
||||
|
|
|
|||
|
|
@ -34,5 +34,19 @@ To upgrade Glances and all its dependencies to the latest versions:
|
|||
|
||||
For additional installation methods, read the official `README`_ file.
|
||||
|
||||
Shell tab completion
|
||||
====================
|
||||
|
||||
Glances 4.3.2 and higher includes shell tab autocompletion thanks to the --print-completion option.
|
||||
|
||||
For example, on a Linux operating system with Bash shell:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --print-completion bash | sudo tee -a /etc/bash_completion.d/glances
|
||||
$ source /etc/bash_completion.d/glances
|
||||
|
||||
Following shells are supported: bash, zsh and tcsh.
|
||||
|
||||
.. _psutil: https://github.com/giampaolo/psutil
|
||||
.. _README: https://github.com/nicolargo/glances/blob/master/README.rst
|
||||
|
|
|
|||
|
|
@ -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" "Mar 22, 2025" "4.3.1" "Glances"
|
||||
.TH "GLANCES" "1" "Jan 03, 2026" "4.4.2_dev1" "Glances"
|
||||
.SH NAME
|
||||
glances \- An eye on your system
|
||||
.SH SYNOPSIS
|
||||
|
|
@ -64,6 +64,11 @@ enable debug mode
|
|||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-print\-completion
|
||||
generate shell tab completion scripts for Glances CLI
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-C CONF_FILE, \-\-config CONF_FILE
|
||||
path to the configuration file
|
||||
.UNINDENT
|
||||
|
|
@ -453,7 +458,7 @@ Reset history
|
|||
Show/hide RAID plugin
|
||||
.TP
|
||||
.B \fBs\fP
|
||||
Show/hide sensors stats
|
||||
Show/hide sensors plugin
|
||||
.TP
|
||||
.B \fBS\fP
|
||||
Enable/disable spark lines
|
||||
|
|
@ -470,6 +475,9 @@ Sort processes by USER
|
|||
.B \fBU\fP
|
||||
View cumulative network I/O
|
||||
.TP
|
||||
.B \fBV\fP
|
||||
Show/hide VMS plugin
|
||||
.TP
|
||||
.B \fBw\fP
|
||||
Delete finished warning log messages
|
||||
.TP
|
||||
|
|
@ -514,12 +522,18 @@ Switch between process command line or command name
|
|||
.B \fBF5\fP or \fBCTRL\-R\fP
|
||||
Refresh user interface
|
||||
.TP
|
||||
.B \fBLEFT\fP
|
||||
.B \fBSHIFT\-LEFT\fP
|
||||
Navigation left through the process sort
|
||||
.TP
|
||||
.B \fBRIGHT\fP
|
||||
.B \fBSHIFT\-RIGHT\fP
|
||||
Navigation right through the process sort
|
||||
.TP
|
||||
.B \fBLEFT\fP
|
||||
Navigation left through the process name
|
||||
.TP
|
||||
.B \fBRIGHT\fP
|
||||
Navigation right through the process name
|
||||
.TP
|
||||
.B \fBUP\fP
|
||||
Up in the processes list
|
||||
.TP
|
||||
|
|
@ -658,7 +672,7 @@ max_processes_display=25
|
|||
# You can download it in a specific folder
|
||||
# thanks to https://github.com/nicolargo/glances/issues/2021
|
||||
# then configure this folder with the webui_root_path key
|
||||
# Default is folder where glances_restfull_api.py is hosted
|
||||
# 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.
|
||||
|
|
@ -673,6 +687,10 @@ 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=./glances.local+3\-key.pem
|
||||
#ssl_keyfile_password=kfp
|
||||
#ssl_certfile=./glances.local+3.pem
|
||||
.EE
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
|
|
@ -945,6 +963,6 @@ If you do not want to see the local Glances Web Server in the browser list pleas
|
|||
.sp
|
||||
Nicolas Hennion aka Nicolargo <\X'tty: link mailto:contact@nicolargo.com'\fI\%contact@nicolargo.com\fP\X'tty: link'>
|
||||
.SH COPYRIGHT
|
||||
2025, Nicolas Hennion
|
||||
2026, Nicolas Hennion
|
||||
.\" Generated by docutils manpage writer.
|
||||
.
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@ Quickstart
|
|||
==========
|
||||
|
||||
This page gives a good introduction to how to get started with Glances.
|
||||
Glances offers three modes:
|
||||
Glances offers multiple modes:
|
||||
|
||||
- Standalone
|
||||
- Client/Server
|
||||
- Web server
|
||||
- Fetch
|
||||
|
||||
Standalone Mode
|
||||
---------------
|
||||
|
|
@ -196,7 +197,7 @@ Here's a screenshot from Chrome on Android:
|
|||
.. image:: _static/screenshot-web2.png
|
||||
|
||||
How do you protect your server (or Web server) with a login/password ?
|
||||
------------------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
|
||||
You can set a password to access the server using the ``--password``.
|
||||
By default, the login is ``glances`` but you can change it with
|
||||
|
|
@ -222,3 +223,22 @@ file:
|
|||
# Additionally (and optionally) a default password could be defined
|
||||
localhost=mylocalhostpassword
|
||||
default=mydefaultpassword
|
||||
|
||||
Fetch mode
|
||||
----------
|
||||
|
||||
It is also possible to get and share a quick look of a machine using the
|
||||
``fetch`` mode. In this mode, current stats are display on the console in
|
||||
a fancy way.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glances --fetch
|
||||
|
||||
Results look like this:
|
||||
|
||||
.. image:: _static/screenshot-fetch.png
|
||||
|
||||
It is also possible to use a custom template with the ``--fetch-template </path/to/template.jinja>`` option.
|
||||
|
||||
Have a look to the :ref:`fetch documentation page<fetch>` to learn how to create your own template.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
import json
|
||||
from unittest.mock import patch
|
||||
|
||||
from fastapi.openapi.utils import get_openapi
|
||||
|
||||
from glances.main import GlancesMain
|
||||
|
||||
# sys.path.append('./glances/outputs')
|
||||
from glances.outputs.glances_restful_api import GlancesRestfulApi
|
||||
|
||||
# Init Glances core
|
||||
testargs = ["glances", "-C", "./conf/glances.conf"]
|
||||
with patch('sys.argv', testargs):
|
||||
core = GlancesMain()
|
||||
test_config = core.get_config()
|
||||
test_args = core.get_args()
|
||||
|
||||
app = GlancesRestfulApi(config=test_config, args=test_args)._app
|
||||
|
||||
with open('./docs/api/openapi.json', 'w') as f:
|
||||
json.dump(
|
||||
get_openapi(
|
||||
title=app.title,
|
||||
version=app.version,
|
||||
# Set the OenAPI version
|
||||
# It's an hack to make openapi.json compatible with tools like https://editor.swagger.io/
|
||||
# Please read https://fastapi.tiangolo.com/reference/fastapi/?h=openapi#fastapi.FastAPI.openapi_version
|
||||
openapi_version="3.0.2",
|
||||
description=app.description,
|
||||
routes=app.routes,
|
||||
),
|
||||
f,
|
||||
)
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import json
|
||||
|
||||
from glances.outputs.glances_curses import _GlancesCurses
|
||||
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"topMenu": list(_GlancesCurses._top),
|
||||
"leftMenu": [p for p in _GlancesCurses._left_sidebar if p != "now"],
|
||||
},
|
||||
indent=4,
|
||||
)
|
||||
)
|
||||
|
|
@ -0,0 +1,523 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "592b8135-c06b-41b7-895e-9dd70787f6ac",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Use Glances API in your Python code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e5ec86ae-ce2b-452f-b715-54e746026a96",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Init the Glances API"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "ba9b3546-65a0-4eec-942b-1855ff5c5d32",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from glances import api"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "e81ad928-3b61-4654-8589-13cb29e7f292",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"gl = api.GlancesAPI()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6ec912a3-0875-4cdb-8539-e84ffb27768a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Get plugins list"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "1ce57a13-a90d-4d65-b4a4-2bc45112697e",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['alert',\n",
|
||||
" 'ports',\n",
|
||||
" 'diskio',\n",
|
||||
" 'containers',\n",
|
||||
" 'processcount',\n",
|
||||
" 'programlist',\n",
|
||||
" 'gpu',\n",
|
||||
" 'percpu',\n",
|
||||
" 'vms',\n",
|
||||
" 'system',\n",
|
||||
" 'network',\n",
|
||||
" 'cpu',\n",
|
||||
" 'amps',\n",
|
||||
" 'processlist',\n",
|
||||
" 'load',\n",
|
||||
" 'sensors',\n",
|
||||
" 'uptime',\n",
|
||||
" 'now',\n",
|
||||
" 'connections',\n",
|
||||
" 'fs',\n",
|
||||
" 'wifi',\n",
|
||||
" 'ip',\n",
|
||||
" 'help',\n",
|
||||
" 'version',\n",
|
||||
" 'psutilversion',\n",
|
||||
" 'core',\n",
|
||||
" 'mem',\n",
|
||||
" 'folders',\n",
|
||||
" 'quicklook',\n",
|
||||
" 'memswap',\n",
|
||||
" 'raid']"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"gl.plugins()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d5be2964-7a28-4b93-9dd0-1481afd2ee50",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Get CPU stats"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "0d1636d2-3f3e-44d4-bb67-45487384f79f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'total': 3.8, 'user': 3.0, 'nice': 0.0, 'system': 0.8, 'idle': 96.1, 'iowait': 0.1, 'irq': 0.0, 'steal': 0.0, 'guest': 0.0, 'ctx_switches': 0, 'interrupts': 0, 'soft_interrupts': 0, 'syscalls': 0, 'cpucore': 16, 'time_since_update': 141.46278643608093, 'ctx_switches_gauge': 12830371, 'ctx_switches_rate_per_sec': 0, 'interrupts_gauge': 9800040, 'interrupts_rate_per_sec': 0, 'soft_interrupts_gauge': 3875931, 'soft_interrupts_rate_per_sec': 0, 'syscalls_gauge': 0, 'syscalls_rate_per_sec': 0}"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"gl.cpu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "99681a33-045e-43bf-927d-88b15872fad0",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"3.1"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"gl.cpu.get('total')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "07e30de4-8f2a-4110-9c43-2a87d91dbf24",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Get MEMORY stats"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "33502d93-acf9-49c5-8bcd-0a0404b47829",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'total': 16422858752, 'available': 6726169136, 'percent': 59.0, 'used': 9696689616, 'free': 541847552, 'active': 8672595968, 'inactive': 5456875520, 'buffers': 354791424, 'cached': 6520318384, 'shared': 729960448}"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"gl.mem"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "404cd8d6-ac38-4830-8ead-4b747e0ca7b1",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"6779998768"
|
||||
]
|
||||
},
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"gl.mem.get('available')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "74e27e9f-3240-4827-a754-3538b7d68119",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Display it in a user friendly way:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "fa83b40a-51e8-45fa-b478-d0fcc9de4639",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'6.28G'"
|
||||
]
|
||||
},
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"gl.auto_unit(gl.mem.get('available'))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "bfaf5b94-7c9c-4fdc-8a91-71f543cafa4b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Get NETWORK stats"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"id": "a0ab2ce7-e9bd-4a60-9b90-095a9023dac7",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'wlp0s20f3': {'bytes_sent': 1130903, 'bytes_recv': 2213272, 'speed': 0, 'key': 'interface_name', 'interface_name': 'wlp0s20f3', 'alias': 'WIFI', 'bytes_all': 3344175, 'time_since_update': 354.35748958587646, 'bytes_recv_gauge': 1108380679, 'bytes_recv_rate_per_sec': 6245.0, 'bytes_sent_gauge': 21062113, 'bytes_sent_rate_per_sec': 3191.0, 'bytes_all_gauge': 1129442792, 'bytes_all_rate_per_sec': 9437.0}}"
|
||||
]
|
||||
},
|
||||
"execution_count": 16,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"gl.network"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b65f7280-d9f0-4719-9e10-8b78dc414bae",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Get the list of networks interfaces:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"id": "1a55d32a-bd7d-4dfa-b239-8875c01f205e",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['wlp0s20f3']"
|
||||
]
|
||||
},
|
||||
"execution_count": 18,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"gl.network.keys()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8c7e0215-e96a-4f7e-a187-9b7bee1abcf9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Get stats for a specific network interface:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"id": "9aacfb32-c0e3-4fc7-b1d2-d216e46088cd",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'bytes_sent': 118799,\n",
|
||||
" 'bytes_recv': 275052,\n",
|
||||
" 'speed': 0,\n",
|
||||
" 'key': 'interface_name',\n",
|
||||
" 'interface_name': 'wlp0s20f3',\n",
|
||||
" 'alias': 'WIFI',\n",
|
||||
" 'bytes_all': 393851,\n",
|
||||
" 'time_since_update': 46.24822926521301,\n",
|
||||
" 'bytes_recv_gauge': 1108795793,\n",
|
||||
" 'bytes_recv_rate_per_sec': 5947.0,\n",
|
||||
" 'bytes_sent_gauge': 21268464,\n",
|
||||
" 'bytes_sent_rate_per_sec': 2568.0,\n",
|
||||
" 'bytes_all_gauge': 1130064257,\n",
|
||||
" 'bytes_all_rate_per_sec': 8516.0}"
|
||||
]
|
||||
},
|
||||
"execution_count": 20,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"gl.network.get('wlp0s20f3')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"id": "4f5ae513-6022-4a52-8d6c-e8b62afacc24",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"5105.0"
|
||||
]
|
||||
},
|
||||
"execution_count": 21,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"gl.network.get('wlp0s20f3').get('bytes_recv_rate_per_sec')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8b0bdbf4-e386-44aa-9585-1d042f0ded5d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Additional information"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5c52a0c7-06fb-432a-bdb7-9921f432d5a6",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Example for the LOAD plugin."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 29,
|
||||
"id": "99303a2b-52a3-440f-a896-ad4951a9de34",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'min1': 1.01123046875, 'min5': 0.83447265625, 'min15': 0.76171875, 'cpucore': 16}"
|
||||
]
|
||||
},
|
||||
"execution_count": 29,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"gl.load"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7a560824-2787-4436-b39b-63de0c455536",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Get the limit configured in the glances.conf:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 34,
|
||||
"id": "cbbc6a81-623f-4eff-9d08-e6a8b5981660",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'min1': {'description': 'Average sum of the number of processes waiting in the run-queue plus the number currently executing over 1 minute.',\n",
|
||||
" 'unit': 'float'},\n",
|
||||
" 'min5': {'description': 'Average sum of the number of processes waiting in the run-queue plus the number currently executing over 5 minutes.',\n",
|
||||
" 'unit': 'float'},\n",
|
||||
" 'min15': {'description': 'Average sum of the number of processes waiting in the run-queue plus the number currently executing over 15 minutes.',\n",
|
||||
" 'unit': 'float'},\n",
|
||||
" 'cpucore': {'description': 'Total number of CPU core.', 'unit': 'number'}}"
|
||||
]
|
||||
},
|
||||
"execution_count": 34,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"gl.load.fields_description"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2bd51d13-77e3-48f0-aa53-af86df6425f8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Get field description and unit:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 30,
|
||||
"id": "8682edcf-a8b9-424c-976f-2a301a05be6a",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'history_size': 1200.0,\n",
|
||||
" 'load_disable': ['False'],\n",
|
||||
" 'load_careful': 0.7,\n",
|
||||
" 'load_warning': 1.0,\n",
|
||||
" 'load_critical': 5.0}"
|
||||
]
|
||||
},
|
||||
"execution_count": 30,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"gl.load.get_limits()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "raw",
|
||||
"id": "3c671ff8-3a0c-48d3-8247-6081c69c19a9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Get current stats views regarding limits:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 33,
|
||||
"id": "45e03e9b-233c-4359-bcbc-7d2f06aca1c6",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'min1': {'decoration': 'DEFAULT',\n",
|
||||
" 'optional': False,\n",
|
||||
" 'additional': False,\n",
|
||||
" 'splittable': False,\n",
|
||||
" 'hidden': False},\n",
|
||||
" 'min5': {'decoration': 'OK',\n",
|
||||
" 'optional': False,\n",
|
||||
" 'additional': False,\n",
|
||||
" 'splittable': False,\n",
|
||||
" 'hidden': False},\n",
|
||||
" 'min15': {'decoration': 'OK_LOG',\n",
|
||||
" 'optional': False,\n",
|
||||
" 'additional': False,\n",
|
||||
" 'splittable': False,\n",
|
||||
" 'hidden': False},\n",
|
||||
" 'cpucore': {'decoration': 'DEFAULT',\n",
|
||||
" 'optional': False,\n",
|
||||
" 'additional': False,\n",
|
||||
" 'splittable': False,\n",
|
||||
" 'hidden': False}}"
|
||||
]
|
||||
},
|
||||
"execution_count": 33,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"gl.load.get_views()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.14.0"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
|
|
@ -19,7 +19,8 @@ import tracemalloc
|
|||
# Global name
|
||||
# Version should start and end with a numerical char
|
||||
# See https://packaging.python.org/specifications/core-metadata/#version
|
||||
__version__ = "4.3.1"
|
||||
# Examples: 1.0.0, 1.0.0rc1, 1.1.0_dev1
|
||||
__version__ = "4.4.2_dev1"
|
||||
__apiversion__ = '4'
|
||||
__author__ = 'Nicolas Hennion <nicolas@nicolargo.com>'
|
||||
__license__ = 'LGPLv3'
|
||||
|
|
@ -52,10 +53,10 @@ if psutil_version_info < psutil_min_version:
|
|||
|
||||
|
||||
# Trac malloc is only available on Python 3.4 or higher
|
||||
|
||||
|
||||
def __signal_handler(signal, frame):
|
||||
logger.debug(f"Signal {signal} caught")
|
||||
def __signal_handler(sig, frame):
|
||||
logger.debug(f"Signal {sig} caught")
|
||||
# Avoid Glances hang when killing process with muliple CTRL-C See #3264
|
||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||
end()
|
||||
|
||||
|
||||
|
|
@ -74,6 +75,53 @@ def end():
|
|||
sys.exit(0)
|
||||
|
||||
|
||||
def start_main_loop(args, start_duration):
|
||||
logger.debug(f"Glances started in {start_duration.get()} seconds")
|
||||
if args.stop_after:
|
||||
logger.info(f'Glances will be stopped in ~{args.stop_after * args.time} seconds')
|
||||
|
||||
|
||||
def check_memleak(args, mode):
|
||||
if args.memory_leak:
|
||||
wait = args.stop_after * args.time * args.memory_leak * 2
|
||||
print(f'Memory leak detection, please wait ~{wait} seconds...')
|
||||
# First run without dump to fill the memory
|
||||
mode.serve_n(args.stop_after)
|
||||
# Then start the memory-leak loop
|
||||
snapshot_begin = tracemalloc.take_snapshot()
|
||||
else:
|
||||
snapshot_begin = None
|
||||
|
||||
return snapshot_begin
|
||||
|
||||
|
||||
def setup_server_mode(args, mode):
|
||||
if args.stdout_issue or args.stdout_api_restful_doc or args.stdout_api_doc:
|
||||
# Serve once for issue and API documentation modes
|
||||
mode.serve_issue()
|
||||
else:
|
||||
# Serve forever
|
||||
mode.serve_forever()
|
||||
|
||||
|
||||
def maybe_trace_memleak(args, snapshot_begin):
|
||||
if args.trace_malloc or args.memory_leak:
|
||||
snapshot_end = tracemalloc.take_snapshot()
|
||||
if args.memory_leak:
|
||||
snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
|
||||
memory_leak = sum([s.size_diff for s in snapshot_diff])
|
||||
print(f"Memory consumption: {memory_leak / 1000:.1f}KB (see log for details)")
|
||||
logger.info("Memory consumption (top 5):")
|
||||
for stat in snapshot_diff[:5]:
|
||||
logger.info(stat)
|
||||
if args.trace_malloc:
|
||||
# See more options here: https://docs.python.org/3/library/tracemalloc.html
|
||||
top_stats = snapshot_end.statistics("filename")
|
||||
print("[ Trace malloc - Top 10 ]")
|
||||
for stat in top_stats[:10]:
|
||||
print(stat)
|
||||
|
||||
|
||||
def start(config, args):
|
||||
"""Start Glances."""
|
||||
|
||||
|
|
@ -101,40 +149,10 @@ def start(config, args):
|
|||
logger.info(f"Start {GlancesMode.__name__} mode")
|
||||
mode = GlancesMode(config=config, args=args)
|
||||
|
||||
# Start the main loop
|
||||
logger.debug(f"Glances started in {start_duration.get()} seconds")
|
||||
if args.stop_after:
|
||||
logger.info(f'Glances will be stopped in ~{args.stop_after * args.time} seconds')
|
||||
|
||||
if args.memory_leak:
|
||||
print(f'Memory leak detection, please wait ~{args.stop_after * args.time * args.memory_leak * 2} seconds...')
|
||||
# First run without dump to fill the memory
|
||||
mode.serve_n(args.stop_after)
|
||||
# Then start the memory-leak loop
|
||||
snapshot_begin = tracemalloc.take_snapshot()
|
||||
|
||||
if args.stdout_issue or args.stdout_apidoc:
|
||||
# Serve once for issue/test mode
|
||||
mode.serve_issue()
|
||||
else:
|
||||
# Serve forever
|
||||
mode.serve_forever()
|
||||
|
||||
if args.memory_leak:
|
||||
snapshot_end = tracemalloc.take_snapshot()
|
||||
snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
|
||||
memory_leak = sum([s.size_diff for s in snapshot_diff])
|
||||
print(f"Memory consumption: {memory_leak / 1000:.1f}KB (see log for details)")
|
||||
logger.info("Memory consumption (top 5):")
|
||||
for stat in snapshot_diff[:5]:
|
||||
logger.info(stat)
|
||||
elif args.trace_malloc:
|
||||
# See more options here: https://docs.python.org/3/library/tracemalloc.html
|
||||
snapshot = tracemalloc.take_snapshot()
|
||||
top_stats = snapshot.statistics("filename")
|
||||
print("[ Trace malloc - Top 10 ]")
|
||||
for stat in top_stats[:10]:
|
||||
print(stat)
|
||||
start_main_loop(args, start_duration)
|
||||
snapshot_begin = check_memleak(args, mode)
|
||||
setup_server_mode(args, mode)
|
||||
maybe_trace_memleak(args, snapshot_begin)
|
||||
|
||||
# Shutdown
|
||||
mode.end()
|
||||
|
|
@ -171,3 +189,6 @@ def main():
|
|||
|
||||
# Glances can be ran in standalone, client or server mode
|
||||
start(config=core.get_config(), args=core.get_args())
|
||||
|
||||
|
||||
# End of glances/__init__.py
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ class Amp(GlancesAmp):
|
|||
"""Update the AMP"""
|
||||
# Get the Nginx status
|
||||
logger.debug('{}: Update stats using status URL {}'.format(self.NAME, self.get('status_url')))
|
||||
res = requests.get(self.get('status_url'))
|
||||
res = requests.get(self.get('status_url'), timeout=15)
|
||||
if res.ok:
|
||||
# u'Active connections: 1 \nserver accepts handled requests\n 1 1 1 \nReading: 0 Writing: 1 Waiting: 0 \n'
|
||||
self.set_result(res.text.rstrip())
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ systemctl_cmd=/usr/bin/systemctl --plain
|
|||
from subprocess import CalledProcessError, check_output
|
||||
|
||||
from glances.amps.amp import GlancesAmp
|
||||
from glances.globals import iteritems, to_ascii
|
||||
from glances.globals import to_ascii
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ class Amp(GlancesAmp):
|
|||
"""Glances' Systemd AMP."""
|
||||
|
||||
NAME = 'Systemd'
|
||||
VERSION = '1.0'
|
||||
VERSION = '1.1'
|
||||
DESCRIPTION = 'Get services list from systemctl (systemd)'
|
||||
AUTHOR = 'Nicolargo'
|
||||
EMAIL = 'contact@nicolargo.com'
|
||||
|
|
@ -77,7 +77,7 @@ class Amp(GlancesAmp):
|
|||
status[column[c]] = 1
|
||||
# Build the output (string) message
|
||||
output = 'Services\n'
|
||||
for k, v in iteritems(status):
|
||||
for k, v in status.items():
|
||||
output += f'{k}: {v}\n'
|
||||
self.set_result(output, separator=' ')
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ service_cmd=/usr/bin/service --status-all
|
|||
"""
|
||||
|
||||
from glances.amps.amp import GlancesAmp
|
||||
from glances.globals import iteritems
|
||||
from glances.logger import logger
|
||||
from glances.secure import secure_popen
|
||||
|
||||
|
|
@ -43,7 +42,7 @@ class Amp(GlancesAmp):
|
|||
"""Glances' Systemd AMP."""
|
||||
|
||||
NAME = 'SystemV'
|
||||
VERSION = '1.0'
|
||||
VERSION = '1.1'
|
||||
DESCRIPTION = 'Get services list from service (initd)'
|
||||
AUTHOR = 'Nicolargo'
|
||||
EMAIL = 'contact@nicolargo.com'
|
||||
|
|
@ -77,7 +76,7 @@ class Amp(GlancesAmp):
|
|||
status['upstart'] += 1
|
||||
# Build the output (string) message
|
||||
output = 'Services\n'
|
||||
for k, v in iteritems(status):
|
||||
for k, v in status.items():
|
||||
output += f'{k}: {v}\n'
|
||||
self.set_result(output, separator=' ')
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import os
|
|||
import re
|
||||
import threading
|
||||
|
||||
from glances.globals import amps_path, iteritems, listkeys
|
||||
from glances.globals import amps_path, listkeys
|
||||
from glances.logger import logger
|
||||
from glances.processes import glances_processes
|
||||
|
||||
|
|
@ -90,7 +90,7 @@ class AmpsList:
|
|||
processlist = glances_processes.get_list()
|
||||
|
||||
# Iter upon the AMPs dict
|
||||
for k, v in iteritems(self.get()):
|
||||
for k, v in self.get().items():
|
||||
if not v.enable():
|
||||
# Do not update if the enable tag is set
|
||||
continue
|
||||
|
|
@ -125,18 +125,14 @@ class AmpsList:
|
|||
|
||||
Search application monitored processes by a regular expression
|
||||
"""
|
||||
ret = []
|
||||
try:
|
||||
# Search in both cmdline and name (for kernel thread, see #1261)
|
||||
for p in processlist:
|
||||
if (re.search(amp_value.regex(), p['name']) is not None) or (
|
||||
p['cmdline'] is not None
|
||||
and p['cmdline'] != []
|
||||
and re.search(amp_value.regex(), ' '.join(p['cmdline'])) is not None
|
||||
):
|
||||
ret.append(
|
||||
{'pid': p['pid'], 'cpu_percent': p['cpu_percent'], 'memory_percent': p['memory_percent']}
|
||||
)
|
||||
ret = [
|
||||
{'pid': p['pid'], 'cpu_percent': p['cpu_percent'], 'memory_percent': p['memory_percent']}
|
||||
for p in processlist
|
||||
if re.search(amp_value.regex(), p['name'])
|
||||
or ((cmdline := p.get('cmdline')) and re.search(amp_value.regex(), ' '.join(cmdline)))
|
||||
]
|
||||
|
||||
except (TypeError, KeyError) as e:
|
||||
logger.debug(f"Can not build AMPS list ({e})")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
#
|
||||
# Glances - An eye on your system
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2025 Nicolas Hennion <nicolas@nicolargo.com>
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
#
|
||||
|
||||
from glances import __version__ as glances_version
|
||||
from glances.globals import auto_unit, weak_lru_cache
|
||||
from glances.main import GlancesMain
|
||||
from glances.outputs.glances_bars import Bar
|
||||
from glances.processes import sort_stats
|
||||
from glances.stats import GlancesStats
|
||||
|
||||
plugin_dependencies_tree = {
|
||||
'processlist': ['processcount'],
|
||||
}
|
||||
|
||||
|
||||
class GlancesAPI:
|
||||
ttl = 2.0 # Default cache TTL in seconds
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
self.__version__ = glances_version.split('.')[0] # Get the major version
|
||||
|
||||
core = GlancesMain()
|
||||
self.args = args if args is not None else core.get_args()
|
||||
self.config = config if config is not None else core.get_config()
|
||||
self._stats = GlancesStats(config=self.config, args=self.args)
|
||||
|
||||
# Set the cache TTL for the API
|
||||
self.ttl = self.args.time if self.args.time is not None else self.ttl
|
||||
|
||||
# Init the stats of all plugins in order to ensure that rate are computed
|
||||
self._stats.update()
|
||||
|
||||
@weak_lru_cache(maxsize=1, ttl=ttl)
|
||||
def __getattr__(self, item):
|
||||
"""Fallback to the stats object for any missing attributes."""
|
||||
if item in self._stats.getPluginsList():
|
||||
if item in plugin_dependencies_tree:
|
||||
# Ensure dependencies are updated before accessing the plugin
|
||||
for dependency in plugin_dependencies_tree[item]:
|
||||
self._stats.get_plugin(dependency).update()
|
||||
# Update the plugin stats
|
||||
self._stats.get_plugin(item).update()
|
||||
return self._stats.get_plugin(item)
|
||||
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'")
|
||||
|
||||
def plugins(self):
|
||||
"""Return the list of available plugins."""
|
||||
return self._stats.getPluginsList()
|
||||
|
||||
def auto_unit(self, number, low_precision=False, min_symbol='K', none_symbol='-'):
|
||||
"""
|
||||
Converts a numeric value into a human-readable string with appropriate units.
|
||||
|
||||
Args:
|
||||
number (float or int): The numeric value to be converted.
|
||||
low_precision (bool, optional): If True, use lower precision for the output. Defaults to False.
|
||||
min_symbol (str, optional): The minimum unit symbol to use (e.g., 'K' for kilo). Defaults to 'K'.
|
||||
none_symbol (str, optional): The symbol to display if the number is None. Defaults to '-'.
|
||||
|
||||
Returns:
|
||||
str: A human-readable string representation of the number with units.
|
||||
"""
|
||||
return auto_unit(number, low_precision, min_symbol, none_symbol)
|
||||
|
||||
def bar(self, value, size=18, bar_char='■', empty_char='□', pre_char='', post_char=''):
|
||||
"""
|
||||
Generate a progress bar representation for a given value.
|
||||
|
||||
Args:
|
||||
value (float): The percentage value to represent in the bar (typically between 0 and 100).
|
||||
size (int, optional): The total length of the bar in characters. Defaults to 18.
|
||||
bar_char (str, optional): The character used to represent the filled portion of the bar. Defaults to '■'.
|
||||
empty_char (str, optional): The character used to represent the empty portion of the bar. Defaults to '□'.
|
||||
pre_char (str, optional): A string to prepend to the bar. Defaults to ''.
|
||||
post_char (str, optional): A string to append to the bar. Defaults to ''.
|
||||
|
||||
Returns:
|
||||
str: A string representing the progress bar.
|
||||
"""
|
||||
b = Bar(
|
||||
size, bar_char=bar_char, empty_char=empty_char, pre_char=pre_char, post_char=post_char, display_value=False
|
||||
)
|
||||
b.percent = value
|
||||
return b.get()
|
||||
|
||||
def top_process(self, limit=3, sorted_by='cpu_percent', sorted_by_secondary='memory_percent'):
|
||||
"""
|
||||
Returns a list of the top processes sorted by specified criteria.
|
||||
|
||||
Args:
|
||||
limit (int, optional): The maximum number of top processes to return. Defaults to 3.
|
||||
sorted_by (str, optional): The primary key to sort processes by (e.g., 'cpu_percent').
|
||||
Defaults to 'cpu_percent'.
|
||||
sorted_by_secondary (str, optional): The secondary key to sort processes by if primary keys are equal
|
||||
(e.g., 'memory_percent'). Defaults to 'memory_percent'.
|
||||
|
||||
Returns:
|
||||
list: A list of dictionaries representing the top processes, excluding those with 'glances' in their
|
||||
command line.
|
||||
|
||||
Note:
|
||||
The 'glances' process is excluded from the returned list to avoid self-generated CPU load affecting
|
||||
the results.
|
||||
"""
|
||||
# Exclude glances process from the top list
|
||||
# because in fetch mode, Glances generate a CPU load
|
||||
all_but_glances = [
|
||||
p
|
||||
for p in self._stats.get_plugin('processlist').get_raw()
|
||||
if p['cmdline'] and 'glances' not in (p['cmdline'] or ())
|
||||
]
|
||||
return sort_stats(all_but_glances, sorted_by=sorted_by, sorted_by_secondary=sorted_by_secondary)[:limit]
|
||||
|
|
@ -10,6 +10,14 @@
|
|||
|
||||
from datetime import datetime
|
||||
|
||||
# Ugly hack waiting for Python 3.10 deprecation
|
||||
try:
|
||||
from datetime import UTC
|
||||
except ImportError:
|
||||
from datetime import timezone
|
||||
|
||||
UTC = timezone.utc
|
||||
|
||||
|
||||
class GlancesAttribute:
|
||||
def __init__(self, name, description='', history_max_size=None):
|
||||
|
|
@ -73,7 +81,7 @@ class GlancesAttribute:
|
|||
|
||||
Value is a tuple: (<timestamp>, <new_value>)
|
||||
"""
|
||||
self._value = (datetime.now(), new_value)
|
||||
self._value = (datetime.now(UTC), new_value)
|
||||
self.history_add(self._value)
|
||||
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ from glances import __version__
|
|||
from glances.globals import json_loads
|
||||
from glances.logger import logger
|
||||
from glances.outputs.glances_curses import GlancesCursesClient
|
||||
from glances.outputs.glances_stdout import GlancesStdout
|
||||
from glances.outputs.glances_stdout_csv import GlancesStdoutCsv
|
||||
from glances.outputs.glances_stdout_fetch import GlancesStdoutFetch
|
||||
from glances.outputs.glances_stdout_json import GlancesStdoutJson
|
||||
from glances.stats_client import GlancesStatsClient
|
||||
from glances.timer import Counter
|
||||
|
||||
|
|
@ -73,7 +77,7 @@ class GlancesClient:
|
|||
def log_and_exit(self, msg=''):
|
||||
"""Log and exit."""
|
||||
if not self.return_to_browser:
|
||||
logger.critical(msg)
|
||||
logger.critical(f"Error when connecting to Glances server: {msg}")
|
||||
sys.exit(2)
|
||||
else:
|
||||
logger.error(msg)
|
||||
|
|
@ -172,6 +176,21 @@ class GlancesClient:
|
|||
if self.quiet:
|
||||
# In quiet mode, nothing is displayed
|
||||
logger.info("Quiet mode is ON: Nothing will be displayed")
|
||||
elif self.args.stdout:
|
||||
logger.info(f"Stdout mode is ON, following stats will be displayed: {self.args.stdout}")
|
||||
# Init screen
|
||||
self.screen = GlancesStdout(config=self.config, args=self.args)
|
||||
elif self.args.stdout_json:
|
||||
logger.info(f"Stdout JSON mode is ON, following stats will be displayed: {self.args.stdout_json}")
|
||||
# Init screen
|
||||
self.screen = GlancesStdoutJson(config=self.config, args=self.args)
|
||||
elif self.args.stdout_csv:
|
||||
logger.info(f"Stdout CSV mode is ON, following stats will be displayed: {self.args.stdout_csv}")
|
||||
# Init screen
|
||||
self.screen = GlancesStdoutCsv(config=self.config, args=self.args)
|
||||
elif self.args.stdout_fetch:
|
||||
logger.info("Fetch mode is ON")
|
||||
self.screen = GlancesStdoutFetch(config=self.config, args=self.args)
|
||||
else:
|
||||
self.screen = GlancesCursesClient(config=self.config, args=self.args)
|
||||
|
||||
|
|
@ -237,6 +256,7 @@ class GlancesClient:
|
|||
return self.client_mode
|
||||
|
||||
exit_key = False
|
||||
|
||||
try:
|
||||
while True and not exit_key:
|
||||
# Update the stats
|
||||
|
|
@ -264,8 +284,8 @@ class GlancesClient:
|
|||
else:
|
||||
# In quiet mode, we only wait adapated_refresh seconds
|
||||
time.sleep(adapted_refresh)
|
||||
except Exception as e:
|
||||
logger.critical(e)
|
||||
except Exception:
|
||||
logger.critical("Critical error in client serve_forever loop")
|
||||
self.end()
|
||||
|
||||
return self.client_mode
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
"""CPU percent stats shared between CPU and Quicklook plugins."""
|
||||
|
||||
import platform
|
||||
from typing import Optional, TypedDict
|
||||
from typing import TypedDict
|
||||
|
||||
import psutil
|
||||
|
||||
|
|
@ -18,11 +18,184 @@ from glances.timer import Timer
|
|||
|
||||
__all__ = ["cpu_percent"]
|
||||
|
||||
CPU_IMPLEMENTERS = {
|
||||
0x41: 'ARM Limited',
|
||||
0x42: 'Broadcom',
|
||||
0x43: 'Cavium',
|
||||
0x44: 'DEC',
|
||||
0x46: 'Fujitsu',
|
||||
0x48: 'HiSilicon',
|
||||
0x49: 'Infineon Technologies',
|
||||
0x4D: 'Motorola/Freescale',
|
||||
0x4E: 'NVIDIA',
|
||||
0x50: 'Applied Micro (APM)',
|
||||
0x51: 'Qualcomm',
|
||||
0x53: 'Samsung',
|
||||
0x56: 'Marvell',
|
||||
0x61: 'Apple',
|
||||
0x66: 'Faraday',
|
||||
0x69: 'Intel',
|
||||
0x6D: 'Microsoft',
|
||||
0x70: 'Phytium',
|
||||
0xC0: 'Ampere Computing',
|
||||
}
|
||||
|
||||
CPU_PARTS = {
|
||||
# ARM Limited (0x41)
|
||||
0x41: {
|
||||
0xD03: 'Cortex-A53',
|
||||
0xD04: 'Cortex-A35',
|
||||
0xD05: 'Cortex-A55',
|
||||
0xD06: 'Cortex-A65',
|
||||
0xD07: 'Cortex-A57',
|
||||
0xD08: 'Cortex-A72',
|
||||
0xD09: 'Cortex-A73',
|
||||
0xD0A: 'Cortex-A75',
|
||||
0xD0B: 'Cortex-A76',
|
||||
0xD0C: 'Neoverse N1',
|
||||
0xD0D: 'Cortex-A77',
|
||||
0xD0E: 'Cortex-A76AE',
|
||||
0xD13: 'Cortex-R52',
|
||||
0xD20: 'Cortex-M23',
|
||||
0xD21: 'Cortex-M33',
|
||||
0xD40: 'Neoverse V1',
|
||||
0xD41: 'Cortex-A78',
|
||||
0xD42: 'Cortex-A78AE',
|
||||
0xD43: 'Cortex-A65AE',
|
||||
0xD44: 'Cortex-X1',
|
||||
0xD46: 'Cortex-A510',
|
||||
0xD47: 'Cortex-A710',
|
||||
0xD48: 'Cortex-X2',
|
||||
0xD49: 'Neoverse N2',
|
||||
0xD4A: 'Neoverse E1',
|
||||
0xD4B: 'Cortex-A78C',
|
||||
0xD4C: 'Cortex-X1C',
|
||||
0xD4D: 'Cortex-A715',
|
||||
0xD4E: 'Cortex-X3',
|
||||
0xD4F: 'Neoverse V2',
|
||||
0xD80: 'Cortex-A520',
|
||||
0xD81: 'Cortex-A720',
|
||||
0xD82: 'Cortex-X4',
|
||||
0xD84: 'Neoverse V3',
|
||||
0xD85: 'Cortex-X925',
|
||||
0xD87: 'Cortex-A725',
|
||||
},
|
||||
# Apple (0x61)
|
||||
0x61: {
|
||||
0x000: 'Swift',
|
||||
0x001: 'Cyclone',
|
||||
0x002: 'Typhoon',
|
||||
0x003: 'Twister',
|
||||
0x004: 'Hurricane',
|
||||
0x005: 'Monsoon/Mistral',
|
||||
0x006: 'Vortex/Tempest',
|
||||
0x007: 'Lightning/Thunder',
|
||||
0x008: 'Firestorm/Icestorm (M1)',
|
||||
0x009: 'Avalanche/Blizzard (M2)',
|
||||
0x00E: 'Everest/Sawtooth (M3)',
|
||||
0x010: 'Blizzard/Avalanche (A16)',
|
||||
0x011: 'Coll (M4)',
|
||||
},
|
||||
# Qualcomm (0x51)
|
||||
0x51: {
|
||||
0x00F: 'Scorpion',
|
||||
0x02D: 'Scorpion',
|
||||
0x04D: 'Krait',
|
||||
0x06F: 'Krait',
|
||||
0x201: 'Kryo',
|
||||
0x205: 'Kryo',
|
||||
0x211: 'Kryo',
|
||||
0x800: 'Kryo 260/280 Gold (Cortex-A73)',
|
||||
0x801: 'Kryo 260/280 Silver (Cortex-A53)',
|
||||
0x802: 'Kryo 385 Gold (Cortex-A75)',
|
||||
0x803: 'Kryo 385 Silver (Cortex-A55)',
|
||||
0x804: 'Kryo 485 Gold (Cortex-A76)',
|
||||
0x805: 'Kryo 485 Silver (Cortex-A55)',
|
||||
0xC00: 'Falkor',
|
||||
0xC01: 'Saphira',
|
||||
},
|
||||
# Samsung (0x53)
|
||||
0x53: {
|
||||
0x001: 'Exynos M1/M2',
|
||||
0x002: 'Exynos M3',
|
||||
0x003: 'Exynos M4',
|
||||
0x004: 'Exynos M5',
|
||||
},
|
||||
# NVIDIA (0x4e)
|
||||
0x4E: {
|
||||
0x000: 'Denver',
|
||||
0x003: 'Denver 2',
|
||||
0x004: 'Carmel',
|
||||
},
|
||||
# Marvell (0x56)
|
||||
0x56: {
|
||||
0x131: 'Feroceon 88FR131',
|
||||
0x581: 'PJ4/PJ4b',
|
||||
0x584: 'PJ4B-MP',
|
||||
},
|
||||
# Cavium (0x43)
|
||||
0x43: {
|
||||
0x0A0: 'ThunderX',
|
||||
0x0A1: 'ThunderX 88XX',
|
||||
0x0A2: 'ThunderX 81XX',
|
||||
0x0A3: 'ThunderX 83XX',
|
||||
0x0AF: 'ThunderX2 99xx',
|
||||
0x0B0: 'OcteonTX2',
|
||||
0x0B1: 'OcteonTX2 T98',
|
||||
0x0B2: 'OcteonTX2 T96',
|
||||
0x0B3: 'OcteonTX2 F95',
|
||||
0x0B4: 'OcteonTX2 F95N',
|
||||
0x0B5: 'OcteonTX2 F95MM',
|
||||
},
|
||||
# Broadcom (0x42)
|
||||
0x42: {
|
||||
0x00F: 'Brahma B15',
|
||||
0x100: 'Brahma B53',
|
||||
0x516: 'Vulcan',
|
||||
},
|
||||
# HiSilicon (0x48)
|
||||
0x48: {
|
||||
0xD01: 'Kunpeng-920',
|
||||
0xD40: 'Cortex-A76 (Kirin)',
|
||||
},
|
||||
# Ampere (0xc0)
|
||||
0xC0: {
|
||||
0xAC3: 'Ampere-1',
|
||||
0xAC4: 'Ampere-1a',
|
||||
},
|
||||
# Fujitsu (0x46)
|
||||
0x46: {
|
||||
0x001: 'A64FX',
|
||||
},
|
||||
# Intel (0x69) - ARM-based chips
|
||||
0x69: {
|
||||
0x200: 'i80200',
|
||||
0x210: 'PXA250A',
|
||||
0x212: 'PXA210A',
|
||||
0x242: 'i80321-400',
|
||||
0x243: 'i80321-600',
|
||||
0x290: 'PXA250B/PXA26x',
|
||||
0x292: 'PXA210B',
|
||||
0x2C2: 'i80321-400-B0',
|
||||
0x2C3: 'i80321-600-B0',
|
||||
0x2D0: 'PXA250C/PXA255/PXA26x',
|
||||
0x2D2: 'PXA210C',
|
||||
0x411: 'PXA27x',
|
||||
0x41C: 'IPX425-533',
|
||||
0x41D: 'IPX425-400',
|
||||
0x41F: 'IPX425-266',
|
||||
0x682: 'PXA32x',
|
||||
0x683: 'PXA930/PXA935',
|
||||
0x688: 'PXA30x',
|
||||
0x689: 'PXA31x',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class CpuInfo(TypedDict):
|
||||
cpu_name: str
|
||||
cpu_hz: Optional[float]
|
||||
cpu_hz_current: Optional[float]
|
||||
cpu_hz: float | None
|
||||
cpu_hz_current: float | None
|
||||
|
||||
|
||||
class PerCpuPercentInfo(TypedDict):
|
||||
|
|
@ -32,15 +205,15 @@ class PerCpuPercentInfo(TypedDict):
|
|||
user: float
|
||||
system: float
|
||||
idle: float
|
||||
nice: Optional[float]
|
||||
iowait: Optional[float]
|
||||
irq: Optional[float]
|
||||
softirq: Optional[float]
|
||||
steal: Optional[float]
|
||||
guest: Optional[float]
|
||||
guest_nice: Optional[float]
|
||||
dpc: Optional[float]
|
||||
interrupt: Optional[float]
|
||||
nice: float | None
|
||||
iowait: float | None
|
||||
irq: float | None
|
||||
softirq: float | None
|
||||
steal: float | None
|
||||
guest: float | None
|
||||
guest_nice: float | None
|
||||
dpc: float | None
|
||||
interrupt: float | None
|
||||
|
||||
|
||||
class CpuPercent:
|
||||
|
|
@ -84,7 +257,7 @@ class CpuPercent:
|
|||
self.cpu_info['cpu_hz_current'] = cpu_freq.current
|
||||
else:
|
||||
self.cpu_info['cpu_hz_current'] = None
|
||||
if hasattr(cpu_freq, 'max'):
|
||||
if hasattr(cpu_freq, 'max') and cpu_freq.max != 0.0:
|
||||
self.cpu_info['cpu_hz'] = cpu_freq.max
|
||||
else:
|
||||
self.cpu_info['cpu_hz'] = None
|
||||
|
|
@ -103,9 +276,18 @@ class CpuPercent:
|
|||
logger.debug("No permission to read '/proc/cpuinfo'")
|
||||
return ret
|
||||
|
||||
cpu_implementer = None
|
||||
for line in cpuinfo_lines:
|
||||
# Look for the CPU name
|
||||
if line.startswith('model name') or line.startswith('Model') or line.startswith('cpu model'):
|
||||
return line.split(':')[1].strip()
|
||||
# Look for the CPU name on ARM architecture (see #3127)
|
||||
if line.startswith('CPU implementer'):
|
||||
cpu_implementer = CPU_IMPLEMENTERS.get(int(line.split(':')[1].strip(), 16), ret)
|
||||
ret = cpu_implementer
|
||||
if line.startswith('CPU part') and cpu_implementer in CPU_PARTS:
|
||||
cpu_part = CPU_PARTS[cpu_implementer].get(int(line.split(':')[1].strip(), 16), 'Unknown')
|
||||
ret = f'{cpu_implementer} {cpu_part}'
|
||||
|
||||
return ret
|
||||
|
||||
|
|
|
|||
|
|
@ -271,6 +271,7 @@ class GlancesEventsList:
|
|||
event_time, event_index, event_state, event_type, event_value, proc_list, proc_desc, global_message
|
||||
)
|
||||
|
||||
# logger.info(self.events_list)
|
||||
return self.len()
|
||||
|
||||
def _create_event(self, event_time, event_state, event_type, event_value, proc_desc, global_message):
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2022 Nicolas Hennion <nicolas@nicolargo.com>
|
||||
# SPDX-FileCopyrightText: 2026 Nicolas Hennion <nicolas@nicolargo.com>
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
#
|
||||
|
||||
"""
|
||||
I am your father...
|
||||
|
||||
...for all Glances exports IF.
|
||||
"""
|
||||
|
||||
from glances.globals import NoOptionError, NoSectionError, iteritems, iterkeys, json_dumps
|
||||
import re
|
||||
|
||||
from glances.globals import NoOptionError, NoSectionError, json_dumps
|
||||
from glances.logger import logger
|
||||
from glances.timer import Counter
|
||||
|
||||
|
|
@ -20,18 +21,14 @@ from glances.timer import Counter
|
|||
class GlancesExport:
|
||||
"""Main class for Glances export IF."""
|
||||
|
||||
# List of non exportable plugins
|
||||
# @TODO: remove this part and make all plugins exportable (see issue #1556)
|
||||
# @TODO: also make this list configurable by the user (see issue #1443)
|
||||
# List of non exportable internal plugins
|
||||
non_exportable_plugins = [
|
||||
'alert',
|
||||
'amps',
|
||||
'help',
|
||||
'now',
|
||||
'plugin',
|
||||
'psutilversion',
|
||||
'quicklook',
|
||||
'version',
|
||||
"alert",
|
||||
"help",
|
||||
"plugin",
|
||||
"psutilversion",
|
||||
"quicklook",
|
||||
"version",
|
||||
]
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
|
|
@ -55,6 +52,13 @@ class GlancesExport:
|
|||
# Save last export list
|
||||
self._last_exported_list = None
|
||||
|
||||
# Fields description
|
||||
self._fields_description = None
|
||||
|
||||
# Load the default common export configuration
|
||||
if self.config is not None:
|
||||
self.load_common_conf()
|
||||
|
||||
def _log_result_decorator(fct):
|
||||
"""Log (DEBUG) the result of the function fct."""
|
||||
|
||||
|
|
@ -73,7 +77,25 @@ class GlancesExport:
|
|||
"""Close the export module."""
|
||||
logger.debug(f"Finalise export interface {self.export_name}")
|
||||
|
||||
def load_conf(self, section, mandatories=['host', 'port'], options=None):
|
||||
def load_common_conf(self):
|
||||
"""Load the common export configuration in the Glances configuration file.
|
||||
|
||||
:returns: Boolean -- True if section is found
|
||||
"""
|
||||
# Read the common [export] section
|
||||
section = "export"
|
||||
|
||||
opt = "exclude_fields"
|
||||
try:
|
||||
setattr(self, opt, self.config.get_list_value(section, opt))
|
||||
except NoOptionError:
|
||||
logger.debug(f"{opt} option not found in the {section} configuration section")
|
||||
|
||||
logger.debug(f"Load common {section} from the Glances configuration file")
|
||||
|
||||
return True
|
||||
|
||||
def load_conf(self, section, mandatories=["host", "port"], options=None):
|
||||
"""Load the export <section> configuration in the Glances configuration file.
|
||||
|
||||
:param section: name of the export section to load
|
||||
|
|
@ -103,7 +125,7 @@ class GlancesExport:
|
|||
try:
|
||||
setattr(self, opt, self.config.get_value(section, opt))
|
||||
except NoOptionError:
|
||||
pass
|
||||
logger.debug(f"{opt} option not found in the {section} configuration section")
|
||||
|
||||
logger.debug(f"Load {section} from the Glances configuration file")
|
||||
logger.debug(f"{section} parameters: { ({opt: getattr(self, opt) for opt in mandatories + options}) }")
|
||||
|
|
@ -114,7 +136,7 @@ class GlancesExport:
|
|||
"""Return the value of the item 'key'."""
|
||||
ret = None
|
||||
try:
|
||||
ret = item[item['key']]
|
||||
ret = item[item["key"]]
|
||||
except KeyError:
|
||||
logger.error(f"No 'key' available in {item}")
|
||||
if isinstance(ret, list):
|
||||
|
|
@ -130,14 +152,81 @@ class GlancesExport:
|
|||
d_tags = {}
|
||||
if tags:
|
||||
try:
|
||||
d_tags = dict([x.split(':') for x in tags.split(',')])
|
||||
d_tags = dict(x.split(":", 1) for x in tags.split(","))
|
||||
except ValueError:
|
||||
# one of the 'key:value' pairs was missing
|
||||
logger.info('Invalid tags passed: %s', tags)
|
||||
logger.info("Invalid tags passed: %s", tags)
|
||||
d_tags = {}
|
||||
|
||||
return d_tags
|
||||
|
||||
def normalize_for_influxdb(self, name, columns, points):
|
||||
"""Normalize data for the InfluxDB's data model.
|
||||
|
||||
:return: a list of measurements.
|
||||
"""
|
||||
FIELD_TO_TAG = ["name", "cmdline", "type"]
|
||||
ret = []
|
||||
|
||||
# Build initial dict by crossing columns and point
|
||||
data_dict = dict(zip(columns, points))
|
||||
|
||||
# issue1871 - Check if a key exist. If a key exist, the value of
|
||||
# the key should be used as a tag to identify the measurement.
|
||||
keys_list = [k.split(".")[0] for k in columns if k.endswith(".key")]
|
||||
if not keys_list:
|
||||
keys_list = [None]
|
||||
|
||||
for measurement in keys_list:
|
||||
# Manage field
|
||||
if measurement is not None:
|
||||
fields = {
|
||||
k.replace(f"{measurement}.", ""): data_dict[k] for k in data_dict if k.startswith(f"{measurement}.")
|
||||
}
|
||||
else:
|
||||
fields = data_dict
|
||||
# Transform to InfluxDB data model
|
||||
# https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_reference/
|
||||
for k in fields:
|
||||
# Do not export empty (None) value
|
||||
if fields[k] is None:
|
||||
continue
|
||||
# Convert numerical to float
|
||||
try:
|
||||
fields[k] = float(fields[k])
|
||||
except (TypeError, ValueError):
|
||||
# Convert others to string
|
||||
try:
|
||||
fields[k] = str(fields[k])
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
# Manage tags
|
||||
tags = self.parse_tags(self.tags)
|
||||
# Add the hostname as a tag
|
||||
tags["hostname"] = self.hostname
|
||||
if "hostname" in fields:
|
||||
fields.pop("hostname")
|
||||
# Others tags...
|
||||
if "key" in fields and fields["key"] in fields:
|
||||
# Create a tag from the key
|
||||
# Tag should be an string (see InfluxDB data model)
|
||||
tags[fields["key"]] = str(fields[fields["key"]])
|
||||
# Remove it from the field list (can not be a field and a tag)
|
||||
fields.pop(fields["key"])
|
||||
# Add name as a tag (example for the process list)
|
||||
for k in FIELD_TO_TAG:
|
||||
if k in fields:
|
||||
tags[k] = str(fields[k])
|
||||
# Remove it from the field list (can not be a field and a tag)
|
||||
fields.pop(k)
|
||||
# Add the measurement to the list
|
||||
ret.append({"measurement": name, "tags": tags, "fields": fields})
|
||||
return ret
|
||||
|
||||
def is_excluded(self, field):
|
||||
"""Return true if the field is excluded."""
|
||||
return any(re.fullmatch(i, field, re.I) for i in (getattr(self, 'exclude_fields') or ()))
|
||||
|
||||
def plugins_to_export(self, stats):
|
||||
"""Return the list of plugins to export.
|
||||
|
||||
|
|
@ -150,12 +239,21 @@ class GlancesExport:
|
|||
"""Return the list of plugins last exported."""
|
||||
return self._last_exported_list
|
||||
|
||||
def init_fields(self, stats):
|
||||
"""Return fields description in order to init stats in a server."""
|
||||
if not self.export_enable:
|
||||
return False
|
||||
|
||||
self._last_exported_list = self.plugins_to_export(stats)
|
||||
self._fields_description = stats.getAllFieldsDescriptionAsDict(plugin_list=self.last_exported_list())
|
||||
return self._fields_description
|
||||
|
||||
def update(self, stats):
|
||||
"""Update stats to a server.
|
||||
|
||||
The method builds two lists: names and values and calls the export method to export the stats.
|
||||
|
||||
Note: this class can be overwritten (for example in CSV and Graph).
|
||||
Note: if needed this class can be overwritten.
|
||||
"""
|
||||
if not self.export_enable:
|
||||
return False
|
||||
|
|
@ -169,10 +267,14 @@ class GlancesExport:
|
|||
for plugin in self.last_exported_list():
|
||||
if isinstance(all_stats[plugin], dict):
|
||||
all_stats[plugin].update(all_limits[plugin])
|
||||
# Remove the <plugin>_disable field
|
||||
all_stats[plugin].pop(f"{plugin}_disable", None)
|
||||
elif isinstance(all_stats[plugin], list):
|
||||
# TypeError: string indices must be integers (Network plugin) #1054
|
||||
for i in all_stats[plugin]:
|
||||
i.update(all_limits[plugin])
|
||||
# Remove the <plugin>_disable field
|
||||
i.pop(f"{plugin}_disable", None)
|
||||
else:
|
||||
continue
|
||||
export_names, export_values = self.build_export(all_stats[plugin])
|
||||
|
|
@ -181,24 +283,28 @@ class GlancesExport:
|
|||
return True
|
||||
|
||||
def build_export(self, stats):
|
||||
"""Build the export lists."""
|
||||
"""Build the export lists.
|
||||
This method builds two lists: names and values.
|
||||
"""
|
||||
|
||||
# Initialize export lists
|
||||
export_names = []
|
||||
export_values = []
|
||||
|
||||
if isinstance(stats, dict):
|
||||
# Stats is a dict
|
||||
# Is there a key ?
|
||||
if 'key' in iterkeys(stats) and stats['key'] in iterkeys(stats):
|
||||
pre_key = '{}.'.format(stats[stats['key']])
|
||||
if "key" in stats and stats["key"] in stats:
|
||||
pre_key = "{}.".format(stats[stats["key"]])
|
||||
else:
|
||||
pre_key = ''
|
||||
pre_key = ""
|
||||
# Walk through the dict
|
||||
for key, value in sorted(iteritems(stats)):
|
||||
for key, value in sorted(stats.items()):
|
||||
if isinstance(value, bool):
|
||||
value = json_dumps(value)
|
||||
value = json_dumps(value).decode()
|
||||
|
||||
if isinstance(value, list):
|
||||
value = ' '.join([str(v) for v in value])
|
||||
value = " ".join([str(v) for v in value])
|
||||
|
||||
if isinstance(value, dict):
|
||||
item_names, item_values = self.build_export(value)
|
||||
|
|
@ -206,6 +312,9 @@ class GlancesExport:
|
|||
export_names += item_names
|
||||
export_values += item_values
|
||||
else:
|
||||
# We are on a simple value
|
||||
if self.is_excluded(pre_key + key.lower()):
|
||||
continue
|
||||
export_names.append(pre_key + key.lower())
|
||||
export_values.append(value)
|
||||
elif isinstance(stats, list):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,165 @@
|
|||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2026 Nicolas Hennion <nicolas@nicolargo.com>
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
#
|
||||
|
||||
"""
|
||||
I am your son...
|
||||
...abstract class for AsyncIO-based Glances exports.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import threading
|
||||
import time
|
||||
from abc import abstractmethod
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class GlancesExportAsyncio(GlancesExport):
|
||||
"""Abstract class for AsyncIO-based export modules.
|
||||
|
||||
This class manages a persistent event loop in a background thread,
|
||||
allowing child classes to use AsyncIO operations for exporting data.
|
||||
|
||||
Child classes must implement:
|
||||
- async _async_init(): AsyncIO initialization (e.g., connection setup)
|
||||
- async _async_exit(): AsyncIO cleanup (e.g., disconnection)
|
||||
- async _async_export(name, columns, points): AsyncIO export operation
|
||||
"""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the AsyncIO export interface."""
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# AsyncIO event loop management
|
||||
self.loop = None
|
||||
self._loop_ready = threading.Event()
|
||||
self._loop_exception = None
|
||||
self._shutdown = False
|
||||
|
||||
# Start the background event loop thread
|
||||
self._loop_thread = threading.Thread(target=self._run_event_loop, daemon=True)
|
||||
self._loop_thread.start()
|
||||
|
||||
# Wait for the loop to be ready
|
||||
if not self._loop_ready.wait(timeout=10):
|
||||
raise RuntimeError("AsyncIO event loop failed to start within timeout")
|
||||
|
||||
if self._loop_exception:
|
||||
raise RuntimeError(f"AsyncIO event loop creation failed: {self._loop_exception}")
|
||||
|
||||
if self.loop is None:
|
||||
raise RuntimeError("AsyncIO event loop is None after initialization")
|
||||
|
||||
# Call child class AsyncIO initialization
|
||||
future = asyncio.run_coroutine_threadsafe(self._async_init(), self.loop)
|
||||
try:
|
||||
future.result(timeout=10)
|
||||
logger.debug(f"{self.export_name} AsyncIO export initialized successfully")
|
||||
except Exception as e:
|
||||
logger.warning(f"{self.export_name} AsyncIO initialization failed: {e}. Will retry in background.")
|
||||
|
||||
def _run_event_loop(self):
|
||||
"""Run event loop in background thread."""
|
||||
try:
|
||||
self.loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(self.loop)
|
||||
self._loop_ready.set()
|
||||
self.loop.run_forever()
|
||||
except Exception as e:
|
||||
self._loop_exception = e
|
||||
self._loop_ready.set()
|
||||
logger.error(f"{self.export_name} AsyncIO event loop thread error: {e}")
|
||||
finally:
|
||||
# Clean up pending tasks
|
||||
pending = asyncio.all_tasks(self.loop)
|
||||
for task in pending:
|
||||
task.cancel()
|
||||
if pending:
|
||||
self.loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
|
||||
self.loop.close()
|
||||
|
||||
@abstractmethod
|
||||
async def _async_init(self):
|
||||
"""AsyncIO initialization method.
|
||||
|
||||
Child classes should implement this method to perform AsyncIO-based
|
||||
initialization such as connecting to servers, setting up clients, etc.
|
||||
|
||||
This method is called once during __init__ after the event loop is ready.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def _async_exit(self):
|
||||
"""AsyncIO cleanup method.
|
||||
|
||||
Child classes should implement this method to perform AsyncIO-based
|
||||
cleanup such as disconnecting from servers, closing clients, etc.
|
||||
|
||||
This method is called during exit() before stopping the event loop.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def _async_export(self, name, columns, points):
|
||||
"""AsyncIO export method.
|
||||
|
||||
Child classes must implement this method to perform the actual
|
||||
export operation using AsyncIO.
|
||||
|
||||
:param name: plugin name
|
||||
:param columns: list of column names
|
||||
:param points: list of values corresponding to columns
|
||||
"""
|
||||
pass
|
||||
|
||||
def exit(self):
|
||||
"""Close the AsyncIO export module."""
|
||||
super().exit()
|
||||
self._shutdown = True
|
||||
logger.info(f"{self.export_name} AsyncIO export shutting down")
|
||||
|
||||
# Call child class cleanup
|
||||
if self.loop:
|
||||
future = asyncio.run_coroutine_threadsafe(self._async_exit(), self.loop)
|
||||
try:
|
||||
future.result(timeout=5)
|
||||
except Exception as e:
|
||||
logger.error(f"{self.export_name} Error in AsyncIO cleanup: {e}")
|
||||
|
||||
# Stop the event loop
|
||||
if self.loop:
|
||||
self.loop.call_soon_threadsafe(self.loop.stop)
|
||||
time.sleep(0.5)
|
||||
|
||||
logger.debug(f"{self.export_name} AsyncIO export shutdown complete")
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Export data using AsyncIO.
|
||||
|
||||
This method bridges the synchronous export() interface with
|
||||
the AsyncIO _async_export() implementation.
|
||||
"""
|
||||
if self._shutdown:
|
||||
logger.debug(f"{self.export_name} Export called during shutdown, skipping")
|
||||
return
|
||||
|
||||
if not self.loop or not self.loop.is_running():
|
||||
logger.error(f"{self.export_name} AsyncIO event loop is not running")
|
||||
return
|
||||
|
||||
# Submit the export operation to the background event loop
|
||||
try:
|
||||
future = asyncio.run_coroutine_threadsafe(self._async_export(name, columns, points), self.loop)
|
||||
# Don't block forever - use a short timeout
|
||||
future.result(timeout=1)
|
||||
except asyncio.TimeoutError:
|
||||
logger.warning(f"{self.export_name} AsyncIO export timeout for {name}")
|
||||
except Exception as e:
|
||||
logger.error(f"{self.export_name} AsyncIO export error for {name}: {e}", exc_info=True)
|
||||
|
|
@ -105,7 +105,7 @@ class Export(GlancesExport):
|
|||
logger.debug(f"Export {name} stats to Cassandra")
|
||||
|
||||
# Remove non number stats and convert all to float (for Boolean)
|
||||
data = {k: float(v) for (k, v) in dict(zip(columns, points)).iteritems() if isinstance(v, Number)}
|
||||
data = {k: float(v) for k, v in zip(columns, points) if isinstance(v, Number)}
|
||||
|
||||
# Write input to the Cassandra table
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
# How to test ?
|
||||
#
|
||||
# 1) docker run -d -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=admin -p 5984:5984 --name my-couchdb couchdb
|
||||
# 2) ./venv/bin/python -m glances -C ./conf/glances.conf --export couchdb --quiet
|
||||
# 2) .venv/bin/python -m glances -C ./conf/glances.conf --export couchdb --quiet
|
||||
# 3) Result can be seen at: http://127.0.0.1:5984/_utils
|
||||
#
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,8 @@ class Export(GlancesExport):
|
|||
# Loop over plugins to export
|
||||
for plugin in self.plugins_to_export(stats):
|
||||
export_names, export_values = self.build_export(all_stats[plugin])
|
||||
# Add the plugin name in the field
|
||||
export_names = [plugin + '.' + n for n in export_names]
|
||||
if self.first_line:
|
||||
csv_header += export_names
|
||||
csv_data += export_values
|
||||
|
|
|
|||
|
|
@ -0,0 +1,195 @@
|
|||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2025 Nicolas Hennion <nicolas@nicolargo.com>
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
#
|
||||
|
||||
"""DuckDB interface class."""
|
||||
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime
|
||||
from platform import node
|
||||
|
||||
import duckdb
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
# Define the type conversions for DuckDB
|
||||
# https://duckdb.org/docs/stable/clients/python/conversion
|
||||
convert_types = {
|
||||
'bool': 'BOOLEAN',
|
||||
'int': 'BIGINT',
|
||||
'float': 'DOUBLE',
|
||||
'str': 'VARCHAR',
|
||||
'tuple': 'VARCHAR', # Store tuples as VARCHAR (comma-separated)
|
||||
'list': 'VARCHAR', # Store lists as VARCHAR (comma-separated)
|
||||
'NoneType': 'VARCHAR',
|
||||
}
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
"""This class manages the DuckDB export module."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the DuckDB export IF."""
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
self.db = None
|
||||
|
||||
# Optional configuration keys
|
||||
self.user = None
|
||||
self.password = None
|
||||
self.hostname = None
|
||||
|
||||
# Load the configuration file
|
||||
self.export_enable = self.load_conf(
|
||||
'duckdb', mandatories=['database'], options=['user', 'password', 'hostname']
|
||||
)
|
||||
if not self.export_enable:
|
||||
exit('Missing DuckDB config')
|
||||
|
||||
# The hostname is always add as an identifier in the DuckDB table
|
||||
# so we can filter the stats by hostname
|
||||
self.hostname = self.hostname or node().split(".")[0]
|
||||
|
||||
# Init the DuckDB client
|
||||
self.client = self.init()
|
||||
|
||||
def init(self):
|
||||
"""Init the connection to the DuckDB server."""
|
||||
if not self.export_enable:
|
||||
return None
|
||||
|
||||
try:
|
||||
db = duckdb.connect(database=self.database)
|
||||
except Exception as e:
|
||||
logger.critical(f"Cannot connect to DuckDB {self.database} ({e})")
|
||||
sys.exit(2)
|
||||
else:
|
||||
logger.info(f"Stats will be exported to DuckDB: {self.database}")
|
||||
|
||||
return db
|
||||
|
||||
def normalize(self, value):
|
||||
# Nothing to do...
|
||||
if isinstance(value, list) and len(value) == 1 and value[0] in ['True', 'False']:
|
||||
return bool(value[0])
|
||||
return value
|
||||
|
||||
def update(self, stats):
|
||||
"""Update the DuckDB export module."""
|
||||
if not self.export_enable:
|
||||
return False
|
||||
|
||||
# Get all the stats & limits
|
||||
# Current limitation with sensors and fs plugins because fields list is not the same
|
||||
self._last_exported_list = [p for p in self.plugins_to_export(stats) if p not in ['sensors', 'fs']]
|
||||
all_stats = stats.getAllExportsAsDict(plugin_list=self.last_exported_list())
|
||||
all_limits = stats.getAllLimitsAsDict(plugin_list=self.last_exported_list())
|
||||
|
||||
# Loop over plugins to export
|
||||
for plugin in self.last_exported_list():
|
||||
# Remove some fields
|
||||
if isinstance(all_stats[plugin], dict):
|
||||
all_stats[plugin].update(all_limits[plugin])
|
||||
# Remove the <plugin>_disable field
|
||||
all_stats[plugin].pop(f"{plugin}_disable", None)
|
||||
elif isinstance(all_stats[plugin], list):
|
||||
for i in all_stats[plugin]:
|
||||
i.update(all_limits[plugin])
|
||||
# Remove the <plugin>_disable field
|
||||
i.pop(f"{plugin}_disable", None)
|
||||
else:
|
||||
continue
|
||||
|
||||
plugin_stats = all_stats[plugin]
|
||||
creation_list = [] # List used to create the DuckDB table
|
||||
values_list = [] # List of values to insert (list of lists, one list per row)
|
||||
if isinstance(plugin_stats, dict):
|
||||
# Create the list to create the table
|
||||
creation_list.append('time TIMETZ')
|
||||
creation_list.append('hostname_id VARCHAR')
|
||||
for key, value in plugin_stats.items():
|
||||
creation_list.append(f"{key} {convert_types[type(self.normalize(value)).__name__]}")
|
||||
# Create the list of values to insert
|
||||
item_list = []
|
||||
item_list.append(self.normalize(datetime.now().replace(microsecond=0)))
|
||||
item_list.append(self.normalize(f"{self.hostname}"))
|
||||
item_list.extend([self.normalize(value) for value in plugin_stats.values()])
|
||||
values_list = [item_list]
|
||||
elif isinstance(plugin_stats, list) and len(plugin_stats) > 0 and 'key' in plugin_stats[0]:
|
||||
# Create the list to create the table
|
||||
creation_list.append('time TIMETZ')
|
||||
creation_list.append('hostname_id VARCHAR')
|
||||
creation_list.append('key_id VARCHAR')
|
||||
for key, value in plugin_stats[0].items():
|
||||
creation_list.append(f"{key} {convert_types[type(self.normalize(value)).__name__]}")
|
||||
# Create the list of values to insert
|
||||
for plugin_item in plugin_stats:
|
||||
item_list = []
|
||||
item_list.append(self.normalize(datetime.now().replace(microsecond=0)))
|
||||
item_list.append(self.normalize(f"{self.hostname}"))
|
||||
item_list.append(self.normalize(f"{plugin_item.get('key')}"))
|
||||
item_list.extend([self.normalize(value) for value in plugin_item.values()])
|
||||
values_list.append(item_list)
|
||||
else:
|
||||
continue
|
||||
|
||||
# Export stats to DuckDB
|
||||
self.export(plugin, creation_list, values_list)
|
||||
|
||||
return True
|
||||
|
||||
def export(self, plugin, creation_list, values_list):
|
||||
"""Export the stats to the DuckDB server."""
|
||||
logger.debug(f"Export {plugin} stats to DuckDB")
|
||||
|
||||
# Create the table if it does not exist
|
||||
table_list = [t[0] for t in self.client.sql("SHOW TABLES").fetchall()]
|
||||
if plugin not in table_list:
|
||||
# Execute the create table query
|
||||
create_query = f"""
|
||||
CREATE TABLE {plugin} (
|
||||
{', '.join(creation_list)}
|
||||
);"""
|
||||
logger.debug(f"Create table: {create_query}")
|
||||
try:
|
||||
self.client.execute(create_query)
|
||||
except Exception as e:
|
||||
logger.error(f"Cannot create table {plugin}: {e}")
|
||||
return
|
||||
|
||||
# Commit the changes
|
||||
self.client.commit()
|
||||
|
||||
# Insert values into the table
|
||||
for values in values_list:
|
||||
insert_query = f"""
|
||||
INSERT INTO {plugin} VALUES (
|
||||
{', '.join(['?' for _ in values])}
|
||||
);"""
|
||||
logger.debug(f"Insert values into table {plugin}: {values}")
|
||||
try:
|
||||
self.client.execute(insert_query, values)
|
||||
except Exception as e:
|
||||
logger.error(f"Cannot insert data into table {plugin}: {e}")
|
||||
|
||||
# Commit the changes
|
||||
self.client.commit()
|
||||
|
||||
def exit(self):
|
||||
"""Close the DuckDB export module."""
|
||||
# Force last write
|
||||
self.client.commit()
|
||||
|
||||
# Close the DuckDB client
|
||||
time.sleep(3) # Wait a bit to ensure all data is written
|
||||
self.client.close()
|
||||
|
||||
# Call the father method
|
||||
super().exit()
|
||||
|
|
@ -17,7 +17,7 @@ import pygal.style
|
|||
from pygal import DateTimeLine
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.globals import iteritems, time_series_subsample
|
||||
from glances.globals import time_series_subsample
|
||||
from glances.logger import logger
|
||||
from glances.timer import Timer
|
||||
|
||||
|
|
@ -120,7 +120,7 @@ class Export(GlancesExport):
|
|||
x_label_rotation=20,
|
||||
x_value_formatter=lambda dt: dt.strftime('%Y/%m/%d %H:%M:%S'),
|
||||
)
|
||||
for k, v in iteritems(time_series_subsample(data, self.width)):
|
||||
for k, v in time_series_subsample(data, self.width).items():
|
||||
chart.add(k, v)
|
||||
chart.render_to_file(os.path.join(self.path, title + '.svg'))
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@ from influxdb.client import InfluxDBClientError
|
|||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
FIELD_TO_TAG = ['name', 'cmdline', 'type']
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
"""This class manages the InfluxDB export module."""
|
||||
|
|
@ -33,20 +31,22 @@ class Export(GlancesExport):
|
|||
self.db = None
|
||||
|
||||
# Optional configuration keys
|
||||
self.protocol = 'http'
|
||||
self.protocol = "http"
|
||||
self.prefix = None
|
||||
self.tags = None
|
||||
self.hostname = None
|
||||
|
||||
# Load the InfluxDB configuration file
|
||||
self.export_enable = self.load_conf(
|
||||
'influxdb', mandatories=['host', 'port', 'user', 'password', 'db'], options=['protocol', 'prefix', 'tags']
|
||||
"influxdb",
|
||||
mandatories=["host", "port", "user", "password", "db"],
|
||||
options=["protocol", "prefix", "tags"],
|
||||
)
|
||||
if not self.export_enable:
|
||||
exit('Missing INFLUXDB version 1 config')
|
||||
exit("Missing influxdb config")
|
||||
|
||||
# The hostname is always add as a tag
|
||||
self.hostname = node().split('.')[0]
|
||||
self.hostname = node().split(".")[0]
|
||||
|
||||
# Init the InfluxDB client
|
||||
self.client = self.init()
|
||||
|
|
@ -57,7 +57,7 @@ class Export(GlancesExport):
|
|||
return None
|
||||
|
||||
# Correct issue #1530
|
||||
if self.protocol is not None and (self.protocol.lower() == 'https'):
|
||||
if self.protocol is not None and (self.protocol.lower() == "https"):
|
||||
ssl = True
|
||||
else:
|
||||
ssl = False
|
||||
|
|
@ -72,7 +72,7 @@ class Export(GlancesExport):
|
|||
password=self.password,
|
||||
database=self.db,
|
||||
)
|
||||
get_all_db = [i['name'] for i in db.get_list_database()]
|
||||
get_all_db = [i["name"] for i in db.get_list_database()]
|
||||
except InfluxDBClientError as e:
|
||||
logger.critical(f"Cannot connect to InfluxDB database '{self.db}' ({e})")
|
||||
sys.exit(2)
|
||||
|
|
@ -85,76 +85,20 @@ class Export(GlancesExport):
|
|||
|
||||
return db
|
||||
|
||||
def _normalize(self, name, columns, points):
|
||||
"""Normalize data for the InfluxDB's data model.
|
||||
|
||||
:return: a list of measurements.
|
||||
"""
|
||||
ret = []
|
||||
|
||||
# Build initial dict by crossing columns and point
|
||||
data_dict = dict(zip(columns, points))
|
||||
|
||||
# issue1871 - Check if a key exist. If a key exist, the value of
|
||||
# the key should be used as a tag to identify the measurement.
|
||||
keys_list = [k.split('.')[0] for k in columns if k.endswith('.key')]
|
||||
if not keys_list:
|
||||
keys_list = [None]
|
||||
|
||||
for measurement in keys_list:
|
||||
# Manage field
|
||||
if measurement is not None:
|
||||
fields = {
|
||||
k.replace(f'{measurement}.', ''): data_dict[k] for k in data_dict if k.startswith(f'{measurement}.')
|
||||
}
|
||||
else:
|
||||
fields = data_dict
|
||||
# Transform to InfluxDB data model
|
||||
# https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_reference/
|
||||
for k in fields:
|
||||
# Do not export empty (None) value
|
||||
if fields[k] is None:
|
||||
continue
|
||||
# Convert numerical to float
|
||||
try:
|
||||
fields[k] = float(fields[k])
|
||||
except (TypeError, ValueError):
|
||||
# Convert others to string
|
||||
try:
|
||||
fields[k] = str(fields[k])
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
# Manage tags
|
||||
tags = self.parse_tags(self.tags)
|
||||
if 'key' in fields and fields['key'] in fields:
|
||||
# Create a tag from the key
|
||||
# Tag should be an string (see InfluxDB data model)
|
||||
tags[fields['key']] = str(fields[fields['key']])
|
||||
# Remove it from the field list (can not be a field and a tag)
|
||||
fields.pop(fields['key'])
|
||||
# Add the hostname as a tag
|
||||
tags['hostname'] = self.hostname
|
||||
# Add name as a tag (example for the process list)
|
||||
for k in FIELD_TO_TAG:
|
||||
if k in fields:
|
||||
tags[k] = str(fields[k])
|
||||
# Remove it from the field list (can not be a field and a tag)
|
||||
fields.pop(k)
|
||||
# Add the measurement to the list
|
||||
ret.append({'measurement': name, 'tags': tags, 'fields': fields})
|
||||
return ret
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Write the points to the InfluxDB server."""
|
||||
# Manage prefix
|
||||
if self.prefix is not None:
|
||||
name = self.prefix + '.' + name
|
||||
name = self.prefix + "." + name
|
||||
# Write input to the InfluxDB database
|
||||
if not points:
|
||||
logger.debug(f"Cannot export empty {name} stats to InfluxDB")
|
||||
else:
|
||||
try:
|
||||
self.client.write_points(self._normalize(name, columns, points), time_precision="s")
|
||||
self.client.write_points(
|
||||
self.normalize_for_influxdb(name, columns, points),
|
||||
time_precision="s",
|
||||
)
|
||||
except Exception as e:
|
||||
# Log level set to warning instead of error (see: issue #1561)
|
||||
logger.warning(f"Cannot export {name} stats to InfluxDB ({e})")
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
#
|
||||
|
||||
"""InfluxDB (from to InfluxDB 1.8+) interface class."""
|
||||
"""InfluxDB (from to InfluxDB 1.8+ to <3.0) interface class."""
|
||||
|
||||
import sys
|
||||
from platform import node
|
||||
|
|
@ -16,8 +16,6 @@ from influxdb_client import InfluxDBClient, WriteOptions
|
|||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
FIELD_TO_TAG = ['name', 'cmdline', 'type']
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
"""This class manages the InfluxDB export module."""
|
||||
|
|
@ -32,7 +30,7 @@ class Export(GlancesExport):
|
|||
self.token = None
|
||||
|
||||
# Optional configuration keys
|
||||
self.protocol = 'http'
|
||||
self.protocol = "http"
|
||||
self.prefix = None
|
||||
self.tags = None
|
||||
self.hostname = None
|
||||
|
|
@ -40,12 +38,12 @@ class Export(GlancesExport):
|
|||
|
||||
# Load the InfluxDB configuration file
|
||||
self.export_enable = self.load_conf(
|
||||
'influxdb2',
|
||||
mandatories=['host', 'port', 'user', 'password', 'org', 'bucket', 'token'],
|
||||
options=['protocol', 'prefix', 'tags', 'interval'],
|
||||
"influxdb2",
|
||||
mandatories=["host", "port", "user", "password", "org", "bucket", "token"],
|
||||
options=["protocol", "prefix", "tags", "interval"],
|
||||
)
|
||||
if not self.export_enable:
|
||||
exit('Missing influxdb2 config')
|
||||
exit("Missing influxdb2 config")
|
||||
|
||||
# Interval between two exports (in seconds)
|
||||
if self.interval is None:
|
||||
|
|
@ -60,7 +58,7 @@ class Export(GlancesExport):
|
|||
logger.debug(f"InfluxDB export interval is set to {self.interval} seconds")
|
||||
|
||||
# The hostname is always add as a tag
|
||||
self.hostname = node().split('.')[0]
|
||||
self.hostname = node().split(".")[0]
|
||||
|
||||
# Init the InfluxDB client
|
||||
self.client = self.init()
|
||||
|
|
@ -70,10 +68,16 @@ class Export(GlancesExport):
|
|||
if not self.export_enable:
|
||||
return None
|
||||
|
||||
url = f'{self.protocol}://{self.host}:{self.port}'
|
||||
url = f"{self.protocol}://{self.host}:{self.port}"
|
||||
try:
|
||||
# See docs: https://influxdb-client.readthedocs.io/en/stable/api.html#influxdbclient
|
||||
client = InfluxDBClient(url=url, enable_gzip=False, verify_ssl=False, org=self.org, token=self.token)
|
||||
client = InfluxDBClient(
|
||||
url=url,
|
||||
enable_gzip=False,
|
||||
verify_ssl=False,
|
||||
org=self.org,
|
||||
token=self.token,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.critical(f"Cannot connect to InfluxDB server '{url}' ({e})")
|
||||
sys.exit(2)
|
||||
|
|
@ -93,76 +97,22 @@ class Export(GlancesExport):
|
|||
)
|
||||
)
|
||||
|
||||
def _normalize(self, name, columns, points):
|
||||
"""Normalize data for the InfluxDB's data model.
|
||||
|
||||
:return: a list of measurements.
|
||||
"""
|
||||
ret = []
|
||||
|
||||
# Build initial dict by crossing columns and point
|
||||
data_dict = dict(zip(columns, points))
|
||||
|
||||
# issue1871 - Check if a key exist. If a key exist, the value of
|
||||
# the key should be used as a tag to identify the measurement.
|
||||
keys_list = [k.split('.')[0] for k in columns if k.endswith('.key')]
|
||||
if not keys_list:
|
||||
keys_list = [None]
|
||||
|
||||
for measurement in keys_list:
|
||||
# Manage field
|
||||
if measurement is not None:
|
||||
fields = {
|
||||
k.replace(f'{measurement}.', ''): data_dict[k] for k in data_dict if k.startswith(f'{measurement}.')
|
||||
}
|
||||
else:
|
||||
fields = data_dict
|
||||
# Transform to InfluxDB datamodel
|
||||
# https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/
|
||||
for k in fields:
|
||||
# Do not export empty (None) value
|
||||
if fields[k] is None:
|
||||
continue
|
||||
# Convert numerical to float
|
||||
try:
|
||||
fields[k] = float(fields[k])
|
||||
except (TypeError, ValueError):
|
||||
# Convert others to string
|
||||
try:
|
||||
fields[k] = str(fields[k])
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
# Manage tags
|
||||
tags = self.parse_tags(self.tags)
|
||||
if 'key' in fields and fields['key'] in fields:
|
||||
# Create a tag from the key
|
||||
# Tag should be an string (see InfluxDB data model)
|
||||
tags[fields['key']] = str(fields[fields['key']])
|
||||
# Remove it from the field list (can not be a field and a tag)
|
||||
fields.pop(fields['key'])
|
||||
# Add the hostname as a tag
|
||||
tags['hostname'] = self.hostname
|
||||
# Add name as a tag (example for the process list)
|
||||
for k in FIELD_TO_TAG:
|
||||
if k in fields:
|
||||
tags[k] = str(fields[k])
|
||||
# Remove it from the field list (can not be a field and a tag)
|
||||
fields.pop(k)
|
||||
# Add the measurement to the list
|
||||
ret.append({'measurement': name, 'tags': tags, 'fields': fields})
|
||||
return ret
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Write the points to the InfluxDB server."""
|
||||
# Manage prefix
|
||||
if self.prefix is not None:
|
||||
name = self.prefix + '.' + name
|
||||
name = self.prefix + "." + name
|
||||
# Write input to the InfluxDB database
|
||||
if not points:
|
||||
logger.debug(f"Cannot export empty {name} stats to InfluxDB")
|
||||
else:
|
||||
try:
|
||||
self.client.write(self.bucket, self.org, self._normalize(name, columns, points), time_precision="s")
|
||||
self.client.write(
|
||||
self.bucket,
|
||||
self.org,
|
||||
self.normalize_for_influxdb(name, columns, points),
|
||||
time_precision="s",
|
||||
)
|
||||
except Exception as e:
|
||||
# Log level set to warning instead of error (see: issue #1561)
|
||||
logger.warning(f"Cannot export {name} stats to InfluxDB ({e})")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2025 Nicolas Hennion <nicolas@nicolargo.com>
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
#
|
||||
|
||||
"""InfluxDB (for InfluxDB 3.x) interface class."""
|
||||
|
||||
import sys
|
||||
from platform import node
|
||||
|
||||
from influxdb_client_3 import InfluxDBClient3
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
"""This class manages the InfluxDB export module."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the InfluxDB export IF."""
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
self.host = None
|
||||
self.port = None
|
||||
self.org = None
|
||||
self.database = None
|
||||
self.token = None
|
||||
|
||||
# Optional configuration keys
|
||||
self.prefix = None
|
||||
self.tags = None
|
||||
self.hostname = None
|
||||
|
||||
# Load the InfluxDB configuration file
|
||||
self.export_enable = self.load_conf(
|
||||
"influxdb3",
|
||||
mandatories=["host", "port", "org", "database", "token"],
|
||||
options=["prefix", "tags"],
|
||||
)
|
||||
if not self.export_enable:
|
||||
exit("Missing influxdb3 config")
|
||||
|
||||
# The hostname is always add as a tag
|
||||
self.hostname = node().split(".")[0]
|
||||
|
||||
# Init the InfluxDB client
|
||||
self.client = self.init()
|
||||
|
||||
def init(self):
|
||||
"""Init the connection to the InfluxDB server."""
|
||||
if not self.export_enable:
|
||||
return None
|
||||
|
||||
try:
|
||||
db = InfluxDBClient3(
|
||||
host=self.host,
|
||||
org=self.org,
|
||||
database=self.database,
|
||||
token=self.token,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.critical(f"Cannot connect to InfluxDB database '{self.database}' ({e})")
|
||||
sys.exit(2)
|
||||
|
||||
if self.database == db._database:
|
||||
logger.info(
|
||||
f"Stats will be exported to InfluxDB server {self.host}:{self.port} in {self.database} database"
|
||||
)
|
||||
else:
|
||||
logger.critical(f"InfluxDB database '{self.database}' did not exist. Please create it")
|
||||
sys.exit(2)
|
||||
|
||||
return db
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Write the points to the InfluxDB server."""
|
||||
# Manage prefix
|
||||
if self.prefix is not None:
|
||||
name = self.prefix + "." + name
|
||||
# Write input to the InfluxDB database
|
||||
if not points:
|
||||
logger.debug(f"Cannot export empty {name} stats to InfluxDB")
|
||||
else:
|
||||
try:
|
||||
self.client.write(
|
||||
record=self.normalize_for_influxdb(name, columns, points),
|
||||
time_precision="s",
|
||||
)
|
||||
except Exception as e:
|
||||
# Log level set to warning instead of error (see: issue #1561)
|
||||
logger.warning(f"Cannot export {name} stats to InfluxDB ({e})")
|
||||
else:
|
||||
logger.debug(f"Export {name} stats to InfluxDB")
|
||||
|
|
@ -48,7 +48,10 @@ class Export(GlancesExport):
|
|||
|
||||
# Export stats to JSON file
|
||||
with open(self.json_filename, "wb") as self.json_file:
|
||||
self.json_file.write(json_dumps(self.buffer) + b'\n')
|
||||
try:
|
||||
self.json_file.write(json_dumps(self.buffer) + b'\n')
|
||||
except Exception as e:
|
||||
logger.error(f'Can not export data to JSON ({e})')
|
||||
|
||||
# Reset buffer
|
||||
self.buffer = {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2026 Nicolas Hennion <nicolas@nicolargo.com>
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
#
|
||||
|
||||
"""NATS interface class."""
|
||||
|
||||
from nats.aio.client import Client as NATS
|
||||
from nats.errors import ConnectionClosedError
|
||||
from nats.errors import TimeoutError as NatsTimeoutError
|
||||
|
||||
from glances.exports.export_asyncio import GlancesExportAsyncio
|
||||
from glances.globals import json_dumps
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
class Export(GlancesExportAsyncio):
|
||||
"""This class manages the NATS export module."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the NATS export IF."""
|
||||
# Load the NATS configuration file before calling super().__init__
|
||||
# because super().__init__ will call _async_init() which needs config
|
||||
self.config = config
|
||||
self.args = args
|
||||
self.export_name = self.__class__.__module__
|
||||
|
||||
export_enable = self.load_conf(
|
||||
'nats',
|
||||
mandatories=['host'],
|
||||
options=['prefix'],
|
||||
)
|
||||
if not export_enable:
|
||||
exit('Missing NATS config')
|
||||
|
||||
self.prefix = self.prefix or 'glances'
|
||||
# Host is a comma-separated list of NATS servers
|
||||
self.hosts = self.host
|
||||
|
||||
# NATS-specific attributes
|
||||
self.client = None
|
||||
self._connected = False
|
||||
self._publish_count = 0
|
||||
|
||||
# Call parent __init__ which will start event loop and call _async_init()
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Restore export_enable after super().__init__() resets it to False
|
||||
self.export_enable = export_enable
|
||||
|
||||
async def _async_init(self):
|
||||
"""Connect to NATS with error handling."""
|
||||
try:
|
||||
if self.client:
|
||||
try:
|
||||
await self.client.close()
|
||||
except Exception as e:
|
||||
logger.debug(f"NATS Error closing existing client: {e}")
|
||||
|
||||
self.client = NATS()
|
||||
|
||||
logger.debug(f"NATS Connecting to servers: {self.hosts}")
|
||||
|
||||
# Configure with reconnection callbacks
|
||||
await self.client.connect(
|
||||
servers=[s.strip() for s in self.hosts.split(',')],
|
||||
reconnect_time_wait=2,
|
||||
max_reconnect_attempts=60,
|
||||
error_cb=self._error_callback,
|
||||
disconnected_cb=self._disconnected_callback,
|
||||
reconnected_cb=self._reconnected_callback,
|
||||
)
|
||||
|
||||
self._connected = True
|
||||
logger.info(f"NATS Successfully connected to servers: {self.hosts}")
|
||||
except Exception as e:
|
||||
self._connected = False
|
||||
logger.error(f"NATS connection error: {e}")
|
||||
raise
|
||||
|
||||
async def _error_callback(self, e):
|
||||
"""Called when NATS client encounters an error."""
|
||||
logger.error(f"NATS error callback: {e}")
|
||||
|
||||
async def _disconnected_callback(self):
|
||||
"""Called when disconnected from NATS."""
|
||||
self._connected = False
|
||||
logger.debug("NATS disconnected callback")
|
||||
|
||||
async def _reconnected_callback(self):
|
||||
"""Called when reconnected to NATS."""
|
||||
self._connected = True
|
||||
logger.debug("NATS reconnected callback")
|
||||
|
||||
async def _async_exit(self):
|
||||
"""Disconnect from NATS."""
|
||||
try:
|
||||
if self.client and self._connected:
|
||||
await self.client.drain()
|
||||
await self.client.close()
|
||||
self._connected = False
|
||||
logger.debug(f"NATS disconnected cleanly. Total messages published: {self._publish_count}")
|
||||
except Exception as e:
|
||||
logger.error(f"NATS Error in disconnect: {e}")
|
||||
|
||||
async def _async_export(self, name, columns, points):
|
||||
"""Write the points to NATS using AsyncIO."""
|
||||
if not self._connected:
|
||||
logger.warning("NATS not connected, skipping export")
|
||||
return
|
||||
|
||||
subject_name = f"{self.prefix}.{name}"
|
||||
subject_data = dict(zip(columns, points))
|
||||
|
||||
# Publish data to NATS
|
||||
try:
|
||||
if not self._connected:
|
||||
raise ConnectionClosedError("NATS Not connected to server")
|
||||
await self.client.publish(subject_name, json_dumps(subject_data))
|
||||
await self.client.flush(timeout=2.0)
|
||||
self._publish_count += 1
|
||||
except (ConnectionClosedError, NatsTimeoutError) as e:
|
||||
self._connected = False
|
||||
logger.error(f"NATS publish failed for {subject_name}: {e}")
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"NATS Unexpected error publishing {subject_name}: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
# End of glances/exports/glances_nats/__init__.py
|
||||
|
|
@ -57,7 +57,7 @@ class Export(GlancesExport):
|
|||
return db
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Export the stats to the Statsd server."""
|
||||
"""Export the stats to the OpenTSDB server."""
|
||||
for i in range(len(columns)):
|
||||
if not isinstance(points[i], Number):
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ from numbers import Number
|
|||
from prometheus_client import Gauge, start_http_server
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.globals import iteritems, listkeys
|
||||
from glances.globals import listkeys
|
||||
from glances.logger import logger
|
||||
|
||||
|
||||
|
|
@ -43,6 +43,9 @@ class Export(GlancesExport):
|
|||
# Perhaps a better method is possible...
|
||||
self._metric_dict = {}
|
||||
|
||||
# Keys name (compute in update() method)
|
||||
self.keys_name = {}
|
||||
|
||||
# Init the Prometheus Exporter
|
||||
self.init()
|
||||
|
||||
|
|
@ -56,29 +59,41 @@ class Export(GlancesExport):
|
|||
else:
|
||||
logger.info(f"Start Prometheus exporter on {self.host}:{self.port}")
|
||||
|
||||
def update(self, stats):
|
||||
self.keys_name = {k: stats.get_plugin(k).get_key() for k in stats.getPluginsList()}
|
||||
super().update(stats)
|
||||
|
||||
def export(self, name, columns, points):
|
||||
"""Write the points to the Prometheus exporter using Gauge."""
|
||||
logger.debug(f"Export {name} stats to Prometheus exporter")
|
||||
|
||||
# Remove non number stats and convert all to float (for Boolean)
|
||||
data = {k: float(v) for (k, v) in iteritems(dict(zip(columns, points))) if isinstance(v, Number)}
|
||||
data = {str(k): float(v) for k, v in zip(columns, points) if isinstance(v, Number)}
|
||||
|
||||
# Write metrics to the Prometheus exporter
|
||||
for k, v in iteritems(data):
|
||||
# Prometheus metric name: prefix_<glances stats name>
|
||||
metric_name = self.prefix + self.METRIC_SEPARATOR + str(name) + self.METRIC_SEPARATOR + str(k)
|
||||
for metric, value in data.items():
|
||||
labels = self.labels
|
||||
metric_name = self.prefix + self.METRIC_SEPARATOR + name + self.METRIC_SEPARATOR
|
||||
try:
|
||||
obj, stat = metric.split('.')
|
||||
metric_name += stat
|
||||
labels += f",{self.keys_name.get(name)}:{obj}"
|
||||
except ValueError:
|
||||
metric_name += metric
|
||||
|
||||
# Prometheus is very sensible to the metric name
|
||||
# See: https://prometheus.io/docs/practices/naming/
|
||||
for c in ' .-/:[]':
|
||||
metric_name = metric_name.replace(c, self.METRIC_SEPARATOR)
|
||||
|
||||
# Get the labels
|
||||
labels = self.parse_tags(self.labels)
|
||||
labels = self.parse_tags(labels)
|
||||
# Manage an internal dict between metric name and Gauge
|
||||
if metric_name not in self._metric_dict:
|
||||
self._metric_dict[metric_name] = Gauge(metric_name, k, labelnames=listkeys(labels))
|
||||
self._metric_dict[metric_name] = Gauge(metric_name, "", labelnames=listkeys(labels))
|
||||
# Write the value
|
||||
if hasattr(self._metric_dict[metric_name], 'labels'):
|
||||
# Add the labels (see issue #1255)
|
||||
self._metric_dict[metric_name].labels(**labels).set(v)
|
||||
self._metric_dict[metric_name].labels(**labels).set(value)
|
||||
else:
|
||||
self._metric_dict[metric_name].set(v)
|
||||
self._metric_dict[metric_name].set(value)
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ class Export(GlancesExport):
|
|||
# One complete loop have been done
|
||||
logger.debug(f"Export stats ({listkeys(self.buffer)}) to RESTful endpoint ({self.client})")
|
||||
# Export stats
|
||||
post(self.client, json=self.buffer, allow_redirects=True)
|
||||
post(self.client, json=self.buffer, allow_redirects=True, timeout=15)
|
||||
# Reset buffer
|
||||
self.buffer = {}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,223 @@
|
|||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2025 Nicolas Hennion <nicolas@nicolargo.com>
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
#
|
||||
|
||||
"""TimescaleDB interface class."""
|
||||
|
||||
import sys
|
||||
import time
|
||||
from platform import node
|
||||
|
||||
import psycopg
|
||||
|
||||
from glances.exports.export import GlancesExport
|
||||
from glances.logger import logger
|
||||
|
||||
# Define the type conversions for TimescaleDB
|
||||
# https://www.postgresql.org/docs/current/datatype.html
|
||||
convert_types = {
|
||||
'bool': 'BOOLEAN',
|
||||
'int': 'BIGINT',
|
||||
'float': 'DOUBLE PRECISION',
|
||||
'str': 'TEXT',
|
||||
'tuple': 'TEXT', # Store tuples as TEXT (comma-separated)
|
||||
'list': 'TEXT', # Store lists as TEXT (comma-separated)
|
||||
'NoneType': 'DOUBLE PRECISION', # Use DOUBLE PRECISION for NoneType to avoid issues with NULL
|
||||
}
|
||||
|
||||
|
||||
class Export(GlancesExport):
|
||||
"""This class manages the TimescaleDB export module."""
|
||||
|
||||
def __init__(self, config=None, args=None):
|
||||
"""Init the TimescaleDB export IF."""
|
||||
super().__init__(config=config, args=args)
|
||||
|
||||
# Mandatory configuration keys (additional to host and port)
|
||||
self.db = None
|
||||
|
||||
# Optional configuration keys
|
||||
self.user = None
|
||||
self.password = None
|
||||
self.hostname = None
|
||||
|
||||
# Load the configuration file
|
||||
self.export_enable = self.load_conf(
|
||||
'timescaledb', mandatories=['host', 'port', 'db'], options=['user', 'password', 'hostname']
|
||||
)
|
||||
if not self.export_enable:
|
||||
exit('Missing TimescaleDB config')
|
||||
|
||||
# The hostname is always add as an identifier in the TimescaleDB table
|
||||
# so we can filter the stats by hostname
|
||||
self.hostname = self.hostname or node().split(".")[0]
|
||||
|
||||
# Init the TimescaleDB client
|
||||
self.client = self.init()
|
||||
|
||||
def init(self):
|
||||
"""Init the connection to the TimescaleDB server."""
|
||||
if not self.export_enable:
|
||||
return None
|
||||
|
||||
try:
|
||||
# See https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
|
||||
conn_str = f"host={self.host} port={self.port} dbname={self.db} user={self.user} password={self.password}"
|
||||
db = psycopg.connect(conn_str)
|
||||
except Exception as e:
|
||||
logger.critical(f"Cannot connect to TimescaleDB server {self.host}:{self.port} ({e})")
|
||||
sys.exit(2)
|
||||
else:
|
||||
logger.info(f"Stats will be exported to TimescaleDB server: {self.host}:{self.port}")
|
||||
|
||||
return db
|
||||
|
||||
def normalize(self, value):
|
||||
"""Normalize the value to be exportable to TimescaleDB."""
|
||||
if value is None:
|
||||
return 'NULL'
|
||||
if isinstance(value, bool):
|
||||
return str(value).upper()
|
||||
if isinstance(value, (list, tuple)):
|
||||
# Special case for list of one boolean
|
||||
if len(value) == 1 and isinstance(value[0], bool):
|
||||
return str(value[0]).upper()
|
||||
return ', '.join([f"'{v}'" for v in value])
|
||||
if isinstance(value, str):
|
||||
return f"'{value}'"
|
||||
|
||||
return f"{value}"
|
||||
|
||||
def update(self, stats):
|
||||
"""Update the TimescaleDB export module."""
|
||||
if not self.export_enable:
|
||||
return False
|
||||
|
||||
# Get all the stats & limits
|
||||
# @TODO: Current limitation with sensors, fs and diskio plugins because fields list is not the same
|
||||
self._last_exported_list = [p for p in self.plugins_to_export(stats) if p not in ['sensors', 'fs', 'diskio']]
|
||||
all_stats = stats.getAllExportsAsDict(plugin_list=self.last_exported_list())
|
||||
all_limits = stats.getAllLimitsAsDict(plugin_list=self.last_exported_list())
|
||||
|
||||
# Loop over plugins to export
|
||||
for plugin in self.last_exported_list():
|
||||
if isinstance(all_stats[plugin], dict):
|
||||
all_stats[plugin].update(all_limits[plugin])
|
||||
# Remove the <plugin>_disable field
|
||||
all_stats[plugin].pop(f"{plugin}_disable", None)
|
||||
# user is a special field that should not be exported
|
||||
# rename it to user_<plugin>
|
||||
if 'user' in all_stats[plugin]:
|
||||
all_stats[plugin][f'user_{plugin}'] = all_stats[plugin].pop('user')
|
||||
elif isinstance(all_stats[plugin], list):
|
||||
for i in all_stats[plugin]:
|
||||
i.update(all_limits[plugin])
|
||||
# Remove the <plugin>_disable field
|
||||
i.pop(f"{plugin}_disable", None)
|
||||
# user is a special field that should not be exported
|
||||
# rename it to user_<plugin>
|
||||
if 'user' in i:
|
||||
i[f'user_{plugin}'] = i.pop('user')
|
||||
else:
|
||||
continue
|
||||
|
||||
plugin_stats = all_stats[plugin]
|
||||
creation_list = [] # List used to create the TimescaleDB table
|
||||
segmented_by = [] # List of columns used to segment the data
|
||||
values_list = [] # List of values to insert (list of lists, one list per row)
|
||||
if isinstance(plugin_stats, dict):
|
||||
# Stats is a dict
|
||||
# Create the list used to create the TimescaleDB table
|
||||
creation_list.append('time TIMESTAMPTZ NOT NULL')
|
||||
creation_list.append('hostname_id TEXT NOT NULL')
|
||||
segmented_by.extend(['hostname_id']) # Segment by hostname
|
||||
for key, value in plugin_stats.items():
|
||||
creation_list.append(f"{key} {convert_types[type(value).__name__]} NULL")
|
||||
values_list.append('NOW()') # Add the current time (insertion time)
|
||||
values_list.append(f"'{self.hostname}'") # Add the hostname
|
||||
values_list.extend([self.normalize(value) for value in plugin_stats.values()])
|
||||
values_list = [values_list]
|
||||
elif isinstance(plugin_stats, list) and len(plugin_stats) > 0 and 'key' in plugin_stats[0]:
|
||||
# Stats is a list
|
||||
# Create the list used to create the TimescaleDB table
|
||||
creation_list.append('time TIMESTAMPTZ NOT NULL')
|
||||
creation_list.append('hostname_id TEXT NOT NULL')
|
||||
creation_list.append('key_id TEXT NOT NULL')
|
||||
segmented_by.extend(['hostname_id', 'key_id']) # Segment by hostname and key
|
||||
for key, value in plugin_stats[0].items():
|
||||
creation_list.append(f"{key} {convert_types[type(value).__name__]} NULL")
|
||||
# Create the values list (it is a list of list to have a single datamodel for all the plugins)
|
||||
for plugin_item in plugin_stats:
|
||||
item_list = []
|
||||
item_list.append('NOW()') # Add the current time (insertion time)
|
||||
item_list.append(f"'{self.hostname}'") # Add the hostname
|
||||
item_list.append(f"'{plugin_item.get('key')}'")
|
||||
item_list.extend([self.normalize(value) for value in plugin_item.values()])
|
||||
values_list.append(item_list[:-1])
|
||||
else:
|
||||
continue
|
||||
|
||||
# Export stats to TimescaleDB
|
||||
# logger.info(plugin)
|
||||
# logger.info(f"Segmented by: {segmented_by}")
|
||||
# logger.info(list(zip(creation_list, values_list[0])))
|
||||
self.export(plugin, creation_list, segmented_by, values_list)
|
||||
|
||||
return True
|
||||
|
||||
def export(self, plugin, creation_list, segmented_by, values_list):
|
||||
"""Export the stats to the TimescaleDB server."""
|
||||
logger.debug(f"Export {plugin} stats to TimescaleDB")
|
||||
|
||||
with self.client.cursor() as cur:
|
||||
# Is the table exists?
|
||||
cur.execute(f"select exists(select * from information_schema.tables where table_name='{plugin}')")
|
||||
if not cur.fetchone()[0]:
|
||||
# Create the table if it does not exist
|
||||
# https://github.com/timescale/timescaledb/blob/main/README.md#create-a-hypertable
|
||||
# Execute the create table query
|
||||
create_query = f"""
|
||||
CREATE TABLE {plugin} (
|
||||
{', '.join(creation_list)}
|
||||
)
|
||||
WITH (
|
||||
timescaledb.hypertable,
|
||||
timescaledb.partition_column='time',
|
||||
timescaledb.segmentby = '{", ".join(segmented_by)}'
|
||||
);"""
|
||||
logger.debug(f"Create table: {create_query}")
|
||||
try:
|
||||
cur.execute(create_query)
|
||||
except Exception as e:
|
||||
logger.error(f"Cannot create table {plugin}: {e}")
|
||||
return
|
||||
|
||||
# Insert the data
|
||||
# https://github.com/timescale/timescaledb/blob/main/README.md#insert-and-query-data
|
||||
insert_list = [f"({','.join(i)})" for i in values_list]
|
||||
insert_query = f"INSERT INTO {plugin} VALUES {','.join(insert_list)};"
|
||||
logger.debug(f"Insert data into table: {insert_query}")
|
||||
try:
|
||||
cur.execute(insert_query)
|
||||
except Exception as e:
|
||||
logger.error(f"Cannot insert data into table {plugin}: {e}")
|
||||
return
|
||||
|
||||
# Commit the changes (for every plugin or to be done at the end ?)
|
||||
self.client.commit()
|
||||
|
||||
def exit(self):
|
||||
"""Close the TimescaleDB export module."""
|
||||
# Force last write
|
||||
self.client.commit()
|
||||
|
||||
# Close the TimescaleDB client
|
||||
time.sleep(3) # Wait a bit to ensure all data is written
|
||||
self.client.close()
|
||||
|
||||
# Call the father method
|
||||
super().exit()
|
||||
|
|
@ -17,10 +17,12 @@ import base64
|
|||
import errno
|
||||
import functools
|
||||
import importlib
|
||||
import multiprocessing
|
||||
import os
|
||||
import platform
|
||||
import queue
|
||||
import re
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import weakref
|
||||
|
|
@ -34,6 +36,8 @@ from urllib.error import HTTPError, URLError
|
|||
from urllib.parse import urlparse
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
import psutil
|
||||
|
||||
# Prefer faster libs for JSON (de)serialization
|
||||
# Preference Order: orjson > ujson > json (builtin)
|
||||
try:
|
||||
|
|
@ -96,6 +100,11 @@ viewkeys = methodcaller('keys')
|
|||
viewvalues = methodcaller('values')
|
||||
viewitems = methodcaller('items')
|
||||
|
||||
# Multiprocessing start method (on POSIX system)
|
||||
if LINUX or BSD or SUNOS or MACOS:
|
||||
ctx_mp_fork = multiprocessing.get_context('fork')
|
||||
else:
|
||||
ctx_mp_fork = multiprocessing.get_context()
|
||||
|
||||
###################
|
||||
# GLOBALS FUNCTIONS
|
||||
|
|
@ -127,18 +136,6 @@ def listvalues(d):
|
|||
return list(d.values())
|
||||
|
||||
|
||||
def iteritems(d):
|
||||
return iter(d.items())
|
||||
|
||||
|
||||
def iterkeys(d):
|
||||
return iter(d.keys())
|
||||
|
||||
|
||||
def itervalues(d):
|
||||
return iter(d.values())
|
||||
|
||||
|
||||
def u(s, errors='replace'):
|
||||
if isinstance(s, text_type):
|
||||
return s
|
||||
|
|
@ -370,11 +367,18 @@ def json_dumps(data) -> bytes:
|
|||
return b(res)
|
||||
|
||||
|
||||
def json_loads(data: Union[str, bytes, bytearray]) -> Union[dict, list]:
|
||||
def json_loads(data: str | bytes | bytearray) -> dict | list:
|
||||
"""Load a JSON buffer into memory as a Python object"""
|
||||
return json.loads(data)
|
||||
|
||||
|
||||
def list_to_dict(data):
|
||||
"""Convert a list of dict (with key in 'key') to a dict with key as key and value as value."""
|
||||
if not isinstance(data, list):
|
||||
return None
|
||||
return {item[item['key']]: item for item in data if 'key' in item}
|
||||
|
||||
|
||||
def dictlist(data, item):
|
||||
if isinstance(data, dict):
|
||||
try:
|
||||
|
|
@ -400,7 +404,7 @@ def dictlist_json_dumps(data, item):
|
|||
return json_dumps(dl)
|
||||
|
||||
|
||||
def dictlist_first_key_value(data: list[dict], key, value) -> Optional[dict]:
|
||||
def dictlist_first_key_value(data: list[dict], key, value) -> dict | None:
|
||||
"""In a list of dict, return first item where key=value or none if not found."""
|
||||
try:
|
||||
ret = next(item for item in data if key in item and item[key] == value)
|
||||
|
|
@ -409,6 +413,65 @@ def dictlist_first_key_value(data: list[dict], key, value) -> Optional[dict]:
|
|||
return ret
|
||||
|
||||
|
||||
def auto_unit(number, low_precision=False, min_symbol='K', none_symbol='-'):
|
||||
"""Make a nice human-readable string out of number.
|
||||
|
||||
Number of decimal places increases as quantity approaches 1.
|
||||
CASE: 613421788 RESULT: 585M low_precision: 585M
|
||||
CASE: 5307033647 RESULT: 4.94G low_precision: 4.9G
|
||||
CASE: 44968414685 RESULT: 41.9G low_precision: 41.9G
|
||||
CASE: 838471403472 RESULT: 781G low_precision: 781G
|
||||
CASE: 9683209690677 RESULT: 8.81T low_precision: 8.8T
|
||||
CASE: 1073741824 RESULT: 1024M low_precision: 1024M
|
||||
CASE: 1181116006 RESULT: 1.10G low_precision: 1.1G
|
||||
|
||||
:low_precision: returns less decimal places potentially (default is False)
|
||||
sacrificing precision for more readability.
|
||||
:min_symbol: Do not approach if number < min_symbol (default is K)
|
||||
:decimal_count: if set, force the number of decimal number (default is None)
|
||||
"""
|
||||
if number is None:
|
||||
return none_symbol
|
||||
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
|
||||
if min_symbol in symbols:
|
||||
symbols = symbols[symbols.index(min_symbol) :]
|
||||
prefix = {
|
||||
'Y': 1208925819614629174706176,
|
||||
'Z': 1180591620717411303424,
|
||||
'E': 1152921504606846976,
|
||||
'P': 1125899906842624,
|
||||
'T': 1099511627776,
|
||||
'G': 1073741824,
|
||||
'M': 1048576,
|
||||
'K': 1024,
|
||||
}
|
||||
|
||||
if number == 0:
|
||||
# Avoid 0.0
|
||||
return '0'
|
||||
|
||||
# If a value is a float, decimal_precision is 2 else 0
|
||||
decimal_precision = 2 if isinstance(number, float) else 0
|
||||
for symbol in reversed(symbols):
|
||||
value = float(number) / prefix[symbol]
|
||||
if value > 1:
|
||||
decimal_precision = 0
|
||||
if value < 10:
|
||||
decimal_precision = 2
|
||||
elif value < 100:
|
||||
decimal_precision = 1
|
||||
if low_precision:
|
||||
if symbol in 'MK':
|
||||
decimal_precision = 0
|
||||
else:
|
||||
decimal_precision = min(1, decimal_precision)
|
||||
elif symbol in 'K':
|
||||
decimal_precision = 0
|
||||
return '{:.{decimal}f}{symbol}'.format(value, decimal=decimal_precision, symbol=symbol)
|
||||
|
||||
return f'{number:.{decimal_precision}f}'
|
||||
|
||||
|
||||
def string_value_to_float(s):
|
||||
"""Convert a string with a value and an unit to a float.
|
||||
Example:
|
||||
|
|
@ -476,18 +539,32 @@ def folder_size(path, errno=0):
|
|||
return ret_size, ret_err
|
||||
|
||||
|
||||
def weak_lru_cache(maxsize=128, typed=False):
|
||||
def _get_ttl_hash(ttl):
|
||||
"""A simple (dummy) function to return a hash based on the current second.
|
||||
TODO: Implement a real TTL mechanism.
|
||||
"""
|
||||
if ttl is None:
|
||||
return 0
|
||||
now = datetime.now()
|
||||
return now.second
|
||||
|
||||
|
||||
def weak_lru_cache(maxsize=1, typed=False, ttl=None):
|
||||
"""LRU Cache decorator that keeps a weak reference to self
|
||||
|
||||
Warning: When used in a class, the class should implement __eq__(self, other) and __hash__(self) methods
|
||||
|
||||
Source: https://stackoverflow.com/a/55990799"""
|
||||
|
||||
def wrapper(func):
|
||||
@functools.lru_cache(maxsize, typed)
|
||||
def _func(_self, *args, **kwargs):
|
||||
def _func(_self, *args, ttl_hash=None, **kwargs):
|
||||
del ttl_hash # Unused parameter, but kept for compatibility
|
||||
return func(_self(), *args, **kwargs)
|
||||
|
||||
@functools.wraps(func)
|
||||
def inner(self, *args, **kwargs):
|
||||
return _func(weakref.ref(self), *args, **kwargs)
|
||||
return _func(weakref.ref(self), *args, ttl_hash=_get_ttl_hash(ttl), **kwargs)
|
||||
|
||||
return inner
|
||||
|
||||
|
|
@ -496,7 +573,7 @@ def weak_lru_cache(maxsize=128, typed=False):
|
|||
|
||||
def namedtuple_to_dict(data):
|
||||
"""Convert a namedtuple to a dict, using the _asdict() method embedded in PsUtil stats."""
|
||||
return {k: (v._asdict() if hasattr(v, '_asdict') else v) for k, v in data.items()}
|
||||
return {k: (v._asdict() if hasattr(v, '_asdict') else v) for k, v in list(data.items())}
|
||||
|
||||
|
||||
def list_of_namedtuple_to_list_of_dict(data):
|
||||
|
|
@ -508,3 +585,190 @@ def replace_special_chars(input_string, by=' '):
|
|||
"""Replace some special char by another in the input_string
|
||||
Return: the string with the chars replaced"""
|
||||
return input_string.replace('\r\n', by).replace('\n', by).replace('\t', by)
|
||||
|
||||
|
||||
def atoi(text):
|
||||
return int(text) if text.isdigit() else text
|
||||
|
||||
|
||||
def natural_keys(text):
|
||||
"""Return a text in a natural/human readable format."""
|
||||
return [atoi(c) for c in re.split(r'(\d+)', text)]
|
||||
|
||||
|
||||
def exit_after(seconds, default=None):
|
||||
"""Exit the function if it takes more than 'seconds' seconds to complete.
|
||||
In this case, return the value of 'default' (default: None)."""
|
||||
|
||||
def handler(q, func, args, kwargs):
|
||||
q.put(func(*args, **kwargs))
|
||||
|
||||
def decorator(func):
|
||||
if not LINUX:
|
||||
return func
|
||||
|
||||
def wraps(*args, **kwargs):
|
||||
try:
|
||||
q = ctx_mp_fork.Queue()
|
||||
except PermissionError:
|
||||
# Manage an exception in Snap packages on Linux
|
||||
# The strict mode prevent the use of multiprocessing.Queue()
|
||||
# There is a "dirty" hack:
|
||||
# https://forum.snapcraft.io/t/python-multiprocessing-permission-denied-in-strictly-confined-snap/15518/2
|
||||
# But i prefer to just disable the timeout feature in this case
|
||||
func(*args, **kwargs)
|
||||
else:
|
||||
p = ctx_mp_fork.Process(target=handler, args=(q, func, args, kwargs))
|
||||
p.start()
|
||||
p.join(timeout=seconds)
|
||||
if not p.is_alive():
|
||||
return q.get()
|
||||
p.terminate()
|
||||
p.join(timeout=0.1)
|
||||
if p.is_alive():
|
||||
# Kill in case processes doesn't terminate
|
||||
# Happens with cases like broken NFS connections
|
||||
p.kill()
|
||||
return default
|
||||
|
||||
return wraps
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def split_esc(input_string, sep=None, maxsplit=-1, esc='\\'):
|
||||
"""
|
||||
Return a list of the substrings in the input_string, using sep as the separator char
|
||||
and esc as the escape character.
|
||||
|
||||
sep
|
||||
The separator used to split the input_string.
|
||||
|
||||
When set to None (the default value), will split on any whitespace
|
||||
character (including \n \r \t \f and spaces) unless the character is escaped
|
||||
and will discard empty strings from the result.
|
||||
maxsplit
|
||||
Maximum number of splits.
|
||||
-1 (the default value) means no limit.
|
||||
esc
|
||||
The character used to escape the separator.
|
||||
|
||||
When set to None, this behaves equivalently to `str.split`.
|
||||
Defaults to '\\\\' i.e. backslash.
|
||||
|
||||
Splitting starts at the front of the input_string and works to the end.
|
||||
|
||||
Note: escape characters in the substrings returned are removed. However, if
|
||||
maxsplit is reached, escape characters in the remaining, unprocessed substring
|
||||
are not removed, which allows split_esc to be called on it again.
|
||||
"""
|
||||
# Input validation
|
||||
if not isinstance(input_string, str):
|
||||
raise TypeError(f'must be str, not {input_string.__class__.__name__}')
|
||||
str.split('', sep=sep, maxsplit=maxsplit) # Use str.split to validate sep and maxsplit
|
||||
if esc is None:
|
||||
return input_string.split(
|
||||
sep=sep, maxsplit=maxsplit
|
||||
) # Short circuit to default implementation if the escape character is None
|
||||
if not isinstance(esc, str):
|
||||
raise TypeError(f'must be str or None, not {esc.__class__.__name__}')
|
||||
if len(esc) == 0:
|
||||
raise ValueError('empty escape character')
|
||||
if len(esc) > 1:
|
||||
raise ValueError('escape must be a single character')
|
||||
|
||||
# Set up a simple state machine keeping track of whether we have seen an escape character
|
||||
ret, esc_seen, i = [''], False, 0
|
||||
while i < len(input_string) and len(ret) - 1 != maxsplit:
|
||||
if not esc_seen:
|
||||
if input_string[i] == esc:
|
||||
# Consume the escape character and transition state
|
||||
esc_seen = True
|
||||
i += 1
|
||||
elif sep is None and input_string[i].isspace():
|
||||
# Consume as much whitespace as possible
|
||||
n = 1
|
||||
while i + n + 1 < len(input_string) and input_string[i + n : i + n + 1].isspace():
|
||||
n += 1
|
||||
ret.append('')
|
||||
i += n
|
||||
elif sep is not None and input_string[i : i + len(sep)] == sep:
|
||||
# Consume the separator
|
||||
ret.append('')
|
||||
i += len(sep)
|
||||
else:
|
||||
# Otherwise just add the current char
|
||||
ret[-1] += input_string[i]
|
||||
i += 1
|
||||
else:
|
||||
# Add the current char and transition state back
|
||||
ret[-1] += input_string[i]
|
||||
esc_seen = False
|
||||
i += 1
|
||||
|
||||
# Append any remaining string if we broke early because of maxsplit
|
||||
if i < len(input_string):
|
||||
ret[-1] += input_string[i:]
|
||||
|
||||
# If splitting on whitespace, discard empty strings from result
|
||||
if sep is None:
|
||||
ret = [sub for sub in ret if len(sub) > 0]
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def get_ip_address(ipv6=False):
|
||||
"""Get current IP address and netmask as a tuple."""
|
||||
family = socket.AF_INET6 if ipv6 else socket.AF_INET
|
||||
|
||||
# Get IP address
|
||||
stats = psutil.net_if_stats()
|
||||
addrs = psutil.net_if_addrs()
|
||||
|
||||
ip_address = None
|
||||
ip_netmask = None
|
||||
for interface, stat in stats.items():
|
||||
if stat.isup and interface != 'lo':
|
||||
if interface in addrs:
|
||||
for addr in addrs[interface]:
|
||||
if addr.family == family:
|
||||
ip_address = addr.address
|
||||
ip_netmask = addr.netmask
|
||||
break
|
||||
|
||||
return ip_address, ip_netmask
|
||||
|
||||
|
||||
def get_default_gateway(ipv6=False):
|
||||
"""Get the default gateway IP address."""
|
||||
|
||||
def convert_ipv4(gateway_hex):
|
||||
"""Convert IPv4 hex (little-endian) to dotted notation."""
|
||||
return '.'.join(str(int(gateway_hex[i : i + 2], 16)) for i in range(6, -1, -2))
|
||||
|
||||
def convert_ipv6(gateway_hex):
|
||||
"""Convert IPv6 hex to colon notation."""
|
||||
return ':'.join(gateway_hex[i : i + 4] for i in range(0, 32, 4))
|
||||
|
||||
if ipv6:
|
||||
route_file = '/proc/net/ipv6_route'
|
||||
default_dest = '00000000000000000000000000000000'
|
||||
dest_field = 0
|
||||
gateway_field = 4
|
||||
converter = convert_ipv6
|
||||
else:
|
||||
route_file = '/proc/net/route'
|
||||
default_dest = '00000000'
|
||||
dest_field = 1
|
||||
gateway_field = 2
|
||||
converter = convert_ipv4
|
||||
|
||||
try:
|
||||
with open(route_file) as f:
|
||||
for line in f:
|
||||
fields = line.strip().split()
|
||||
if fields[dest_field] == default_dest:
|
||||
return converter(fields[gateway_field])
|
||||
except (FileNotFoundError, IndexError, ValueError):
|
||||
return None
|
||||
return None
|
||||
|
|
|
|||