`_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.
`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.
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.
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
_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
The decoration for `bytes_sent_rate_per_sec` was assigned `alert_rx`, while
the line directly above correctly assigns `alert_tx` to `bytes_sent`.
This is user-visible because the two front ends read different keys: the
curses UI reads `bytes_sent`, and the Web UI reads `bytes_sent_rate_per_sec`
(plugin-network.vue). So on an interface with asymmetric traffic the two
UIs can show different alert states for the same value in the same poll --
for example a 1 Gbps link at 950 Mbps RX / 10 Mbps TX shows Tx/s undecorated
in the terminal and in the critical colour in the browser.
Fixes#3660
sort_stats() has five callers; making its fallback branch copy changed behaviour for
all of them to suit one cache. The cache now clears itself where the sort happens, so
the shared helper is left alone and the in-place branch keeps not allocating.
The base class carried a lazy_views flag, a _views_source field, a _build_views split
and an introspection check for subclasses that decorate their views - all of it shared
by 37 plugins to serve one. Overriding update_views/get_views in processlist does the
same with no change to model.py, and set_views/reset_views clear the pending flag so a
reset stays reset.
_build_views() already dispatches on list, dict and neither, so repeating the condition
one line earlier only duplicated it. Verified against dict stats, None and an empty list.
update_views() called get_raw() twice and returned an empty dict from a separate exit;
one read and one exit instead, with the deferred case named in the docstring.
The test fixture built its own ProcesslistPlugin, whose load() writes Mock attributes
into the glances_processes singleton and broke seven tests in test_core.py whenever the
two files ran together. It now takes the shared plugin and restores what it changes.
The sort test only compared object identity, which said nothing about the result;
it now asserts the programs come back in the new order. Verified by breaking the
invalidation on purpose - the test fails.
One rationale in one place instead of three, and the class attribute no longer
describes the per-key build that is gone. test_unknown_key_raises only exercised
dict lookup and passed unchanged on develop.
A dict subclass that built entries on access turned out to be more machinery than the
problem needs: nothing outside the plugin reads .views directly, and every per-item
caller belongs to a plugin that builds eagerly. Remembering the stats and building once,
on the first read, gives the same result with a plain dict.