Skip to content

fix(quicklook): colour each --percpu bar by that core's own load - #3716

Merged
nicolargo merged 1 commit into
nicolargo:developfrom
ntdatt812:fix/quicklook-percpu-decoration
Sep 10, 2026
Merged

nicolargo merged 1 commit into
nicolargo:developfrom
ntdatt812:fix/quicklook-percpu-decoration

Conversation

@ntdatt812

Copy link
Copy Markdown
Contributor

In --percpu mode the quicklook bars are per core, but their colour was not: every bar was drawn with the aggregate CPU style.

  • curses: _msg_create_line reads self.get_views(key=key, option='decoration'), and key is 'cpu' for every row _msg_per_cpu emits.
  • WebUI: plugin-quicklook.vue calls getDecoration('cpu') inside the v-for over percpus.

So on a box where one core is pegged at 100% and the rest are idle, that core's bar is painted with the colour of the average — the one number that cannot show it. The cpu_careful/warning/critical limits are configured and correct; nothing reads them per core.

The change

update_views now publishes views['percpu_decoration'], a style per cpu_number plus 'other' for the row that averages the cores neither interface displays (CPU* in curses, x in the WebUI). Both interfaces read that one map, so they agree by construction rather than by both getting it right separately. The WebUI falls back to the old aggregate style when the key is absent, so an older server does not break a newer page.

The styles come from the plugin's own quicklook_cpu_* limits directly rather than through get_alert(). That is deliberate: get_alert() also calls manage_threshold, which writes glances_thresholds[stat_name] that glances/events_list.py:153 reads, and manage_action, which can run a configured [quicklook] command. Both are keyed by the stat name alone, and every core shares quicklook_cpu with the aggregate — so running the cores through get_alert() would leave whichever core went last as the value the event list and any action see. _decoration_for_percent is the same threshold ladder with none of that.

One unrelated token: msg = msg = f'{key.upper():3}* ' on a line the change already touches loses its duplicated assignment.

Verification

pytest tests/test_plugin_quicklook.py is 22 passed; tests/test_core.py tests/test_glances_curses.py tests/test_lazy_views.py is 63 passed, 6 skipped. ruff check and ruff format --check are clean on both changed Python files.

Each claim was checked by breaking it, and each mutation fails only the test that owns it:

reverted fails
_msg_create_line back to the aggregate lookup test_the_styles_reach_the_curses_bars only
the map built from stats['cpu'] instead of each core's total the six value tests
_decoration_for_percent replaced by get_alert() test_scanning_the_cores_does_not_move_the_global_threshold only

The first is the one that matters: without it the map is correct and unread, and every other test still passes.

The summary-row test picks values where the row disagrees with both of the things it could have borrowed — four cores at 95% and two at 60% give CRITICAL bars, a WARNING aggregate (83%), and a CAREFUL row.

I have no Linux box to hand for the curses rendering itself; the assertions above are on the message list msg_curse builds, not on the terminal output.

Both interfaces drew every per-core bar with the aggregate CPU style:
`_msg_create_line` read `views['cpu']['decoration']` for every row, and
plugin-quicklook.vue called `getDecoration('cpu')` inside the percpu loop.
A core pegged at 100% among idle ones was therefore painted with the colour
of the average, which is the one number that cannot show it.

update_views now publishes `views['percpu_decoration']`: a style per core,
plus 'other' for the row that averages the cores neither interface shows.
Both interfaces read it, so they agree.

The styles are computed from the plugin's own quicklook_cpu_* limits rather
than through get_alert(), because get_alert() also records a threshold that
glances.events_list reads and can run a configured action, and both are keyed
by the stat name alone. Every core shares 'quicklook_cpu' with the aggregate,
so a per-core call would leave the last core as the value those two see.

Also drops a duplicated `msg = msg =` assignment on a line already touched.
@nicolargo
nicolargo merged commit 766cfc8 into nicolargo:develop Sep 10, 2026
17 of 19 checks passed
@nicolargo nicolargo added the bug label Sep 10, 2026
@nicolargo nicolargo added this to the Glances 4.5.7 milestone Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants