Path: blob/main/tests/integration_tests/performance/test_network_latency.py
1958 views
# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.1# SPDX-License-Identifier: Apache-2.02"""Tests the network latency of a Firecracker guest."""3import logging4import platform5import re6import pytest7import host_tools.network as net_tools8from conftest import ARTIFACTS_COLLECTION9from framework.artifacts import ArtifactSet10from framework.matrix import TestMatrix, TestContext11from framework.builder import MicrovmBuilder12from framework.stats import core, consumer, producer, types, criteria,\13function14from framework.utils import eager_map, CpuMap15from framework.artifacts import DEFAULT_HOST_IP16from framework.utils_cpuid import get_cpu_model_name17from integration_tests.performance.utils import handle_failure, \18dump_test_result1920PING = "ping -c {} -i {} {}"21LATENCY_AVG_BASELINES = {22"x86_64": {23"target": 0.250, # milliseconds24"delta": 0.020 # milliseconds25}26}2728PKT_LOSS = "pkt_loss"29PKT_LOSS_STAT_KEY = "value"30LATENCY = "latency"313233def pass_criteria():34"""Define pass criteria for the statistics."""35return {36"Avg": criteria.EqualWith(LATENCY_AVG_BASELINES[platform.machine()])37}383940def measurements():41"""Define measurements."""42latency = types.MeasurementDef.create_measurement(43LATENCY,44"ms",45[function.ValuePlaceholder("Avg"),46function.ValuePlaceholder("Min"),47function.ValuePlaceholder("Max"),48function.ValuePlaceholder("Stddev"),49function.ValuePlaceholder("Percentile99"),50function.ValuePlaceholder("Percentile90"),51function.ValuePlaceholder("Percentile50")],52pass_criteria())53pkt_loss = types.MeasurementDef.create_measurement(54PKT_LOSS,55"percentage",56[function.ValuePlaceholder(PKT_LOSS_STAT_KEY)])5758return [latency, pkt_loss]596061def consume_ping_output(cons, raw_data, requests):62"""Consume ping output.6364Output example:65PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.6664 bytes from 8.8.8.8: icmp_seq=1 ttl=118 time=17.7 ms6764 bytes from 8.8.8.8: icmp_seq=2 ttl=118 time=17.7 ms6864 bytes from 8.8.8.8: icmp_seq=3 ttl=118 time=17.4 ms6964 bytes from 8.8.8.8: icmp_seq=4 ttl=118 time=17.8 ms7071--- 8.8.8.8 ping statistics ---724 packets transmitted, 4 received, 0% packet loss, time 3005ms73rtt min/avg/max/mdev = 17.478/17.705/17.808/0.210 ms74"""75eager_map(cons.set_measurement_def, measurements())7677st_keys = ["Min",78"Avg",79"Max",80"Stddev"]8182output = raw_data.strip().split('\n')83assert len(output) > 28485# E.g: round-trip min/avg/max/stddev = 17.478/17.705/17.808/0.210 ms86stat_values = output[-1]87pattern_stats = "round-trip min/avg/max/stddev = (.+)/(.+)/(.+)/(.+) ms"88stat_values = re.findall(pattern_stats, stat_values)[0]89assert len(stat_values) == 49091for index, stat_value in enumerate(stat_values[:4]):92cons.consume_stat(st_name=st_keys[index],93ms_name=LATENCY,94value=float(stat_value))9596# E.g: 4 packets transmitted, 4 received, 0% packet loss97packet_stats = output[-2]98pattern_packet = ".+ packet.+transmitted, .+ received," \99" (.+)% packet loss"100pkt_loss = re.findall(pattern_packet, packet_stats)[0]101assert len(pkt_loss) == 1102cons.consume_stat(st_name=PKT_LOSS_STAT_KEY,103ms_name=PKT_LOSS,104value=pkt_loss[0])105106# Compute percentiles.107seqs = output[1:requests + 1]108times = list()109pattern_time = ".+ bytes from .+: icmp_seq=.+ ttl=.+ time=(.+) ms"110for index, seq in enumerate(seqs):111time = re.findall(pattern_time, seq)112assert len(time) == 1113times.append(time[0])114115times.sort()116cons.consume_stat(st_name="Percentile50",117ms_name=LATENCY,118value=times[int(requests * 0.5)])119cons.consume_stat(st_name="Percentile90",120ms_name=LATENCY,121value=times[int(requests * 0.9)])122cons.consume_stat(st_name="Percentile99",123ms_name=LATENCY,124value=times[int(requests * 0.99)])125126127@pytest.mark.nonci128@pytest.mark.skipif(platform.machine() != "x86_64",129reason="This test was observed only on x86_64. Further "130"support need to be added for aarch64 and amd64.")131@pytest.mark.timeout(3600)132def test_network_latency(bin_cloner_path, results_file_dumper):133"""Test network latency driver for multiple artifacts."""134logger = logging.getLogger("network_latency")135microvm_artifacts = ArtifactSet(136ARTIFACTS_COLLECTION.microvms(keyword="1vcpu_1024mb")137)138kernel_artifacts = ArtifactSet(ARTIFACTS_COLLECTION.kernels())139disk_artifacts = ArtifactSet(ARTIFACTS_COLLECTION.disks(keyword="ubuntu"))140141# Create a test context and add builder, logger, network.142test_context = TestContext()143test_context.custom = {144'builder': MicrovmBuilder(bin_cloner_path),145'logger': logger,146'requests': 1000,147'interval': 0.2, # Seconds.148'name': 'network_latency',149'results_file_dumper': results_file_dumper150}151152# Create the test matrix.153test_matrix = TestMatrix(context=test_context,154artifact_sets=[155microvm_artifacts,156kernel_artifacts,157disk_artifacts158])159160test_matrix.run_test(_g2h_send_ping)161162163def _g2h_send_ping(context):164"""Send ping from guest to host."""165logger = context.custom['logger']166vm_builder = context.custom['builder']167interval_between_req = context.custom['interval']168name = context.custom['name']169file_dumper = context.custom['results_file_dumper']170171logger.info("Testing {} with microvm: \"{}\", kernel {}, disk {} "172.format(name,173context.microvm.name(),174context.kernel.name(),175context.disk.name()))176177# Create a rw copy artifact.178rw_disk = context.disk.copy()179# Get ssh key from read-only artifact.180ssh_key = context.disk.ssh_key()181# Create a fresh microvm from aftifacts.182vm_instance = vm_builder.build(kernel=context.kernel,183disks=[rw_disk],184ssh_key=ssh_key,185config=context.microvm)186basevm = vm_instance.vm187basevm.start()188189# Check if the needed CPU cores are available. We have the API thread, VMM190# thread and then one thread for each configured vCPU.191assert CpuMap.len() >= 2 + basevm.vcpus_count192193# Pin uVM threads to physical cores.194current_cpu_id = 0195assert basevm.pin_vmm(current_cpu_id), \196"Failed to pin firecracker thread."197current_cpu_id += 1198assert basevm.pin_api(current_cpu_id), \199"Failed to pin fc_api thread."200for i in range(basevm.vcpus_count):201current_cpu_id += 1202assert basevm.pin_vcpu(i, current_cpu_id + i), \203f"Failed to pin fc_vcpu {i} thread."204205custom = {"microvm": context.microvm.name(),206"kernel": context.kernel.name(),207"disk": context.disk.name(),208"cpu_model_name": get_cpu_model_name()}209210st_core = core.Core(name="network_latency", iterations=1, custom=custom)211cons = consumer.LambdaConsumer(212func=consume_ping_output,213func_kwargs={"requests": context.custom['requests']}214)215cmd = PING.format(context.custom['requests'],216interval_between_req,217DEFAULT_HOST_IP)218prod = producer.SSHCommand(cmd,219net_tools.SSHConnection(basevm.ssh_config))220st_core.add_pipe(producer=prod, consumer=cons, tag="ping")221222# Gather results and verify pass criteria.223try:224result = st_core.run_exercise()225except core.CoreException as err:226handle_failure(file_dumper, err)227228dump_test_result(file_dumper, result)229230231