diff --git a/Cargo.lock b/Cargo.lock index 1054fed7d..03dad4eca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3833,7 +3833,6 @@ dependencies = [ "libc", "newtype_derive", "propolis-client", - "propolis-server-config", "rand", "reqwest 0.12.7", "ring 0.17.8", @@ -4383,7 +4382,6 @@ dependencies = [ "oximeter-instruments", "oximeter-producer", "propolis", - "propolis-server-config", "propolis_api_types", "propolis_types", "reqwest 0.12.7", diff --git a/bin/propolis-server/Cargo.toml b/bin/propolis-server/Cargo.toml index 5132955fd..c748d4dfb 100644 --- a/bin/propolis-server/Cargo.toml +++ b/bin/propolis-server/Cargo.toml @@ -59,7 +59,6 @@ strum = { workspace = true, features = ["derive"] } propolis = { workspace = true, features = ["crucible-full", "oximeter"] } propolis_api_types = { workspace = true } propolis_types.workspace = true -propolis-server-config.workspace = true rgb_frame.workspace = true rfb = { workspace = true, features = ["tungstenite"] } uuid.workspace = true diff --git a/bin/propolis-server/README.md b/bin/propolis-server/README.md index 49cf967ad..80de49048 100644 --- a/bin/propolis-server/README.md +++ b/bin/propolis-server/README.md @@ -1,66 +1,24 @@ # Propolis Server -## Running - -Propolis is mostly intended to be used via a REST API to drive all of its -functionality. The standard `cargo build` will produce a `propolis-server` -binary you can run: - -``` -# propolis-server run -``` - -Note that the server must run as root. One way to ensure propolis-server has -sufficient privileges is by using `pfexec(1)`, as such: - -``` -# pfexec propolis-server run -``` - -## Example Configuration +This binary provides a REST API to create and manage a Propolis VM. It typically +runs in the context of a complete Oxide control plane deployment, but it can +also be run as a freestanding binary for ad hoc testing of Propolis VMs. -**Note**: the goal is to move the device config from the toml to instead be -configured via REST API calls. - -```toml -bootrom = "/path/to/bootrom/OVMF_CODE.fd" +## Running -[block_dev.alpine_iso] -type = "file" -path = "/path/to/alpine-extended-3.12.0-x86_64.iso" +The server requires a path to a [guest bootrom +image](../propolis-standalone#guest-bootrom) on the local filesystem. It also +must be run with privileges sufficient to create bhyve virtual machines. The +`pfexec(1)` utility can help enable these privileges. -[dev.block0] -driver = "pci-virtio-block" -block_dev = "alpine_iso" -pci-path = "0.4.0" +To build and run the server: -[dev.net0] -driver = "pci-virtio-viona" -vnic = "vnic_name" -pci-path = "0.5.0" +```bash +cargo build --bin propolis-server +pfexec target/debug/propolis-server ``` -## Prerequisites - -When running the server by hand, the appropriate bootrom is required to start -guests properly. See the [standalone -documentation](../propolis-standalone#guest-bootrom) for more details. Details -for [creating necessary vnics](../propolis-standalone#vnic) can be found there -as well, if exposing network devices to the guest. - -## CLI Interaction - -Once you've got `propolis-server` running you can interact with it via the REST -API with any of the usual suspects (e.g. cURL, wget). Alternatively, there's a -`propolis-cli` binary to make things a bit easier: - -### Running - -The following CLI commands will create a VM, start the VM, and then attach to -its serial console: - -``` -# propolis-cli -s -p new -# propolis-cli -s -p state run -# propolis-cli -s -p serial -``` +The API will be served on `ip:port`. The easiest way to interact with the server +is to use [`propolis-cli`](../propolis-cli), but you can also use tools like +cURL to interact with the API directly. The server's OpenAPI specification is +[checked into the repo](../../openapi/propolis-server.json). diff --git a/bin/propolis-server/src/lib/config.rs b/bin/propolis-server/src/lib/config.rs index 20dd6e12c..909e0a982 100644 --- a/bin/propolis-server/src/lib/config.rs +++ b/bin/propolis-server/src/lib/config.rs @@ -4,8 +4,6 @@ //! Describes a server config which may be parsed from a TOML file. -pub use propolis_server_config::*; - #[cfg(not(feature = "omicron-build"))] pub fn reservoir_decide(log: &slog::Logger) -> bool { // Automatically enable use of the memory reservoir (rather than transient diff --git a/bin/propolis-server/src/lib/initializer.rs b/bin/propolis-server/src/lib/initializer.rs index 2507e2321..b986d9367 100644 --- a/bin/propolis-server/src/lib/initializer.rs +++ b/bin/propolis-server/src/lib/initializer.rs @@ -187,7 +187,6 @@ pub struct MachineInitializer<'a> { pub(crate) crucible_backends: CrucibleBackendMap, pub(crate) spec: &'a Spec, pub(crate) properties: &'a InstanceProperties, - pub(crate) toml_config: &'a crate::server::VmTomlConfig, pub(crate) producer_registry: Option, pub(crate) state: MachineInitializerState, pub(crate) kstat_sampler: Option, @@ -911,14 +910,15 @@ impl<'a> MachineInitializer<'a> { chipset.pci_attach(p9fs.pci_path.into(), vio9p); } - fn generate_smbios(&self) -> smbios::TableBytes { + fn generate_smbios( + &self, + bootrom_version: &Option, + ) -> smbios::TableBytes { use smbios::table::{type0, type1, type16, type4}; let rom_size = self.state.rom_size_bytes.expect("ROM is already populated"); - let bios_version = self - .toml_config - .bootrom_version + let bios_version = bootrom_version .as_deref() .unwrap_or("v0.8") .try_into() @@ -1153,6 +1153,7 @@ impl<'a> MachineInitializer<'a> { pub fn initialize_fwcfg( &mut self, cpus: u8, + bootrom_version: &Option, ) -> Result, MachineInitError> { let fwcfg = fwcfg::FwCfg::new(); fwcfg @@ -1163,7 +1164,7 @@ impl<'a> MachineInitializer<'a> { .map_err(|e| MachineInitError::FwcfgInsertFailed("cpu count", e))?; let smbios::TableBytes { entry_point, structure_table } = - self.generate_smbios(); + self.generate_smbios(bootrom_version); fwcfg .insert_named( "etc/smbios/smbios-tables", diff --git a/bin/propolis-server/src/lib/server.rs b/bin/propolis-server/src/lib/server.rs index a5e669cb0..0a32d4464 100644 --- a/bin/propolis-server/src/lib/server.rs +++ b/bin/propolis-server/src/lib/server.rs @@ -14,6 +14,7 @@ use std::net::IpAddr; use std::net::Ipv6Addr; use std::net::SocketAddr; use std::net::SocketAddrV6; +use std::path::PathBuf; use std::sync::Arc; use crate::{ @@ -42,7 +43,6 @@ use propolis_api_types as api; use propolis_api_types::instance_spec::{ self, components::devices::QemuPvpanic, VersionedInstanceSpec, }; -pub use propolis_server_config::Config as VmTomlConfig; use rfb::tungstenite::BinaryWs; use slog::{error, warn, Logger}; use tokio::sync::MutexGuard; @@ -70,8 +70,12 @@ pub struct MetricsEndpointConfig { /// this configuration at startup time and refers to it when manipulating its /// objects. pub struct StaticConfig { - /// The TOML-driven configuration for this server's instances. - pub vm: Arc, + /// The path to the bootrom image to expose to the guest. + pub bootrom_path: PathBuf, + + /// The bootrom version string to expose to the guest. If None, machine + /// initialization chooses a default value. + pub bootrom_version: Option, /// Whether to use the host's guest memory reservoir to back guest memory. pub use_reservoir: bool, @@ -92,7 +96,8 @@ pub struct DropshotEndpointContext { impl DropshotEndpointContext { /// Creates a new server context object. pub fn new( - config: VmTomlConfig, + bootrom_path: PathBuf, + bootrom_version: Option, use_reservoir: bool, log: slog::Logger, metric_config: Option, @@ -100,7 +105,8 @@ impl DropshotEndpointContext { let vnc_server = VncServer::new(log.clone()); Self { static_config: StaticConfig { - vm: Arc::new(config), + bootrom_path, + bootrom_version, use_reservoir, metrics: metric_config, }, @@ -115,12 +121,9 @@ impl DropshotEndpointContext { /// this crate, so implementing TryFrom for them is not allowed.) fn instance_spec_from_request( request: &api::InstanceEnsureRequest, - toml_config: &VmTomlConfig, ) -> Result { let mut spec_builder = SpecBuilder::new(request.vcpus, request.memory); - spec_builder.add_devices_from_config(toml_config)?; - for nic in &request.nics { spec_builder.add_nic_from_request(nic)?; } @@ -257,7 +260,8 @@ async fn instance_ensure_common( .await; let ensure_options = crate::vm::EnsureOptions { - toml_config: server_context.static_config.vm.clone(), + bootrom_path: server_context.static_config.bootrom_path.clone(), + bootrom_version: server_context.static_config.bootrom_version.clone(), use_reservoir: server_context.static_config.use_reservoir, metrics_config: server_context.static_config.metrics.clone(), oximeter_registry, @@ -300,19 +304,13 @@ async fn instance_ensure( rqctx: RequestContext>, request: TypedBody, ) -> Result, HttpError> { - let server_context = rqctx.context(); let request = request.into_inner(); - let instance_spec = - instance_spec_from_request(&request, &server_context.static_config.vm) - .map_err(|e| { - HttpError::for_bad_request( - None, - format!( - "failed to generate instance spec from request: {:#?}", - e - ), - ) - })?; + let instance_spec = instance_spec_from_request(&request).map_err(|e| { + HttpError::for_bad_request( + None, + format!("failed to generate instance spec from request: {:#?}", e), + ) + })?; instance_ensure_common( rqctx, diff --git a/bin/propolis-server/src/lib/spec/builder.rs b/bin/propolis-server/src/lib/spec/builder.rs index b9012d212..877317b9a 100644 --- a/bin/propolis-server/src/lib/spec/builder.rs +++ b/bin/propolis-server/src/lib/spec/builder.rs @@ -24,11 +24,10 @@ use propolis_api_types::instance_spec::components::devices::{ P9fs, SoftNpuP9, SoftNpuPciPort, }; -use crate::{config, spec::SerialPortDevice}; +use crate::spec::SerialPortDevice; use super::{ api_request::{self, DeviceRequestError}, - config_toml::{ConfigTomlError, ParsedConfig}, Board, BootOrderEntry, BootSettings, Disk, Nic, QemuPvpanic, SerialPort, }; @@ -36,14 +35,11 @@ use super::{ use super::MigrationFailure; #[cfg(feature = "falcon")] -use super::{ParsedSoftNpu, SoftNpuPort}; +use super::SoftNpuPort; /// Errors that can arise while building an instance spec from component parts. #[derive(Debug, Error)] pub(crate) enum SpecBuilderError { - #[error("error parsing config TOML")] - ConfigToml(#[from] ConfigTomlError), - #[error("error parsing device in ensure request")] DeviceRequest(#[from] DeviceRequestError), @@ -192,59 +188,6 @@ impl SpecBuilder { Ok(()) } - /// Adds all the devices and backends specified in the supplied - /// configuration TOML to the spec under construction. - pub fn add_devices_from_config( - &mut self, - config: &config::Config, - ) -> Result<(), SpecBuilderError> { - let parsed = ParsedConfig::try_from(config)?; - - let Chipset::I440Fx(ref mut i440fx) = self.spec.board.chipset; - i440fx.enable_pcie = parsed.enable_pcie; - - for disk in parsed.disks { - self.add_storage_device(disk.name, disk.disk)?; - } - - for nic in parsed.nics { - self.add_network_device(nic.name, nic.nic)?; - } - - for bridge in parsed.pci_bridges { - self.add_pci_bridge(bridge.name, bridge.bridge)?; - } - - #[cfg(feature = "falcon")] - self.add_parsed_softnpu_devices(parsed.softnpu)?; - - Ok(()) - } - - #[cfg(feature = "falcon")] - fn add_parsed_softnpu_devices( - &mut self, - devices: ParsedSoftNpu, - ) -> Result<(), SpecBuilderError> { - if let Some(pci_port) = devices.pci_port { - self.set_softnpu_pci_port(pci_port)?; - } - - for port in devices.ports { - self.add_softnpu_port(port.name, port.port)?; - } - - if let Some(p9) = devices.p9_device { - self.set_softnpu_p9(p9)?; - } - - if let Some(p9fs) = devices.p9fs { - self.set_p9fs(p9fs)?; - } - - Ok(()) - } - /// Adds a PCI path to this builder's record of PCI locations with an /// attached device. If the path is already in use, returns an error. fn register_pci_device( diff --git a/bin/propolis-server/src/lib/spec/mod.rs b/bin/propolis-server/src/lib/spec/mod.rs index dc133fa02..8ba51ba1d 100644 --- a/bin/propolis-server/src/lib/spec/mod.rs +++ b/bin/propolis-server/src/lib/spec/mod.rs @@ -46,7 +46,6 @@ use propolis_api_types::instance_spec::components::{ mod api_request; pub(crate) mod api_spec_v0; pub(crate) mod builder; -mod config_toml; #[derive(Debug, Error)] #[error("input component type can't convert to output type")] @@ -317,26 +316,6 @@ struct ParsedNicRequest { nic: Nic, } -struct ParsedPciBridgeRequest { - name: String, - bridge: PciPciBridge, -} - -#[cfg(feature = "falcon")] -struct ParsedSoftNpuPort { - name: String, - port: SoftNpuPort, -} - -#[cfg(feature = "falcon")] -#[derive(Default)] -struct ParsedSoftNpu { - pub pci_port: Option, - pub ports: Vec, - pub p9_device: Option, - pub p9fs: Option, -} - /// Generates NIC device and backend names from the NIC's PCI path. This is /// needed because the `name` field in a propolis-client /// `NetworkInterfaceRequest` is actually the name of the host vNIC to bind to, diff --git a/bin/propolis-server/src/lib/vm/ensure.rs b/bin/propolis-server/src/lib/vm/ensure.rs index 41632194e..ae6040f4d 100644 --- a/bin/propolis-server/src/lib/vm/ensure.rs +++ b/bin/propolis-server/src/lib/vm/ensure.rs @@ -352,7 +352,7 @@ async fn initialize_vm_objects( "spec" => #?spec, "properties" => #?properties, "use_reservoir" => options.use_reservoir, - "bootrom" => %options.toml_config.bootrom.display()); + "bootrom" => %options.bootrom_path.display()); let vmm_log = log.new(slog::o!("component" => "vmm")); @@ -373,7 +373,6 @@ async fn initialize_vm_objects( crucible_backends: Default::default(), spec: &spec, properties: &properties, - toml_config: &options.toml_config, producer_registry: options.oximeter_registry.clone(), state: MachineInitializerState::default(), kstat_sampler: initialize_kstat_sampler( @@ -384,7 +383,7 @@ async fn initialize_vm_objects( stats_vm: VirtualMachine::new(spec.board.cpus, &properties), }; - init.initialize_rom(options.toml_config.bootrom.as_path())?; + init.initialize_rom(options.bootrom_path.as_path())?; let chipset = init.initialize_chipset( &(event_queue.clone() as Arc), @@ -416,7 +415,9 @@ async fn initialize_vm_objects( init.initialize_storage_devices(&chipset, options.nexus_client.clone()) .await?; - let ramfb = init.initialize_fwcfg(spec.board.cpus)?; + let ramfb = + init.initialize_fwcfg(spec.board.cpus, &options.bootrom_version)?; + init.initialize_cpus().await?; let vcpu_tasks = Box::new(crate::vcpu_tasks::VcpuTasks::new( &machine, diff --git a/bin/propolis-server/src/lib/vm/mod.rs b/bin/propolis-server/src/lib/vm/mod.rs index 258c1976f..c820b7dea 100644 --- a/bin/propolis-server/src/lib/vm/mod.rs +++ b/bin/propolis-server/src/lib/vm/mod.rs @@ -79,7 +79,7 @@ //! In the latter case, the driver moves to `Rundown` and allows `VmObjects` //! teardown to drive the state machine to `RundownComplete`. -use std::{collections::BTreeMap, net::SocketAddr, sync::Arc}; +use std::{collections::BTreeMap, net::SocketAddr, path::PathBuf, sync::Arc}; use active::ActiveVm; use ensure::VmEnsureRequest; @@ -279,9 +279,12 @@ impl std::fmt::Display for VmState { /// Parameters to an instance ensure operation. pub(super) struct EnsureOptions { - /// A reference to the VM configuration specified in the config TOML passed - /// to this propolis-server process. - pub(super) toml_config: Arc, + /// The path to the bootrom to load into the guest. + pub(super) bootrom_path: PathBuf, + + /// The bootrom version string to expose to the guest. If None, the machine + /// initializer chooses a default. + pub(super) bootrom_version: Option, /// True if VMs should allocate memory from the kernel VMM reservoir. pub(super) use_reservoir: bool, diff --git a/bin/propolis-server/src/main.rs b/bin/propolis-server/src/main.rs index b4127ec31..a45718405 100644 --- a/bin/propolis-server/src/main.rs +++ b/bin/propolis-server/src/main.rs @@ -75,11 +75,14 @@ enum Args { /// Runs the Propolis server. Run { #[clap(action)] - cfg: PathBuf, + bootrom_path: PathBuf, #[clap(name = "PROPOLIS_IP:PORT", action)] propolis_addr: SocketAddr, + #[clap(long, action)] + bootrom_version: Option, + /// Method for registering as an Oximeter metric producer. /// /// The following values are supported: @@ -117,7 +120,8 @@ pub fn run_openapi() -> Result<(), String> { } fn run_server( - config_app: config::Config, + bootrom_path: PathBuf, + bootrom_version: Option, config_dropshot: dropshot::ConfigDropshot, config_metrics: Option, vnc_addr: Option, @@ -146,7 +150,8 @@ fn run_server( let use_reservoir = config::reservoir_decide(&log); let context = server::DropshotEndpointContext::new( - config_app, + bootrom_path, + bootrom_version, use_reservoir, log.new(slog::o!()), config_metrics, @@ -279,9 +284,14 @@ fn main() -> anyhow::Result<()> { match args { Args::OpenApi => run_openapi() .map_err(|e| anyhow!("Cannot generate OpenAPI spec: {}", e)), - Args::Run { cfg, propolis_addr, metric_addr, vnc_addr, log_level } => { - let config = config::parse(cfg)?; - + Args::Run { + bootrom_path, + bootrom_version, + propolis_addr, + metric_addr, + vnc_addr, + log_level, + } => { // Dropshot configuration. let config_dropshot = ConfigDropshot { bind_address: propolis_addr, @@ -298,7 +308,14 @@ fn main() -> anyhow::Result<()> { propolis_addr.ip(), )?; - run_server(config, config_dropshot, metric_config, vnc_addr, log) + run_server( + bootrom_path, + bootrom_version, + config_dropshot, + metric_config, + vnc_addr, + log, + ) } } } diff --git a/packaging/smf/method_script.sh b/packaging/smf/method_script.sh index 5c0768697..23a72412b 100755 --- a/packaging/smf/method_script.sh +++ b/packaging/smf/method_script.sh @@ -30,7 +30,7 @@ route get -inet6 default -inet6 "$GATEWAY" || route add -inet6 default -inet6 "$ args=( 'run' - '/var/svc/manifest/site/propolis-server/config.toml' + '/opt/oxide/propolis-server/blob/OVMF_CODE.fd' "[$LISTEN_ADDR]:$LISTEN_PORT" '--metric-addr' "$METRIC_ADDR" ) diff --git a/packaging/smf/propolis-server/config.toml b/packaging/smf/propolis-server/config.toml deleted file mode 100644 index c325ce3f0..000000000 --- a/packaging/smf/propolis-server/config.toml +++ /dev/null @@ -1,14 +0,0 @@ -# Configuration for propolis server. -# -# Refer to https://github.com/oxidecomputer/propolis#readme -# for more detail on the config format. - -bootrom = "/opt/oxide/propolis-server/blob/OVMF_CODE.fd" - -# NOTE: This VNIC is here for reference, but VNICs are typically managed by the -# Sled Agent. - -# [dev.net0] -# driver = "pci-virtio-viona" -# vnic = "vnic_prop0" -# pci-path = "0.5.0" diff --git a/phd-tests/framework/Cargo.toml b/phd-tests/framework/Cargo.toml index afb7856f4..ee666d722 100644 --- a/phd-tests/framework/Cargo.toml +++ b/phd-tests/framework/Cargo.toml @@ -24,7 +24,6 @@ hex.workspace = true libc.workspace = true newtype_derive.workspace = true propolis-client.workspace = true -propolis-server-config.workspace = true reqwest = { workspace = true, features = ["blocking"] } ring.workspace = true serde = { workspace = true, features = ["derive"] } diff --git a/phd-tests/framework/src/test_vm/config.rs b/phd-tests/framework/src/test_vm/config.rs index 68ddaaa9b..be39b19ca 100644 --- a/phd-tests/framework/src/test_vm/config.rs +++ b/phd-tests/framework/src/test_vm/config.rs @@ -209,21 +209,12 @@ impl<'dr> VmConfig<'dr> { migration_failure, } = self; - // Figure out where the bootrom is and generate the serialized contents - // of a Propolis server config TOML that points to it. - let bootrom = framework + let bootrom_path = framework .artifact_store .get_bootrom(bootrom_artifact) .await .context("looking up bootrom artifact")?; - let config_toml_contents = - toml::ser::to_string(&propolis_server_config::Config { - bootrom: bootrom.clone().into(), - ..Default::default() - }) - .context("serializing Propolis server config")?; - // The first disk in the boot list might not be the disk a test // *actually* expects to boot. // @@ -370,7 +361,7 @@ impl<'dr> VmConfig<'dr> { instance_spec: spec, disk_handles, guest_os_kind, - config_toml_contents, + bootrom_path, metadata, }) } diff --git a/phd-tests/framework/src/test_vm/mod.rs b/phd-tests/framework/src/test_vm/mod.rs index ca2fef4f9..c8f419dcc 100644 --- a/phd-tests/framework/src/test_vm/mod.rs +++ b/phd-tests/framework/src/test_vm/mod.rs @@ -5,7 +5,7 @@ //! Routines for starting VMs, changing their states, and interacting with their //! guest OSes. -use std::{fmt::Debug, io::Write, sync::Arc, time::Duration}; +use std::{fmt::Debug, sync::Arc, time::Duration}; use crate::{ guest_os::{ @@ -227,33 +227,12 @@ impl TestVm { params: ServerProcessParameters, cleanup_task_tx: UnboundedSender>, ) -> Result { - let config_filename = format!("{}.config.toml", &vm_spec.vm_name); - let mut config_toml_path = params.data_dir.to_path_buf(); - config_toml_path.push(config_filename); - let mut config_file = std::fs::OpenOptions::new() - .write(true) - .truncate(true) - .create(true) - .open(&config_toml_path) - .with_context(|| { - format!("opening config file {} for writing", config_toml_path) - })?; - - config_file - .write_all(vm_spec.config_toml_contents.as_bytes()) - .with_context(|| { - format!( - "writing config toml to config file {}", - config_toml_path - ) - })?; - let data_dir = params.data_dir.to_path_buf(); let server_addr = params.server_addr; let server = server::PropolisServer::new( &vm_spec.vm_name, params, - &config_toml_path, + &vm_spec.bootrom_path, )?; let client = Client::new(&format!("http://{}", server_addr)); diff --git a/phd-tests/framework/src/test_vm/server.rs b/phd-tests/framework/src/test_vm/server.rs index a1be00a01..01da19d37 100644 --- a/phd-tests/framework/src/test_vm/server.rs +++ b/phd-tests/framework/src/test_vm/server.rs @@ -42,7 +42,7 @@ impl PropolisServer { pub(crate) fn new( vm_name: &str, process_params: ServerProcessParameters, - config_toml_path: &Utf8Path, + bootrom_path: &Utf8Path, ) -> Result { let ServerProcessParameters { server_path, @@ -54,7 +54,7 @@ impl PropolisServer { info!( ?server_path, - ?config_toml_path, + ?bootrom_path, ?server_addr, "Launching Propolis server" ); @@ -67,7 +67,7 @@ impl PropolisServer { .args([ server_path.as_str(), "run", - config_toml_path.as_str(), + bootrom_path.as_str(), server_addr.to_string().as_str(), vnc_addr.to_string().as_str(), ]) diff --git a/phd-tests/framework/src/test_vm/spec.rs b/phd-tests/framework/src/test_vm/spec.rs index 97899c025..761fffd72 100644 --- a/phd-tests/framework/src/test_vm/spec.rs +++ b/phd-tests/framework/src/test_vm/spec.rs @@ -8,6 +8,7 @@ use crate::{ disk::{self, DiskConfig}, guest_os::GuestOsKind, }; +use camino::Utf8PathBuf; use propolis_client::types::{ ComponentV0, DiskRequest, InstanceMetadata, InstanceSpecV0, PciPath, Slot, }; @@ -27,8 +28,8 @@ pub struct VmSpec { /// The guest OS adapter to use for the VM. pub guest_os_kind: GuestOsKind, - /// The contents of the config TOML to write to run this VM. - pub config_toml_contents: String, + /// The bootrom path to pass to this VM's Propolis server processes. + pub bootrom_path: Utf8PathBuf, /// Metadata used to track instance timeseries data. pub metadata: InstanceMetadata,