diff --git a/nexus/src/app/disk.rs b/nexus/src/app/disk.rs index 2f045272437..82163162dc7 100644 --- a/nexus/src/app/disk.rs +++ b/nexus/src/app/disk.rs @@ -517,4 +517,26 @@ impl super::Nexus { .await?; Ok(()) } + + /// Remove a read only parent from a disk. + /// This is just a wrapper around the volume operation of the same + /// name, but we provide this interface when all the caller has is + /// the disk UUID as the internal volume_id is not exposed. + pub async fn disk_remove_read_only_parent( + self: &Arc, + opctx: &OpContext, + disk_id: Uuid, + ) -> DeleteResult { + // First get the internal volume ID that is stored in the disk + // database entry, once we have that just call the volume method + // to remove the read only parent. + let (.., db_disk) = LookupPath::new(opctx, &self.db_datastore) + .disk_id(disk_id) + .fetch() + .await?; + + self.volume_remove_read_only_parent(db_disk.volume_id).await?; + + Ok(()) + } } diff --git a/nexus/src/internal_api/http_entrypoints.rs b/nexus/src/internal_api/http_entrypoints.rs index 01d160b7e6c..f5c2cae8e52 100644 --- a/nexus/src/internal_api/http_entrypoints.rs +++ b/nexus/src/internal_api/http_entrypoints.rs @@ -42,6 +42,7 @@ pub fn internal_api() -> NexusApiDescription { api.register(cpapi_instances_put)?; api.register(cpapi_disks_put)?; api.register(cpapi_volume_remove_read_only_parent)?; + api.register(cpapi_disk_remove_read_only_parent)?; api.register(cpapi_producers_post)?; api.register(cpapi_collectors_post)?; api.register(cpapi_metrics_collect)?; @@ -226,6 +227,31 @@ async fn cpapi_volume_remove_read_only_parent( apictx.internal_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Request removal of a read_only_parent from a disk +/// This is a thin wrapper around the volume_remove_read_only_parent saga. +/// All we are doing here is, given a disk UUID, figure out what the +/// volume_id is for that disk, then use that to call the +/// volume_remove_read_only_parent saga on it. +#[endpoint { + method = POST, + path = "/disk/{disk_id}/remove-read-only-parent", + }] +async fn cpapi_disk_remove_read_only_parent( + rqctx: Arc>>, + path_params: Path, +) -> Result { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + + let handler = async { + let opctx = OpContext::for_internal_api(&rqctx).await; + nexus.disk_remove_read_only_parent(&opctx, path.disk_id).await?; + Ok(HttpResponseUpdatedNoContent()) + }; + apictx.internal_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + /// Accept a registration from a new metric producer #[endpoint { method = POST, diff --git a/openapi/nexus-internal.json b/openapi/nexus-internal.json index 48a2eb7e40d..b320e5cb1f8 100644 --- a/openapi/nexus-internal.json +++ b/openapi/nexus-internal.json @@ -59,6 +59,35 @@ } } }, + "/disk/{disk_id}/remove-read-only-parent": { + "post": { + "summary": "Request removal of a read_only_parent from a disk", + "description": "This is a thin wrapper around the volume_remove_read_only_parent saga. All we are doing here is, given a disk UUID, figure out what the volume_id is for that disk, then use that to call the volume_remove_read_only_parent saga on it.", + "operationId": "cpapi_disk_remove_read_only_parent", + "parameters": [ + { + "in": "path", + "name": "disk_id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, "/disks/{disk_id}": { "put": { "summary": "Report updated state for a disk.",