Skip to content

Commit ad4ea19

Browse files
committed
Tests for snapshot network renames
Test that we can correctly parse configuration and API calls in a backwards compatible way. Signed-off-by: Andrew Laucius <[email protected]>
1 parent c406f60 commit ad4ea19

File tree

5 files changed

+91
-2
lines changed

5 files changed

+91
-2
lines changed

src/firecracker/src/api_server/request/snapshot.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ fn parse_put_snapshot_load(body: &Body) -> Result<ParsedRequest, RequestError> {
121121

122122
#[cfg(test)]
123123
mod tests {
124-
use vmm::vmm_config::snapshot::{MemBackendConfig, MemBackendType};
124+
use vmm::vmm_config::snapshot::{MemBackendConfig, MemBackendType, NetworkOverride};
125125

126126
use super::*;
127127
use crate::api_server::parsed_request::tests::{depr_action_from_req, vmm_action_from_request};
@@ -182,6 +182,7 @@ mod tests {
182182
},
183183
enable_diff_snapshots: false,
184184
resume_vm: false,
185+
network_overrides: vec![],
185186
};
186187
let mut parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap();
187188
assert!(parsed_request
@@ -209,6 +210,7 @@ mod tests {
209210
},
210211
enable_diff_snapshots: true,
211212
resume_vm: false,
213+
network_overrides: vec![],
212214
};
213215
let mut parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap();
214216
assert!(parsed_request
@@ -236,6 +238,44 @@ mod tests {
236238
},
237239
enable_diff_snapshots: false,
238240
resume_vm: true,
241+
network_overrides: vec![],
242+
};
243+
let mut parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap();
244+
assert!(parsed_request
245+
.parsing_info()
246+
.take_deprecation_message()
247+
.is_none());
248+
assert_eq!(
249+
vmm_action_from_request(parsed_request),
250+
VmmAction::LoadSnapshot(expected_config)
251+
);
252+
253+
let body = r#"{
254+
"snapshot_path": "foo",
255+
"mem_backend": {
256+
"backend_path": "bar",
257+
"backend_type": "Uffd"
258+
},
259+
"resume_vm": true,
260+
"network_overrides": [
261+
{
262+
"iface_id": "eth0",
263+
"host_dev_name": "vmtap2"
264+
}
265+
]
266+
}"#;
267+
let expected_config = LoadSnapshotParams {
268+
snapshot_path: PathBuf::from("foo"),
269+
mem_backend: MemBackendConfig {
270+
backend_path: PathBuf::from("bar"),
271+
backend_type: MemBackendType::Uffd,
272+
},
273+
enable_diff_snapshots: false,
274+
resume_vm: true,
275+
network_overrides: vec![NetworkOverride {
276+
iface_id: String::from("eth0"),
277+
host_dev_name: String::from("vmtap2"),
278+
}],
239279
};
240280
let mut parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap();
241281
assert!(parsed_request
@@ -260,6 +300,7 @@ mod tests {
260300
},
261301
enable_diff_snapshots: false,
262302
resume_vm: true,
303+
network_overrides: vec![],
263304
};
264305
let parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap();
265306
assert_eq!(

src/vmm/src/persist.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,10 @@ pub fn restore_from_snapshot(
425425
.iter_mut()
426426
.find(|x| x.device_state.id == entry.iface_id)
427427
{
428-
device.device_state.tap_if_name = entry.host_dev_name.clone();
428+
device
429+
.device_state
430+
.tap_if_name
431+
.clone_from(&entry.host_dev_name);
429432
} else {
430433
return Err(SnapshotStateFromFileError::UnknownNetworkDevice.into());
431434
}

src/vmm/tests/integration_tests.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ fn verify_load_snapshot(snapshot_file: TempFile, memory_file: TempFile) {
261261
},
262262
enable_diff_snapshots: false,
263263
resume_vm: true,
264+
network_overrides: vec![],
264265
}))
265266
.unwrap();
266267

@@ -344,6 +345,7 @@ fn verify_load_snap_disallowed_after_boot_resources(res: VmmAction, res_name: &s
344345
},
345346
enable_diff_snapshots: false,
346347
resume_vm: false,
348+
network_overrides: vec![],
347349
});
348350
let err = preboot_api_controller.handle_preboot_request(req);
349351
assert!(

tests/framework/microvm.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,7 @@ def restore_from_snapshot(
972972
snapshot: Snapshot,
973973
resume: bool = False,
974974
uffd_path: Path = None,
975+
rename_interfaces: dict = None,
975976
):
976977
"""Restore a snapshot"""
977978
jailed_snapshot = snapshot.copy_to_chroot(Path(self.chroot()))
@@ -999,11 +1000,19 @@ def restore_from_snapshot(
9991000
# Adjust things just in case
10001001
self.kernel_file = Path(self.kernel_file)
10011002

1003+
iface_overrides = []
1004+
if rename_interfaces is not None:
1005+
iface_overrides = [
1006+
{"iface_id": k, "host_dev_name": v}
1007+
for k, v in rename_interfaces.items()
1008+
]
1009+
10021010
self.api.snapshot_load.put(
10031011
mem_backend=mem_backend,
10041012
snapshot_path=str(jailed_vmstate),
10051013
enable_diff_snapshots=snapshot.is_diff,
10061014
resume_vm=resume,
1015+
network_overrides=iface_overrides,
10071016
)
10081017
# This is not a "wait for boot", but rather a "VM still works after restoration"
10091018
if snapshot.net_ifaces and resume:

tests/integration_tests/functional/test_snapshot_basic.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# SPDX-License-Identifier: Apache-2.0
33
"""Basic tests scenarios for snapshot save/restore."""
44

5+
import dataclasses
56
import filecmp
67
import logging
78
import os
@@ -16,6 +17,7 @@
1617
import host_tools.cargo_build as host
1718
import host_tools.drive as drive_tools
1819
from framework import utils
20+
import host_tools.network as net_tools
1921
from framework.microvm import SnapshotType
2022
from framework.properties import global_props
2123
from framework.utils import check_filesystem, check_output
@@ -597,3 +599,35 @@ def test_physical_couter_reset_aarch64(uvm_nano):
597599
reg_id, reg_value = parts
598600
if reg_id == cntpct_el0:
599601
assert int(reg_value, 16) < max_value
602+
603+
604+
def test_snapshot_rename_interface(uvm_nano, microvm_factory):
605+
"""
606+
Test that we can restore a snapshot and point its interface to a
607+
different host interface.
608+
"""
609+
base_iface = net_tools.NetIfaceConfig.with_id(0)
610+
611+
vm = uvm_nano
612+
iface1 = dataclasses.replace(base_iface, tap_name="tap1")
613+
vm.add_net_iface(iface=iface1)
614+
# Create an interface but don't attach it to the device
615+
vm.start()
616+
617+
snapshot = vm.snapshot_full()
618+
619+
restored_vm = microvm_factory.build()
620+
restored_vm.spawn()
621+
iface2 = dataclasses.replace(base_iface, tap_name="tap2")
622+
623+
# The snapshot.net_faces is used by the test framework to create the
624+
# appropriate tap devices on the host; we replace those here with the new
625+
# name, otherwise the framework would create `tap1` when restoring the
626+
# snapshot
627+
snapshot.net_ifaces.clear()
628+
snapshot.net_ifaces.append(iface2)
629+
630+
restored_vm.restore_from_snapshot(
631+
snapshot, rename_interfaces={base_iface.dev_name: "tap2"}
632+
)
633+
restored_vm.resume()

0 commit comments

Comments
 (0)