Skip to content

Cluster API

Activity

Jobs + alerts + audit highlights, merged newest-first (doc 05, doc 06 ActivityFeed).

An audit row that spawned a job is skipped: the job entry already represents it, and showing both would double every lifecycle action. Alerts are the third source; the kind discriminator lets the frontend distinguish all three without extra endpoints.

Paging: each source is independently queried with LIMIT limit (not limit // 3), so the merged-then-sliced result is always the true top-limit rows across all three kinds; the top limit merged rows can contain at most limit rows from any one source, and each source already supplies that many. A source can only return fewer than limit rows (including zero) than the feed asks for when it genuinely has fewer displayable rows, e.g. every audit row in view is a job-spawned dupe that gets skipped; that is the intended dedup, not starvation.

The merge sorts on the raw datetime, not the serialized .isoformat() string used for the at field: Python’s isoformat() drops the microsecond component when it is exactly 0, which would make a same-instant row from one source sort inconsistently against a row from the other if compared as strings.

Parameters

NameInTypeRequiredDescription
limitqueryintegerno

Responses

StatusDescriptionSchema
200Successful Responseany
422Validation ErrorHTTPValidationError

Cluster Nodes

One row per NODE, not per Host.

A Proxploy Host is ONE Proxmox API endpoint; the cluster behind that endpoint is many nodes. /cluster/resources returns all of them and the poller stores all of them in snap.nodes, but this endpoint used to pick the one matching host.node_name and drop the rest, so a 3-node cluster rendered as a single card.

is_entry marks the node we actually connect through (the local: 1 node recorded at enrolment). Exactly one row per host carries it, including a host with no snapshot yet, so a consumer can always resolve a host to a node: /hosts/{id} redirects there, node shells open there, and the host-level metric series belongs to it.

apps/vms stay HOST-level counts on every row: neither table records which node a guest sits on (App has host_id + ctid only), so a per-node split would be invented, not measured.

disk_* IS per node, and a SHARED datastore counts on every node that can use it, because the question a node card answers is “how much storage can this node put a guest on”. The consequence, stated here because a future reader will otherwise discover it as a bug: SUMMING disk_bytes / disk_total_bytes across these rows double-counts every shared pool. The cluster-wide figure is GET /cluster/summary (name-deduped), and the correctly deduped shared-vs-local aggregate is pollers._disk_pct, which is what the disk_pct metric series and therefore alerting use.

Two Hosts can be two nodes of the SAME cluster; cluster_resources() returns the whole cluster from either one, so both snapshots list both nodes. A real node must appear once, attributed to the Host actually registered at it (owner_by_node); a node nobody is registered at (an unregistered cluster member) is attributed to whichever host’s snapshot reports it first, same as before this had multiple hosts to consider. Both are keyed on cluster_scope(h) too: a node name is only unique WITHIN a cluster, so a same-named node on a different cluster (or another standalone host) must not be merged in.

Responses

StatusDescriptionSchema
200Successful Responseany

Cluster Summary

Responses

StatusDescriptionSchema
200Successful Responseany