Skip to content

Commit 0cfc2ed

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 7ef760c commit 0cfc2ed

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
@@ -422,7 +422,10 @@ pub fn restore_from_snapshot(
422422
.iter_mut()
423423
.find(|x| x.device_state.id == entry.iface_id)
424424
{
425-
device.device_state.tap_if_name = entry.host_dev_name.clone();
425+
device
426+
.device_state
427+
.tap_if_name
428+
.clone_from(&entry.host_dev_name);
426429
} else {
427430
return Err(SnapshotStateFromFileError::UnknownNetworkDevice.into());
428431
}

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
@@ -969,6 +969,7 @@ def restore_from_snapshot(
969969
snapshot: Snapshot,
970970
resume: bool = False,
971971
uffd_path: Path = None,
972+
rename_interfaces: dict = None,
972973
):
973974
"""Restore a snapshot"""
974975
jailed_snapshot = snapshot.copy_to_chroot(Path(self.chroot()))
@@ -996,11 +997,19 @@ def restore_from_snapshot(
996997
# Adjust things just in case
997998
self.kernel_file = Path(self.kernel_file)
998999

1000+
iface_overrides = []
1001+
if rename_interfaces is not None:
1002+
iface_overrides = [
1003+
{"iface_id": k, "host_dev_name": v}
1004+
for k, v in rename_interfaces.items()
1005+
]
1006+
9991007
self.api.snapshot_load.put(
10001008
mem_backend=mem_backend,
10011009
snapshot_path=str(jailed_vmstate),
10021010
enable_diff_snapshots=snapshot.is_diff,
10031011
resume_vm=resume,
1012+
network_overrides=iface_overrides,
10041013
)
10051014
# This is not a "wait for boot", but rather a "VM still works after restoration"
10061015
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
@@ -13,6 +14,7 @@
1314
import pytest
1415

1516
import host_tools.drive as drive_tools
17+
import host_tools.network as net_tools
1618
from framework.microvm import SnapshotType
1719
from framework.utils import check_filesystem, check_output
1820
from framework.utils_vsock import (
@@ -540,3 +542,35 @@ def test_vmgenid(guest_kernel_linux_6_1, rootfs, microvm_factory, snapshot_type)
540542

541543
# Update the base for next iteration
542544
base_snapshot = snapshot
545+
546+
547+
def test_snapshot_rename_interface(uvm_nano, microvm_factory):
548+
"""
549+
Test that we can restore a snapshot and point its interface to a
550+
different host interface.
551+
"""
552+
base_iface = net_tools.NetIfaceConfig.with_id(0)
553+
554+
vm = uvm_nano
555+
iface1 = dataclasses.replace(base_iface, tap_name="tap1")
556+
vm.add_net_iface(iface=iface1)
557+
# Create an interface but don't attach it to the device
558+
vm.start()
559+
560+
snapshot = vm.snapshot_full()
561+
562+
restored_vm = microvm_factory.build()
563+
restored_vm.spawn()
564+
iface2 = dataclasses.replace(base_iface, tap_name="tap2")
565+
566+
# The snapshot.net_faces is used by the test framework to create the
567+
# appropriate tap devices on the host; we replace those here with the new
568+
# name, otherwise the framework would create `tap1` when restoring the
569+
# snapshot
570+
snapshot.net_ifaces.clear()
571+
snapshot.net_ifaces.append(iface2)
572+
573+
restored_vm.restore_from_snapshot(
574+
snapshot, rename_interfaces={base_iface.dev_name: "tap2"}
575+
)
576+
restored_vm.resume()

0 commit comments

Comments
 (0)