Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bin/propolis-server/src/lib/spec/api_spec_latest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ pub(crate) fn latest_to_spec_builder(
value: latest::instance_spec::InstanceSpec,
) -> Result<SpecBuilder, ApiSpecError> {
let mut builder = SpecBuilder::with_instance_spec_board(value.board)?;

if let Some(smbios) = value.smbios {
builder.set_smbios_type1_input(smbios);
}

let mut devices: Vec<(SpecKey, latest::instance_spec::Component)> = vec![];
let mut boot_settings = None;
let mut storage_backends: BTreeMap<SpecKey, StorageBackend> =
Expand Down
72 changes: 72 additions & 0 deletions bin/propolis-server/src/lib/spec/api_spec_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,75 @@ pub(crate) fn amend(

Ok(())
}

#[cfg(test)]
mod test {
use propolis_api_types_versions::latest::components::board::{
Board, Chipset, Cpuid, GuestHypervisorInterface, I440Fx,
};
use propolis_api_types_versions::latest::{
self,
instance_spec::{CpuidVendor, SmbiosType1Input},
};
use propolis_api_types_versions::{v1, v2, v3, v6};

#[test]
fn smbios_type1() {
let smbios_input = SmbiosType1Input {
manufacturer: "a4x2".to_string(),
product_name: "913-0000019".to_string(),
serial_number: "2FAKE000".to_string(),
version: 2,
};

let api_spec = latest::instance_spec::InstanceSpec {
board: Board {
cpus: 4,
memory_mb: 512,
chipset: Chipset::I440Fx(I440Fx { enable_pcie: false }),
guest_hv_interface: GuestHypervisorInterface::Bhyve,
// Providing *any* CPUID settings keeps Propolis from querying
// bhyve for defaults to use instead, which requires .. byhve
// *and* VMM access. Provide a (useless) empty CPUID set so this
// test can run on non-illumos test systems.
cpuid: Some(Cpuid {
entries: vec![],
vendor: CpuidVendor::Amd,
}),
},
components: Default::default(),
smbios: Some(smbios_input.clone()),
};

let spec =
crate::spec::api_spec_latest::latest_to_spec_builder(api_spec)
.unwrap()
.finish();
let smbios = spec
.smbios_type1_input
.as_ref()
.expect("SMBIOS type 1 input preserved");
assert_eq!(&smbios_input, smbios);

let v6_spec =
v6::instance_spec::InstanceSpec::try_from(spec.clone()).unwrap();
let smbios =
v6_spec.smbios.as_ref().expect("SMBIOS type 1 input preserved");
assert_eq!(&smbios_input, smbios);

let v3_spec =
v3::instance_spec::InstanceSpec::try_from(spec.clone()).unwrap();
let smbios =
v3_spec.smbios.as_ref().expect("SMBIOS type 1 input preserved");
assert_eq!(&smbios_input, smbios);

let v2_spec =
v2::instance_spec::InstanceSpec::try_from(spec.clone()).unwrap();
let smbios =
v2_spec.smbios.as_ref().expect("SMBIOS type 1 input preserved");
assert_eq!(&smbios_input, smbios);

let v1_res = v1::instance_spec::InstanceSpec::try_from(spec);
assert!(v1_res.is_err());
}
}
74 changes: 74 additions & 0 deletions bin/propolis-server/src/lib/spec/api_spec_v3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,77 @@ pub(crate) fn amend(

Ok(())
}

#[cfg(test)]
mod test {
use propolis_api_types_versions::latest::components::board::{
Board, Chipset, Cpuid, GuestHypervisorInterface, I440Fx,
};
use propolis_api_types_versions::latest::{
self,
components::devices::VirtioSocket,
instance_spec::{self, CpuidVendor, SmbiosType1Input, SpecKey},
};
use propolis_api_types_versions::{v1, v2, v3, v6};

#[test]
fn vsock_component() {
let mut api_spec = latest::instance_spec::InstanceSpec {
board: Board {
cpus: 4,
memory_mb: 512,
chipset: Chipset::I440Fx(I440Fx { enable_pcie: false }),
guest_hv_interface: GuestHypervisorInterface::Bhyve,
// Providing *any* CPUID settings keeps Propolis from querying
// bhyve for defaults to use instead, which requires .. byhve
// *and* VMM access. Provide a (useless) empty CPUID set so this
// test can run on non-illumos test systems.
cpuid: Some(Cpuid {
entries: vec![],
vendor: CpuidVendor::Amd,
}),
},
components: Default::default(),
smbios: Some(SmbiosType1Input {
manufacturer: "a4x2".to_string(),
product_name: "913-0000019".to_string(),
serial_number: "2FAKE000".to_string(),
version: 2,
}),
};

let vsock_id: SpecKey = SpecKey::Name("vsock-id".to_string());
let test_vsock: VirtioSocket = VirtioSocket {
guest_cid: 0,
pci_path: instance_spec::PciPath::new(0, 4, 0).unwrap(),
};

let vsock_comp = instance_spec::Component::VirtioSocket(test_vsock);

api_spec.components.insert(vsock_id.clone(), vsock_comp.clone());

let spec =
crate::spec::api_spec_latest::latest_to_spec_builder(api_spec)
.unwrap()
.finish();
assert!(spec.vsock.is_some());

let v6_spec =
v6::instance_spec::InstanceSpec::try_from(spec.clone()).unwrap();
let v6_comp: v6::instance_spec::Component =
vsock_comp.clone().try_into().unwrap();
assert_eq!(v6_spec.components.get(&vsock_id), Some(&v6_comp));

let v3_spec =
v3::instance_spec::InstanceSpec::try_from(spec.clone()).unwrap();
let v3_comp: v3::instance_spec::Component =
vsock_comp.clone().try_into().unwrap();
assert_eq!(v3_spec.components.get(&vsock_id), Some(&v3_comp));

let v2_res = v2::instance_spec::InstanceSpec::try_from(spec.clone());
assert!(v2_res.is_err());

let v1_res = v1::instance_spec::InstanceSpec::try_from(spec);
assert!(v1_res.is_err());
}
}
7 changes: 6 additions & 1 deletion bin/propolis-server/src/lib/spec/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use propolis_api_types::instance_spec::{
board::Board as InstanceSpecBoard,
devices::{PciPciBridge, SerialPortNumber},
},
PciPath, SpecKey,
PciPath, SmbiosType1Input, SpecKey,
};
use thiserror::Error;

