diff --git a/phd-tests/framework/src/test_vm/config.rs b/phd-tests/framework/src/test_vm/config.rs index 819257de9..9412923b2 100644 --- a/phd-tests/framework/src/test_vm/config.rs +++ b/phd-tests/framework/src/test_vm/config.rs @@ -11,7 +11,7 @@ use propolis_client::{ Board, BootOrderEntry, BootSettings, Chipset, Component, Cpuid, CpuidEntry, CpuidVendor, GuestHypervisorInterface, InstanceMetadata, InstanceSpec, MigrationFailureInjector, NvmeDisk, PciPath, SerialPort, - SerialPortNumber, SpecKey, VirtioDisk, + SerialPortNumber, SpecKey, VirtioDisk, VirtioSocket, }, support::nvme_serial_from_str, }; @@ -56,6 +56,7 @@ pub struct VmConfig<'dr> { disks: Vec>, migration_failure: Option, guest_hv_interface: Option, + vsock: Option, } impl<'dr> VmConfig<'dr> { @@ -76,6 +77,7 @@ impl<'dr> VmConfig<'dr> { disks: Vec::new(), migration_failure: None, guest_hv_interface: None, + vsock: None, }; config.boot_disk( @@ -121,6 +123,12 @@ impl<'dr> VmConfig<'dr> { self } + pub fn vsock(&mut self, guest_cid: u64, pci_device_num: u8) -> &mut Self { + let pci_path = PciPath::new(0, pci_device_num, 0).unwrap(); + self.vsock = Some(VirtioSocket { guest_cid, pci_path }); + self + } + pub fn fail_migration_exports(&mut self, exports: u32) -> &mut Self { let injector = self.migration_failure.get_or_insert(MigrationFailureInjector { @@ -218,6 +226,7 @@ impl<'dr> VmConfig<'dr> { disks, migration_failure, guest_hv_interface, + vsock, } = self; let framework = &ctx.framework; let bootrom_path = framework @@ -369,6 +378,13 @@ impl<'dr> VmConfig<'dr> { assert!(_old.is_none()); } + if let Some(vsock) = vsock { + let _old = spec + .components + .insert("vsock".into(), Component::VirtioSocket(*vsock)); + assert!(_old.is_none()); + } + if let Some(mig) = migration_failure.as_ref() { let _old = spec.components.insert( "migration-failure".into(), diff --git a/phd-tests/tests/src/lib.rs b/phd-tests/tests/src/lib.rs index da2437a87..d70a4735b 100644 --- a/phd-tests/tests/src/lib.rs +++ b/phd-tests/tests/src/lib.rs @@ -15,3 +15,4 @@ mod migrate; mod server_state_machine; mod smoke; mod stats; +mod vsock; diff --git a/phd-tests/tests/src/vsock.rs b/phd-tests/tests/src/vsock.rs new file mode 100644 index 000000000..22f4a731c --- /dev/null +++ b/phd-tests/tests/src/vsock.rs @@ -0,0 +1,44 @@ +use phd_testcase::*; + +const GUEST_CID: u64 = 16; +const PCI_DEV_NUM: u8 = 26; + +#[phd_testcase] +async fn vsock_smoke_test(ctx: &TestCtx) { + let mut cfg = ctx.vm_config_builder("vsock_smoke_test"); + cfg.vsock(GUEST_CID, PCI_DEV_NUM); + cfg.cpus(4); + + let mut vm = ctx.spawn_vm(&cfg, None).await?; + vm.launch().await?; + vm.wait_to_boot().await?; + + // This doesn't tell the whole story since linux will sometimes make this + // device available even if the hypervisor does not present the virtio + // device itself. Either way, it would be an error if it's not present. + vm.run_shell_command("test -e /dev/vsock").await?; +} + +#[phd_testcase] +async fn vsock_get_cid(ctx: &TestCtx) { + let mut cfg = ctx.vm_config_builder("vsock_get_cid"); + cfg.vsock(GUEST_CID, PCI_DEV_NUM); + cfg.cpus(4); + + let mut vm = ctx.spawn_vm(&cfg, None).await?; + vm.launch().await?; + vm.wait_to_boot().await?; + + // TODO: Remove the dependency on python + if vm.run_shell_command("test -e /usr/bin/python3").await.is_err() { + phd_skip!("guest doesn't have python3 installed"); + } + + // We don't really want to have to deal with python in a test but this is + // an easy way to get the cid until we support custom VM test images or + // tool block devices. + const GET_CID: &str = r#"python3 -c "import struct,fcntl;f=open('/dev/vsock','rb');print(struct.unpack('I',fcntl.ioctl(f,0x7b9,bytes(4)))[0])""#; + + let cid = vm.run_shell_command(GET_CID).await?.parse::()?; + assert_eq!(cid, GUEST_CID, "guest cid matches what was configured"); +}