diff --git a/Cargo.lock b/Cargo.lock index 790775c9e..3d7f39101 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1213,7 +1213,7 @@ checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crucible" version = "0.0.1" -source = "git+https://github.com/oxidecomputer/crucible?rev=ae1da83e66c648574827298f4bc444632bf4d047#ae1da83e66c648574827298f4bc444632bf4d047" +source = "git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431#3c1708d86e10f0370807388a1efe092edd99d431" dependencies = [ "aes-gcm-siv", "anyhow", @@ -1224,7 +1224,7 @@ dependencies = [ "bytes", "cfg-if", "chrono", - "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=ae1da83e66c648574827298f4bc444632bf4d047)", + "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431)", "crucible-common", "crucible-protocol", "crucible-workspace-hack", @@ -1269,7 +1269,7 @@ dependencies = [ [[package]] name = "crucible-client-types" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/crucible?rev=7103cd3a3d7b0112d2949dd135db06fef0c156bb#7103cd3a3d7b0112d2949dd135db06fef0c156bb" +source = "git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431#3c1708d86e10f0370807388a1efe092edd99d431" dependencies = [ "base64 0.22.1", "crucible-workspace-hack", @@ -1282,7 +1282,7 @@ dependencies = [ [[package]] name = "crucible-client-types" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/crucible?rev=ae1da83e66c648574827298f4bc444632bf4d047#ae1da83e66c648574827298f4bc444632bf4d047" +source = "git+https://github.com/oxidecomputer/crucible?rev=7103cd3a3d7b0112d2949dd135db06fef0c156bb#7103cd3a3d7b0112d2949dd135db06fef0c156bb" dependencies = [ "base64 0.22.1", "crucible-workspace-hack", @@ -1295,7 +1295,7 @@ dependencies = [ [[package]] name = "crucible-common" version = "0.0.1" -source = "git+https://github.com/oxidecomputer/crucible?rev=ae1da83e66c648574827298f4bc444632bf4d047#ae1da83e66c648574827298f4bc444632bf4d047" +source = "git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431#3c1708d86e10f0370807388a1efe092edd99d431" dependencies = [ "anyhow", "atty", @@ -1325,7 +1325,7 @@ dependencies = [ [[package]] name = "crucible-protocol" version = "0.0.0" -source = "git+https://github.com/oxidecomputer/crucible?rev=ae1da83e66c648574827298f4bc444632bf4d047#ae1da83e66c648574827298f4bc444632bf4d047" +source = "git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431#3c1708d86e10f0370807388a1efe092edd99d431" dependencies = [ "anyhow", "bincode", @@ -6432,7 +6432,7 @@ dependencies = [ "cpuid_utils", "crossbeam-channel", "crucible", - "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=ae1da83e66c648574827298f4bc444632bf4d047)", + "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431)", "dice-verifier", "dladm", "dlpi 0.2.0 (git+https://github.com/oxidecomputer/dlpi-sys?branch=main)", @@ -6477,7 +6477,7 @@ dependencies = [ name = "propolis-api-types-versions" version = "0.0.0" dependencies = [ - "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=ae1da83e66c648574827298f4bc444632bf4d047)", + "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431)", "propolis_types 0.0.0", "schemars 0.8.22", "serde", @@ -6493,7 +6493,7 @@ dependencies = [ "anyhow", "base64 0.21.7", "clap", - "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=ae1da83e66c648574827298f4bc444632bf4d047)", + "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431)", "futures", "libc", "newtype-uuid", @@ -6516,7 +6516,7 @@ version = "0.1.0" dependencies = [ "async-trait", "base64 0.21.7", - "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=ae1da83e66c648574827298f4bc444632bf4d047)", + "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431)", "futures", "progenitor 0.14.0", "progenitor-client 0.14.0", @@ -6642,7 +6642,7 @@ dependencies = [ "clap", "const_format", "cpuid_utils", - "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=ae1da83e66c648574827298f4bc444632bf4d047)", + "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431)", "dropshot 0.17.0", "erased-serde 0.4.5", "expectorate", @@ -6698,7 +6698,7 @@ dependencies = [ name = "propolis-server-api" version = "0.1.0" dependencies = [ - "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=ae1da83e66c648574827298f4bc444632bf4d047)", + "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431)", "dropshot 0.17.0", "dropshot-api-manager-types", "propolis-api-types-versions", @@ -6714,7 +6714,7 @@ dependencies = [ "clap", "cpuid_profile_config", "cpuid_utils", - "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=ae1da83e66c648574827298f4bc444632bf4d047)", + "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431)", "ctrlc", "erased-serde 0.4.5", "fatfs", @@ -6757,7 +6757,7 @@ dependencies = [ name = "propolis_api_types" version = "0.0.0" dependencies = [ - "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=ae1da83e66c648574827298f4bc444632bf4d047)", + "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431)", "propolis-api-types-versions", ] diff --git a/Cargo.toml b/Cargo.toml index a59c68a43..84575acef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,8 +92,8 @@ oximeter = { git = "https://github.com/oxidecomputer/omicron", branch = "main" } sled-agent-client = { git = "https://github.com/oxidecomputer/omicron", branch = "main" } # Crucible -crucible = { git = "https://github.com/oxidecomputer/crucible", rev = "ae1da83e66c648574827298f4bc444632bf4d047" } -crucible-client-types = { git = "https://github.com/oxidecomputer/crucible", rev = "ae1da83e66c648574827298f4bc444632bf4d047" } +crucible = { git = "https://github.com/oxidecomputer/crucible", rev = "3c1708d86e10f0370807388a1efe092edd99d431" } +crucible-client-types = { git = "https://github.com/oxidecomputer/crucible", rev = "3c1708d86e10f0370807388a1efe092edd99d431" } # Attestation dice-verifier = { git = "https://github.com/oxidecomputer/dice-util", rev = "1d3084b514389847e8e0f5d966d2be4f18d02d32", features = ["sled-agent"] } diff --git a/bin/propolis-server/src/lib/server.rs b/bin/propolis-server/src/lib/server.rs index 15404be72..cba4f65cd 100644 --- a/bin/propolis-server/src/lib/server.rs +++ b/bin/propolis-server/src/lib/server.rs @@ -55,6 +55,7 @@ use propolis_api_types::serial::{ InstanceSerialConsoleHistoryRequest, InstanceSerialConsoleHistoryResponse, InstanceSerialConsoleStreamRequest, }; +use propolis_api_types_versions::v1::disk::VolumeStatus as VolumeStatusV1; use propolis_server_api::PropolisServerApi; use rfb::tungstenite::BinaryWs; use slog::{error, warn, Logger}; @@ -557,10 +558,10 @@ impl PropolisServerApi for PropolisServerImpl { Ok(HttpResponseOk(())) } - async fn disk_volume_status( + async fn disk_volume_status_v1( rqctx: RequestContext, path_params: Path, - ) -> Result, HttpError> { + ) -> Result, HttpError> { let path_params = path_params.into_inner(); let vm = rqctx .context() @@ -578,13 +579,41 @@ impl PropolisServerApi for PropolisServerImpl { HttpError::for_not_found(Some(s.clone()), s) })?; - Ok(HttpResponseOk(VolumeStatus { + Ok(HttpResponseOk(VolumeStatusV1 { active: backend.volume_is_active().await.map_err(|e| { HttpError::for_bad_request(Some(e.to_string()), e.to_string()) })?, })) } + async fn disk_volume_status( + rqctx: RequestContext, + path_params: Path, + ) -> Result, HttpError> { + let path_params = path_params.into_inner(); + let vm = rqctx + .context() + .vm + .active_vm() + .await + .ok_or_else(not_created_error)?; + let objects = vm.objects().lock_shared().await; + let backend = objects + .crucible_backends() + .get(&SpecKey::from(path_params.id.clone())) + .ok_or_else(|| { + let s = + format!("No crucible backend for id {}", path_params.id); + HttpError::for_not_found(Some(s.clone()), s) + })?; + + let volume_info = backend.query_volume_info().await.map_err(|e| { + HttpError::for_bad_request(Some(e.to_string()), e.to_string()) + })?; + + Ok(HttpResponseOk(VolumeStatus { volume_info })) + } + async fn instance_issue_crucible_vcr_request( rqctx: RequestContext, path_params: Path, diff --git a/crates/propolis-api-types-versions/src/crucible_volume_info/disk.rs b/crates/propolis-api-types-versions/src/crucible_volume_info/disk.rs new file mode 100644 index 000000000..24ae23b74 --- /dev/null +++ b/crates/propolis-api-types-versions/src/crucible_volume_info/disk.rs @@ -0,0 +1,13 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Disk and volume types for the CRUCIBLE_VOLUME_INFO API version. + +use schemars::JsonSchema; +use serde::Serialize; + +#[derive(Debug, Serialize, JsonSchema)] +pub struct VolumeStatus { + pub volume_info: crucible_client_types::VolumeInfo, +} diff --git a/crates/propolis-api-types-versions/src/crucible_volume_info/mod.rs b/crates/propolis-api-types-versions/src/crucible_volume_info/mod.rs new file mode 100644 index 000000000..5edcc16bb --- /dev/null +++ b/crates/propolis-api-types-versions/src/crucible_volume_info/mod.rs @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Version `CRUCIBLE_VOLUME_INFO` of the Propolis Server API. +//! +//! This version changes the `disk_volume_status` endpoint to use the new +//! VolumeInfo query + +pub mod disk; diff --git a/crates/propolis-api-types-versions/src/latest.rs b/crates/propolis-api-types-versions/src/latest.rs index 160d1f648..0d9675aeb 100644 --- a/crates/propolis-api-types-versions/src/latest.rs +++ b/crates/propolis-api-types-versions/src/latest.rs @@ -50,8 +50,8 @@ pub mod disk { pub use crate::v1::disk::InstanceVCRReplace; pub use crate::v1::disk::SnapshotRequestPathParams; pub use crate::v1::disk::VCRRequestPathParams; - pub use crate::v1::disk::VolumeStatus; pub use crate::v1::disk::VolumeStatusPathParams; + pub use crate::v5::disk::VolumeStatus; } pub mod instance { diff --git a/crates/propolis-api-types-versions/src/lib.rs b/crates/propolis-api-types-versions/src/lib.rs index d9853a806..77dd5e31c 100644 --- a/crates/propolis-api-types-versions/src/lib.rs +++ b/crates/propolis-api-types-versions/src/lib.rs @@ -37,3 +37,5 @@ pub mod v1; pub mod v2; #[path = "add_vsock/mod.rs"] pub mod v3; +#[path = "crucible_volume_info/mod.rs"] +pub mod v5; diff --git a/crates/propolis-server-api/src/lib.rs b/crates/propolis-server-api/src/lib.rs index 748ba425f..8670ab02d 100644 --- a/crates/propolis-server-api/src/lib.rs +++ b/crates/propolis-server-api/src/lib.rs @@ -22,6 +22,7 @@ api_versions!([ // | example for the next person. // v // (next_int, IDENT), + (5, CRUCIBLE_VOLUME_INFO), (4, DROPSHOT_BUMP_WEBSOCKET), (3, ADD_VSOCK), (2, PROGRAMMABLE_SMBIOS), @@ -266,8 +267,21 @@ pub trait PropolisServerApi { /// Gets the status of a Crucible volume backing a disk #[endpoint { + operation_id = "disk_volume_status", method = GET, path = "/instance/disk/{id}/status", + versions = VERSION_INITIAL..VERSION_CRUCIBLE_VOLUME_INFO, + }] + async fn disk_volume_status_v1( + rqctx: RequestContext, + path_params: Path, + ) -> Result, HttpError>; + + /// Gets the status of a Crucible volume backing a disk + #[endpoint { + method = GET, + path = "/instance/disk/{id}/status", + versions = VERSION_CRUCIBLE_VOLUME_INFO.., }] async fn disk_volume_status( rqctx: RequestContext, diff --git a/lib/propolis/src/block/crucible.rs b/lib/propolis/src/block/crucible.rs index 5ba04a14b..11f3276cb 100644 --- a/lib/propolis/src/block/crucible.rs +++ b/lib/propolis/src/block/crucible.rs @@ -18,6 +18,7 @@ use crucible::{ VolumeBuilder, }; use crucible_client_types::VolumeConstructionRequest; +use crucible_client_types::VolumeInfo; use oximeter::types::ProducerRegistry; use slog::{error, info}; use thiserror::Error; @@ -371,6 +372,10 @@ impl CrucibleBackend { pub fn is_read_only(&self) -> bool { self.state.info.read_only } + + pub async fn query_volume_info(&self) -> Result { + self.state.volume.query_volume_info().await + } } #[async_trait::async_trait] diff --git a/openapi/propolis-server/propolis-server-4.0.0-5ce09a.json.gitstub b/openapi/propolis-server/propolis-server-4.0.0-5ce09a.json.gitstub new file mode 100644 index 000000000..2552cbde1 --- /dev/null +++ b/openapi/propolis-server/propolis-server-4.0.0-5ce09a.json.gitstub @@ -0,0 +1 @@ +d9e5dc0d2bb8a185f008a43be9d8131fb9ca3a06:openapi/propolis-server/propolis-server-4.0.0-5ce09a.json diff --git a/openapi/propolis-server/propolis-server-4.0.0-5ce09a.json b/openapi/propolis-server/propolis-server-5.0.0-0c6dd9.json similarity index 91% rename from openapi/propolis-server/propolis-server-4.0.0-5ce09a.json rename to openapi/propolis-server/propolis-server-5.0.0-0c6dd9.json index 1eed05bc2..94d0a20d8 100644 --- a/openapi/propolis-server/propolis-server-4.0.0-5ce09a.json +++ b/openapi/propolis-server/propolis-server-5.0.0-0c6dd9.json @@ -7,7 +7,7 @@ "url": "https://oxide.computer", "email": "api@oxide.computer" }, - "version": "4.0.0" + "version": "5.0.0" }, "paths": { "/instance": { @@ -1045,6 +1045,117 @@ ], "additionalProperties": false }, + "DownstairsInfo": { + "type": "object", + "properties": { + "region_id": { + "nullable": true, + "type": "string", + "format": "uuid" + }, + "repair_addr": { + "nullable": true, + "type": "string" + }, + "state": { + "$ref": "#/components/schemas/DownstairsInfoStatus" + }, + "target_addr": { + "nullable": true, + "type": "string" + } + }, + "required": [ + "state" + ] + }, + "DownstairsInfoConnectionMode": { + "type": "string", + "enum": [ + "new", + "offline", + "faulted", + "replaced" + ] + }, + "DownstairsInfoNegotiationStatus": { + "type": "string", + "enum": [ + "wait_connect", + "negotiating", + "wait_quorum", + "reconcile", + "live_repair_ready" + ] + }, + "DownstairsInfoStatus": { + "oneOf": [ + { + "type": "object", + "properties": { + "mode": { + "$ref": "#/components/schemas/DownstairsInfoConnectionMode" + }, + "state": { + "$ref": "#/components/schemas/DownstairsInfoNegotiationStatus" + }, + "type": { + "type": "string", + "enum": [ + "connecting" + ] + } + }, + "required": [ + "mode", + "state", + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "active" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "live_repair" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "stopping" + ] + } + }, + "required": [ + "type" + ] + } + ] + }, "Error": { "description": "Error information from a response.", "type": "object", @@ -1979,6 +2090,16 @@ } ] }, + "UpstairsInfoStatus": { + "type": "string", + "enum": [ + "initializing", + "go_active", + "active", + "deactivating", + "disabled" + ] + }, "VirtioDisk": { "description": "A disk that presents a virtio-block interface to the guest.", "type": "object", @@ -2078,15 +2199,116 @@ ], "additionalProperties": false }, + "VolumeInfo": { + "description": "A tree representation of the info and status of all parts of a Volume.", + "oneOf": [ + { + "type": "object", + "properties": { + "volume": { + "type": "object", + "properties": { + "read_only_parent": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/VolumeInfo" + } + ] + }, + "sub_volumes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VolumeInfo" + } + } + }, + "required": [ + "sub_volumes" + ] + } + }, + "required": [ + "volume" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "upstairs": { + "type": "object", + "properties": { + "block_size": { + "nullable": true, + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "encrypted": { + "type": "boolean" + }, + "generation": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "live_repair_in_progress": { + "type": "boolean" + }, + "read_only": { + "type": "boolean" + }, + "reconcile_in_progress": { + "type": "boolean" + }, + "session_id": { + "type": "string", + "format": "uuid" + }, + "state": { + "$ref": "#/components/schemas/UpstairsInfoStatus" + }, + "targets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DownstairsInfo" + } + }, + "upstairs_id": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "encrypted", + "generation", + "live_repair_in_progress", + "read_only", + "reconcile_in_progress", + "session_id", + "state", + "targets", + "upstairs_id" + ] + } + }, + "required": [ + "upstairs" + ], + "additionalProperties": false + } + ] + }, "VolumeStatus": { "type": "object", "properties": { - "active": { - "type": "boolean" + "volume_info": { + "$ref": "#/components/schemas/VolumeInfo" } }, "required": [ - "active" + "volume_info" ] } }, diff --git a/openapi/propolis-server/propolis-server-latest.json b/openapi/propolis-server/propolis-server-latest.json index 827f14277..452ab4db5 120000 --- a/openapi/propolis-server/propolis-server-latest.json +++ b/openapi/propolis-server/propolis-server-latest.json @@ -1 +1 @@ -propolis-server-4.0.0-5ce09a.json \ No newline at end of file +propolis-server-5.0.0-0c6dd9.json \ No newline at end of file