Skip to content

Commit 18c70af

Browse files
committed
allow writing snapshot on top of existing files
Signed-off-by: Ives van Hoorne <[email protected]>
1 parent 0fa080b commit 18c70af

File tree

3 files changed

+133
-5
lines changed

3 files changed

+133
-5
lines changed

src/vmm/src/persist.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -328,14 +328,20 @@ fn snapshot_memory_to_file(
328328
let mut file = OpenOptions::new()
329329
.write(true)
330330
.create(true)
331-
.truncate(true)
332331
.open(mem_file_path)
333332
.map_err(|e| MemoryBackingFile("open", e))?;
334333

335-
// Set the length of the file to the full size of the memory area.
336334
let mem_size_mib = mem_size_mib(vmm.guest_memory());
337-
file.set_len((mem_size_mib * 1024 * 1024) as u64)
338-
.map_err(|e| MemoryBackingFile("set_length", e))?;
335+
let expected_size = (mem_size_mib * 1024 * 1024) as u64;
336+
let file_size = file
337+
.metadata()
338+
.map_err(|e| MemoryBackingFile("get_metadata", e))?
339+
.len();
340+
if file_size != expected_size {
341+
// Set the length of the file to the full size of the memory area.
342+
file.set_len(expected_size)
343+
.map_err(|e| MemoryBackingFile("set_length", e))?;
344+
}
339345

340346
match snapshot_type {
341347
SnapshotType::Diff => {

tests/integration_tests/build/test_coverage.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
# Checkout the cpuid crate. In the future other
3030
# differences may appear.
3131
if utils.is_io_uring_supported():
32-
COVERAGE_DICT = {"Intel": 84.83, "AMD": 84.32, "ARM": 84.06}
32+
COVERAGE_DICT = {"Intel": 84.83, "AMD": 84.32, "ARM": 84.01}
3333
else:
3434
COVERAGE_DICT = {"Intel": 81.89, "AMD": 81.37, "ARM": 81.05}
3535

tests/integration_tests/functional/test_snapshot_basic.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import filecmp
66
import logging
77
import os
8+
import shutil
89
import tempfile
910
from pathlib import Path
1011

@@ -188,6 +189,90 @@ def _test_compare_mem_files(context):
188189
basevm.kill()
189190

190191

192+
def _test_overwrite_diff_snapshot(context):
193+
logger = context.custom['logger']
194+
vm_builder = context.custom['builder']
195+
196+
# Create a rw copy artifact.
197+
root_disk = context.disk.copy()
198+
# Get ssh key from read-only artifact.
199+
ssh_key = context.disk.ssh_key()
200+
# Create a fresh microvm from artifacts.
201+
vm_instance = vm_builder.build(kernel=context.kernel,
202+
disks=[root_disk],
203+
ssh_key=ssh_key,
204+
config=context.microvm,
205+
diff_snapshots=True)
206+
microvm = vm_instance.vm
207+
microvm.start()
208+
ssh_connection = net_tools.SSHConnection(microvm.ssh_config)
209+
210+
# Verify if guest can run commands.
211+
exit_code, _, _ = ssh_connection.execute_command("sync")
212+
assert exit_code == 0
213+
214+
# Create a snapshot builder from a microvm.
215+
snapshot_builder = SnapshotBuilder(microvm)
216+
217+
logger.info("Create full snapshot.")
218+
# Create full snapshot.
219+
full_snapshot = snapshot_builder.create([root_disk.local_path()],
220+
ssh_key,
221+
SnapshotType.DIFF,
222+
mem_file_name="vm.mem",
223+
snapshot_name="vm.vmstate")
224+
225+
microvm.kill()
226+
227+
logger.info("Load VM from snapshot.")
228+
229+
logger.info("Load snapshot, mem %s", full_snapshot.mem)
230+
microvm, _ = vm_builder.build_from_snapshot(
231+
full_snapshot,
232+
resume=True,
233+
diff_snapshots=True
234+
)
235+
ssh_connection = net_tools.SSHConnection(microvm.ssh_config)
236+
237+
# Verify that guest is able to run commands.
238+
exit_code, _, _ = ssh_connection.execute_command("cat /proc/cpuinfo")
239+
assert exit_code == 0
240+
241+
# Create a snapshot builder from a microvm.
242+
snapshot_builder = SnapshotBuilder(microvm)
243+
244+
# Copy over the existing snapshot to the snapshot save location,
245+
# this snapshot will be overwritten.
246+
snapshot_dir = snapshot_builder.create_snapshot_dir()
247+
shutil.copyfile(full_snapshot.mem, os.path.join(snapshot_dir, "vm.mem"))
248+
os.chown(os.path.join(snapshot_dir, "vm.mem"),
249+
microvm.jailer.uid, microvm.jailer.gid)
250+
251+
logger.info("Create diff snapshot.")
252+
# Create diff snapshot _on top_ of full snapshot.
253+
new_snapshot = snapshot_builder.create([root_disk.local_path()],
254+
ssh_key,
255+
SnapshotType.DIFF,
256+
mem_file_name="vm.mem",
257+
snapshot_name="vm.vmstate")
258+
microvm.kill()
259+
260+
logger.info("Load VM from combined snapshot.")
261+
# Verify that we can load from new snapshot
262+
logger.info("Load snapshot, mem %s", new_snapshot.mem)
263+
microvm, _ = vm_builder.build_from_snapshot(
264+
new_snapshot,
265+
resume=True,
266+
diff_snapshots=True
267+
)
268+
ssh_connection = net_tools.SSHConnection(microvm.ssh_config)
269+
# Run command to verify it loaded
270+
exit_code, _, _ = ssh_connection.execute_command("sync")
271+
assert exit_code == 0
272+
273+
microvm.kill()
274+
275+
191276
def test_patch_drive_snapshot(bin_cloner_path):
192277
"""
193278
Test that a patched drive is correctly used by guests loaded from snapshot.
@@ -416,6 +501,43 @@ def test_cmp_full_and_first_diff_mem(network_config,
416501
test_matrix.run_test(_test_compare_mem_files)
417502

418503

504+
def test_overwrite_diff_snapshot(network_config,
505+
bin_cloner_path):
506+
"""
507+
Can write a diff snapshot on top of a full snapshot.
508+
509+
@type: functional
510+
"""
511+
logger = logging.getLogger("snapshot_diff_overwrite")
512+
513+
artifacts = ArtifactCollection(_test_images_s3_bucket())
514+
# Testing matrix:
515+
# - Guest kernel: All supported ones
516+
# - Rootfs: Ubuntu 18.04
517+
# - Microvm: 2vCPU with 512 MB RAM
518+
microvm_artifacts = ArtifactSet(artifacts.microvms(keyword="2vcpu_512mb"))
519+
kernel_artifacts = ArtifactSet(artifacts.kernels())
520+
disk_artifacts = ArtifactSet(artifacts.disks(keyword="ubuntu"))
521+
522+
# Create a test context and add builder, logger, network.
523+
test_context = TestContext()
524+
test_context.custom = {
525+
'builder': MicrovmBuilder(bin_cloner_path),
526+
'network_config': network_config,
527+
'logger': logger
528+
}
529+
530+
# Create the test matrix.
531+
test_matrix = TestMatrix(context=test_context,
532+
artifact_sets=[
533+
microvm_artifacts,
534+
kernel_artifacts,
535+
disk_artifacts
536+
])
537+
538+
test_matrix.run_test(_test_overwrite_diff_snapshot)
539+
540+
419541
def test_negative_postload_api(bin_cloner_path):
420542
"""
421543
Test APIs fail after loading from snapshot.

0 commit comments

Comments
 (0)