Cluster API
GET /api/v1/cluster/activity
Section titled “GET /api/v1/cluster/activity”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
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit | query | integer | no |
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Successful Response | any |
| 422 | Validation Error | HTTPValidationError |
GET /api/v1/cluster/nodes
Section titled “GET /api/v1/cluster/nodes”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
| Status | Description | Schema |
|---|---|---|
| 200 | Successful Response | any |
GET /api/v1/cluster/summary
Section titled “GET /api/v1/cluster/summary”Cluster Summary
Responses
| Status | Description | Schema |
|---|---|---|
| 200 | Successful Response | any |