Commit Graph
7325 Commits
Author SHA1 Message Date
Nguyen Thanh Dat aa4674d9cd fix(config): strip whitespace around comma-separated config values
load_limits split a config list on ',' and kept the spaces, so 'list=cpu, mem,
load' became ['cpu', ' mem', ' load']. glances.conf writes lists that way in its
own comments, so following the shipped documentation produced a config that did
not work.

The loudest symptom is show/hide: every item is used as a re.fullmatch pattern,
and a leading space makes the pattern match nothing. 'hide=sda2, loop.*' hid
sda2 and silently kept showing every loop device.

Quicklook also answered a bad list by falling back to AVAILABLE_STATS_LIST -
more than the user asked for, and its two GPU entries flip the gpu_stats polling
flags, so a typo started polling the GPU. It now falls back to the documented
DEFAULT_STATS_LIST and names the offending entries in the warning.
2026-08-28 12:18:08 +07:00
nicolargo a240d8dfb3 Change token for Star History Chart 2026-08-27 19:06:28 +02:00
Nicolas Hennion 03949b6220 Merge pull request #3694 from ntdatt812/fix/network-zero-rate-alert
fix(network): a rate of 0 in one direction must not mute the other
2026-08-27 18:18:13 +02:00
Nicolas Hennion 079a1a1133 Merge pull request #3693 from ntdat812/fix/sort-tolerates-missing-cpu-times
fix(sort): read a missing sort value as zero instead of discarding the sort
2026-08-27 18:16:40 +02:00
Nguyen Thanh Dat 8b89bf68a4 fix(network): a rate of 0 in one direction must not mute the other
`update_views` skipped the whole interface when either direction measured
0 bytes/s:

    if not i.get('bytes_recv_rate_per_sec') or not i.get('bytes_sent_rate_per_sec'):
        continue

The guard is `or`, so one idle direction suppressed the alert for *both*. A
send-only interface saturating its uplink went undecorated because its rx rate
happened to be exactly 0, and a receive-only one — a monitor/SPAN port — the
same way. 0 bytes/s is a real measurement, not a missing one.

The rates now default to 0 instead of skipping, which is what the diskio plugin
already does: it dropped this same guard in #3684 and reads the rate through
`or 0` for the same reason.

The WebUI needs no change — `plugin-network.vue` renders `getDecoration(...)`
straight from the server-supplied view, so it inherits this.

Tests: four cases in `TestNetworkPluginZeroRateAlerts`. Three fail before this
change (saturated tx with idle rx, saturated rx with idle tx, and a fully idle
interface reading DEFAULT instead of OK); the fourth — both directions busy —
passes before and after, which is the path that already worked.
`tests/test_plugin_network.py` 39 passed.
2026-08-27 10:31:01 +07:00
Nguyen Thanh Dat 7928ee55ea fix(sort): read a missing sort value as zero instead of discarding the sort
sort_stats wraps the specific sort helpers in try/except and falls back to
cpu_percent for the whole list. _sort_cpu_times indexed cpu_times['user'] and
_sort_io_counters indexed io_counters[0..3], so one row with an empty value
raised and silently reordered every other row while the header still read TIME
or IOR/IOW.

Those empty values are expected rather than corrupt: programs.py builds a
program with p['cpu_times'] or {} and list(p['io_counters'] or NO_IO_COUNTERS),
and its own comment says some values can be None on macOS system processes.

