Cluster-wide HA status.
curl --request GET \ --url https://stratum.example.com/v1/ha/status \ --header 'Authorization: Bearer <token>'Bootstrap-admin endpoint returning the cluster’s HA topology and
rolled-up state. A single-node install reports
clusterState: single-node with haEnabled: false — the v1
shape, unchanged. An HA install reports the declared node
membership (id, role, AZ, mgmt IP) and the cluster’s lifecycle
state.
2.0 first slice: topology + config-level state. Live per-node health (reachable / draining / leader) joins the node registry in a follow-up; multi-node aggregation lands with the rest of the HA build.
Authorizations
Section titled “Authorizations”Responses
Section titled “Responses”Cluster HA status.
object
Whether the install is configured for HA (3+ nodes). False = single-node v1.
Rolled-up cluster lifecycle state. single-node
when HA is off; the others per the cluster-state
table once HA is on.
object
Availability zone; empty on single-AZ installs.
Management-plane address.
The node’s traffic-class bindings, keyed by class
(storage, overlay). Carried in the join bundle so a joiner’s
ClusterConfig matches the founder’s.
Without it a joined node has no networks entry, so ClassAddr falls
through to mgmt_ip and EVERY traffic class silently collapses onto the
management plane. That put Ceph’s public_network — and therefore all
client I/O — on the 1G mgmt NIC while only OSD replication used the 10G
storage VLAN, with the cluster reporting HEALTH_OK throughout.
object
One traffic class’s binding on a node (-TOPO-1).
object
The address this class uses on the node (a bare IP, not a CIDR).
Host interface carrying the class, when one is declared.
The node’s DECLARED label map (topology.rack,
topology.power, and any operator-namespace key). Carried in the join
bundle so a joiner’s ClusterConfig matches the founder’s.
Without it a joined node has NO declared labels at all, and every consumer degrades to “the founder has a rack, nobody else does” — silently. Rack spread sees one value and two empties, so the anti-affinity guarantee reports success and places anywhere; a CRUSH hierarchy built from labels finds one rack and is refused as a half-declared topology; a rack failure domain can never be satisfied; and a taint declared on a joiner repels nothing.
topology.az is NOT carried here — the AZ rides its own az field, and
two records of one fact is the bug each cost a
session.
This is the THIRD field to go missing from this schema the same way (networks in the bundle, networks in the status report). A field added to config.ClusterNode does not reach a joiner unless someone remembers this file.
object
Per-node boot progress, probed from the node’s /startupz.
ready = serving; starting = still booting (see bootPhase);
unreachable = the node didn’t answer (down, early-boot before the
listener, or wedged). Lets the UI tell “booting / stuck / down”
apart from a vague “unknown”. Omitted when the boot-health probe
isn’t wired (single-node).
The in-flight startup phase when bootState=starting (e.g. migrations).
How long the current boot phase has run (ms); climbs while a phase is stuck.
The node’s last boot duration (ms), when bootState=ready.
The node id of the current control-plane leader — the node holding the leader advisory lock, which runs the cluster-wide singleton operational surface. The API VIP follows this node. Empty on single-node or when the holder can’t be determined.
Live Patroni member state for the Postgres-HA cluster (M1). Empty on single-node, or when the live state can’t be reached. Joined to nodes by host/mgmtIp.
object
The member’s name (the node id).
The member’s Postgres host (its mgmt IP).
Patroni role.
Patroni member state (running, streaming, stopped,…).
The member’s Postgres timeline; matches across in-sync members.
Replication lag in MB (0 on the leader + in-sync replicas). Null/absent if unknown.
Live OpenBao integrated-storage raft state. Per-node leadership + seal status, probed from each CP node’s local OpenBao. Absent on single-node / when no node is reachable.
object
The node id of the current OpenBao raft leader (the node whose /v1/sys/leader reports is_self, or that the others name as leader_address). The OpenBao VIP follows this node. Empty when no leader is reachable.
Per-CP-node OpenBao state (one entry per probed node).
object
The CP node this entry describes.
Whether the control plane could reach this node’s OpenBao.
Whether this node is the OpenBao raft leader (is_self=true).
Whether this node’s OpenBao is sealed. A sealed node can’t serve secrets; on a healthy cluster every node is unsealed (auto-unseal via TPM at boot,). Absent when unreachable.
OVN central (NB/SB) leadership. Single-instance until OVN raft — singleInstance=true then means leaderNodeId is the node running ovn-central, not a raft leader.
object
The node id leading OVN central — the NB raft leader (the OVN VIPs follow it). Empty when not determinable. (Convenience alias of nbLeaderNodeId for callers that want a single “OVN leader”.)
The OVN_Northbound raft leader node id. Empty if unknown.
The OVN_Southbound raft leader node id. May differ from the NB leader. Empty if unknown.
True while OVN central is single-instance (pre-): the UI should show “active” rather than “leader”. False once OVN runs as a 3-node raft.
Per-CP-node OVN raft state; empty single-instance.
object
The CP node this entry describes.
Whether the control plane could reach this node’s OVN DBs.
Whether this node leads the OVN_Northbound raft.
Whether this node leads the OVN_Southbound raft.
Whether the OVN Southbound DB Chassis table was successfully read this pass. When false the chassis list is unknown (the probe failed/timed out) and consumers MUST NOT infer a dataplane outage from an empty chassis.
Registered ovn-controller chassis, read from the OVN Southbound DB. One row per node whose ovn-controller is connected to the SB DB; a node missing here has a down/disconnected dataplane agent (no OVS flows, no FIP GARP) — a silent outage the raft-leadership rollup misses. Only meaningful when chassisQueried=true.
object
The chassis name — the node’s OVN system-id.
The hostname ovn-controller registered for this chassis.
The cluster’s service VIPs and which node currently holds each. The Postgres VIP follows the Patroni primary, so its holder is the leader; other VIP holders are reported when known.
object
Which VIP (api / postgres / openbao / ovnNb / ovnSb / vmselect / providerGw).
The VIP address.
The node id currently holding this VIP, when known: the node whose own underlay report (at most 5 minutes old) has the address on a link, for every VIP vmselect included; else the leader the VIP follows (the Patroni leader for Postgres, the raft leaders for OpenBao and OVN, the control-plane leader for the API). providerGw follows the active VPC gateway chassis. Empty when neither is known.
The Postgres cluster’s promotion history, newest first — each timeline bump is a promotion, a planned switchover or a failover: Postgres records them alike. Sourced from Patroni’s /history.
object
The Postgres timeline this promotion created.
The member promoted to leader at this timeline.
When the promotion happened (RFC3339).
The recorded reason for the timeline switch, when there is one. Absent for Postgres’s default “no recovery target specified”, which it writes for every switchover and failover alike.
Example
{ "clusterState": "single-node", "nodes": [ { "nodeId": "n-abc12def", "role": "control-plane", "networks": { "additionalProperty": { "ip": "192.0.2.111", "interface": "vlan90" } }, "bootState": "ready", "bootPhase": "migrations" } ], "members": [ { "name": "n-c6ttm6tl", "host": "192.0.2.196", "role": "leader", "state": "streaming" } ], "vips": [ { "name": "api" } ]}Authenticated but not authorized.
object
The findings of a rejected IAM policy document (custom and resource policies).
One finding of a rejected IAM policy document. A rejection lists every finding, so the author fixes the document in one round trip.
object
The finding, e.g. INVALID_DOCUMENT, UNKNOWN_ACTION, ACTION_NOT_APPLICABLE.
Index of the statement at fault; -1 for the document as a whole.
The offending action, where one is at fault (ACTION_NOT_APPLICABLE).
Field errors, for codes that carry them: each problem at its field
(see FieldError). Documented on the responses that set it, e.g.
approveNodeJoin’s INVALID_INPUT and JOIN_APPROVAL_REFUSED.
Absent otherwise.
One field’s problem, the stage-2 installer API’s shape.
field is the field’s path, message what is wrong with it.
object
The field’s path, e.g. links[0].interface, disks[1].confirm,
mgmt.address or role; empty for the whole request.
Structured facts about the refusal, for codes that carry them. The
shape is fixed per code and documented on the response that answers
it, e.g. IMAGE_POOL_MISMATCH (see ImagePoolMismatchDetails) and
IMAGE_IN_USE. Absent for codes that carry none.
object
Example
{ "issues": [ { "code": "ACTION_NOT_APPLICABLE", "action": "compute:CreateVpc" } ]}