From 742f09f1f03c91d64eb90927adfbf226335f0257 Mon Sep 17 00:00:00 2001 From: Mike Zeller Date: Wed, 11 Mar 2026 17:31:49 -0400 Subject: [PATCH] [jj-spr] initial version Created using jj-spr 0.1.0 --- Cargo.lock | 87 +++++++++++++++--- Cargo.toml | 9 +- nexus/src/app/instance_platform/mod.rs | 63 +++++++------ .../sled-agent-27.0.0-42911d.json.gitstub | 1 + ...11d.json => sled-agent-28.0.0-126106.json} | 91 +++++++++++++++++-- openapi/sled-agent/sled-agent-latest.json | 2 +- package-manifest.toml | 4 +- sled-agent/api/src/lib.rs | 24 ++++- sled-agent/src/instance.rs | 43 +++------ sled-agent/src/sim/instance.rs | 2 +- sled-agent/src/sim/sled_agent.rs | 6 +- sled-agent/types/versions/Cargo.toml | 1 + .../src/add_vsock_component/instance.rs | 70 ++++++++++++++ .../versions/src/add_vsock_component/mod.rs | 11 +++ .../types/versions/src/impls/instance.rs | 9 +- .../types/versions/src/initial/instance.rs | 4 +- sled-agent/types/versions/src/latest.rs | 5 +- sled-agent/types/versions/src/lib.rs | 2 + 18 files changed, 330 insertions(+), 104 deletions(-) create mode 100644 openapi/sled-agent/sled-agent-27.0.0-42911d.json.gitstub rename openapi/sled-agent/{sled-agent-27.0.0-42911d.json => sled-agent-28.0.0-126106.json} (99%) create mode 100644 sled-agent/types/versions/src/add_vsock_component/instance.rs create mode 100644 sled-agent/types/versions/src/add_vsock_component/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 31e144f33ec..549780d1f4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -617,7 +617,7 @@ dependencies = [ [[package]] name = "bhyve_api" version = "0.0.0" -source = "git+https://github.com/oxidecomputer/propolis?rev=36f20be9bb4c3b362029237f5feb6377c982395f#36f20be9bb4c3b362029237f5feb6377c982395f" +source = "git+https://github.com/oxidecomputer/propolis?rev=19c5faa556ebab465ac6cf42a133125c41b9e2c5#19c5faa556ebab465ac6cf42a133125c41b9e2c5" dependencies = [ "bhyve_api_sys", "libc", @@ -627,7 +627,7 @@ dependencies = [ [[package]] name = "bhyve_api_sys" version = "0.0.0" -source = "git+https://github.com/oxidecomputer/propolis?rev=36f20be9bb4c3b362029237f5feb6377c982395f#36f20be9bb4c3b362029237f5feb6377c982395f" +source = "git+https://github.com/oxidecomputer/propolis?rev=19c5faa556ebab465ac6cf42a133125c41b9e2c5#19c5faa556ebab465ac6cf42a133125c41b9e2c5" dependencies = [ "libc", "strum 0.26.3", @@ -5942,7 +5942,7 @@ dependencies = [ "libnet", "oxnet", "prettyplease", - "propolis-client", + "propolis-client 0.1.0 (git+https://github.com/oxidecomputer/propolis?rev=36f20be9bb4c3b362029237f5feb6377c982395f)", "quote", "rand 0.8.5", "regex", @@ -8581,7 +8581,7 @@ dependencies = [ "pretty_assertions", "progenitor-client 0.13.0", "progenitor-extras", - "propolis-client", + "propolis-client 0.1.0 (git+https://github.com/oxidecomputer/propolis?rev=19c5faa556ebab465ac6cf42a133125c41b9e2c5)", "qorb", "rand 0.9.2", "range-requests", @@ -9025,9 +9025,9 @@ dependencies = [ "oxnet", "pretty_assertions", "progenitor 0.13.0", - "propolis-client", + "propolis-client 0.1.0 (git+https://github.com/oxidecomputer/propolis?rev=19c5faa556ebab465ac6cf42a133125c41b9e2c5)", "propolis-mock-server", - "propolis_api_types", + "propolis_api_types 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=19c5faa556ebab465ac6cf42a133125c41b9e2c5)", "rand 0.9.2", "range-requests", "rcgen", @@ -11126,6 +11126,43 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "propolis-api-types-versions" +version = "0.0.0" +source = "git+https://github.com/oxidecomputer/propolis?rev=19c5faa556ebab465ac6cf42a133125c41b9e2c5#19c5faa556ebab465ac6cf42a133125c41b9e2c5" +dependencies = [ + "crucible-client-types", + "propolis_types 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=19c5faa556ebab465ac6cf42a133125c41b9e2c5)", + "schemars 0.8.22", + "serde", + "thiserror 1.0.69", + "uuid", +] + +[[package]] +name = "propolis-client" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/propolis?rev=19c5faa556ebab465ac6cf42a133125c41b9e2c5#19c5faa556ebab465ac6cf42a133125c41b9e2c5" +dependencies = [ + "async-trait", + "base64 0.21.7", + "crucible-client-types", + "futures", + "progenitor 0.13.0", + "progenitor-client 0.13.0", + "propolis-api-types-versions", + "rand 0.9.2", + "reqwest 0.13.2", + "schemars 0.8.22", + "serde", + "serde_json", + "slog", + "thiserror 1.0.69", + "tokio", + "tokio-tungstenite 0.21.0", + "uuid", +] + [[package]] name = "propolis-client" version = "0.1.0" @@ -11137,7 +11174,7 @@ dependencies = [ "futures", "progenitor 0.10.0", "progenitor-client 0.10.0", - "propolis_api_types", + "propolis_api_types 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=36f20be9bb4c3b362029237f5feb6377c982395f)", "rand 0.9.2", "reqwest 0.12.28", "schemars 0.8.22", @@ -11153,7 +11190,7 @@ dependencies = [ [[package]] name = "propolis-mock-server" version = "0.0.0" -source = "git+https://github.com/oxidecomputer/propolis?rev=36f20be9bb4c3b362029237f5feb6377c982395f#36f20be9bb4c3b362029237f5feb6377c982395f" +source = "git+https://github.com/oxidecomputer/propolis?rev=19c5faa556ebab465ac6cf42a133125c41b9e2c5#19c5faa556ebab465ac6cf42a133125c41b9e2c5" dependencies = [ "anyhow", "atty", @@ -11162,11 +11199,12 @@ dependencies = [ "dropshot", "futures", "hyper", - "progenitor 0.10.0", - "propolis_api_types", - "propolis_types", + "progenitor 0.13.0", + "propolis-api-types-versions", + "propolis_api_types 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=19c5faa556ebab465ac6cf42a133125c41b9e2c5)", + "propolis_types 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=19c5faa556ebab465ac6cf42a133125c41b9e2c5)", "rand 0.9.2", - "reqwest 0.12.28", + "reqwest 0.13.2", "schemars 0.8.22", "semver 1.0.27", "serde", @@ -11182,19 +11220,37 @@ dependencies = [ "uuid", ] +[[package]] +name = "propolis_api_types" +version = "0.0.0" +source = "git+https://github.com/oxidecomputer/propolis?rev=19c5faa556ebab465ac6cf42a133125c41b9e2c5#19c5faa556ebab465ac6cf42a133125c41b9e2c5" +dependencies = [ + "crucible-client-types", + "propolis-api-types-versions", +] + [[package]] name = "propolis_api_types" version = "0.0.0" source = "git+https://github.com/oxidecomputer/propolis?rev=36f20be9bb4c3b362029237f5feb6377c982395f#36f20be9bb4c3b362029237f5feb6377c982395f" dependencies = [ "crucible-client-types", - "propolis_types", + "propolis_types 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=36f20be9bb4c3b362029237f5feb6377c982395f)", "schemars 0.8.22", "serde", "thiserror 1.0.69", "uuid", ] +[[package]] +name = "propolis_types" +version = "0.0.0" +source = "git+https://github.com/oxidecomputer/propolis?rev=19c5faa556ebab465ac6cf42a133125c41b9e2c5#19c5faa556ebab465ac6cf42a133125c41b9e2c5" +dependencies = [ + "schemars 0.8.22", + "serde", +] + [[package]] name = "propolis_types" version = "0.0.0" @@ -13241,7 +13297,7 @@ dependencies = [ "omicron-workspace-hack", "oxnet", "progenitor 0.13.0", - "propolis-client", + "propolis-client 0.1.0 (git+https://github.com/oxidecomputer/propolis?rev=19c5faa556ebab465ac6cf42a133125c41b9e2c5)", "regress", "reqwest 0.13.2", "schemars 0.8.22", @@ -13455,7 +13511,8 @@ dependencies = [ "omicron-uuid-kinds", "omicron-workspace-hack", "oxnet", - "propolis_api_types", + "propolis-api-types-versions", + "propolis_api_types 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=19c5faa556ebab465ac6cf42a133125c41b9e2c5)", "proptest", "schemars 0.8.22", "serde", diff --git a/Cargo.toml b/Cargo.toml index 17e9dd6acf4..61e3e5d6c96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -698,10 +698,11 @@ progenitor-client010 = { package = "progenitor-client", version = "0.10.0" } # NOTE: if you change the pinned revision of the `bhyve_api` and propolis # dependencies, you must also update the references in package-manifest.toml to # match the new revision. -bhyve_api = { git = "https://github.com/oxidecomputer/propolis", rev = "36f20be9bb4c3b362029237f5feb6377c982395f" } -propolis_api_types = { git = "https://github.com/oxidecomputer/propolis", rev = "36f20be9bb4c3b362029237f5feb6377c982395f" } -propolis-client = { git = "https://github.com/oxidecomputer/propolis", rev = "36f20be9bb4c3b362029237f5feb6377c982395f" } -propolis-mock-server = { git = "https://github.com/oxidecomputer/propolis", rev = "36f20be9bb4c3b362029237f5feb6377c982395f" } +bhyve_api = { git = "https://github.com/oxidecomputer/propolis", rev = "19c5faa556ebab465ac6cf42a133125c41b9e2c5" } +propolis-api-types-versions = { git = "https://github.com/oxidecomputer/propolis", rev = "19c5faa556ebab465ac6cf42a133125c41b9e2c5" } +propolis_api_types = { git = "https://github.com/oxidecomputer/propolis", rev = "19c5faa556ebab465ac6cf42a133125c41b9e2c5" } +propolis-client = { git = "https://github.com/oxidecomputer/propolis", rev = "19c5faa556ebab465ac6cf42a133125c41b9e2c5" } +propolis-mock-server = { git = "https://github.com/oxidecomputer/propolis", rev = "19c5faa556ebab465ac6cf42a133125c41b9e2c5" } # NOTE: see above! proptest = "1.7.0" qorb = "0.4.1" diff --git a/nexus/src/app/instance_platform/mod.rs b/nexus/src/app/instance_platform/mod.rs index fdd4b85ce4d..6352f5a0446 100644 --- a/nexus/src/app/instance_platform/mod.rs +++ b/nexus/src/app/instance_platform/mod.rs @@ -82,15 +82,19 @@ use crate::db::datastore::Disk; use nexus_db_queries::db; use omicron_common::api::external::Error; use omicron_common::api::internal::shared::NetworkInterface; +use sled_agent_client::types::VirtioSocket; use sled_agent_client::types::{ BlobStorageBackend, Board, BootOrderEntry, BootSettings, Chipset, - ComponentV0, Cpuid, CpuidVendor, CrucibleStorageBackend, - FileStorageBackend, I440Fx, InstanceSpecV0, NvmeDisk, PciPath, QemuPvpanic, - SerialPort, SerialPortNumber, SpecKey, VirtioDisk, VirtioNetworkBackend, - VirtioNic, VmmSpec, + Component, Cpuid, CpuidVendor, CrucibleStorageBackend, FileStorageBackend, + I440Fx, InstanceSpec, NvmeDisk, PciPath, QemuPvpanic, SerialPort, + SerialPortNumber, SpecKey, VirtioDisk, VirtioNetworkBackend, VirtioNic, + VmmSpec, }; use uuid::Uuid; +/// Default `guest_cid` for a propolis guest. +const VSOCK_GUEST_CID: u64 = 16; + /// Constants and functions used to assign names to assorted VM components. mod component_names { pub(super) const COM1: &'static str = "com1"; @@ -101,6 +105,7 @@ mod component_names { pub(super) const BOOT_SETTINGS: &'static str = "boot-settings"; pub(super) const CLOUD_INIT_DEVICE: &'static str = "cloud-init-dev"; pub(super) const CLOUD_INIT_BACKEND: &'static str = "cloud-init-backend"; + pub(super) const VSOCK: &'static str = "vsock"; /// Given an object ID, derives a name for the "device" half of the /// device/backend component pair that describes that object. @@ -194,8 +199,8 @@ pub fn zero_padded_nvme_serial_from_str(s: &str) -> [u8; 20] { /// instance specification. pub struct DiskComponents { device_name: String, - device: ComponentV0, - backend: ComponentV0, + device: Component, + backend: Component, } /// Stores a mapping from Nexus disk IDs to disk component descriptors. This @@ -217,7 +222,7 @@ impl DisksByIdBuilder { fn add_generic_disk( &mut self, disk: &Disk, - backend: ComponentV0, + backend: Component, ) -> Result<(), Error> { let slot = disk.slot().ok_or(Error::internal_error(&format!( "disk {} is attached but has no PCI slot assignment", @@ -235,7 +240,7 @@ impl DisksByIdBuilder { let pci_path = slot_to_pci_bdf(slot, PciDeviceKind::Disk)?; - let device = ComponentV0::NvmeDisk(NvmeDisk { + let device = Component::NvmeDisk(NvmeDisk { backend_id: SpecKey::Uuid(disk.id()), pci_path, serial_number: zero_padded_nvme_serial_from_str( @@ -263,7 +268,7 @@ impl DisksByIdBuilder { volume: &db::model::Volume, ) -> Result<(), Error> { let backend = - ComponentV0::CrucibleStorageBackend(CrucibleStorageBackend { + Component::CrucibleStorageBackend(CrucibleStorageBackend { readonly: disk.is_read_only(), request_json: volume.data().to_owned(), }); @@ -276,7 +281,7 @@ impl DisksByIdBuilder { disk: &Disk, path: String, ) -> Result<(), Error> { - let backend = ComponentV0::FileStorageBackend(FileStorageBackend { + let backend = Component::FileStorageBackend(FileStorageBackend { path, // Presently, this will always be false for local storage disks, but // we may as well ask the disk rather than hard-coding it... @@ -299,7 +304,7 @@ impl From for DisksById { // // This is a HashMap so that it can be moved directly into a sled-agent instance // spec (Progenitor generates HashMaps when it needs a map type). -struct Components(HashMap); +struct Components(HashMap); impl Default for Components { /// Adds the default set of VM components that are expected to exist for all @@ -309,31 +314,38 @@ impl Default for Components { let map = [ ( component_names::COM1, - ComponentV0::SerialPort(SerialPort { + Component::SerialPort(SerialPort { num: SerialPortNumber::Com1, }), ), ( component_names::COM2, - ComponentV0::SerialPort(SerialPort { + Component::SerialPort(SerialPort { num: SerialPortNumber::Com2, }), ), ( component_names::COM3, - ComponentV0::SerialPort(SerialPort { + Component::SerialPort(SerialPort { num: SerialPortNumber::Com3, }), ), ( component_names::COM4, - ComponentV0::SerialPort(SerialPort { + Component::SerialPort(SerialPort { num: SerialPortNumber::Com4, }), ), ( component_names::PVPANIC, - ComponentV0::QemuPvpanic(QemuPvpanic { enable_isa: true }), + Component::QemuPvpanic(QemuPvpanic { enable_isa: true }), + ), + ( + component_names::VSOCK, + Component::VirtioSocket(VirtioSocket { + guest_cid: VSOCK_GUEST_CID, + pci_path: PciPath { bus: 0, device: 0x19, function: 0 }, + }), ), ] .into_iter() @@ -347,11 +359,7 @@ impl Default for Components { impl Components { /// Adds a named component to this component list. Returns a 500 error if /// the component name was already present in the list. - fn add( - &mut self, - key: String, - component: ComponentV0, - ) -> Result<(), Error> { + fn add(&mut self, key: String, component: Component) -> Result<(), Error> { use std::collections::hash_map::Entry; // Component names are an implementation detail internal to this module, @@ -395,7 +403,7 @@ impl Components { self.0.reserve(nics.len() * 2); for nic in nics.iter() { let device_name = component_names::device_name_from_id(&nic.id); - let device = ComponentV0::VirtioNic(VirtioNic { + let device = Component::VirtioNic(VirtioNic { backend_id: SpecKey::Uuid(nic.id), interface_id: nic.id, pci_path: slot_to_pci_bdf(nic.slot, PciDeviceKind::Nic)?, @@ -405,7 +413,7 @@ impl Components { // per-sled port allocator, so it needs to (and will) fill in the // correct port name once one is assigned. let backend = - ComponentV0::VirtioNetworkBackend(VirtioNetworkBackend { + Component::VirtioNetworkBackend(VirtioNetworkBackend { vnic_name: "".to_string(), }); @@ -433,7 +441,7 @@ impl Components { instance.generate_cidata(&keys)?, ); - let device = ComponentV0::VirtioDisk(VirtioDisk { + let device = Component::VirtioDisk(VirtioDisk { backend_id: SpecKey::Name( component_names::CLOUD_INIT_BACKEND.to_string(), ), @@ -441,7 +449,7 @@ impl Components { .expect("slot 0 is always valid for cloud-init disks"), }); - let backend = ComponentV0::BlobStorageBackend(BlobStorageBackend { + let backend = Component::BlobStorageBackend(BlobStorageBackend { base64, readonly: true, }); @@ -533,7 +541,7 @@ impl super::Nexus { components.add( component_names::BOOT_SETTINGS.to_owned(), - ComponentV0::BootSettings(BootSettings { + Component::BootSettings(BootSettings { order: vec![entry], }), )?; @@ -548,7 +556,7 @@ impl super::Nexus { components.add_nics(nics)?; components.add_cloud_init(instance, ssh_keys)?; - let spec = InstanceSpecV0 { + let spec = InstanceSpec { board: Board { chipset: Chipset::I440Fx(I440Fx { enable_pcie: false }), cpuid: cpuid_from_vmm_cpu_platform(vmm.cpu_platform), @@ -557,6 +565,7 @@ impl super::Nexus { memory_mb: instance.memory.to_whole_mebibytes(), }, components: components.0, + smbios: None, }; Ok(VmmSpec(spec)) diff --git a/openapi/sled-agent/sled-agent-27.0.0-42911d.json.gitstub b/openapi/sled-agent/sled-agent-27.0.0-42911d.json.gitstub new file mode 100644 index 00000000000..09fb077b22c --- /dev/null +++ b/openapi/sled-agent/sled-agent-27.0.0-42911d.json.gitstub @@ -0,0 +1 @@ +df724fae54459e6ffa609f932547c643fb52e3f7:openapi/sled-agent/sled-agent-27.0.0-42911d.json diff --git a/openapi/sled-agent/sled-agent-27.0.0-42911d.json b/openapi/sled-agent/sled-agent-28.0.0-126106.json similarity index 99% rename from openapi/sled-agent/sled-agent-27.0.0-42911d.json rename to openapi/sled-agent/sled-agent-28.0.0-126106.json index dd26d73d836..4258a4da16d 100644 --- a/openapi/sled-agent/sled-agent-27.0.0-42911d.json +++ b/openapi/sled-agent/sled-agent-28.0.0-126106.json @@ -7,7 +7,7 @@ "url": "https://oxide.computer", "email": "api@oxide.computer" }, - "version": "27.0.0" + "version": "28.0.0" }, "paths": { "/artifacts": { @@ -4033,7 +4033,7 @@ "pending" ] }, - "ComponentV0": { + "Component": { "oneOf": [ { "type": "object", @@ -4168,6 +4168,25 @@ ], "additionalProperties": false }, + { + "type": "object", + "properties": { + "component": { + "$ref": "#/components/schemas/VirtioSocket" + }, + "type": { + "type": "string", + "enum": [ + "virtio_socket" + ] + } + }, + "required": [ + "component", + "type" + ], + "additionalProperties": false + }, { "type": "object", "properties": { @@ -5939,7 +5958,7 @@ "nics" ] }, - "InstanceSpecV0": { + "InstanceSpec": { "type": "object", "properties": { "board": { @@ -5948,15 +5967,22 @@ "components": { "type": "object", "additionalProperties": { - "$ref": "#/components/schemas/ComponentV0" + "$ref": "#/components/schemas/Component" } + }, + "smbios": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/SmbiosType1Input" + } + ] } }, "required": [ "board", "components" - ], - "additionalProperties": false + ] }, "InstanceUuid": { "x-rust-type": { @@ -9222,6 +9248,32 @@ "vmm_state" ] }, + "SmbiosType1Input": { + "type": "object", + "properties": { + "manufacturer": { + "type": "string" + }, + "product_name": { + "type": "string" + }, + "serial_number": { + "type": "string" + }, + "version": { + "type": "integer", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "manufacturer", + "product_name", + "serial_number", + "version" + ], + "additionalProperties": false + }, "SoftNpuP9": { "description": "Describes a PCI device that shares host files with the guest using the P9 protocol.\n\nThis is only supported by Propolis servers compiled with the `falcon` feature.", "type": "object", @@ -9721,6 +9773,31 @@ ], "additionalProperties": false }, + "VirtioSocket": { + "description": "A socket device that presents a virtio-socket interface to the guest.", + "type": "object", + "properties": { + "guest_cid": { + "description": "The guest's Context ID.", + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "pci_path": { + "description": "The PCI path at which to attach this device.", + "allOf": [ + { + "$ref": "#/components/schemas/PciPath" + } + ] + } + }, + "required": [ + "guest_cid", + "pci_path" + ], + "additionalProperties": false + }, "VirtualNetworkInterfaceHost": { "description": "A mapping from a virtual NIC to a physical host", "type": "object", @@ -9841,7 +9918,7 @@ "description": "Specifies the virtual hardware configuration of a new Propolis VMM in the form of a Propolis instance specification.", "allOf": [ { - "$ref": "#/components/schemas/InstanceSpecV0" + "$ref": "#/components/schemas/InstanceSpec" } ] }, diff --git a/openapi/sled-agent/sled-agent-latest.json b/openapi/sled-agent/sled-agent-latest.json index 6caf751e245..3156d884aed 120000 --- a/openapi/sled-agent/sled-agent-latest.json +++ b/openapi/sled-agent/sled-agent-latest.json @@ -1 +1 @@ -sled-agent-27.0.0-42911d.json \ No newline at end of file +sled-agent-28.0.0-126106.json \ No newline at end of file diff --git a/package-manifest.toml b/package-manifest.toml index 7a911a23824..0c900b5b2b9 100644 --- a/package-manifest.toml +++ b/package-manifest.toml @@ -650,10 +650,10 @@ service_name = "propolis-server" only_for_targets.image = "standard" source.type = "prebuilt" source.repo = "propolis" -source.commit = "36f20be9bb4c3b362029237f5feb6377c982395f" +source.commit = "19c5faa556ebab465ac6cf42a133125c41b9e2c5" # The SHA256 digest is automatically posted to: # https://buildomat.eng.oxide.computer/public/file/oxidecomputer/propolis/image//propolis-server.sha256.txt -source.sha256 = "18f244ef765ae26bc7852e3af9b61016a6ea5a59a8eafbe36780a92e5fc75b46" +source.sha256 = "d626086c0e3fe4b7e297c0bf67f2da8d5fca3140b5bc266d2b5a1df85813818e" output.type = "zone" [package.mg-ddm-gz] diff --git a/sled-agent/api/src/lib.rs b/sled-agent/api/src/lib.rs index f7627c31319..7837af24928 100644 --- a/sled-agent/api/src/lib.rs +++ b/sled-agent/api/src/lib.rs @@ -20,8 +20,8 @@ use omicron_common::api::internal::{ }, }; use sled_agent_types_versions::{ - latest, v1, v4, v6, v7, v9, v10, v11, v12, v14, v16, v17, v20, v22, v25, - v26, + latest, v1, v4, v6, v7, v9, v10, v11, v12, v14, v16, v17, v18, v20, v22, + v25, v26, }; use sled_diagnostics::SledDiagnosticsQueryOutput; @@ -37,6 +37,7 @@ api_versions!([ // | example for the next person. // v // (next_int, IDENT), + (28, ADD_VSOCK_COMPONENT), (27, RENAME_SWITCH_LOCATION_TO_SWITCH_SLOT), (26, RACK_NETWORK_CONFIG_NOT_OPTIONAL), (25, BOOTSTORE_VERSIONING), @@ -430,7 +431,7 @@ pub trait SledAgentApi { operation_id = "vmm_register", method = PUT, path = "/vmms/{propolis_id}", - versions = VERSION_ADD_ATTACHED_SUBNETS.. + versions = VERSION_ADD_VSOCK_COMPONENT.. }] async fn vmm_register( rqctx: RequestContext, @@ -438,6 +439,21 @@ pub trait SledAgentApi { body: TypedBody, ) -> Result, HttpError>; + #[endpoint { + operation_id = "vmm_register", + method = PUT, + path = "/vmms/{propolis_id}", + versions = + VERSION_ADD_ATTACHED_SUBNETS..VERSION_ADD_VSOCK_COMPONENT + }] + async fn vmm_register_v18( + rqctx: RequestContext, + path_params: Path, + body: TypedBody, + ) -> Result, HttpError> { + Self::vmm_register(rqctx, path_params, body.map(Into::into)).await + } + #[endpoint { operation_id = "vmm_register", method = PUT, @@ -450,7 +466,7 @@ pub trait SledAgentApi { path_params: Path, body: TypedBody, ) -> Result, HttpError> { - Self::vmm_register(rqctx, path_params, body.map(Into::into)).await + Self::vmm_register_v18(rqctx, path_params, body.map(Into::into)).await } #[endpoint { diff --git a/sled-agent/src/instance.rs b/sled-agent/src/instance.rs index 68e4a4d325d..0bea2f40683 100644 --- a/sled-agent/src/instance.rs +++ b/sled-agent/src/instance.rs @@ -40,11 +40,9 @@ use omicron_uuid_kinds::{ GenericUuid, InstanceUuid, OmicronZoneUuid, PropolisUuid, }; use oxnet::IpNet; -use propolis_api_types::ErrorCode as PropolisErrorCode; +use propolis_api_types::instance::ErrorCode as PropolisErrorCode; use propolis_client::Client as PropolisClient; -use propolis_client::instance_spec::{ - ComponentV0, InstanceSpec, InstanceSpecV0, SpecKey, -}; +use propolis_client::instance_spec::{Component, SpecKey}; use rand::SeedableRng; use rand::prelude::IteratorRandom; use sled_agent_config_reconciler::AvailableDatasetsReceiver; @@ -417,7 +415,7 @@ impl InstanceMonitorRunner { // Update the state generation for the next poll. if let InstanceMonitorUpdate::State(ref state) = update { - generation = state.r#gen + 1; + generation = state.gen_ + 1; } // Now that we have the response from Propolis' HTTP server, we @@ -444,7 +442,7 @@ impl InstanceMonitorRunner { .client .instance_state_monitor() .body(propolis_client::types::InstanceStateMonitorRequest { - r#gen: generation, + gen_: generation, }) .send() .await; @@ -602,19 +600,6 @@ struct InstanceRunner { attached_subnets: IdOrdMap, } -/// Translate a `propolis-client` `InstanceSpecV0` into the newer -/// `InstanceSpec`. -/// -/// This can be done losslessly, and probably should be an `impl From` or -/// inherent method in `propolis-client`, but Propolis itself converts between -/// these types a little less directly. It hadn't occurred to me that this is -/// useful for clients as well. -pub(crate) fn spec_v0_to_v1(orig_spec: InstanceSpecV0) -> InstanceSpec { - let InstanceSpecV0 { board, components } = orig_spec; - - InstanceSpec { board, components, smbios: None } -} - impl InstanceRunner { /// How long to wait for VMM shutdown to complete before forcefully /// terminating the zone. @@ -1239,9 +1224,7 @@ impl InstanceRunner { } else { propolis_client::types::InstanceEnsureRequest { properties: self.properties.clone(), - init: InstanceInitializationMethod::Spec { - spec: spec_v0_to_v1(spec.0), - }, + init: InstanceInitializationMethod::Spec { spec: spec.0 }, } }; @@ -1280,7 +1263,7 @@ impl InstanceRunner { return Err(Error::NicNotInPropolisSpec(nic.id)); }; - let ComponentV0::VirtioNetworkBackend(be) = backend else { + let Component::VirtioNetworkBackend(be) = backend else { return Err(Error::NicNotInPropolisSpec(nic.id)); }; @@ -2624,11 +2607,7 @@ impl InstanceRunner { ), } - // We use a custom client builder here because the default progenitor - // one has a timeout of 15s but we want to be able to wait indefinitely. - // Use reqwest012 because the rev-pinned propolis-client is still on - // reqwest 0.12. - let reqwest_client = reqwest012::ClientBuilder::new().build().unwrap(); + let reqwest_client = reqwest::ClientBuilder::new().build().unwrap(); let client = Arc::new(PropolisClient::new_with_client( &format!("http://{}", &self.propolis_addr), reqwest_client, @@ -2944,6 +2923,7 @@ mod tests { }; use omicron_common::disk::DiskIdentity; use omicron_uuid_kinds::InternalZpoolUuid; + use propolis_client::ClientInfo; use propolis_client::types::{ InstanceMigrateStatusResponse, InstanceStateMonitorResponse, }; @@ -3134,8 +3114,8 @@ mod tests { fn fake_instance_initial_state( propolis_addr: SocketAddr, ) -> InstanceInitialState { - use propolis_client::instance_spec::{Board, InstanceSpecV0}; - let spec = VmmSpec(InstanceSpecV0 { + use propolis_client::instance_spec::{Board, InstanceSpec}; + let spec = VmmSpec(InstanceSpec { board: Board { cpus: 1, memory_mb: 1024, @@ -3144,6 +3124,7 @@ mod tests { cpuid: None, }, components: Default::default(), + smbios: None, }); let external_ips = Some( @@ -3885,7 +3866,7 @@ mod tests { .send(InstanceMonitorMessage { update: InstanceMonitorUpdate::State( InstanceStateMonitorResponse { - r#gen: 5, + gen_: 5, migration: InstanceMigrateStatusResponse { migration_in: None, migration_out: None, diff --git a/sled-agent/src/sim/instance.rs b/sled-agent/src/sim/instance.rs index 67f45ede666..16832b79110 100644 --- a/sled-agent/src/sim/instance.rs +++ b/sled-agent/src/sim/instance.rs @@ -457,7 +457,7 @@ impl Simulatable for SimInstance { current.migration_in.map(|m| m.migration_id), ), last_response: InstanceStateMonitorResponse { - r#gen: 1, + gen_: 1, state: PropolisInstanceState::Starting, migration: PropolisMigrateResponse { migration_in: None, diff --git a/sled-agent/src/sim/sled_agent.rs b/sled-agent/src/sim/sled_agent.rs index 18b63c519ef..9b296058dec 100644 --- a/sled-agent/src/sim/sled_agent.rs +++ b/sled-agent/src/sim/sled_agent.rs @@ -221,10 +221,10 @@ impl SledAgent { metadata, .. } = instance; - let v1_spec = crate::instance::spec_v0_to_v1(vmm_spec.0.clone()); + let spec = vmm_spec.0.clone(); // respond with a fake 500 level failure if asked to ensure an instance // with more than 16 CPUs. - let ncpus = v1_spec.board.cpus; + let ncpus = spec.board.cpus; if ncpus > 16 { return Err(Error::internal_error( &"could not allocate an instance: ran out of CPUs!", @@ -302,7 +302,7 @@ impl SledAgent { let body = propolis_client::types::InstanceEnsureRequest { properties, - init: InstanceInitializationMethod::Spec { spec: v1_spec }, + init: InstanceInitializationMethod::Spec { spec }, }; // Try to create the instance diff --git a/sled-agent/types/versions/Cargo.toml b/sled-agent/types/versions/Cargo.toml index 3eea9c61266..98c1904e7be 100644 --- a/sled-agent/types/versions/Cargo.toml +++ b/sled-agent/types/versions/Cargo.toml @@ -23,6 +23,7 @@ omicron-uuid-kinds.workspace = true omicron-workspace-hack.workspace = true oxnet.workspace = true slog.workspace = true +propolis-api-types-versions.workspace = true propolis_api_types.workspace = true proptest = { workspace = true, optional = true } schemars.workspace = true diff --git a/sled-agent/types/versions/src/add_vsock_component/instance.rs b/sled-agent/types/versions/src/add_vsock_component/instance.rs new file mode 100644 index 00000000000..950d9761584 --- /dev/null +++ b/sled-agent/types/versions/src/add_vsock_component/instance.rs @@ -0,0 +1,70 @@ +// 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/. + +use std::net::SocketAddr; + +use omicron_common::api::internal::nexus::VmmRuntimeState; +use omicron_uuid_kinds::InstanceUuid; +use propolis_api_types::instance_spec::InstanceSpec; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::v1::instance::InstanceMetadata; +use crate::v18; +use crate::v18::instance::InstanceSledLocalConfig; + +/// Specifies the virtual hardware configuration of a new Propolis VMM in the +/// form of a Propolis instance specification. +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct VmmSpec(pub InstanceSpec); + +/// The body of a request to ensure that a instance and VMM are known to a sled +/// agent. +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct InstanceEnsureBody { + /// The virtual hardware configuration this virtual machine should have when + /// it is started. + pub vmm_spec: VmmSpec, + + /// Information about the sled-local configuration that needs to be + /// established to make the VM's virtual hardware fully functional. + pub local_config: InstanceSledLocalConfig, + + /// The initial VMM runtime state for the VMM being registered. + pub vmm_runtime: VmmRuntimeState, + + /// The ID of the instance for which this VMM is being created. + pub instance_id: InstanceUuid, + + /// The ID of the migration in to this VMM, if this VMM is being + /// ensured is part of a migration in. If this is `None`, the VMM is not + /// being created due to a migration. + pub migration_id: Option, + + /// The address at which this VMM should serve a Propolis server API. + pub propolis_addr: SocketAddr, + + /// Metadata used to track instance statistics. + pub metadata: InstanceMetadata, +} + +impl From for InstanceEnsureBody { + fn from(old: v18::instance::InstanceEnsureBody) -> InstanceEnsureBody { + // Conversion goes v1 -> v2 -> v3 (latest) through propolis's + // versioned From impls. + let v2_spec: propolis_api_types_versions::v2::instance_spec::InstanceSpec = + old.vmm_spec.0.into(); + let v3_spec: InstanceSpec = v2_spec.into(); + InstanceEnsureBody { + vmm_spec: VmmSpec(v3_spec), + local_config: old.local_config, + vmm_runtime: old.vmm_runtime, + instance_id: old.instance_id, + migration_id: old.migration_id, + propolis_addr: old.propolis_addr, + metadata: old.metadata, + } + } +} diff --git a/sled-agent/types/versions/src/add_vsock_component/mod.rs b/sled-agent/types/versions/src/add_vsock_component/mod.rs new file mode 100644 index 00000000000..7b9d33bdc73 --- /dev/null +++ b/sled-agent/types/versions/src/add_vsock_component/mod.rs @@ -0,0 +1,11 @@ +// 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 `ADD_VSOCK_COMPONENT` of Sled Agent API. +//! +//! This version updates the instance spec types to use the latest propolis +//! types, which add the `VirtioSocket` component variant and an optional +//! `smbios` field to `InstanceSpec`. + +pub mod instance; diff --git a/sled-agent/types/versions/src/impls/instance.rs b/sled-agent/types/versions/src/impls/instance.rs index 3596b561789..3f94e91e043 100644 --- a/sled-agent/types/versions/src/impls/instance.rs +++ b/sled-agent/types/versions/src/impls/instance.rs @@ -4,11 +4,10 @@ use crate::latest::instance::{VmmSpec, VmmStateRequested}; use propolis_api_types::instance_spec::{ - SpecKey, + Component, SpecKey, components::backends::{ CrucibleStorageBackend, FileStorageBackend, VirtioNetworkBackend, }, - v0::ComponentV0, }; impl std::fmt::Display for VmmStateRequested { @@ -34,7 +33,7 @@ impl VmmSpec { ) -> impl Iterator { self.0.components.iter().filter_map( |(key, component)| match component { - ComponentV0::CrucibleStorageBackend(be) => Some((key, be)), + Component::CrucibleStorageBackend(be) => Some((key, be)), _ => None, }, ) @@ -45,7 +44,7 @@ impl VmmSpec { ) -> impl Iterator { self.0.components.iter().filter_map( |(key, component)| match component { - ComponentV0::VirtioNetworkBackend(be) => Some((key, be)), + Component::VirtioNetworkBackend(be) => Some((key, be)), _ => None, }, ) @@ -56,7 +55,7 @@ impl VmmSpec { ) -> impl Iterator { self.0.components.iter().filter_map( |(key, component)| match component { - ComponentV0::FileStorageBackend(be) => Some((key, be)), + Component::FileStorageBackend(be) => Some((key, be)), _ => None, }, ) diff --git a/sled-agent/types/versions/src/initial/instance.rs b/sled-agent/types/versions/src/initial/instance.rs index f1a597076f8..9cd99593014 100644 --- a/sled-agent/types/versions/src/initial/instance.rs +++ b/sled-agent/types/versions/src/initial/instance.rs @@ -14,7 +14,7 @@ use omicron_common::api::internal::shared::DhcpConfig; use omicron_common::api::internal::shared::external_ip::v1::SourceNatConfig; use omicron_common::api::internal::shared::network_interface::v1::NetworkInterface; use omicron_uuid_kinds::{InstanceUuid, PropolisUuid}; -use propolis_api_types::instance_spec::v0::InstanceSpecV0; +use propolis_api_types_versions::v1::instance_spec::InstanceSpec; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -105,7 +105,7 @@ pub struct InstanceMetadata { /// Specifies the virtual hardware configuration of a new Propolis VMM in the /// form of a Propolis instance specification. #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] -pub struct VmmSpec(pub InstanceSpecV0); +pub struct VmmSpec(pub InstanceSpec); /// VPC firewall rule after object name resolution has been performed by Nexus #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] diff --git a/sled-agent/types/versions/src/latest.rs b/sled-agent/types/versions/src/latest.rs index d3c3e7cc5b1..a89f1709481 100644 --- a/sled-agent/types/versions/src/latest.rs +++ b/sled-agent/types/versions/src/latest.rs @@ -93,7 +93,6 @@ pub mod instance { pub use crate::v1::instance::VmmPathParam; pub use crate::v1::instance::VmmPutStateBody; pub use crate::v1::instance::VmmPutStateResponse; - pub use crate::v1::instance::VmmSpec; pub use crate::v1::instance::VmmStateRequested; pub use crate::v1::instance::VmmUnregisterResponse; pub use crate::v1::instance::VpcPathParam; @@ -101,9 +100,11 @@ pub mod instance { pub use crate::v7::instance::InstanceMulticastBody; pub use crate::v7::instance::InstanceMulticastMembership; - pub use crate::v18::instance::InstanceEnsureBody; pub use crate::v18::instance::InstanceSledLocalConfig; + pub use crate::v28::instance::InstanceEnsureBody; + pub use crate::v28::instance::VmmSpec; + pub use omicron_common::api::internal::shared::ResolvedVpcFirewallRule; } diff --git a/sled-agent/types/versions/src/lib.rs b/sled-agent/types/versions/src/lib.rs index 7c144d507fa..6b648c42e7c 100644 --- a/sled-agent/types/versions/src/lib.rs +++ b/sled-agent/types/versions/src/lib.rs @@ -65,6 +65,8 @@ pub mod v24; pub mod v25; #[path = "rack_network_config_not_optional/mod.rs"] pub mod v26; +#[path = "add_vsock_component/mod.rs"] +pub mod v28; #[path = "add_switch_zone_operator_policy/mod.rs"] pub mod v3; #[path = "add_nexus_lockstep_port_to_inventory/mod.rs"]