Each helper now reads a missing value as zero, so the degenerate row sorts last
and the rest keep their order.
2026-08-26 23:38:44 +07:00
Nicolas Hennion 6808b87fcf Merge pull request #3692 from ntdat812/fix/program-io-counters-sum
fix(programs): sum a program's io_counters instead of concatenating them
2026-08-26 16:06:06 +02:00
Nicolas Hennion 8d6a84524c Merge pull request #3691 from ntdatt812/fix/wifi-partial-thresholds
fix(wifi): honour a partially configured set of signal thresholds
2026-08-26 16:04:10 +02:00
Nicolas Hennion c71504b7e9 Merge pull request #3690 from ntdatt812/fix/webui-raid-alert-parity
fix(webui): match the RAID alert to raid_alert instead of a bare comparison
2026-08-26 16:01:45 +02:00
Nicolas Hennion 629c9f8362 Merge pull request #3688 from ntdatt812/fix/sensors-zero-value-alert
fix(sensors): alert on a reading of 0 instead of skipping it
2026-08-26 15:58:43 +02:00
Nicolas Hennion 89d7b61446 Merge pull request #3687 from ntdatt812/fix/percpu-summarize-not-displayed
fix(percpu): summarize the cores that are not displayed, not the ones that are
2026-08-26 15:56:17 +02:00
Nguyen Thanh Dat b362582562 style: move the long explanations out of docstrings and into comments
Codacy's Prospector/pydocstyle profile flagged 23 issues on the new file: the
multi-line docstrings all put a description straight under the summary line
(D205, D209, D213, D400, D415). Keeping the summary to one line and putting the
reasoning in comments below says the same thing and leaves the file clean.

No test logic changed: still 7 red on origin/develop, 9 green with the fix.
2026-08-26 19:15:40 +07:00
Nguyen Thanh Dat 042b9384f6 fix(programs): sum a program's io_counters instead of concatenating them
io_counters is a fixed five-slot list -
[read_bytes, write_bytes, read_bytes_old, write_bytes_old, io_tag] - and
update_program_dict merged it with '+=', which concatenates lists rather than
adding them. Three things followed:

- Readers index the list, so a program showed only its first process's disk
  IO. Two processes reading 100 and 1000 bytes displayed 90 B/s where the
  sum is 990 B/s.
- '+=' extends in place, and create_program_dict stored the process's own
  list, so aggregating wrote back into the process list.
- Re-aggregating the same processes - which a re-sort does - appended again
  every time, growing the list without bound.

Sum the four byte slots and OR the io_tag: processlist only displays a rate
when the tag is exactly 1, so adding it would blank the columns instead.
2026-08-26 18:56:00 +07:00
Nguyen Thanh Dat ea62d0abe6 fix(wifi): honour a partially configured set of signal thresholds
WifiPlugin.get_alert chained its three comparisons, so the first undefined level
was compared against None, raised TypeError, and the blanket handler dropped the
result to DEFAULT. Because 'critical' is tested first, leaving it out silenced
the other two: a config defining only wifi_careful produced no alert at all, and
even a strong signal came back undecorated instead of OK.

GlancesPluginModel.get_alert already guards each level separately, and the
sensors plugin has tests pinning that partial thresholds work. This brings wifi
in line. `is not None` rather than truthiness because these limits are negative
dBm values, where `if threshold` would discard a 0.

