feat(cluster): sizing report for OS, kernel, virtualization - #1291
Draft
gsanchietti wants to merge 4 commits into
Draft
feat(cluster): sizing report for OS, kernel, virtualization#1291gsanchietti wants to merge 4 commits into
gsanchietti wants to merge 4 commits into
Conversation
gsanchietti
force-pushed
the
insights
branch
2 times, most recently
from
September 3, 2026 10:18
1b8ea99 to
6783c65
Compare
Add the ns8-core half of a fleet-sizing pipeline: a leader-only daily reporter that builds a per-node hardware/utilization/workload JSON report (last complete UTC day) from Prometheus and module get-facts, and ships it via HTTP Basic auth to the insights server. The server side (scoring, cohort baselines) lives in a separate repository. Extracts list-nodes' Prometheus discovery/query helpers into a shared cluster.prometheus module so print-sizing-report can reuse them for historical, day-windowed queries. Wires send-sizing-report.timer into check-subscription alongside send-inventory.timer, and documents the new unit in docs/core/subscription.md. Assisted-by: Claude Code:claude-sonnet-5
gsanchietti
force-pushed
the
insights
branch
from
September 3, 2026 10:19
6783c65 to
c01d0c8
Compare
Remove three unused leftovers from send-sizing-report: - MAX_METRICS_PER_FAMILY and MAX_FAMILIES_PER_NODE were defined but never applied: the comment next to them already explains that the reporter deliberately does not pre-truncate, so the server can count what it drops. Only MAX_NODES_PER_REPORT is enforced locally. - collect_measurements() took day_start_ts and never read it; every query in it is anchored to day_end_ts. - post_report()'s trailing `raise last_exc` was unreachable, because the retry loop either returns a response or re-raises on the last attempt. The last_exc bookkeeping goes with it. No behaviour change: the report payload is byte-identical before and after on a live cluster, for both the default day and a three-day backfill, and post_report keeps the same retry and TLS semantics. Assisted-by: Claude Code:claude-opus-5
The sizing reporter fell back to reading INSIGHTS_SERVER_URL and INSIGHTS_VERIFY_TLS from the environment of the module holding cluster/default_instance/loki. That tied an unrelated module to the reporter's configuration and hid where the endpoint actually comes from. Read them from the cluster agent environment and nowhere else: an empty INSIGHTS_SERVER_URL is how the feature stays off. insights_endpoint() no longer needs a Redis handle. Document the two variables, how to set them, the last_acked_day marker and the --print preview in the subscription manual page. Assisted-by: Claude Code:claude-opus-5
send_report treated any HTTP 202 as a delivery and advanced cluster/sizing_report last_acked_day to the newest day it had sent. The server answers 202 for a well-formed report even when it stores nothing from it, so sending a day outside its accepted window moved the marker onto a day that was never stored. Sending today, which is still accumulating and always dropped, therefore made the next run skip that day for good once it became complete: the run only considers days newer than the marker. Read stored_days and dropped.dropped_day from the response body: hold the marker when the server stored nothing, warn about dropped days, and never mark a day later than yesterday, which cannot have been stored. An unparsable body still counts as delivered, since a 202 with no readable payload is no evidence against the send. Also replace the three silent exits in main() with a notice each -- not the leader, no subscription, nothing owed -- so a manual run says why it sent nothing instead of exiting 0 without output. Assisted-by: Claude Code:claude-opus-5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a leader-only
send-sizing-reportscript and timer that build ananonymized per-node sizing report for the last complete UTC day — hardware
descriptors, Prometheus utilization percentiles, per-module workload counters
from
get-facts— and ship it to the insights server. Missed days arebackfilled, up to a week back.
The report never carries identifying strings: no FQDN, IP address, host name or
DMI serial. Only numeric workload values and coarse descriptors (CPU model, OS
id, kernel release, virtualization) are forwarded.
The endpoint is read only from the cluster agent environment. With
INSIGHTS_SERVER_URLunset the timer runs and exits without sending anything,so merging this changes nothing on a cluster that has not opted in.
Docs:
docs/core/subscription.md, section Sizing report.Configuration
Two variables in the cluster agent environment:
INSIGHTS_SERVER_URL$INSIGHTS_SERVER_URL/v1/sizing-reportswith HTTP Basic auth, usingsystem_id/auth_tokenfromcluster/subscription. Empty or unset ⇒ nothing is sentINSIGHTS_VERIFY_TLS0skips TLS certificate verification; any other value, including unset, keeps it onHow to test
1. Deploy manually on a leader node
No core update needed — one script and two units, nothing existing is touched:
Or, via a full core update:
2. Preview the payload, without sending anything
runagent -m cluster send-sizing-report --print | jqA specific day, or a list of days, in ISO format:
Check the output carries no FQDN, IP address, host name or serial.
3. Point it at an insights server
No restart needed —
runagentreads the file at every run.4. Send
Run the unit the timer would run:
Expected on success:
Or send a chosen day on demand, bypassing the marker:
5. Backfill and redelivery
The last accepted day lives in
cluster/sizing_report. Only complete UTC daysnewer than it are sent, at most 7 per run:
Rewind it to force a resend of the last three days, then run the unit again —
the marker should come back to yesterday:
redis-cli HSET cluster/sizing_report last_acked_day $(date -u -d '4 days ago' +%F) systemctl start send-sizing-report.service redis-cli HGET cluster/sizing_report last_acked_dayRedelivery is safe: a day is an absolute fact and the server recomputes the
stored row instead of accumulating into it.
6. A wrong day cannot poison the marker
The server answers
202for a well-formed report even when it stores nothingfrom it. Sending today — still accumulating, always outside the accepted window
— must be reported and must leave
last_acked_dayalone:Expected:
Same for a day older than the server's retention. In a mixed batch the marker
advances only to the newest day that was actually stored.
7. A run that sends nothing says why
Each no-op path logs a notice instead of exiting 0 in silence:
8. Opt-out is the default
With
INSIGHTS_SERVER_URLremoved from/var/lib/nethserver/cluster/state/environment,systemctl start send-sizing-report.servicemust succeed and leavelast_acked_dayuntouched.Verified on a live cluster
Single-node NS8 leader, Rocky 9.8,
metrics1+loki1installed, insightsserver on
127.0.0.1:19595:--printoutput byte-identical before/after the dead-code removal, for thedefault day and for a three-day backfill
post_reportretry matrix (network error exhaust/recover, 503 retry,persistent 503, 400 not retried,
retries=0,verify_tls0/1): 8/8 identicalbefore and after
module/loki1/environment INSIGHTS_SERVER_URLis stillset ⇒ nothing sent, marker unchanged — the old loki fallback is gone
202, server reportsaccepted_nodes:1 accepted_modules:6 accepted_metrics:7, alldropped_*:0dropped_day:1,stored_days:0, marker held; mixed batch ⇒ marker advances only to thenewest stored day
parse_ack/acked_day/send_reportmarkerhandling (accept, reject, mixed, unparsable body, 400, 500) and 6 on the
three
main()no-op notices: all pass