Request a Day-2 Ceph cluster bootstrap (bootstrap-admin).
curl --request POST \ --url https://stratum.example.com/v1/admin/storage/ceph/bootstrap \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "image": "example", "publicNetwork": "example", "clusterNetwork": "example", "monCount": 1, "mgrCount": 1, "pgNum": 1, "osdDevices": "example", "osdPaths": [ { "host": "example", "devices": [ "example" ] } ], "placement": { "additionalProperty": { "hosts": [ "example" ], "label": "example" } }, "confirmWidenOsdDevices": true }'Records the desired Ceph cluster (topology + storage
networks) and returns 202; a reconciler converges the live cephadm
cluster toward it asynchronously. Never runs at firstboot. All fields
optional: image + mon/mgr counts default, and the networks default to
the reconciler deriving them from each node’s storage traffic
class. Idempotent — re-requesting updates the desired state.
Install-scope; gates on storage:BootstrapCeph.
Authorizations
Section titled “Authorizations”Request Body
Section titled “Request Body”Day-2 bootstrap request. All fields optional — image + counts default, networks default to the reconciler deriving them from each node’s storage traffic class.
This is a FULL REPLACE. Anything omitted is set to its default, not
preserved (the sole exception being the RGW fields, which are owned by
POST /rgw and carried over). That is right for first bring-up and wrong
for a Day-2 edit: sending {"placement": {...}} alone against a live
cluster also resets osdDevices and blanks the networks. For any change
after bootstrap use PATCH /v1/admin/storage/ceph/cluster,
which leaves unmentioned fields alone.
object
Ceph container image pin (defaults to the Tentacle el10 image).
Ceph public_network CIDR, e.g. 192.0.2.0/24.
Ceph cluster_network CIDR (OSD replication).
Monitor count (default 3).
Manager count (default 2).
Pool placement-group count; 0 = the PG autoscaler picks.
OSD disk selection. Omitted or empty: each member’s hardware-plan ceph-osd disks, by /dev/disk/by-id path, one OSD service per host (members that join later get theirs). all-available takes every blank disk on every host and only when asked for by name; model:<m> | size:<s> filter every host.
Explicit per-host OSD device paths. When set it takes precedence over osdDevices: each entry becomes a per-host cephadm drive-group with exactly these /dev paths — nothing else on the host is consumed.
The disks an install reserved for Ceph are listed by
GET /v1/admin/storage/ceph/osd-candidates in this very shape; the
console pre-fills osdPaths from it. Without osdPaths or osdDevices the
selection is those same disks, from the hardware plan. A
device the plan gives another role (system, compute, unused) is
refused with 400; mark it ceph-osd in the node’s plan first.
One host’s explicit OSD device contribution.
object
Cephadm hostname (as reported by GET /ceph/hosts).
Absolute /dev paths on that host to consume as OSDs.
Optional per-daemon-kind placement,
keyed by kind. Only mon and mgr are accepted: OSD placement
follows the DEVICES, via osdDevices / osdPaths.
Omit it entirely for the default behavior, where cephadm’s own scheduler spreads the requested count over the enrolled hosts. That is correct on a small cluster where count == hosts; pin placement when the hosts are not interchangeable (“mons on the control nodes, never the storage-heavy ones”).
object
Where one Ceph daemon kind may run.
Set exactly one of hosts or label. Both empty is rejected rather than
treated as “anywhere”: an empty entry would render as a bare count, which
is the opposite of what asking for a placement means.
The daemon COUNT still comes from monCount / mgrCount and is combined
with this constraint, so a label matching five hosts still yields
mgrCount managers rather than five.
object
Explicit cephadm host names. Validated against the enrolled member set — an unknown name is a 400, because cephadm itself accepts one and then simply never schedules the daemon, which is indistinguishable from “still converging”.
Cephadm host label to place on (ceph orch host label add <host> <label>). Mutually exclusive with hosts.
Required when this request would set osdDevices to all-available
over another selection; without it that specific transition is refused
with 409. An omitted selector is the hardware plan’s
disks, not all-available.
It is the one field here whose loss is not recoverable: the reconciler acts on a widened selector by consuming every blank disk on every node, and a disk consumed as an OSD has been written to. Everything else a full replace resets can simply be re-applied.
Example generated
{ "image": "example", "publicNetwork": "example", "clusterNetwork": "example", "monCount": 1, "mgrCount": 1, "pgNum": 1, "osdDevices": "example", "osdPaths": [ { "host": "example", "devices": [ "example" ] } ], "placement": { "additionalProperty": { "hosts": [ "example" ], "label": "example" } }, "confirmWidenOsdDevices": true}Responses
Section titled “Responses”Bootstrap requested; desired state recorded.
Desired + observed Ceph cluster. configured=false before any bootstrap. exists, bootstrapFailed and rgwDeployed are the platform’s own answers, derived from the observed status, never from the desired row.
object
The operator’s desired Ceph cluster (singleton).
object
“” = the members’ hardware-plan ceph-osd disks |
all-available | model:. What it resolves to is the
view’s osdSelection.
One host’s explicit OSD device contribution.
object
Cephadm hostname (as reported by GET /ceph/hosts).
Absolute /dev paths on that host to consume as OSDs.
Desired RADOS Gateway (S3 object storage) deployment.
When true, the reconciler runs ceph orch apply rgw.
Cephadm rgw service name; empty = ‘stratum’.
Radosgw daemon count; 0 = default to mon count.
Radosgw beast frontend port; 0 = default 7480.
The per-daemon-kind placement in effect, keyed by kind. Absent means bare counts: cephadm’s scheduler chooses the hosts.
object
Where one Ceph daemon kind may run.
Set exactly one of hosts or label. Both empty is rejected rather than
treated as “anywhere”: an empty entry would render as a bare count, which
is the opposite of what asking for a placement means.
The daemon COUNT still comes from monCount / mgrCount and is combined
with this constraint, so a label matching five hosts still yields
mgrCount managers rather than five.
object
Explicit cephadm host names. Validated against the enrolled member set — an unknown name is a 400, because cephadm itself accepts one and then simply never schedules the daemon, which is indistinguishable from “still converging”.
Cephadm host label to place on (ceph orch host label add <host> <label>). Mutually exclusive with hosts.
The reconciler’s observed Ceph cluster state (singleton).
object
The control plane (node id) that wrote this status.
The radosgw daemons the reconciler observed running at its last probe. Absent when not observed (no cluster answered, or the gateway listing failed): unknown, not zero.
A Ceph cluster runs, as last observed: an fsid, and a cluster that answered (phase ready or degraded, or error from a converge step on a cluster that answered). What S3, readiness and the RGW deploy act on.
A requested bootstrap failed before any cluster existed (phase error, no fsid). Readiness raises CephBootstrapFailed and the ceph-bootstrap-failed alarm fires on it. Request the bootstrap again to retry.
The object gateway is deployed: the cluster exists, RGW is requested, and a radosgw daemon was observed running. Absent while unknown.
The disks the reconciler makes OSDs, and where they come from: the same selection it converges. Absent before any bootstrap request.
object
Hardware-plan: each member’s ceph-osd disks (the default); osd-paths: the request’s osdPaths; all-available: every blank disk, asked for by name; selector: a model:/size: filter on every host.
The model:/size: filter, for source selector.
The per-host devices the OSD services name, for the hardware-plan and osd-paths sources (empty for the others). A member with no ceph-osd disk, or whose plan could not be read, has no entry.
One host’s explicit OSD device contribution.
object
Cephadm hostname (as reported by GET /ceph/hosts).
Absolute /dev paths on that host to consume as OSDs.
OsdPaths devices left out because the hardware plan gives the disk another role, as “host:device: role”.
The node that holds the bootstrap claim: the one node that
runs cephadm bootstrap for this request. Absent before a reconcile
pass has claimed it and after a teardown. It moves to another node only
on a new request while it is not active.
Example
{ "status": { "phase": "unconfigured" }, "osdSelection": { "source": "hardware-plan" }}Bad request.
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" } ]}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" } ]}