The TypeError handler is kept for a non-numeric signal level (issue #1373) but
narrowed to that case, which is now the only way it can fire.
2026-08-26 16:03:26 +07:00
Nguyen Thanh Dat 08e1ebc451 fix(webui): match the RAID alert to raid_alert instead of a bare comparison
The WebUI decided degradation with `diskData.used < diskData.available` and
nothing else, while the authoritative RaidPlugin.raid_alert applies three rules
before that comparison: raid0 is always OK, an inactive array is CRITICAL, and
an unknown device count is left undecorated.

Two of those omissions are visible:

- A raid0 array reporting fewer used than available devices was drawn as
  "Degraded mode" in warning colour. raid0 has no redundancy, so that comparison
  carries no meaning there — msg_curse excludes raid0 from the same banner.
- `used` is None whenever the parser cannot read the device count, and in
  JavaScript `null < 5` is true, so an unreadable array was reported as degraded.
  The TUI leaves it undecorated.

Comparing both implementations across seven arrays, four disagreed before and
none after.

One asymmetry is preserved rather than fixed: raid_alert returns OK for an
inactive raid0 because the raid0 branch runs first. This change matches that so
the two views agree; whether the Python ordering itself is right is a separate
question, and the inactive line is rendered regardless.
2026-08-26 16:01:40 +07:00
Nguyen Thanh Dat b196a809ea fix(sensors): alert on a reading of 0 instead of skipping it
`update_views` guarded the alert loop with `if not i['value']: continue`, which
skips a genuine reading of 0 along with the absent ones.

The battery alert is computed on `100 - value`, so an empty battery is the most
critical reading there is — yet a battery at 0% was left undecorated while one at
3%, a strictly better state, was flagged CRITICAL. A fan reporting 0 RPM was
likewise never evaluated.

Tests for a usable number instead. Sensors reporting a placeholder — no battery
is an empty list, hddtemp uses b'ERR'/b'SLP'/b'UNK'/b'NOS' — keep the DEFAULT
decoration the parent update_views() already assigns, so nothing that was skipped
before starts being decorated now. It also keeps non-numbers away from the
`100 - value` subtraction, which raises TypeError on them today.
2026-08-26 14:42:35 +07:00
Nguyen Thanh Dat 294fefe3db fix(percpu): summarize the cores that are not displayed, not the ones that are
`summarize_all_cpus_not_displayed` sliced `percpu_list[0 : max_cpu_display]` —
the exact slice `msg_curse` has just printed one line above. So the CPU* row
averaged the cores already on screen instead of the ones that did not fit.

`manage_max_cpu_to_display` sorts by total descending whenever the list
overflows, so the displayed slice is the busiest cores. On an 8-core box showing
4, with four cores at ~87% and four idle at 2%, the CPU* row read 87.0% — a
figure for cores the user can already see, and 43x the load of the group it
claims to represent. The busier the top cores, the more misleading the summary.

Slices the tail instead, so the displayed slice and the summarized slice
partition the list.
2026-08-26 14:39:33 +07:00
Nicolas Hennion c15ab4c73b Merge pull request #3685 from justadityaraj/fix/percpu-guest-nice
fix(percpu): report the guest nice CPU value
2026-08-26 09:38:31 +02:00
Nicolas Hennion 32344ad6e2 Merge pull request #3684 from ntdatt812/fix/diskio-alert-on-rate
fix(diskio): alert on the bitrate, and decorate the field the WebUI reads
2026-08-26 09:36:46 +02:00
Nicolas Hennion 5a38e1550d Merge pull request #3683 from ntdatt812/fix/connections-conntrack-alert
fix(connections): alert on the tracked percentage, not on 0
2026-08-26 09:34:32 +02:00
Nicolas Hennion 5b41982377 Merge pull request #3678 from ntdatt812/fix/containers-title-duplicate-fragment
fix(containers): stop repeating a title fragment when several engines run
2026-08-26 09:30:24 +02:00
Nicolas Hennion 00bbe40974 Merge pull request #3677 from ntdatt812/fix/ports-icmp-timeout-arg
fix(ports): send the ICMP timeout in the unit each ping expects
2026-08-26 09:27:39 +02:00
Nicolas Hennion aa42f0fde5 Merge pull request #3676 from ntdatt812/fix/folder-refresh-timer-index
fix(folders): honour folder_N_refresh instead of walking every cycle
2026-08-26 09:25:01 +02:00
Nicolas Hennion 2da4ccb656 Merge pull request #3675 from ntdatt812/fix/sum-stats-list-index
fix(processlist): index io_counters instead of testing membership in it
2026-08-26 09:20:16 +02:00
Nicolas Hennion 6726803187 Merge branch 'develop' into fix/sum-stats-list-index 2026-08-26 09:19:50 +02:00
Nicolas Hennion a334b47010 Merge pull request #3674 from ntdatt812/fix/filter-key-split-maxsplit
fix(filter): keep the whole regex when it contains a colon
2026-08-26 09:05:18 +02:00
Nicolas Hennion 46fb62ce28 Merge pull request #3673 from ntdatt812/fix/windows-nice-labels-3672
fix(processlist): show Windows priority classes as labels in the NI column (#3672)
2026-08-26 08:43:02 +02:00
Aditya Raj Singh 93ef76d8ca fix(percpu): report the guest nice CPU value
Per-CPU stats checked for guest_nice but returned the steal field, so systems exposing both values reported incorrect guest_nice usage. Read the matching psutil field and cover the conversion with distinct fixture values.
2026-08-26 03:29:06 +05:30
Nguyen Thanh Dat 9efec19af7 fix(diskio): alert on the bitrate, and decorate the field the WebUI reads
The rx/tx thresholds describe a bitrate, but update_views measured them
against read_bytes / write_bytes -- the lifetime counters psutil returns.
Those only grow, so on any machine with uptime a configured threshold latched
on within seconds and never came back down, whatever the disk was doing.
The network plugin next door already alerts on bytes_recv_rate_per_sec.

The decoration also went only to the counter keys. The TUI prints the rate and
reads read_bytes' decoration, so it showed a colour derived from the wrong
quantity; the WebUI asks for read_bytes_rate_per_sec, which nothing set, so it
showed no colour at all. Both keys now carry it, matching network again.

The rate view exists only once there is a timespan to divide by, so the rate
key is set when present -- setting it unconditionally raises KeyError on the
first sample, which the existing view tests catch.
2026-08-25 19:18:18 +07:00
Nguyen Thanh Dat 068d8ab487 fix(connections): alert on the tracked percentage, not on 0
update_views called get_alert(header='nf_conntrack_percent') without a value,
so it measured get_alert's default current=0 on every refresh. The tracked
connection percentage was drawn OK at any fill level, and the thresholds
conf/glances.conf ships for it -- careful 70, warning 80, critical 90 -- could
never fire. A conntrack table close to nf_conntrack_max drops new connections,
which is exactly what those thresholds exist to warn about.

Both interfaces read this one decoration (the WebUI through
getDecoration('nf_conntrack_percent')), so both were silent.
2026-08-25 19:13:25 +07:00
Nguyen Thanh Dat 9e625e1e08 fix(containers): stop repeating a title fragment when several engines run
build_title appends the pieces of the header to one list, and the last
append sits outside the branch that produces its message:

        if not self.views['show_engine_name']:
            msg = f' (served by {self.stats[0].get("engine", "")})'
        ret.append(self.curse_add_line(msg))

With one engine the branch runs and the append is correct. With several -
Docker and Podman on the same host, which is the only case
show_engine_name is True - the branch is skipped, msg still holds the
previous fragment, and appending it again repeats it.

Measured against the real build_title:

    2 containers, 2 engines -> 'CONTAINERS 2 sorted by CPU consumption sorted by CPU consumption'
    1 container,  2 engines -> 'CONTAINERSCONTAINERS'
    2 containers, 1 engine  -> 'CONTAINERS 2 sorted by CPU consumption (served by docker)'
    1 container,  1 engine  -> 'CONTAINERS (served by docker)'

Move the append inside the branch. The engine name genuinely has nothing
to add in the multi-engine case: maybe_add_engine_name_or_pod_line adds a
per-row Engine column there instead.
2026-08-25 10:05:41 +07:00
Nguyen Thanh Dat 57f12247be fix(ports): send the ICMP timeout in the unit each ping expects
Two problems on the same argument.

Windows 'ping -w' is a per-reply timeout in **milliseconds**, not
seconds, so a configured 'timeout = 3' became a 3 ms deadline. Measured
against two hosts that answer well within 3 seconds:

    200.160.2.3 (350 ms)  ping -n 1 -w 3    -> exit 1
    200.160.2.3 (350 ms)  ping -n 1 -w 3000 -> exit 0
    139.130.4.5 (168 ms)  ping -n 1 -w 3    -> exit 1
    139.130.4.5 (168 ms)  ping -n 1 -w 3000 -> exit 0

Windows clamps the wait to about 50 ms, so a host on the LAN still
answers in time and the bug hides; anything further away is reported
offline. Multiply by 1000 on Windows and leave -W/-t in seconds.

The timeout was also passed through _resolv_name(), which runs
socket.gethostbyname() on it. It is a number of seconds, not a host: the
lookup can only fail, and it logs a misleading 'Cannot convert 3 to IP
address' on every ICMP check.
2026-08-24 17:48:48 +07:00
Nguyen Thanh Dat 443034eda4 fix(folders): honour folder_N_refresh instead of walking every cycle
update() guarded the per-folder timer with `i in self.timer_folders`,
which asks whether the integer i is one of the Timer objects in the list.
Timer does not implement __eq__, so that test is always False: the skip
branch was unreachable and the reset branch never ran.

The consequence is that folder_N_refresh has no effect. Every folder is
re-walked on every refresh cycle (2s by default) rather than on its own
schedule, and folder_size() is a full recursive walk - which is exactly
why the setting exists.

Use the index bound the code meant, `i < len(self.timer_folders)`. The
guard still earns its place: the folder list lives on the class, so it
can outgrow the per-instance timer list.
2026-08-24 17:45:05 +07:00
Nguyen Thanh Dat bf74f43cd3 fix(processlist): index io_counters instead of testing membership in it
`_sum_stats(key, sub_key)` guarded the accumulation with `sub_key in p[key]`.
That is a mapping test, and `sub_key` is only a mapping key for `memory_info`.
For `io_counters` it is an index — the value is the list

  [read_bytes, write_bytes, read_bytes_old, write_bytes_old, io_tag]

so `0 in p['io_counters']` asked whether the counters *contain the number zero*.
A process doing real IO was skipped, and one whose counters happened to hold the
literal 0/1/2/3 was summed instead:

  [[5000, 700, 2000, 300, 1],            _sum_stats('io_counters', 0) -> 0
   [3000, 400, 1000, 200, 1]]            (expected 8000)

  [[5000, 700, 0, 300, 1]]               _sum_stats('io_counters', 0) -> 5000
                                          (summed only because 0 is in the list)

So the R/s and W/s totals on the process-list summary row — rendered whenever a
process filter is active — were not merely zero but arbitrary. The VIRT and RES
totals beside them were correct, because `memory_info` really is a mapping.

Branch on the container type, and bounds-check the index so a short list is
skipped rather than raising.
2026-08-24 16:19:04 +07:00
Nguyen Thanh Dat 6f1b8db44c fix(filter): keep the whole regex when it contains a colon
`GlancesFilter.filter` split the input on every colon and kept only the second
field, so everything after the second colon was discarded:

  cmdline:C:/Program Files/.*   ->  key=cmdline  regex=C
  name:foo:bar                  ->  key=name     regex=foo

The truncation is silent. The leading fragment is usually still a valid regex —
`C` compiles — so the compile guard never fires, the filter matches nothing, and
the process list comes back empty with no error and only a debug log line.

On Windows that is every absolute path, since each one has a drive colon. It
also truncates URLs, IPv6 literals and any `foo:bar` process name.

`split(':', 1)` rather than the repo's `split_esc`: that helper escapes on
backslash and strips the escape characters from its result, which would mangle a
Windows path regex. The separator here needs no escaping — the key never
contains a colon.
2026-08-24 16:16:10 +07:00
Nguyen Thanh Dat 1b092b8822 fix(processlist): show Windows priority classes as labels in the NI column
psutil reports the Win32 priority *class* for `nice` on Windows, not a nice
value. Those numbers are neither ordered nor small — 32 is normal while 32768 is
*above* normal — so the NI column showed a five-digit number that is meaningless
as a nice value and does not fit its 3-character width.

Render the six classes as the short labels Windows itself uses, in the TUI and
the WebUI alike:

  256 RT   128 Hi   32768 AN   32 No   16384 BN   64 Lo

Only the rendering changes. The API keeps the raw value, so sorting and any
consumer of `/api/*/processlist` are untouched, and `get_nice_alert()` still
receives the number: the `nice_*` limits in glances.conf are POSIX nice values,
and matching a label against them would silence them. An unmapped class falls
through to its number rather than disappearing, so a class Windows adds later
stays visible.

ProgramlistPlugin inherits `_get_process_curses_nice`, so the program view is
covered by the same change.

Closes #3672.
2026-08-24 09:08:44 +07:00
nicolargo fca88aaa0b Correct sensors unit test 2026-08-22 13:56:13 +02:00
nicolargo 635d3a04fa Correct issue with unit test following PR #3671 2026-08-22 13:32:34 +02:00
nicolargo bec0df282c Merge branch 'ntdatt812-fix/3669-container-network-all-interfaces' into develop 2026-08-22 13:13:36 +02:00
nicolargo 207b38965f Remove line in the NEWS.md file because the patch will be applied in the 4.5.7 version. 2026-08-22 13:13:14 +02:00
nicolargo a81f7372a3 Merge branch 'fix/3669-container-network-all-interfaces' of https://github.com/ntdatt812/glances into ntdatt812-fix/3669-container-network-all-interfaces 2026-08-22 13:10:58 +02:00
Nicolas Hennion 962b82513d Merge pull request #3671 from justadityaraj/fix/issue-3582-containers-vms-init-value
fix(plugins): init containers, vms and smart stats as a list
2026-08-22 13:05:23 +02:00
Nicolas Hennion 5475f41f5e Merge pull request #3667 from nightcityblade/fix/issue-3659
feat(fs): allow free-space display in config
2026-08-22 13:05:04 +02:00
Nicolas Hennion ffbd3ebec4 Merge pull request #3665 from VXNCXNX/fix/init-value-comparison
fix: compare stats to the init value, not to the bound method
2026-08-22 13:02:45 +02:00
Aditya Raj Singh 946c34f687 fix(plugins): init containers, vms and smart stats as a list
These three plugins expose a list of stats but inherited the base class's
stats_init_value default of {}. get_init_value() is what update() returns when
it has nothing to report, and what reset() installs, so /api/4/containers
answered {} instead of [] whenever no container engine library was importable -
the empty-dict-for-empty-list the issue reports.

containers and vms simply never passed stats_init_value. smart declared
stats_init_value=[] as its own parameter default and then dropped it on the way
to super().__init__(), so it read as correct while behaving the same way; a grep
for the keyword finds it, only running it does not.

Every other list plugin already passes stats_init_value=[]. tests/test_restful.py
carries a workaround for this exact symptom, added in 4c92e1b as "allow empty
dicts for list plugins" - it was read as test flakiness rather than a bug.

Fixes #3582
2026-08-22 01:27:51 +05:30
Nguyen Thanh Dat b11fd3947c test(containers): add the class docstrings the sibling plugin tests carry
Matches the convention in tests/test_plugin_sensors.py and clears the
docstring findings Codacy raised on the PR.
2026-08-19 11:16:28 +07:00
Nguyen Thanh Dat 061402b012 fix(containers): aggregate network stats over all container interfaces
_get_network_stats() read the container network counters from a single
hardcoded interface. The Docker stats endpoint returns one entry per
container interface, so a container attached to several networks gets
eth0, eth1... and everything past eth0 was silently dropped, making the
reported RX/TX rates too low.

Sum rx_bytes/tx_bytes across all the interfaces instead. Loopback is
excluded, since Docker-API-compatible runtimes have been observed to
report it and its traffic would inflate the totals. Interfaces with
missing counters are skipped rather than aborting the whole method, so
one malformed interface no longer discards the valid ones. None is still
returned when nothing usable remains, keeping --network host containers
unchanged.

The output contract is untouched: same keys, same types, same units.

Fixes #3669
2026-08-19 10:35:48 +07:00
nightcityblade c288b002ee test(fs): cover free-space config in web mode 2026-08-16 23:10:45 +08:00
nightcityblade 5c78c4b0db feat(fs): allow free-space display in config 2026-08-15 23:27:40 +08:00
Nicolas Hennion 3bda428bec Merge pull request #3664 from williamqwu/fix/amd-gpu-ids-lookup-path
Fix AMD GPU name resolution when the card is only listed in AMD's `amdgpu.ids`
2026-08-15 16:45:51 +02:00