Partially update the desired Ceph cluster spec (bootstrap-admin).
curl --request PATCH \ --url https://stratum.example.com/v1/admin/storage/ceph/cluster \ --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 }'Change one field of the desired cluster without re-sending the rest.
POST /bootstrap is a full replace: whatever it omits is defaulted, not
preserved, so a Day-2 edit through it can silently reset osdDeviceSelector
to all-available and point the reconciler at every blank disk on every node.
Use bootstrap for first bring-up and this for everything after.
Absent field = unchanged, explicit null = reset to default. The merged
result is validated as a whole. Install-scope; gates on
storage:BootstrapCeph.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”A Day-2 PARTIAL update of the desired Ceph cluster spec. Merge semantics, and the three cases are genuinely distinct:
- field absent — left exactly as it is.
- field set to
null— reset to its default. - field set to a value — set to that value.
This is the endpoint to use for any change after first bring-up.
POST /v1/admin/storage/ceph/bootstrap is a FULL REPLACE — whatever it
omits is defaulted, not preserved — which is correct for bootstrap and
dangerous afterwards. On a live cluster whose spec is
{"image":"quay.io/ceph/ceph:v20.2.2","clusterNetwork":"192.0.2.0/24",
"monCount":3,"mgrCount":2,"pgNum":0,"osdDeviceSelector":"model:WUS"}
a bootstrap request of {"placement": {...}} alone resets
osdDeviceSelector to its default and blanks clusterNetwork. The same
request here changes the placement and nothing else.
The merged result is validated as a whole, not field by field: a patch value can only be judged against the fields it lands beside.
Returns 202 — the desired state is recorded and the reconciler converges the live cluster asynchronously. 404 if no cluster has been bootstrapped: creating one implicitly, from mostly-defaults, is the silent-wipe behavior this endpoint exists to remove.
object
Ceph container image pin. Null resets to the pinned Tentacle el10 image.
Ceph public_network CIDR. Null clears it, which returns the network to being derived from each node’s storage traffic class.
Ceph cluster_network CIDR (OSD replication). Null clears it, as above.
Monitor count. Null resets to 3.
Manager count. Null resets to 2.
Pool placement-group count. Null resets to 0 (the PG autoscaler picks).
OSD disk selection: “” (the members’ hardware-plan ceph-osd disks,) | all-available | model:. Null resets to the hardware plan’s disks. all-available needs confirmWidenOsdDevices.
Explicit per-host OSD device paths; takes precedence over osdDevices. Null clears the pinning. A device the hardware plan gives another role (system, compute, unused) is refused with 400.
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.
Per-daemon-kind placement, keyed by kind (mon, mgr only — OSD placement follows the devices). Null clears it, returning to cephadm’s own scheduler spreading the requested counts.
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 to set osdDevices to all-available over another selection;
without it that specific change is refused with 409.
It is the one change here that cannot be undone. Every other field is recoverable — a cleared network gets re-derived, a wrong count gets re-applied — but 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.
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”Desired state recorded; the reconciler converges the live cluster asynchronously.
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" } ]}Resource not found.
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" } ]}Conflicting state.
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" } ]}