Skip to content

Commit

Permalink
initrd: add integration tests
Browse files Browse the repository at this point in the history
Add integration tests for booting VMs with initrd and no rootfs.
- Functional: the VM shall boot and / should be mounted as "rootfs"
- Performance: VM shall boot in less than 400ms

Co-developed-by: Tim Deegan <tdeegan@amazon.com>
Signed-off-by: Tim Deegan <tdeegan@amazon.com>
Co-developed-by: Marco Vedovati <mvedovati@suse.com>
Signed-off-by: Marco Vedovati <mvedovati@suse.com>
  • Loading branch information
marcov committed Sep 17, 2019
1 parent 8d04683 commit ca67557
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 15 deletions.
1 change: 1 addition & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ s3://<bucket-url>/img/
<microvm_test_image_folder_n>/
kernel/
<optional_kernel_name.>vmlinux.bin
<optional_initrd_name.>initrd.img
fsfiles/
<rootfs_name>rootfs.ext4
<other_fsfile_n>
Expand Down
32 changes: 25 additions & 7 deletions tests/framework/microvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def __init__(
self._fsfiles_path = os.path.join(self._path, MICROVM_FSFILES_RELPATH)
self._kernel_file = ''
self._rootfs_file = ''
self._initrd_file = ''

# The binaries this microvm will use to start.
self._fc_binary_path = fc_binary_path
Expand Down Expand Up @@ -170,6 +171,16 @@ def kernel_file(self, path):
"""Set the path to the kernel file."""
self._kernel_file = path

@property
def initrd_file(self):
"""Return the name of the initrd file used by this microVM to boot."""
return self._initrd_file

@initrd_file.setter
def initrd_file(self, path):
"""Set the path to the initrd file."""
self._initrd_file = path

@property
def rootfs_file(self):
"""Return the path to the image this microVM can boot into."""
Expand Down Expand Up @@ -228,6 +239,7 @@ def setup(self):
<microvm_uuid>/
kernel/
<kernel_file_n>
<initrd_file_n>
....
fsfiles/
<fsfile_n>
Expand Down Expand Up @@ -275,7 +287,7 @@ def spawn(self):
if self.bin_cloner_path:
cmd = [self.bin_cloner_path] + \
[self._jailer_binary_path] + \
jailer_param_list
jailer_param_list
_p = run(cmd, stdout=PIPE, stderr=PIPE, check=True)
# Terrible hack to make the tests fail when starting the
# jailer fails with a panic. This is needed because we can't
Expand Down Expand Up @@ -334,7 +346,8 @@ def basic_config(
vcpu_count: int = 2,
ht_enabled: bool = False,
mem_size_mib: int = 256,
add_root_device: bool = True
add_root_device: bool = True,
use_initrd: bool = False
):
"""Shortcut for quickly configuring a microVM.
Expand All @@ -361,13 +374,18 @@ def basic_config(
self._memory_events_queue
)

# Add a kernel to start booting from.
response = self.boot.put(
kernel_image_path=self.create_jailed_resource(self.kernel_file)
)
boot_source_args = {}
boot_source_args.update(
kernel_image_path=self.create_jailed_resource(self.kernel_file))

if use_initrd and self.initrd_file != '':
boot_source_args.update(
initrd_path=self.create_jailed_resource(self.initrd_file))

response = self.boot.put(**boot_source_args)
assert self._api_session.is_status_no_content(response.status_code)

if add_root_device:
if add_root_device and self.rootfs_file != '':
# Add the root file system with rw permissions.
response = self.drive.put(
drive_id='rootfs',
Expand Down
5 changes: 4 additions & 1 deletion tests/framework/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,15 @@ def get(cls):
@staticmethod
def create_json(
boot_args=None,
kernel_image_path=None
kernel_image_path=None,
initrd_path=None
):
"""Compose the json associated to this type of API request."""
datax = {}
if kernel_image_path is not None:
datax['kernel_image_path'] = kernel_image_path
if initrd_path is not None:
datax['initrd_path'] = initrd_path
if boot_args is not None:
datax['boot_args'] = boot_args
return datax
Expand Down
24 changes: 24 additions & 0 deletions tests/framework/s3fetcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class MicrovmImageS3Fetcher:
<microvm_image_folder_n>/
kernel/
<optional_kernel_name>vmlinux.bin
<optional_initrd_name>initrd.img
fsfiles/
<rootfs_file_name>rootfs.ext4
<other_fsfile_n>
Expand Down Expand Up @@ -55,6 +56,7 @@ class MicrovmImageS3Fetcher:
MICROVM_IMAGE_KERNEL_RELPATH = 'kernel/'
MICROVM_IMAGE_BLOCKDEV_RELPATH = 'fsfiles/'
MICROVM_IMAGE_KERNEL_FILE_SUFFIX = r'vmlinux.bin'
MICROVM_IMAGE_INITRD_FILE_SUFFIX = r'initrd.img'
MICROVM_IMAGE_ROOTFS_FILE_SUFFIX = r'rootfs.ext4'
MICROVM_IMAGE_SSH_KEY_SUFFIX = r'.id_rsa'

Expand Down Expand Up @@ -142,13 +144,32 @@ def init_vm_resources(self, microvm_image_name, microvm):

if not os.path.exists(microvm_dest_path):
copyfile(resource_local_path, microvm_dest_path)
""""
Disclaimer: Ugly HACK...!
While an initrd.img is missing on the S3 bucket, place a copy of
it in the local ubuntu_with_ssh/kernel/ image folder, and then run
initrd tests using the "test_microvm_with_ssh" fixture.
"""
if microvm_dest_path.endswith(self.MICROVM_IMAGE_KERNEL_FILE_SUFFIX) and \
resource_local_path.find('ubuntu_with_ssh') > 0:
src_initrd = resource_local_path.replace(
self.MICROVM_IMAGE_KERNEL_FILE_SUFFIX, self.MICROVM_IMAGE_INITRD_FILE_SUFFIX)
dest_initrd = microvm_dest_path.replace(
self.MICROVM_IMAGE_KERNEL_FILE_SUFFIX, self.MICROVM_IMAGE_INITRD_FILE_SUFFIX)
copyfile(src_initrd, dest_initrd)
microvm.initrd_file = dest_initrd
""" END OF HACK """

if resource_key.endswith(self.MICROVM_IMAGE_KERNEL_FILE_SUFFIX):
microvm.kernel_file = microvm_dest_path

if resource_key.endswith(self.MICROVM_IMAGE_ROOTFS_FILE_SUFFIX):
microvm.rootfs_file = microvm_dest_path

if resource_key.endswith(self.MICROVM_IMAGE_INITRD_FILE_SUFFIX):
microvm.initrd_file = microvm_dest_path

if resource_key.endswith(self.MICROVM_IMAGE_SSH_KEY_SUFFIX):
# Add the key path to the config dictionary and set
# permissions.
Expand Down Expand Up @@ -193,6 +214,9 @@ def hardlink_vm_resources(
if resource_key.endswith(self.MICROVM_IMAGE_ROOTFS_FILE_SUFFIX):
to_microvm.rootfs_file = microvm_dest_path

if resource_key.endswith(self.MICROVM_IMAGE_INITRD_FILE_SUFFIX):
to_microvm.initrd_file = microvm_dest_path

if resource_key.endswith(self.MICROVM_IMAGE_SSH_KEY_SUFFIX):
# Add the key path to the config dictionary and set
# permissions.
Expand Down
31 changes: 31 additions & 0 deletions tests/integration_tests/functional/test_initrd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright 2019 SUSE LLC
# SPDX-License-Identifier: Apache-2.0
"""Tests for initd."""

from host_tools.network import SSHConnection

INITRD_FILESYSTEM = "rootfs"


def test_microvm_initrd(
test_microvm_with_ssh,
network_config):
""" Check microvm started with an inird has / mounted as rootfs."""

vm = test_microvm_with_ssh

vm.spawn()

vm.basic_config(
add_root_device=False,
use_initrd=True
)
_tap, _, _ = vm.ssh_network_config(network_config, '1')

vm.start()

# Find out what is the filesystem of /, and make sure initrd is used
conn = SSHConnection(vm.ssh_config)
ecode, stdout, _ = conn.execute_command(
f"findmnt / | grep -q -w {INITRD_FILESYSTEM}")
assert ecode == 0
47 changes: 40 additions & 7 deletions tests/integration_tests/performance/test_boottime.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

# The maximum acceptable boot time in us.
MAX_BOOT_TIME_US = 150000
MAX_BOOT_TIME_INITRD_US = 400000
# TODO: Keep a `current` boot time in S3 and validate we don't regress
# Regex for obtaining boot time from some string.
TIMESTAMP_LOG_REGEX = r'Guest-boot-time\s+\=\s+(\d+)\s+us'
Expand Down Expand Up @@ -76,7 +77,32 @@ def test_single_microvm_boottime_with_network(
print("Boot time with network configured is: " + str(boottime_us) + " us")


def _test_microvm_boottime(log_fifo):
def test_single_microvm_net_initrd_boottime(
test_microvm_with_ssh,
network_config
):
"""Check guest boottime of microvm with network."""
log_fifo, _tap = _configure_vm(test_microvm_with_ssh, {
"config": network_config, "iface_id": "1"
}, initrd=True)
time.sleep(0.4)
boottime_us = _test_microvm_boottime(
log_fifo, max_time_us=MAX_BOOT_TIME_INITRD_US)
print("Boot time with initrd + network configured is: " +
str(boottime_us) + " us")


def test_single_microvm_initrd_boottime(
test_microvm_with_ssh):
"""Check guest boottime of microvm with network."""
log_fifo, _tap = _configure_vm(test_microvm_with_ssh, initrd=True)
time.sleep(0.4)
boottime_us = _test_microvm_boottime(
log_fifo, max_time_us=MAX_BOOT_TIME_INITRD_US)
print("Boot time with initrd is: " + str(boottime_us) + " us")


def _test_microvm_boottime(log_fifo, max_time_us=MAX_BOOT_TIME_US):
"""Assert that we meet the minimum boot time.
TODO: Should use a microVM with the `boottime` capability.
Expand All @@ -90,19 +116,26 @@ def _test_microvm_boottime(log_fifo):
boot_time_us = int(timestamps[0])

assert boot_time_us > 0
assert boot_time_us < MAX_BOOT_TIME_US
assert boot_time_us < max_time_us
return boot_time_us


def _configure_vm(microvm, network_info=None):
def _configure_vm(microvm, network_info=None, initrd=False):
"""Auxiliary function for preparing microvm before measuring boottime."""
microvm.spawn()

# Machine configuration specified in the SLA.
microvm.basic_config(
vcpu_count=1,
mem_size_mib=128
)
config = {
'vcpu_count': 1,
'mem_size_mib': 128
}

if initrd:
config['add_root_device'] = False
config['use_initrd'] = True

microvm.basic_config(**config)

if network_info:
_tap, _, _ = microvm.ssh_network_config(
network_info["config"],
Expand Down

0 comments on commit ca67557

Please sign in to comment.