Expand Down Expand Up @@ -391,6 +391,11 @@ impl SpecBuilder {
Ok(self)
}

/// Sets the SMBIOS type 1 table contents to expose to the guest.
pub fn set_smbios_type1_input(&mut self, input: SmbiosType1Input) {
self.spec.smbios_type1_input = Some(input);
}

/// Yields the completed spec, consuming the builder.
pub fn finish(self) -> super::Spec {
self.spec
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::v2::instance_spec::SmbiosType1Input;

pub use super::components::devices::VirtioSocket;

#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(
deny_unknown_fields,
tag = "type",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
use std::num::NonZeroUsize;

/// A Crucible storage backend.
#[derive(Clone, Deserialize, Serialize, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct CrucibleStorageBackend {
/// A serialized `[crucible_client_types::VolumeConstructionRequest]`. This
Expand Down Expand Up @@ -40,7 +40,7 @@ impl std::fmt::Debug for CrucibleStorageBackend {
}

/// A storage backend backed by a file in the host system's file system.
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct FileStorageBackend {
/// A path to a file that backs a disk.
Expand All @@ -58,7 +58,7 @@ pub struct FileStorageBackend {

/// A storage backend for a disk whose initial contents are given explicitly
/// by the specification.
#[derive(Clone, Deserialize, Serialize, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct BlobStorageBackend {
/// The disk's initial contents, encoded as a base64 string.
Expand All @@ -78,15 +78,15 @@ impl std::fmt::Debug for BlobStorageBackend {
}

/// A network backend associated with a virtio-net (viona) VNIC on the host.
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct VirtioNetworkBackend {
/// The name of the viona VNIC to use as a backend.
pub vnic_name: String,
}

/// A network backend associated with a DLPI VNIC on the host.
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct DlpiNetworkBackend {
/// The name of the VNIC to use as a backend.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// A disk that presents a virtio-block interface to the guest.
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct VirtioDisk {
/// The name of the disk's backend component.
Expand All @@ -21,7 +21,7 @@ pub struct VirtioDisk {
}

/// A disk that presents an NVMe interface to the guest.
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct NvmeDisk {
/// The name of the disk's backend component.
Expand All @@ -36,7 +36,7 @@ pub struct NvmeDisk {
}

/// A network card that presents a virtio-net interface to the guest.
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct VirtioNic {
/// The name of the device's backend.
Expand Down Expand Up @@ -111,15 +111,17 @@ pub struct QemuPvpanic {
/// Settings supplied to the guest's firmware image that specify the order in
/// which it should consider its options when selecting a device to try to boot
/// from.
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, Default)]
#[derive(
Clone, Deserialize, Serialize, Debug, JsonSchema, Default, PartialEq,
)]
#[serde(deny_unknown_fields)]
pub struct BootSettings {
/// An ordered list of components to attempt to boot from.
pub order: Vec<BootOrderEntry>,
}

/// An entry in the boot order stored in a [`BootSettings`] component.
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
pub struct BootOrderEntry {
/// The ID of another component in the spec that Propolis should try to
/// boot from.
Expand All @@ -136,7 +138,7 @@ pub struct BootOrderEntry {
///
/// This is only supported by Propolis servers compiled with the `falcon`
/// feature.
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct SoftNpuPciPort {
/// The PCI path at which to attach the guest to this port.
Expand All @@ -147,7 +149,7 @@ pub struct SoftNpuPciPort {
///
/// This is only supported by Propolis servers compiled with the `falcon`
/// feature.
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct SoftNpuPort {
/// The data link name for this port.
Expand All @@ -162,7 +164,7 @@ pub struct SoftNpuPort {
///
/// This is only supported by Propolis servers compiled with the `falcon`
/// feature.
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct SoftNpuP9 {
/// The PCI path at which to attach the guest to this port.
Expand All @@ -173,7 +175,7 @@ pub struct SoftNpuP9 {
///
/// This is only supported by Propolis servers compiled with the `falcon`
/// feature.
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct P9fs {
/// The host source path to mount into the guest.
Expand All @@ -195,7 +197,7 @@ pub struct P9fs {
///
/// This is only supported by Propolis servers compiled with the
/// `failure-injection` feature.
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct MigrationFailureInjector {
/// The number of times this device should fail requests to export state.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ impl JsonSchema for SpecKey {
}
}

#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(
deny_unknown_fields,
tag = "type",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// A disk that presents an NVMe interface to the guest.
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct NvmeDisk {
/// The name of the disk's backend component.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::v3::instance_spec::Component as V3Component;

pub use super::components::devices::NvmeDisk;

#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(
deny_unknown_fields,
tag = "type",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::v1::components::board;
use crate::v1::instance::{InstanceProperties, InstanceState};
use crate::v1::instance_spec::{Component, SpecKey};

#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct SmbiosType1Input {
pub manufacturer: String,
Expand Down
Loading