def test_block_performance_async(bin_cloner_path, results_file_dumper): """ Test block performance for multiple vm configurations. @type: performance """ logger = logging.getLogger(TEST_ID) if not is_io_uring_supported(): logger.info("io_uring is not supported. Skipping..") pytest.skip('Cannot run async if io_uring is not supported') artifacts = ArtifactCollection(_test_images_s3_bucket()) vm_artifacts = ArtifactSet(artifacts.microvms(keyword="1vcpu_1024mb")) vm_artifacts.insert(artifacts.microvms(keyword="2vcpu_1024mb")) kernel_artifacts = ArtifactSet(artifacts.kernels()) disk_artifacts = ArtifactSet(artifacts.disks(keyword="ubuntu")) logger.info("Testing on processor %s", get_cpu_model_name()) # Create a test context and add builder, logger, network. test_context = TestContext() test_context.custom = { 'builder': MicrovmBuilder(bin_cloner_path), 'logger': logger, 'name': TEST_ID, 'results_file_dumper': results_file_dumper, 'io_engine': 'Async' } test_matrix = TestMatrix( context=test_context, artifact_sets=[vm_artifacts, kernel_artifacts, disk_artifacts]) test_matrix.run_test(fio_workload)
def test_drive_io_engine(test_microvm_with_api, network_config): """ Test io_engine configuration. Test that the io_engine can be configured via the API on kernels that support the given type and that FC returns an error otherwise. @type: functional """ test_microvm = test_microvm_with_api test_microvm.spawn() test_microvm.basic_config(add_root_device=False) test_microvm.ssh_network_config(network_config, '1') supports_io_uring = is_io_uring_supported() response = test_microvm.drive.put( drive_id='rootfs', path_on_host=test_microvm.create_jailed_resource( test_microvm.rootfs_file), is_root_device=True, is_read_only=False, # Set the opposite of the default backend type. io_engine="Sync" if supports_io_uring else "Async" ) if not supports_io_uring: # The Async engine is not supported for older kernels. assert test_microvm.api_session.is_status_bad_request( response.status_code) test_microvm.check_log_message( "Received Error. Status code: 400 Bad Request. Message: Unable" " to create the block device FileEngine(UnsupportedEngine(Async))") # Now configure the default engine type and check that it works. response = test_microvm.drive.put_with_default_io_engine( drive_id='rootfs', path_on_host=test_microvm.create_jailed_resource( test_microvm.rootfs_file), is_root_device=True, is_read_only=False, ) assert test_microvm.api_session.is_status_no_content( response.status_code) test_microvm.start() ssh_conn = net_tools.SSHConnection(test_microvm.ssh_config) # Execute a simple command to check that the guest booted successfully. rc, _, stderr = ssh_conn.execute_command("sync") assert rc == 0 assert stderr.read() == '' assert test_microvm.full_cfg.get().json( )['drives'][0]['io_engine'] == "Sync"
def put(self, **args): """Attach a block device or update the details of a previous one.""" # Default the io engine to Async on kernels > 5.10 so that we # make sure to exercise both Sync and Async behaviour in the CI. # Also check the FC version to make sure that it has support for # configurable io_engine. if (is_io_uring_supported() and compare_versions(self._firecracker_version, "0.25.0") > 0 and ("io_engine" not in args or args["io_engine"] is None)): args["io_engine"] = "Async" datax = self.create_json(**args) return self._api_session.put("{}/{}".format(self._drive_cfg_url, args["drive_id"]), json=datax)
def snapshot_resume_measurements(vm_type): """Define measurements for snapshot resume tests.""" load_latency = LOAD_LATENCY_BASELINES[platform.machine()][vm_type] if is_io_uring_supported(): # There is added latency caused by the io_uring syscalls used by the # block device. load_latency["target"] += 115 if compare_versions(get_kernel_version(), "5.4.0") > 0: # Host kernels >= 5.4 add an up to ~30ms latency. # See: https://github.com/firecracker-microvm/firecracker/issues/2129 load_latency["target"] += 30 latency = types.MeasurementDef.create_measurement( "latency", "ms", [function.Max("max")], {"max": criteria.LowerThan(load_latency)}) return [latency]
def test_drive_patch(test_microvm_with_api): """ Extensively test drive PATCH scenarios before and after boot. @type: functional """ test_microvm = test_microvm_with_api test_microvm.spawn() # Sets up the microVM with 2 vCPUs, 256 MiB of RAM and # a root file system with the rw permission. test_microvm.basic_config(rootfs_io_engine="Sync") # The drive to be patched. fs = drive_tools.FilesystemFile( os.path.join(test_microvm.fsfiles, 'scratch') ) response = test_microvm.drive.put( drive_id='scratch', path_on_host=test_microvm.create_jailed_resource(fs.path), is_root_device=False, is_read_only=False, io_engine="Async" if is_io_uring_supported() else "Sync" ) assert test_microvm.api_session.is_status_no_content(response.status_code) # Patching drive before boot is not allowed. response = test_microvm.drive.patch( drive_id='scratch', path_on_host='foo.bar' ) assert test_microvm.api_session.is_status_bad_request(response.status_code) assert "The requested operation is not supported before starting the " \ "microVM." in response.text test_microvm.start() _drive_patch(test_microvm)
import pytest from framework import utils import host_tools.cargo_build as host # pylint: disable=import-error from host_tools import proc # We have different coverages based on the host kernel version. This is # caused by io_uring, which is only supported by FC for kernels newer # than 5.10. # AMD has a slightly different coverage due to # the appearance of the brand string. On Intel, # this contains the frequency while on AMD it does not. # Checkout the cpuid crate. In the future other # differences may appear. if utils.is_io_uring_supported(): COVERAGE_DICT = {"Intel": 84.89, "AMD": 84.38, "ARM": 84.06} else: COVERAGE_DICT = {"Intel": 81.89, "AMD": 81.43, "ARM": 81.05} PROC_MODEL = proc.proc_type() COVERAGE_MAX_DELTA = 0.05 CARGO_KCOV_REL_PATH = os.path.join(host.CARGO_BUILD_REL_PATH, 'kcov') KCOV_COVERAGE_FILE = 'index.js' """kcov will aggregate coverage data in this file.""" KCOV_COVERED_LINES_REGEX = r'"covered_lines":"(\d+)"' """Regex for extracting number of total covered lines found by kcov."""
def _drive_patch(test_microvm): """Exercise drive patch test scenarios.""" # Patches without mandatory fields are not allowed. response = test_microvm.drive.patch( drive_id='scratch' ) assert test_microvm.api_session.is_status_bad_request(response.status_code) assert "at least one property to patch: path_on_host, rate_limiter" \ in response.text # Cannot patch drive permissions post boot. response = test_microvm.drive.patch( drive_id='scratch', path_on_host='foo.bar', is_read_only=True ) assert test_microvm.api_session.is_status_bad_request(response.status_code) assert "unknown field `is_read_only`" in response.text # Cannot patch io_engine post boot. response = test_microvm.drive.patch( drive_id='scratch', path_on_host='foo.bar', io_engine='Sync' ) assert test_microvm.api_session.is_status_bad_request(response.status_code) assert "unknown field `io_engine`" in response.text # Updates to `is_root_device` with a valid value are not allowed. response = test_microvm.drive.patch( drive_id='scratch', path_on_host='foo.bar', is_root_device=False ) assert test_microvm.api_session.is_status_bad_request(response.status_code) assert "unknown field `is_root_device`" in response.text # Updates to `path_on_host` with an invalid path are not allowed. response = test_microvm.drive.patch( drive_id='scratch', path_on_host='foo.bar' ) assert test_microvm.api_session.is_status_bad_request(response.status_code) assert "drive update (patch): device error: BackingFile(Os { code: 2, " \ "kind: NotFound, message: \\\"No such file or directory\\\" })" \ in response.text fs = drive_tools.FilesystemFile( os.path.join(test_microvm.fsfiles, 'scratch_new') ) # Updates to `path_on_host` with a valid path are allowed. response = test_microvm.drive.patch( drive_id='scratch', path_on_host=test_microvm.create_jailed_resource(fs.path) ) assert test_microvm.api_session.is_status_no_content(response.status_code) # Updates to valid `path_on_host` and `rate_limiter` are allowed. response = test_microvm.drive.patch( drive_id='scratch', path_on_host=test_microvm.create_jailed_resource(fs.path), rate_limiter={ 'bandwidth': { 'size': 1000000, 'refill_time': 100 }, 'ops': { 'size': 1, 'refill_time': 100 } } ) assert test_microvm.api_session.is_status_no_content(response.status_code) # Updates to `rate_limiter` only are allowed. response = test_microvm.drive.patch( drive_id='scratch', rate_limiter={ 'bandwidth': { 'size': 5000, 'refill_time': 100 }, 'ops': { 'size': 500, 'refill_time': 100 } } ) assert test_microvm.api_session.is_status_no_content(response.status_code) # Updates to `rate_limiter` and invalid path fail. response = test_microvm.drive.patch( drive_id='scratch', path_on_host='foo.bar', rate_limiter={ 'bandwidth': { 'size': 5000, 'refill_time': 100 }, 'ops': { 'size': 500, 'refill_time': 100 } } ) assert test_microvm.api_session.is_status_bad_request(response.status_code) assert "No such file or directory" in response.text # Validate full vm configuration after patching drives. response = test_microvm.full_cfg.get() assert test_microvm.api_session.is_status_ok(response.status_code) assert response.json()['drives'] == [{ 'drive_id': 'rootfs', 'path_on_host': '/bionic.rootfs.ext4', 'is_root_device': True, 'partuuid': None, 'is_read_only': False, 'cache_type': 'Unsafe', 'io_engine': 'Sync', 'rate_limiter': None }, { 'drive_id': 'scratch', 'path_on_host': '/scratch_new.ext4', 'is_root_device': False, 'partuuid': None, 'is_read_only': False, 'cache_type': 'Unsafe', 'io_engine': 'Async' if is_io_uring_supported() else 'Sync', 'rate_limiter': { 'bandwidth': { 'size': 5000, 'one_time_burst': None, 'refill_time': 100 }, 'ops': { 'size': 500, 'one_time_burst': None, 'refill_time': 100 } } }]