Skip to content

Commit

Permalink
Tests for snapshot network renames
Browse files Browse the repository at this point in the history
Test that we can correctly parse configuration and API calls in a
backwards compatible way.

Signed-off-by: Andrew Laucius <andrewla@gmail.com>
  • Loading branch information
andrewla committed Jan 10, 2025
1 parent b834eeb commit 3710891
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 2 deletions.
43 changes: 42 additions & 1 deletion src/firecracker/src/api_server/request/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ fn parse_put_snapshot_load(body: &Body) -> Result<ParsedRequest, RequestError> {

#[cfg(test)]
mod tests {
use vmm::vmm_config::snapshot::{MemBackendConfig, MemBackendType};
use vmm::vmm_config::snapshot::{MemBackendConfig, MemBackendType, NetworkOverride};

use super::*;
use crate::api_server::parsed_request::tests::{depr_action_from_req, vmm_action_from_request};
Expand Down Expand Up @@ -182,6 +182,7 @@ mod tests {
},
enable_diff_snapshots: false,
resume_vm: false,
network_overrides: vec![],
};
let mut parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap();
assert!(parsed_request
Expand Down Expand Up @@ -209,6 +210,7 @@ mod tests {
},
enable_diff_snapshots: true,
resume_vm: false,
network_overrides: vec![],
};
let mut parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap();
assert!(parsed_request
Expand Down Expand Up @@ -236,6 +238,44 @@ mod tests {
},
enable_diff_snapshots: false,
resume_vm: true,
network_overrides: vec![],
};
let mut parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap();
assert!(parsed_request
.parsing_info()
.take_deprecation_message()
.is_none());
assert_eq!(
vmm_action_from_request(parsed_request),
VmmAction::LoadSnapshot(expected_config)
);

let body = r#"{
"snapshot_path": "foo",
"mem_backend": {
"backend_path": "bar",
"backend_type": "Uffd"
},
"resume_vm": true,
"network_overrides": [
{
"iface_id": "eth0",
"host_dev_name": "vmtap2"
}
]
}"#;
let expected_config = LoadSnapshotParams {
snapshot_path: PathBuf::from("foo"),
mem_backend: MemBackendConfig {
backend_path: PathBuf::from("bar"),
backend_type: MemBackendType::Uffd,
},
enable_diff_snapshots: false,
resume_vm: true,
network_overrides: vec![NetworkOverride {
iface_id: String::from("eth0"),
host_dev_name: String::from("vmtap2"),
}],
};
let mut parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap();
assert!(parsed_request
Expand All @@ -260,6 +300,7 @@ mod tests {
},
enable_diff_snapshots: false,
resume_vm: true,
network_overrides: vec![],
};
let parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap();
assert_eq!(
Expand Down
5 changes: 4 additions & 1 deletion src/vmm/src/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,10 @@ pub fn restore_from_snapshot(
.iter_mut()
.find(|x| x.device_state.id == entry.iface_id)
{
device.device_state.tap_if_name = entry.host_dev_name.clone();
device
.device_state
.tap_if_name
.clone_from(&entry.host_dev_name);
} else {
return Err(SnapshotStateFromFileError::UnknownNetworkDevice.into());
}
Expand Down
9 changes: 9 additions & 0 deletions tests/framework/microvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,7 @@ def restore_from_snapshot(
snapshot: Snapshot,
resume: bool = False,
uffd_path: Path = None,
rename_interfaces: dict = None,
):
"""Restore a snapshot"""
jailed_snapshot = snapshot.copy_to_chroot(Path(self.chroot()))
Expand Down Expand Up @@ -996,11 +997,19 @@ def restore_from_snapshot(
# Adjust things just in case
self.kernel_file = Path(self.kernel_file)

iface_overrides = []
if rename_interfaces is not None:
iface_overrides = [
{"iface_id": k, "host_dev_name": v}
for k, v in rename_interfaces.items()
]

self.api.snapshot_load.put(
mem_backend=mem_backend,
snapshot_path=str(jailed_vmstate),
enable_diff_snapshots=snapshot.is_diff,
resume_vm=resume,
network_overrides=iface_overrides,
)
# This is not a "wait for boot", but rather a "VM still works after restoration"
if snapshot.net_ifaces and resume:
Expand Down
34 changes: 34 additions & 0 deletions tests/integration_tests/functional/test_snapshot_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# SPDX-License-Identifier: Apache-2.0
"""Basic tests scenarios for snapshot save/restore."""

import dataclasses
import filecmp
import logging
import os
Expand All @@ -13,6 +14,7 @@
import pytest

import host_tools.drive as drive_tools
import host_tools.network as net_tools
from framework.microvm import SnapshotType
from framework.utils import check_filesystem, check_output
from framework.utils_vsock import (
Expand Down Expand Up @@ -540,3 +542,35 @@ def test_vmgenid(guest_kernel_linux_6_1, rootfs, microvm_factory, snapshot_type)

# Update the base for next iteration
base_snapshot = snapshot


def test_snapshot_rename_interface(uvm_nano, microvm_factory):
"""
Test that we can restore a snapshot and point its interface to a
different host interface.
"""
base_iface = net_tools.NetIfaceConfig.with_id(0)

vm = uvm_nano
iface1 = dataclasses.replace(base_iface, tap_name="tap1")
vm.add_net_iface(iface=iface1)
# Create an interface but don't attach it to the device
vm.start()

snapshot = vm.snapshot_full()

restored_vm = microvm_factory.build()
restored_vm.spawn()
iface2 = dataclasses.replace(base_iface, tap_name="tap2")

# The snapshot.net_faces is used by the test framework to create the
# appropriate tap devices on the host; we replace those here with the new
# name, otherwise the framework would create `tap1` when restoring the
# snapshot
snapshot.net_ifaces.clear()
snapshot.net_ifaces.append(iface2)

restored_vm.restore_from_snapshot(
snapshot, rename_interfaces={base_iface.dev_name: "tap2"}
)
restored_vm.resume()

0 comments on commit 3710891

Please sign in to comment.