diff --git a/CHANGELOG.md b/CHANGELOG.md index 15ee1e04e09..05d0965b867 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,10 @@ Refactored error propagation to avoid logging and printing an error on exits with a zero exit code. Now, on successful exit "Firecracker exited successfully" is logged. +- [#4194](https://github.com/firecracker-microvm/firecracker/pull/4194): + Removed support for creating Firecracker snapshots targeting older versions + of Firecracker. With this change, running 'firecracker --version' will not + print the supported snapshot versions. ### Deprecated diff --git a/src/api_server/src/lib.rs b/src/api_server/src/lib.rs index 6c7752725fa..9f759505620 100644 --- a/src/api_server/src/lib.rs +++ b/src/api_server/src/lib.rs @@ -281,7 +281,6 @@ mod tests { snapshot_type: SnapshotType::Diff, snapshot_path: PathBuf::new(), mem_file_path: PathBuf::new(), - version: None, })), start_time_us, ); @@ -295,7 +294,6 @@ mod tests { snapshot_type: SnapshotType::Diff, snapshot_path: PathBuf::new(), mem_file_path: PathBuf::new(), - version: None, })), start_time_us, ); diff --git a/src/api_server/src/parsed_request.rs b/src/api_server/src/parsed_request.rs index aba39843a32..a43c6e587f3 100644 --- a/src/api_server/src/parsed_request.rs +++ b/src/api_server/src/parsed_request.rs @@ -846,8 +846,7 @@ pub mod tests { fn test_try_from_put_snapshot() { let (mut sender, receiver) = UnixStream::pair().unwrap(); let mut connection = HttpConnection::new(receiver); - let body = - "{ \"snapshot_path\": \"foo\", \"mem_file_path\": \"bar\", \"version\": \"0.23.0\" }"; + let body = "{ \"snapshot_path\": \"foo\", \"mem_file_path\": \"bar\" }"; sender .write_all(http_request("PUT", "/snapshot/create", Some(body)).as_bytes()) .unwrap(); diff --git a/src/api_server/src/request/snapshot.rs b/src/api_server/src/request/snapshot.rs index 2fb6bdec67a..c8895a8c4cc 100644 --- a/src/api_server/src/request/snapshot.rs +++ b/src/api_server/src/request/snapshot.rs @@ -14,9 +14,6 @@ use crate::request::{Body, Method, StatusCode}; /// Deprecation message for the `mem_file_path` field. const LOAD_DEPRECATION_MESSAGE: &str = "PUT /snapshot/load: mem_file_path field is deprecated."; -/// Deprecation message for the `version` field. -const CREATE_WITH_VERSION_DEPRECATION_MESSAGE: &str = - "PUT /snapshot/create: 'version' field is deprecated."; /// None of the `mem_backend` or `mem_file_path` fields has been specified. pub const MISSING_FIELD: &str = "missing field: either `mem_backend` or `mem_file_path` is required"; @@ -56,19 +53,9 @@ pub(crate) fn parse_patch_vm_state(body: &Body) -> Result fn parse_put_snapshot_create(body: &Body) -> Result { let snapshot_config = serde_json::from_slice::(body.raw())?; - let uses_deprecated_version_field = snapshot_config.version.is_some(); - - let mut parsed_req = ParsedRequest::new_sync(VmmAction::CreateSnapshot(snapshot_config)); - // `version` field is deprecated as a parameter for `CreateSnapshotParams`. - // Add a deprecation message if the request includes the parameter. - if uses_deprecated_version_field { - METRICS.deprecated_api.deprecated_http_api_calls.inc(); - parsed_req - .parsing_info() - .append_deprecation_message(CREATE_WITH_VERSION_DEPRECATION_MESSAGE); - } - - Ok(parsed_req) + Ok(ParsedRequest::new_sync(VmmAction::CreateSnapshot( + snapshot_config, + ))) } fn parse_put_snapshot_load(body: &Body) -> Result { @@ -127,7 +114,7 @@ fn parse_put_snapshot_load(body: &Body) -> Result { #[cfg(test)] mod tests { - use vmm::vmm_config::snapshot::{MemBackendConfig, MemBackendType, Version}; + use vmm::vmm_config::snapshot::{MemBackendConfig, MemBackendType}; use super::*; use crate::parsed_request::tests::{depr_action_from_req, vmm_action_from_request}; @@ -141,21 +128,15 @@ mod tests { let body = r#"{ "snapshot_type": "Diff", "snapshot_path": "foo", - "mem_file_path": "bar", - "version": "0.23.0" + "mem_file_path": "bar" }"#; let expected_config = CreateSnapshotParams { snapshot_type: SnapshotType::Diff, snapshot_path: PathBuf::from("foo"), mem_file_path: PathBuf::from("bar"), - version: Some(Version::new(0, 23, 0)), }; - let parsed_request = parse_put_snapshot(&Body::new(body), Some("create")).unwrap(); assert_eq!( - depr_action_from_req( - parsed_request, - Some(CREATE_WITH_VERSION_DEPRECATION_MESSAGE.to_string()) - ), + vmm_action_from_request(parse_put_snapshot(&Body::new(body), Some("create")).unwrap()), VmmAction::CreateSnapshot(expected_config) ); @@ -167,7 +148,6 @@ mod tests { snapshot_type: SnapshotType::Full, snapshot_path: PathBuf::from("foo"), mem_file_path: PathBuf::from("bar"), - version: None, }; assert_eq!( vmm_action_from_request(parse_put_snapshot(&Body::new(body), Some("create")).unwrap()), diff --git a/src/api_server/swagger/firecracker.yaml b/src/api_server/swagger/firecracker.yaml index 9519f710936..bd167a4c5a1 100644 --- a/src/api_server/swagger/firecracker.yaml +++ b/src/api_server/swagger/firecracker.yaml @@ -1177,12 +1177,6 @@ definitions: description: Type of snapshot to create. It is optional and by default, a full snapshot is created. - version: - type: string - description: - The microVM version for which we want to create the snapshot. - It is optional and it defaults to the current version. This parameter - has been deprecated and it will be removed in future Firecracker release. SnapshotLoadParams: type: object diff --git a/src/firecracker/src/main.rs b/src/firecracker/src/main.rs index a1f63d480bd..b41f3739911 100644 --- a/src/firecracker/src/main.rs +++ b/src/firecracker/src/main.rs @@ -256,7 +256,6 @@ fn main_exec() -> Result<(), MainError> { if arguments.flag_present("version") { println!("Firecracker v{}\n", FIRECRACKER_VERSION); - print_supported_snapshot_versions(); return Ok(()); } @@ -509,20 +508,6 @@ pub fn enable_ssbd_mitigation() { #[allow(unused)] fn warn_deprecated_parameters() {} -// Print supported snapshot data format versions. -fn print_supported_snapshot_versions() { - let mut versions: Vec<_> = FC_VERSION_TO_SNAP_VERSION - .iter() - .map(|(key, _)| key.clone()) - .collect(); - versions.sort(); - - println!("Supported snapshot data format versions:"); - for v in versions.iter() { - println!("{v}"); - } -} - #[derive(Debug, thiserror::Error, displaydoc::Display)] enum SnapshotVersionError { /// Unable to open snapshot state file: {0} diff --git a/src/vmm/src/device_manager/mmio.rs b/src/vmm/src/device_manager/mmio.rs index 2c31aed8252..44e16db8f18 100644 --- a/src/vmm/src/device_manager/mmio.rs +++ b/src/vmm/src/device_manager/mmio.rs @@ -294,16 +294,6 @@ impl MMIODeviceManager { &self.id_to_dev_info } - #[cfg(target_arch = "x86_64")] - /// Gets the number of interrupts used by the devices registered. - pub fn used_irqs_count(&self) -> usize { - let mut irq_number = 0; - self.get_device_info() - .iter() - .for_each(|(_, device_info)| irq_number += device_info.irqs.len()); - irq_number - } - /// Gets the specified device. pub fn get_device( &self, @@ -488,6 +478,16 @@ mod tests { self.register_mmio_virtio_for_boot(vm, dev_id.to_string(), mmio_device, cmdline)?; Ok(device_info.addr) } + + #[cfg(target_arch = "x86_64")] + /// Gets the number of interrupts used by the devices registered. + pub fn used_irqs_count(&self) -> usize { + let mut irq_number = 0; + self.get_device_info() + .iter() + .for_each(|(_, device_info)| irq_number += device_info.irqs.len()); + irq_number + } } #[allow(dead_code)] diff --git a/src/vmm/src/device_manager/persist.rs b/src/vmm/src/device_manager/persist.rs index 2d9d3148084..fdc507032f3 100644 --- a/src/vmm/src/device_manager/persist.rs +++ b/src/vmm/src/device_manager/persist.rs @@ -8,7 +8,7 @@ use std::sync::{Arc, Mutex}; use event_manager::{MutEventSubscriber, SubscriberOps}; use kvm_ioctls::VmFd; -use log::{error, warn}; +use log::error; use snapshot::Persist; use versionize::{VersionMap, Versionize, VersionizeError, VersionizeResult}; use versionize_derive::Versionize; @@ -181,13 +181,13 @@ pub struct DeviceStates { /// Vsock device state. pub vsock_device: Option, /// Balloon device state. - #[version(start = 2, ser_fn = "balloon_serialize")] + #[version(start = 2)] pub balloon_device: Option, /// Mmds version. - #[version(start = 3, ser_fn = "mmds_version_serialize")] + #[version(start = 3)] pub mmds_version: Option, /// Entropy device state. - #[version(start = 4, ser_fn = "entropy_serialize")] + #[version(start = 4)] pub entropy_device: Option, } @@ -202,39 +202,6 @@ pub enum SharedDeviceType { Entropy(Arc>), } -impl DeviceStates { - fn balloon_serialize(&mut self, target_version: u16) -> VersionizeResult<()> { - if target_version < 2 && self.balloon_device.is_some() { - return Err(VersionizeError::Semantic( - "Target version does not implement the virtio-balloon device.".to_owned(), - )); - } - - Ok(()) - } - - fn mmds_version_serialize(&mut self, target_version: u16) -> VersionizeResult<()> { - if target_version < 3 && self.mmds_version.is_some() { - warn!( - "Target version does not support persisting the MMDS version. The default will be \ - used when restoring." - ); - } - - Ok(()) - } - - fn entropy_serialize(&mut self, target_version: u16) -> VersionizeResult<()> { - if target_version < 4 && self.entropy_device.is_some() { - return Err(VersionizeError::Semantic( - "Target version does not support persisting the virtio-rng device.".to_owned(), - )); - } - - Ok(()) - } -} - pub struct MMIODevManagerConstructorArgs<'a> { pub mem: GuestMemoryMmap, pub vm: &'a VmFd, @@ -769,15 +736,6 @@ mod tests { }; insert_vsock_device(&mut vmm, &mut cmdline, &mut event_manager, vsock_config); - assert_eq!( - vmm.mmio_device_manager - .save() - .serialize(&mut buf.as_mut_slice(), &version_map, 1), - Err(VersionizeError::Semantic( - "Target version does not implement the virtio-balloon device.".to_string() - )) - ); - version_map .new_version() .set_type_version(DeviceStates::type_id(), 2); @@ -815,16 +773,6 @@ mod tests { .new_version() .set_type_version(DeviceStates::type_id(), 4); - // Entropy device not supported in version < 4 - assert_eq!( - vmm.mmio_device_manager - .save() - .serialize(&mut buf.as_mut_slice(), &version_map, 3), - Err(VersionizeError::Semantic( - "Target version does not support persisting the virtio-rng device.".to_string() - )) - ); - version_map .new_version() .set_type_version(DeviceStates::type_id(), 4) diff --git a/src/vmm/src/devices/virtio/block/persist.rs b/src/vmm/src/devices/virtio/block/persist.rs index d10db720cb4..f3b78f7f389 100644 --- a/src/vmm/src/devices/virtio/block/persist.rs +++ b/src/vmm/src/devices/virtio/block/persist.rs @@ -88,35 +88,17 @@ impl From for FileEngineType { pub struct BlockState { id: String, partuuid: Option, - #[version( - start = 2, - ser_fn = "block_cache_type_ser", - default_fn = "default_cache_type_flush" - )] + #[version(start = 2, default_fn = "default_cache_type_flush")] cache_type: CacheTypeState, root_device: bool, disk_path: String, virtio_state: VirtioDeviceState, rate_limiter_state: RateLimiterState, #[version(start = 3)] - // We don't need to specify a `ser_fn` for the `file_engine_type` since snapshots created in - // v1.0 are incompatible with older FC versions (due to incompatible notification suppression - // feature). file_engine_type: FileEngineTypeState, } impl BlockState { - fn block_cache_type_ser(&mut self, target_version: u16) -> VersionizeResult<()> { - if target_version < 3 && self.cache_type != CacheTypeState::Unsafe { - warn!( - "Target version does not implement the current cache type. Defaulting to \ - \"unsafe\" mode." - ); - } - - Ok(()) - } - fn default_cache_type_flush(_source_version: u16) -> CacheTypeState { CacheTypeState::Unsafe } diff --git a/src/vmm/src/devices/virtio/net/persist.rs b/src/vmm/src/devices/virtio/net/persist.rs index 7ce65ec20c3..7f09e12cefa 100644 --- a/src/vmm/src/devices/virtio/net/persist.rs +++ b/src/vmm/src/devices/virtio/net/persist.rs @@ -31,7 +31,7 @@ use crate::vstate::memory::GuestMemoryMmap; pub struct NetConfigSpaceState { #[version(end = 2, default_fn = "def_guest_mac_old")] guest_mac: [u8; MAC_ADDR_LEN as usize], - #[version(start = 2, de_fn = "de_guest_mac_v2", ser_fn = "ser_guest_mac_v2")] + #[version(start = 2, de_fn = "de_guest_mac_v2")] guest_mac_v2: Option, } @@ -45,16 +45,6 @@ impl NetConfigSpaceState { Ok(()) } - fn ser_guest_mac_v2(&mut self, _target_version: u16) -> VersionizeResult<()> { - // v1.1 and older versions do not have optional MAC address. - warn!("Saving to older snapshot version, optional MAC address will not be saved."); - match self.guest_mac_v2 { - Some(mac) => self.guest_mac = mac.into(), - None => self.guest_mac = Default::default(), - } - Ok(()) - } - fn def_guest_mac_old(_: u16) -> [u8; MAC_ADDR_LEN as usize] { // v1.2 and newer don't use this field anyway Default::default() diff --git a/src/vmm/src/devices/virtio/persist.rs b/src/vmm/src/devices/virtio/persist.rs index 3fa263467b7..992e77222a2 100644 --- a/src/vmm/src/devices/virtio/persist.rs +++ b/src/vmm/src/devices/virtio/persist.rs @@ -103,11 +103,7 @@ pub struct VirtioDeviceState { /// List of queues. pub queues: Vec, /// The MMIO interrupt status. - #[version( - start = 2, - de_fn = "de_interrupt_status", - ser_fn = "ser_interrupt_status" - )] + #[version(start = 2, de_fn = "de_interrupt_status")] pub interrupt_status: u32, /// The MMIO interrupt status as a usize. #[version(end = 2)] @@ -188,12 +184,6 @@ impl VirtioDeviceState { } Ok(()) } - - fn ser_interrupt_status(&mut self, _target_version: u16) -> VersionizeResult<()> { - // v1 uses a usize type for interrupt status. - self.interrupt_status_old = self.interrupt_status as usize; - Ok(()) - } } /// Transport information saved in snapshot. diff --git a/src/vmm/src/persist.rs b/src/vmm/src/persist.rs index 1bcf2502c6a..4ee41dacef1 100644 --- a/src/vmm/src/persist.rs +++ b/src/vmm/src/persist.rs @@ -12,7 +12,6 @@ use std::path::Path; use std::sync::{Arc, Mutex}; use seccompiler::BpfThreadMap; -use semver::Version; use serde::Serialize; use snapshot::Snapshot; use userfaultfd::{FeatureFlags, Uffd, UffdBuilder}; @@ -30,15 +29,8 @@ use crate::cpu_config::x86_64::cpuid::common::get_vendor_id_from_host; #[cfg(target_arch = "x86_64")] use crate::cpu_config::x86_64::cpuid::CpuidTrait; use crate::device_manager::persist::{DevicePersistError, DeviceStates}; -use crate::devices::virtio::gen::virtio_ring::VIRTIO_RING_F_EVENT_IDX; -use crate::devices::virtio::TYPE_NET; use crate::logger::{info, warn}; use crate::resources::VmResources; -#[cfg(target_arch = "x86_64")] -use crate::version_map::FC_V0_23_SNAP_VERSION; -use crate::version_map::{ - FC_V1_0_SNAP_VERSION, FC_V1_1_SNAP_VERSION, FC_V1_5_SNAP_VERSION, FC_VERSION_TO_SNAP_VERSION, -}; use crate::vmm_config::boot_source::BootSourceConfig; use crate::vmm_config::instance_info::InstanceInfo; use crate::vmm_config::machine_config::MAX_SUPPORTED_VCPUS; @@ -62,17 +54,13 @@ pub struct VmInfo { /// Guest memory size. pub mem_size_mib: u64, /// smt information - #[version(start = 2, default_fn = "def_smt", ser_fn = "ser_smt")] + #[version(start = 2, default_fn = "def_smt")] pub smt: bool, /// CPU template type - #[version( - start = 2, - default_fn = "def_cpu_template", - ser_fn = "ser_cpu_template" - )] + #[version(start = 2, default_fn = "def_cpu_template")] pub cpu_template: StaticCpuTemplate, /// Boot source information. - #[version(start = 2, default_fn = "def_boot_source", ser_fn = "ser_boot_source")] + #[version(start = 2, default_fn = "def_boot_source")] pub boot_source: BootSourceConfig, } @@ -82,33 +70,15 @@ impl VmInfo { false } - fn ser_smt(&mut self, _target_version: u16) -> VersionizeResult<()> { - // v1.1 and older versions do not include smt info. - warn!("Saving to older snapshot version, SMT information will not be saved."); - Ok(()) - } - fn def_cpu_template(_: u16) -> StaticCpuTemplate { warn!("CPU template field not found in snapshot."); StaticCpuTemplate::default() } - fn ser_cpu_template(&mut self, _target_version: u16) -> VersionizeResult<()> { - // v1.1 and older versions do not include cpu template info. - warn!("Saving to older snapshot version, CPU template information will not be saved."); - Ok(()) - } - fn def_boot_source(_: u16) -> BootSourceConfig { warn!("Boot source information not found in snapshot."); BootSourceConfig::default() } - - fn ser_boot_source(&mut self, _target_version: u16) -> VersionizeResult<()> { - // v1.1 and older versions do not include boot source info. - warn!("Saving to older snapshot version, boot source information will not be saved."); - Ok(()) - } } impl From<&VmResources> for VmInfo { @@ -217,15 +187,12 @@ pub fn create_snapshot( params: &CreateSnapshotParams, version_map: VersionMap, ) -> Result<(), CreateSnapshotError> { - // Fail early from invalid target version. - let snapshot_data_version = get_snapshot_data_version(¶ms.version, &version_map, vmm)?; + let snapshot_data_version = version_map.latest_version(); let microvm_state = vmm .save_state(vm_info) .map_err(CreateSnapshotError::MicrovmState)?; - extra_version_check(µvm_state, snapshot_data_version)?; - snapshot_state_to_file( µvm_state, ¶ms.snapshot_path, @@ -297,65 +264,6 @@ fn snapshot_memory_to_file( .map_err(|err| MemoryBackingFile("sync_all", err)) } -/// Validate the microVM version and translate it to its corresponding snapshot data format. -pub fn get_snapshot_data_version( - maybe_fc_version: &Option, - version_map: &VersionMap, - vmm: &Vmm, -) -> Result { - let fc_version = match maybe_fc_version { - None => return Ok(version_map.latest_version()), - Some(version) => version, - }; - let data_version = *FC_VERSION_TO_SNAP_VERSION - .get(fc_version) - .ok_or(CreateSnapshotError::UnsupportedVersion)?; - - #[cfg(target_arch = "x86_64")] - if data_version <= FC_V0_23_SNAP_VERSION { - validate_devices_number(vmm.mmio_device_manager.used_irqs_count())?; - } - - if data_version < FC_V1_1_SNAP_VERSION { - vmm.mmio_device_manager - .for_each_virtio_device(|virtio_type, _id, _info, dev| { - // Incompatibility between current version and all versions smaller than 1.0. - // Also, incompatibility between v1.1 and v1.0 for VirtIO net device - if dev - .lock() - .expect("Poisoned lock") - .has_feature(u64::from(VIRTIO_RING_F_EVENT_IDX)) - && (data_version < FC_V1_0_SNAP_VERSION || virtio_type == TYPE_NET) - { - return Err(CreateSnapshotError::IncompatibleVirtioFeature( - "notification suppression", - )); - } - Ok(()) - })?; - } - - Ok(data_version) -} - -/// Additional checks on snapshot version dependent on microvm saved state. -pub fn extra_version_check( - microvm_state: &MicrovmState, - version: u16, -) -> Result<(), CreateSnapshotError> { - // We forbid snapshots older than 1.5 if any additional capabilities are requested - if !microvm_state.vm_state.kvm_cap_modifiers.is_empty() && version < FC_V1_5_SNAP_VERSION { - return Err(CreateSnapshotError::UnsupportedVersion); - } - - // We forbid snapshots older then 1.5 if any additional vcpu features are requested - #[cfg(target_arch = "aarch64")] - if microvm_state.vcpu_states[0].kvi.is_some() && version < FC_V1_5_SNAP_VERSION { - return Err(CreateSnapshotError::UnsupportedVersion); - } - Ok(()) -} - /// Validates that snapshot CPU vendor matches the host CPU vendor. /// /// # Errors @@ -667,15 +575,6 @@ fn guest_memory_from_uffd( Ok((guest_memory, Some(uffd))) } -#[cfg(target_arch = "x86_64")] -fn validate_devices_number(device_number: usize) -> Result<(), CreateSnapshotError> { - use self::CreateSnapshotError::TooManyDevices; - if device_number > FC_V0_23_MAX_DEVICES as usize { - return Err(TooManyDevices(device_number)); - } - Ok(()) -} - #[cfg(test)] mod tests { use snapshot::Persist; @@ -689,7 +588,6 @@ mod tests { }; #[cfg(target_arch = "aarch64")] use crate::construct_kvm_mpidrs; - use crate::version_map::{FC_VERSION_TO_SNAP_VERSION, VERSION_MAP}; use crate::vmm_config::balloon::BalloonDeviceConfig; use crate::vmm_config::drive::CacheType; use crate::vmm_config::net::NetworkInterfaceConfig; @@ -778,10 +676,6 @@ mod tests { let mut buf = vec![0; 10000]; let mut version_map = VersionMap::new(); - assert!(microvm_state - .serialize(&mut buf.as_mut_slice(), &version_map, 1) - .is_err()); - version_map .new_version() .set_type_version(DeviceStates::type_id(), 2); @@ -799,32 +693,6 @@ mod tests { ) } - #[test] - fn test_get_snapshot_data_version() { - let vmm = default_vmm_with_devices(); - - assert_eq!( - VERSION_MAP.latest_version(), - get_snapshot_data_version(&None, &VERSION_MAP, &vmm).unwrap() - ); - - for version in FC_VERSION_TO_SNAP_VERSION.keys() { - let res = get_snapshot_data_version(&Some(version.clone()), &VERSION_MAP, &vmm); - - #[cfg(target_arch = "x86_64")] - assert!(res.is_ok()); - - #[cfg(target_arch = "aarch64")] - // Validate sanity checks fail because aarch64 does not support "0.23.0" - // snapshot target version. - if version == &Version::new(0, 23, 0) { - assert!(res.is_err()) - } else { - assert!(res.is_ok()) - } - } - } - #[test] fn test_create_snapshot_error_display() { use crate::persist::CreateSnapshotError::*; diff --git a/src/vmm/src/rpc_interface.rs b/src/vmm/src/rpc_interface.rs index d7ee989d4ff..67482f5d5ba 100644 --- a/src/vmm/src/rpc_interface.rs +++ b/src/vmm/src/rpc_interface.rs @@ -1797,7 +1797,6 @@ mod tests { snapshot_type: SnapshotType::Full, snapshot_path: PathBuf::new(), mem_file_path: PathBuf::new(), - version: None, }), VmmActionError::OperationNotSupportedPreBoot, ); diff --git a/src/vmm/src/vmm_config/snapshot.rs b/src/vmm/src/vmm_config/snapshot.rs index 28b111b9f7d..b79cc6374c5 100644 --- a/src/vmm/src/vmm_config/snapshot.rs +++ b/src/vmm/src/vmm_config/snapshot.rs @@ -45,9 +45,6 @@ pub struct CreateSnapshotParams { pub snapshot_path: PathBuf, /// Path to the file that will contain the guest memory. pub mem_file_path: PathBuf, - /// Optional field for the microVM version. The default - /// value is the current version. - pub version: Option, } /// Stores the configuration that will be used for loading a snapshot. diff --git a/src/vmm/src/vstate/vcpu/aarch64.rs b/src/vmm/src/vstate/vcpu/aarch64.rs index 2364292b7e1..fa9f3d5c0a3 100644 --- a/src/vmm/src/vstate/vcpu/aarch64.rs +++ b/src/vmm/src/vstate/vcpu/aarch64.rs @@ -258,7 +258,7 @@ pub struct VcpuState { #[version(end = 2, default_fn = "default_old_regs")] pub old_regs: Vec, /// Vcpu registers. - #[version(start = 2, de_fn = "de_regs", ser_fn = "ser_regs")] + #[version(start = 2, de_fn = "de_regs")] pub regs: Aarch64RegisterVec, /// We will be using the mpidr for passing it to the VmState. /// The VmState will give this away for saving restoring the icc and redistributor @@ -291,16 +291,6 @@ impl VcpuState { self.regs = regs; Ok(()) } - - fn ser_regs(&mut self, _target_version: u16) -> VersionizeResult<()> { - self.old_regs = self - .regs - .iter() - .map(TryInto::try_into) - .collect::>() - .map_err(|e: &str| VersionizeError::Serialize(e.into()))?; - Ok(()) - } } #[cfg(test)] diff --git a/src/vmm/src/vstate/vcpu/x86_64.rs b/src/vmm/src/vstate/vcpu/x86_64.rs index ceff91d6220..60fff47daa9 100644 --- a/src/vmm/src/vstate/vcpu/x86_64.rs +++ b/src/vmm/src/vstate/vcpu/x86_64.rs @@ -13,7 +13,7 @@ use kvm_bindings::{ }; use kvm_ioctls::{VcpuExit, VcpuFd}; use log::{error, warn}; -use versionize::{VersionMap, Versionize, VersionizeError, VersionizeResult}; +use versionize::{VersionMap, Versionize, VersionizeResult}; use versionize_derive::Versionize; use crate::arch::x86_64::interrupts; @@ -550,7 +550,7 @@ pub struct VcpuState { #[version(end = 3, default_fn = "default_msrs")] pub msrs: Msrs, /// Saved msrs. - #[version(start = 3, de_fn = "de_saved_msrs", ser_fn = "ser_saved_msrs")] + #[version(start = 3, de_fn = "de_saved_msrs")] pub saved_msrs: Vec, /// Debug regs. pub debug_regs: kvm_debugregs, @@ -569,7 +569,7 @@ pub struct VcpuState { /// Xsave. pub xsave: kvm_xsave, /// Tsc khz. - #[version(start = 2, default_fn = "default_tsc_khz", ser_fn = "ser_tsc")] + #[version(start = 2, default_fn = "default_tsc_khz")] pub tsc_khz: Option, } @@ -579,18 +579,6 @@ impl VcpuState { None } - fn ser_tsc(&mut self, _target_version: u16) -> VersionizeResult<()> { - // v0.24 and older versions do not support TSC scaling. - warn!( - "Saving to older snapshot version, TSC freq {}", - self.tsc_khz - .map(|freq| freq.to_string() + "KHz not included in snapshot.") - .unwrap_or_else(|| "not available.".to_string()) - ); - - Ok(()) - } - fn default_msrs(_source_version: u16) -> Msrs { // Safe to unwrap since Msrs::new() only returns an error if the number // of elements exceeds KVM_MAX_MSR_ENTRIES @@ -603,30 +591,6 @@ impl VcpuState { } Ok(()) } - - fn ser_saved_msrs(&mut self, target_version: u16) -> VersionizeResult<()> { - match self.saved_msrs.len() { - 0 => Err(VersionizeError::Serialize( - "Cannot serialize MSRs because the MSR list is empty".to_string(), - )), - 1 => { - if target_version < 3 { - self.msrs = self.saved_msrs[0].clone(); - Ok(()) - } else { - Err(VersionizeError::Serialize(format!( - "Cannot serialize MSRs to target version {}", - target_version - ))) - } - } - _ => Err(VersionizeError::Serialize( - "Cannot serialize MSRs. The uVM state needs to save - more MSRs than the target snapshot version supports." - .to_string(), - )), - } - } } #[cfg(test)] diff --git a/src/vmm/tests/integration_tests.rs b/src/vmm/tests/integration_tests.rs index 06c7c036c8a..f2097431b9d 100644 --- a/src/vmm/tests/integration_tests.rs +++ b/src/vmm/tests/integration_tests.rs @@ -17,7 +17,7 @@ use vmm::utilities::test_utils::dirty_tracking_vmm; use vmm::utilities::test_utils::{create_vmm, default_vmm, default_vmm_no_boot}; use vmm::version_map::VERSION_MAP; use vmm::vmm_config::instance_info::{InstanceInfo, VmState}; -use vmm::vmm_config::snapshot::{CreateSnapshotParams, SnapshotType, Version}; +use vmm::vmm_config::snapshot::{CreateSnapshotParams, SnapshotType}; use vmm::{DumpCpuConfigError, EventManager, FcExitCode}; #[test] @@ -189,7 +189,6 @@ fn verify_create_snapshot(is_diff: bool) -> (TempFile, TempFile) { snapshot_type, snapshot_path: snapshot_file.as_path().to_path_buf(), mem_file_path: memory_file.as_path().to_path_buf(), - version: Some(Version::new(0, 24, 0)), }; let vm_info = VmInfo { mem_size_mib: 1u64, diff --git a/tests/framework/artifacts.py b/tests/framework/artifacts.py index ff9c059af57..358c63b6124 100644 --- a/tests/framework/artifacts.py +++ b/tests/framework/artifacts.py @@ -125,6 +125,14 @@ def current_release(version): return binaries +def working_version_as_artifact(): + """ + Return working copy of Firecracker as a release artifact + """ + cargo_version = get_firecracker_version_from_toml() + return FirecrackerArtifact(current_release(cargo_version.base_version)[0]) + + def firecracker_artifacts(): """Return all supported firecracker binaries.""" cargo_version = get_firecracker_version_from_toml() @@ -141,7 +149,7 @@ def firecracker_artifacts(): continue yield pytest.param(fc, id=fc.name) - fc = FirecrackerArtifact(current_release(cargo_version.base_version)[0]) + fc = working_version_as_artifact() yield pytest.param(fc, id=fc.name) diff --git a/tests/framework/microvm.py b/tests/framework/microvm.py index a65b12c02d3..c8d030ccaaa 100644 --- a/tests/framework/microvm.py +++ b/tests/framework/microvm.py @@ -710,9 +710,7 @@ def resume(self): """Resume the microVM""" self.api.vm.patch(state="Resumed") - def make_snapshot( - self, snapshot_type: SnapshotType | str, target_version: str = None - ): + def make_snapshot(self, snapshot_type: SnapshotType | str): """Create a Snapshot object from a microvm. It pauses the microvm before taking the snapshot. @@ -725,7 +723,6 @@ def make_snapshot( mem_file_path=str(mem_path), snapshot_path=str(vmstate_path), snapshot_type=snapshot_type.value, - version=target_version, ) root = Path(self.chroot()) return Snapshot( @@ -737,13 +734,13 @@ def make_snapshot( snapshot_type=snapshot_type, ) - def snapshot_diff(self, target_version: str = None): + def snapshot_diff(self): """Make a Diff snapshot""" - return self.make_snapshot("Diff", target_version) + return self.make_snapshot("Diff") - def snapshot_full(self, target_version: str = None): + def snapshot_full(self): """Make a Full snapshot""" - return self.make_snapshot("Full", target_version) + return self.make_snapshot("Full") def restore_from_snapshot( self, diff --git a/tests/integration_tests/functional/test_api.py b/tests/integration_tests/functional/test_api.py index bd74610a91e..a61a1645901 100644 --- a/tests/integration_tests/functional/test_api.py +++ b/tests/integration_tests/functional/test_api.py @@ -1291,28 +1291,6 @@ def test_map_private_seccomp_regression(test_microvm_with_api): test_microvm.api.mmds.put(**data_store) -def test_negative_snapshot_create_api(microvm_factory, guest_kernel, rootfs): - """ - Test snapshot create API. - """ - - vm = microvm_factory.build(guest_kernel, rootfs) - vm.spawn() - vm.basic_config() - vm.start() - - # Specifying `version` in the create API is deprecated - vm.pause() - response = vm.api.snapshot_create.put( - mem_file_path="mem", - snapshot_path="vmstate", - snapshot_type="Full", - version="1.4.0", - ) - assert response.headers["deprecation"] - assert "PUT /snapshot/create: 'version' field is deprecated." in vm.log_data - - # pylint: disable=protected-access def test_negative_snapshot_load_api(microvm_factory): """ diff --git a/tests/integration_tests/functional/test_drives.py b/tests/integration_tests/functional/test_drives.py index a9f0d49d112..daa8fbf4bd6 100644 --- a/tests/integration_tests/functional/test_drives.py +++ b/tests/integration_tests/functional/test_drives.py @@ -361,46 +361,6 @@ def test_flush(uvm_plain_rw): assert fc_metrics["block"]["flush_count"] > 0 -def test_block_default_cache_old_version(test_microvm_with_api): - """ - Verify that saving a snapshot for old versions works correctly. - """ - test_microvm = test_microvm_with_api - test_microvm.spawn() - - test_microvm.basic_config(vcpu_count=1, add_root_device=False) - - # Add the block device with explicitly enabling flush. - test_microvm.add_drive( - "rootfs", - test_microvm.rootfs_file, - is_root_device=True, - cache_type="Writeback", - ) - - test_microvm.start() - - # Pause the VM to create the snapshot. - test_microvm.pause() - - # Create the snapshot for a version without block cache type. - test_microvm.api.snapshot_create.put( - mem_file_path="memfile", - snapshot_path="snapsfile", - snapshot_type="Full", - version="0.24.0", - ) - - # We should find a warning in the logs for this case as this - # cache type was not supported in 0.24.0 and we should default - # to "Unsafe" mode. - test_microvm.check_log_message( - "Target version does not implement the" - " current cache type. " - 'Defaulting to "unsafe" mode.' - ) - - def _check_block_size(ssh_connection, dev_path, size): _, stdout, stderr = ssh_connection.run("blockdev --getsize64 {}".format(dev_path)) assert stderr == "" diff --git a/tests/integration_tests/functional/test_mmds.py b/tests/integration_tests/functional/test_mmds.py index c6ccfdccb78..28627851fce 100644 --- a/tests/integration_tests/functional/test_mmds.py +++ b/tests/integration_tests/functional/test_mmds.py @@ -9,6 +9,7 @@ import pytest +from framework.artifacts import working_version_as_artifact from framework.properties import global_props from framework.utils import ( configure_mmds, @@ -33,7 +34,6 @@ def _validate_mmds_snapshot( basevm, microvm_factory, version, - target_fc_version=None, fc_binary_path=None, jailer_binary_path=None, ): @@ -76,7 +76,7 @@ def _validate_mmds_snapshot( run_guest_cmd(ssh_connection, cmd, data_store, use_json=True) # Create snapshot. - snapshot = basevm.snapshot_full(target_version=target_fc_version) + snapshot = basevm.snapshot_full() # Resume microVM and ensure session token is still valid on the base. response = basevm.resume() @@ -591,7 +591,7 @@ def test_mmds_limit_scenario(test_microvm_with_api, version): @pytest.mark.parametrize("version", MMDS_VERSIONS) -def test_mmds_snapshot(uvm_nano, microvm_factory, version, firecracker_release): +def test_mmds_snapshot(uvm_nano, microvm_factory, version): """ Test MMDS behavior by restoring a snapshot on current FC versions. @@ -599,14 +599,14 @@ def test_mmds_snapshot(uvm_nano, microvm_factory, version, firecracker_release): the firecracker version does not support it. """ + current_release = working_version_as_artifact() uvm_nano.add_net_iface() _validate_mmds_snapshot( uvm_nano, microvm_factory, version, - target_fc_version=firecracker_release.snapshot_version, - fc_binary_path=firecracker_release.path, - jailer_binary_path=firecracker_release.jailer, + fc_binary_path=current_release.path, + jailer_binary_path=current_release.jailer, ) @@ -640,7 +640,6 @@ def test_mmds_older_snapshot( microvm, microvm_factory, mmds_version, - firecracker_release.snapshot_version, *get_firecracker_binaries(), ) diff --git a/tests/integration_tests/functional/test_snapshot_advanced.py b/tests/integration_tests/functional/test_snapshot_advanced.py index bd5e45cc3c8..2bd26cd56c3 100644 --- a/tests/integration_tests/functional/test_snapshot_advanced.py +++ b/tests/integration_tests/functional/test_snapshot_advanced.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 """Advanced tests scenarios for snapshot save/restore.""" -import platform import tempfile import pytest @@ -55,51 +54,6 @@ def test_restore_old_to_current( print(vm.log_data) -def test_restore_current_to_old(microvm_factory, uvm_plain, firecracker_release): - """ - Restore current snapshot with previous versions of Firecracker. - - For each firecracker release: - 1. Snapshot with the current build - 2. Restore with the past release - """ - - # Microvm: 2vCPU 256MB RAM, balloon, 4 disks and 4 net devices. - vm = uvm_plain - vm.spawn() - vm.basic_config(track_dirty_pages=True) - - # Create a snapshot with current FC version targeting the old version. - snapshot = create_snapshot_helper( - vm, - target_version=firecracker_release.snapshot_version, - drives=scratch_drives, - balloon=True, - diff_snapshots=True, - ) - - # Resume microvm using FC/Jailer binary artifacts. - vm = microvm_factory.build( - fc_binary_path=firecracker_release.path, - jailer_binary_path=firecracker_release.jailer, - ) - vm.spawn() - vm.restore_from_snapshot(snapshot, resume=True) - validate_all_devices(vm, True) - print("========== Firecracker restore snapshot log ==========") - print(vm.log_data) - - -@pytest.mark.skipif(platform.machine() != "x86_64", reason="TSC is x86_64 specific.") -def test_save_tsc_old_version(uvm_nano): - """ - Test TSC warning message when saving old snapshot. - """ - uvm_nano.start() - uvm_nano.snapshot_full(target_version="0.24.0") - uvm_nano.check_log_message("Saving to older snapshot version, TSC freq") - - def validate_all_devices(microvm, balloon): """Perform a basic validation for all devices of a microvm.""" # Test that net devices have connectivity after restore. @@ -139,7 +93,6 @@ def validate_all_devices(microvm, balloon): def create_snapshot_helper( vm, - target_version=None, drives=None, balloon=False, diff_snapshots=False, @@ -196,7 +149,7 @@ def create_snapshot_helper( exit_code, _, _ = vm.ssh.run(cmd) assert exit_code == 0 - snapshot = vm.make_snapshot(snapshot_type, target_version=target_version) + snapshot = vm.make_snapshot(snapshot_type) print("========== Firecracker create snapshot log ==========") print(vm.log_data) vm.kill() diff --git a/tests/integration_tests/functional/test_snapshot_basic.py b/tests/integration_tests/functional/test_snapshot_basic.py index 14da9f259a6..876fd74a82c 100644 --- a/tests/integration_tests/functional/test_snapshot_basic.py +++ b/tests/integration_tests/functional/test_snapshot_basic.py @@ -12,7 +12,12 @@ import host_tools.drive as drive_tools from framework.microvm import SnapshotType -from framework.utils import check_filesystem, wait_process_termination +from framework.utils import ( + check_filesystem, + get_firecracker_version_from_toml, + run_cmd, + wait_process_termination, +) from framework.utils_vsock import ( ECHO_SERVER_PORT, VSOCK_UDS_PATH, @@ -23,6 +28,7 @@ make_host_port_path, start_guest_echo_server, ) +from host_tools.cargo_build import get_firecracker_binaries def _get_guest_drive_size(ssh_connection, guest_dev_name="/dev/vdb"): @@ -35,6 +41,32 @@ def _get_guest_drive_size(ssh_connection, guest_dev_name="/dev/vdb"): return lines[1].strip() +def test_snapshot_current_version(uvm_nano): + """Tests taking a snapshot at the version specified in Cargo.toml + + Check that it is possible to take a snapshot at the version of the upcoming + release (during the release process this ensures that if we release version + x.y, then taking a snapshot at version x.y works - something we'd otherwise + only be able to test once the x.y binary has been uploaded to S3, at which + point it is too late, see also the 1.3 release). + """ + vm = uvm_nano + vm.start() + + version = get_firecracker_version_from_toml() + # normalize to a snapshot version + version = f"{version.major}.{version.minor}.0" + snapshot = vm.snapshot_full() + + # Fetch Firecracker binary for the latest version + fc_binary, _ = get_firecracker_binaries() + # Verify the output of `--describe-snapshot` command line parameter + cmd = [str(fc_binary)] + ["--describe-snapshot", str(snapshot.vmstate)] + + _, stdout, _ = run_cmd(cmd) + assert version in stdout + + # Testing matrix: # - Guest kernel: All supported ones # - Rootfs: Ubuntu 18.04 diff --git a/tests/integration_tests/functional/test_snapshot_version.py b/tests/integration_tests/functional/test_snapshot_version.py deleted file mode 100644 index d5a92a51b73..00000000000 --- a/tests/integration_tests/functional/test_snapshot_version.py +++ /dev/null @@ -1,200 +0,0 @@ -# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 -"""Basic tests scenarios for snapshot save/restore.""" - -import json -import platform - -import pytest - -from framework.utils import get_firecracker_version_from_toml, run_cmd -from host_tools.cargo_build import get_firecracker_binaries - -# Firecracker v0.23 used 16 IRQ lines. For virtio devices, -# IRQs are available from 5 to 23, so the maximum number -# of devices allowed at the same time was 11. -FC_V0_23_MAX_DEVICES_ATTACHED = 11 - - -def _create_and_start_microvm_with_net_devices(test_microvm, devices_no=0): - test_microvm.spawn() - # Set up a basic microVM: configure the boot source and - # add a root device. - test_microvm.basic_config(track_dirty_pages=True) - - # Add network devices on top of the already configured rootfs for a - # total of (`devices_no` + 1) devices. - for _ in range(devices_no): - # Create tap before configuring interface. - test_microvm.add_net_iface() - - test_microvm.start() - - if devices_no > 0: - # Verify if guest can run commands. - exit_code, _, _ = test_microvm.ssh.run("sync") - assert exit_code == 0 - - -@pytest.mark.skipif( - platform.machine() != "x86_64", reason="Exercises specific x86_64 functionality." -) -def test_create_with_too_many_devices(test_microvm_with_api): - """ - Create snapshot with unexpected device count for previous versions. - """ - test_microvm = test_microvm_with_api - - # Create and start a microVM with `FC_V0_23_MAX_DEVICES_ATTACHED` - # network devices. - devices_no = FC_V0_23_MAX_DEVICES_ATTACHED - _create_and_start_microvm_with_net_devices(test_microvm, devices_no) - - # Pause microVM for snapshot. - test_microvm.pause() - - # Attempt to create a snapshot with version: `0.23.0`. Firecracker - # v0.23 allowed a maximum of `FC_V0_23_MAX_DEVICES_ATTACHED` virtio - # devices at a time. This microVM has `FC_V0_23_MAX_DEVICES_ATTACHED` - # network devices on top of the rootfs, so the limit is exceeded. - with pytest.raises(RuntimeError, match="Too many devices attached"): - test_microvm.api.snapshot_create.put( - mem_file_path="/vm.mem", - snapshot_path="/vm.vmstate", - snapshot_type="Diff", - version="0.23.0", - ) - - -def test_create_invalid_version(uvm_nano): - """ - Test scenario: create snapshot targeting invalid version. - """ - # Use a predefined vm instance. - test_microvm = uvm_nano - test_microvm.start() - - # Target an invalid Firecracker version string. - with pytest.raises(RuntimeError, match="unexpected character 'i'"): - test_microvm.api.snapshot_create.put( - mem_file_path="/vm.mem", - snapshot_path="/vm.vmstate", - snapshot_type="Full", - version="invalid", - ) - - # Target a valid version string but with no snapshot support. - with pytest.raises( - RuntimeError, match="Cannot translate microVM version to snapshot data version" - ): - test_microvm.api.snapshot_create.put( - mem_file_path="/vm.mem", - snapshot_path="/vm.vmstate", - snapshot_type="Full", - version="0.22.0", - ) - - -def test_snapshot_current_version(uvm_nano): - """Tests taking a snapshot at the version specified in Cargo.toml - - Check that it is possible to take a snapshot at the version of the upcoming - release (during the release process this ensures that if we release version - x.y, then taking a snapshot at version x.y works - something we'd otherwise - only be able to test once the x.y binary has been uploaded to S3, at which - point it is too late, see also the 1.3 release). - """ - vm = uvm_nano - vm.start() - - version = get_firecracker_version_from_toml() - # normalize to a snapshot version - target_version = f"{version.major}.{version.minor}.0" - snapshot = vm.snapshot_full(target_version=target_version) - - # Fetch Firecracker binary for the latest version - fc_binary, _ = get_firecracker_binaries() - # Verify the output of `--describe-snapshot` command line parameter - cmd = [str(fc_binary)] + ["--describe-snapshot", str(snapshot.vmstate)] - - code, stdout, stderr = run_cmd(cmd) - assert code == 0, stderr - assert stderr == "" - assert target_version in stdout - - -def test_create_with_newer_virtio_features(uvm_nano): - """ - Attempt to create a snapshot with newer virtio features. - """ - test_microvm = uvm_nano - test_microvm.add_net_iface() - test_microvm.start() - - # Init a ssh connection in order to wait for the VM to boot. This way - # we can be sure that the block device was activated. - test_microvm.ssh.run("true") - - # Pause microVM for snapshot. - test_microvm.pause() - - # We try to create a snapshot to a target version < 1.0.0. - # This should fail because Fc versions < 1.0.0 don't support - # virtio notification suppression. - target_fc_versions = ["0.24.0", "0.25.0"] - if platform.machine() == "x86_64": - target_fc_versions.insert(0, "0.23.0") - - expected_msg = ( - "The virtio devices use a features that is incompatible " - "with older versions of Firecracker: notification suppression" - ) - for target_fc_version in target_fc_versions: - with pytest.raises(RuntimeError, match=expected_msg): - test_microvm.api.snapshot_create.put( - mem_file_path="/vm.mem", - snapshot_path="/vm.vmstate", - version=target_fc_version, - ) - - # We try to create a snapshot for target version 1.0.0. This should - # fail because in 1.0.0 we do not support notification suppression for Net. - with pytest.raises(RuntimeError, match=expected_msg): - test_microvm.api.snapshot_create.put( - mem_file_path="/vm.mem", - snapshot_path="/vm.vmstate", - version="1.0.0", - ) - - # It should work when we target a version >= 1.1.0 - test_microvm.api.snapshot_create.put( - mem_file_path="/vm.mem", - snapshot_path="/vm.vmstate", - version="1.1.0", - ) - - -def test_create_with_1_5_cpu_template(uvm_plain): - """ - Verifies that we can't create a snapshot with target version - less than 1.5 if cpu template with additional vcpu features or - kvm capabilities is in use. - """ - - # We remove KVM_CAP_IOEVENTFD from kvm checks just for testing purpose. - custom_cpu_template = json.loads('{"kvm_capabilities": ["!36"]}') - - test_microvm = uvm_plain - test_microvm.spawn() - test_microvm.basic_config(vcpu_count=2, mem_size_mib=256) - test_microvm.api.cpu_config.put(**custom_cpu_template) - test_microvm.start() - - # Should fail because target version is less than 1.5 - with pytest.raises( - RuntimeError, match="Cannot translate microVM version to snapshot data version" - ): - test_microvm.snapshot_full(target_version="1.4.0") - - # Should pass because target version is >=1.5 - test_microvm.snapshot_full() diff --git a/tests/integration_tests/performance/test_snapshot_perf.py b/tests/integration_tests/performance/test_snapshot_perf.py index adaa6df43bf..cd77e37bc4a 100644 --- a/tests/integration_tests/performance/test_snapshot_perf.py +++ b/tests/integration_tests/performance/test_snapshot_perf.py @@ -31,9 +31,9 @@ ) -def snapshot_create_producer(vm, target_version): +def snapshot_create_producer(vm): """Produce results for snapshot create tests.""" - vm.snapshot_full(target_version=target_version) + vm.snapshot_full() metrics = vm.flush_metrics() value = metrics["latencies_us"]["full_create_snapshot"] / USEC_IN_MSEC @@ -178,7 +178,6 @@ def test_snapshot_create_latency( func=snapshot_create_producer, func_kwargs={ "vm": vm, - "target_version": None, }, )