List machines that registered to join (bootstrap-admin).
curl --request GET \ --url https://stratum.example.com/v1/admin/cluster/joins \ --header 'Authorization: Bearer <token>'. Every registration, newest first, with its
hardware, pairing code, state and steps. Gates on ha:DescribeNodeJoins.
Authorizations
Section titled “Authorizations”Responses
Section titled “Responses”The registrations.
object
A machine that registered to join, on the founder.
States: needs-upgrade (the machine runs an older release than the
cluster’s, within its upgrade rules, and upgrades itself before it can be
approved: upgrade says from and to, failedStep and failedError the
last failure of its upgrade, which it retries by itself; it becomes
pending when it registers again on the cluster’s release; approving it is
refused, rejecting it is not); pending (registered, waiting for approval); approved (queued:
one join runs at a time, only on a green cluster, and waitReason says
what it waits for); delivering (its member row is written and its
node.yaml and bundle are ready for it); installing (it reported
progress); joined; failed (with the step and the error; retry queues
it again); rejected; expired (nobody approved it within 24 hours of
its code’s expiry).
object
Shown on the joiner’s console too. Approve the machine whose console shows the same code.
The release the joiner was installed from.
The address it registered from.
SHA-256 of the joiner’s client key; every later call is pinned to it.
A joining machine’s hardware, as it reported it at registration (the inventory). The approval’s assignment is checked against it.
object
One whole disk of a joining machine, as its discovery reports it.
object
Live mounts on the disk or anything stacked on it. A mounted disk is the system disk.
What already holds data on the disk. Empty means blank.
object
The disk’s partitions. On an OS disk they say what stage 1 left free at its end (the compute tail, or nothing with the whole-disk layout).
object
Every md array and device-mapper device built on the disk, deepest first.
object
One network interface of a joining machine.
object
up with a carrier, down without one, unknown.
The IPv4 destinations the main routing table sends out of this NIC (CIDRs; a host route as /32), without the default route.
object
vmx, svm, or empty.
How stage 1 laid out the OS disks, from the joiner’s install
record: compute-tail keeps the end of each OS disk for the compute
pool, whole-disk gives the disks whole to the system, so compute is a
data disk (a compute pool, or Ceph on a disk marked ceph-osd). Absent
on a machine with no record; the founder then reads it off the disks’
partitions.
Everything about a joiner, chosen on the founder. It becomes the joiner’s node.yaml and its member row. What the cluster decides (region, parent domain, upstream DNS, floating IPs, the first admin, services, base images) is not here: the joiner adopts it.
object
Display name. Default the hostname.
Every joiner is hybrid (control plane, compute and Ceph together),
the default. Any other role (a compute-only hypervisor, a
storage-only node, control-plane) is refused at role: those nodes
are not supported yet and are planned for a later release.
The planned size (3 by default, set at install) is the plan, not a
cap: a cluster takes joiners up to 5 control-plane nodes, and an
approval that would pass 5 is refused at role.
The topology.rack label.
The mgmt link. Default: the joiner’s NIC holding the address it
registered from, and that address.
object
An IP, or a CIDR when the address is new on that NIC (with gateway).
One link of a joiner: an ethernet NIC, a bond, or a VLAN
on either, and the purposes it carries. The same shape as a node.yaml
network.connections entry; a bond member is an ethernet link naming its
bond in controller. The oob link is the stage-1 NIC and is not listed.
object
Default ethernet.
A VLAN’s parent device.
The bond this ethernet link is a member of.
object
Static CIDRs. Optional: a link that carries storage or overlay and
names none gets the next free host address of each of those cluster
networks (GET /v1/admin/cluster/networks), picked at approval and
returned here with addressesAllocated. Free means no member, VIP or
other approved, running or failed join holds it; a re-approval keeps
the address it had while it is still free, and a retry installs the
same one. A named address must be free and in the purpose’s network.
None on a bond member or a provider-only link.
The founder picked addresses from the cluster’s networks. Ignored on input.
Default the largest MTU of the cluster networks the link carries.
What a joiner’s disk becomes. The system disk(s) were
fixed by stage 1 and are not listed. A disk that holds data is wiped, so it
needs confirm set to its key.
object
The disk’s WWN or one of its /dev/disk/by-id paths.
A compute disk’s pool name.
A compute pool’s storage class. Default the pool’s name.
The typed wipe confirmation, the disk’s key, for a disk that holds data.
Only when this node’s NTP servers differ from the cluster’s (the founder’s), which it gets otherwise.
The VM uplink: where the joiner’s floating IPs live and its
VM traffic leaves. Default the cluster’s provider choice
(GET /v1/admin/cluster/networks providerTopology, providerVlan,
providerUpstreamInterface), recorded from the founder. Topology and
VLAN are the cluster’s: a different value is refused at
provider.topology / provider.vlan. The uplink NIC defaults to the
interface of a link carrying provider, else the joiner’s NIC of the
founder’s uplink’s purpose (providerUpstreamPurpose mgmt: its mgmt
NIC), else the joiner’s NIC named like the founder’s. A bridged cluster
refuses, at provider.upstreamInterface, a joiner without that NIC or
whose uplink carries storage or overlay.
object
Must equal the cluster’s providerTopology when set.
The joiner’s NIC (or bond) that carries the provider network. Default by the founder’s uplink (above).
Default the cluster’s provider VLAN; any other value is refused.
The node the assignment was copied from (copyFrom).
What the copy could not match and left for the operator.
Assigned at approval.
The upgrade a joiner on an older release makes before it can register: from the release it was installed with to
the release the cluster runs. Only a joiner that sent selfUpgrade: true
is ever offered one; any other joiner on another release is refused with
RELEASE_MISMATCH.
object
The release the joiner reported.
The release the cluster runs, which the joiner upgrades to.
Where the joiner fetches the release from (the release mirror’s URL of the release directory). Every file is checked against the release’s signed manifest. Empty when the founder names none.
The founder gives the release itself, when no release mirror can (a lab,
an air gap): fetch it from GET /v1/join/registrations/{joinId}/release/{path}
on the join listener, the release layout a mirror publishes
(payload/manifest.json, os/blobs/..., sysext/...). source is then
empty. Served only to this registration while it is needs-upgrade,
read only, a few files at once (503 RELEASE_BUSY: retry); ranges are
accepted so a download resumes. The joiner verifies the manifest’s
signature against the release keys of its own OS and every file against
the manifest, as it does from a mirror.
A running join whose joiner has been silent for 10 minutes.
The step that failed, as the joiner named it: a firstboot phase
(storage), a join step (join/patroni), upgrade or
upgrade/<step> while it upgrades itself, or join.
What a person is told failedStep is, e.g. “Upgrading to the
cluster’s release” for upgrade. Set whenever failedStep is, for
any step name, so a console needs no table of step names.
One step of a join, as the joiner reported it.
object
What a person is told the step is, e.g. “Joining the OpenBao raft”.
The last lines of the joiner’s install log (the founder keeps 2,000). Only on a single join.
When a pending join expires.
Example
{ "items": [ { "id": "nj-8w2k4m1p", "state": "needs-upgrade", "pairingCode": "4F7-K2Q", "inventory": { "disks": [ { "name": "nvme1n1", "path": "/dev/nvme1n1" } ], "nics": [ { "name": "enp87s0" } ], "osDiskLayout": "compute-tail" }, "assignment": { "role": "hybrid", "links": [ { "name": "storage", "kind": "ethernet", "interface": "vlan90", "bond": { "mode": "active-backup" }, "purposes": [ "oob" ] } ], "disks": [ { "key": "/dev/disk/by-id/nvme-Samsung_SSD_980_PRO_2TB_S6B0NL0W123456", "role": "compute" } ], "provider": { "topology": "bridged" } }, "upgrade": { "from": "0.5.0", "to": "0.6.0", "source": "https://mirror.example/releases/stratum/0.6.0/bundle" }, "steps": [ { "name": "join/raft-openbao", "title": "Joining the OpenBao raft", "status": "running" } ] } ]}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" } ]}