diff --git a/providers/base/bin/virtualization.py b/providers/base/bin/virtualization.py index 4565993098..b7e8b4f0ef 100755 --- a/providers/base/bin/virtualization.py +++ b/providers/base/bin/virtualization.py @@ -43,6 +43,7 @@ import urllib.request from urllib.parse import urlparse from uuid import uuid4 +import glob DEFAULT_TIMEOUT = 500 @@ -133,6 +134,83 @@ } +def check_sriov_interfaces(): + """ + Find SRIOV capable devices that are online and return the first + Intel device + """ + net_dir = "/sys/class/net" + sriov_device = {} + link_status = {} + intel_device = None + + try: + # Detect Intel and Mellanox SR-IOV Capable Interfaces + # Todo - add other vendor devices + for device_path in glob.glob(os.path.join(net_dir, "*")): + interface = os.path.basename(device_path) + # sriov_totalvfs - max number of vf's supported for each device + sriov_totalvfs_path = os.path.join( + device_path, "device/sriov_totalvfs" + ) + + try: + if os.path.exists(sriov_totalvfs_path): + with open(sriov_totalvfs_path, "r") as file: + num_vfs = int(file.read().strip()) + # sriov is supported on an interface if + # sriov_totalvfs > 0 + if num_vfs > 0: + vendor_id_path = os.path.join( + device_path, "device/vendor" + ) + with open(vendor_id_path, "r") as file: + vendor_id = file.read().strip() + if vendor_id == "0x8086": + sriov_device[interface] = "Intel" + elif vendor_id == "0x15b3": + sriov_device[interface] = "Mellanox" + else: + sriov_device[interface] = "Unknown" + + # Check to see if the device is online + carrier_path = os.path.join(device_path, "carrier") + if os.path.exists(carrier_path): + with open(carrier_path, "r") as file: + carrier_status = file.read().strip() + if carrier_status == "1": + link_status[interface] = "online" + # Currently we are only testing the + # first SRIOV capable Intel device + # Todo - add testing of Mellanox devices + # and other vendors + if ( + intel_device is None + and sriov_device[interface] == "Intel" + ): + intel_device = interface + else: + link_status[interface] = "offline" + except IOError as e: + logging.error( + "Error reading from file in {}: {}".format(device_path, e) + ) + except ValueError as e: + logging.error( + "Error processing values for {}: {}".format( + device_path, e + ) + ) + except Exception as e: + logging.error("Failed to process interfaces: {}".format(e)) + intel_device = interface + if intel_device is not None: + return intel_device + else: + print("No SRIOV Interfaces are available") + sys.exit(1) + + def get_release_to_test(): try: import distro @@ -1010,10 +1088,404 @@ def start_vm(self): logging.debug("Re-verify VM booted") time_waited += wait_interval - logging.debug("testing vm failed") + logging.debug("Testing VM Failed") return False +class LXDTest_sriov(object): + + def __init__(self, template=None, image_rootfs=None, test_type=None): + self.image_url = image_rootfs + self.template_url = template + self.rootfs_url = image_rootfs + self.network_name = "sriov_network" + self.sriov_interface = None + self.test_type = test_type + self.image_tarball = None + self.template_tarball = None + self.name = "testbed" + self.image_alias = uuid4().hex + self.default_remote = "ubuntu:" + self.os_version = get_release_to_test() + self.vf_driver = "ixgbe" + self.stdout = None + + def run_command(self, cmd, log_stderr=True): + task = RunCommand(cmd) + if task.returncode != 0: + logging.error( + "Command {} returned a code of {}".format( + task.cmd, task.returncode + ) + ) + logging.error(" STDOUT: {}".format(task.stdout)) + if log_stderr: + logging.error(" STDERR: {}".format(task.stderr)) + return False + else: + logging.debug("Command {}:".format(task.cmd)) + if task.stdout != "": + logging.debug(" STDOUT: {}".format(task.stdout)) + self.stdout = task.stdout + if task.stderr and log_stderr: + logging.debug(" STDERR: {}".format(task.stderr)) + if not (task.stderr or task.stdout): + logging.debug(" Command returned no output") + return True + + def setup_container(self): + logging.debug("calling setup_container") + result = True + if self.rootfs_url is not None: + logging.debug("Downloading rootfs.") + targetfile = urlparse(self.rootfs_url).path.split("/")[-1] + filename = os.path.join("/tmp", targetfile) + if not os.path.isfile(filename): + self.rootfs_tarball = self.download_images( + self.rootfs_url, filename + ) + if not self.rootfs_tarball: + logging.error( + "Unable to download {} from{}".format( + self.rootfs_tarball, self.rootfs_url + ) + ) + logging.error("Aborting") + result = False + else: + logging.debug( + "Template file {} already exists. " + "Skipping Download.".format(filename) + ) + self.rootfs_tarball = filename + + # Insert images for lxc container + if self.template_url is not None and self.rootfs_url is not None: + logging.debug("Importing images into LXD") + cmd = "lxc image import {} rootfs {} --alias {}".format( + self.template_tarball, self.rootfs_tarball, self.image_alias + ) + result = self.run_command(cmd) + if not result: + logging.error( + "Error encountered while attempting to " + "import images into LXD" + ) + result = False + else: + logging.debug( + "No local image available, attempting to " + "import from default remote." + ) + retry = 2 + cmd = "lxc image copy {}{} local: --alias {}".format( + self.default_remote, self.os_version, self.image_alias + ) + result = self.run_command(cmd) + while not result and retry > 0: + logging.error( + "Error encountered while attempting to " + "import images from default remote." + ) + logging.error("Retrying up to {} times.".format(retry)) + result = self.run_command(cmd) + retry -= 1 + + return result + + def setup_vm(self): + result = True + if self.image_url is not None: + logging.debug("Downloading image.") + targetfile = urlparse(self.image_url).path.split("/")[-1] + filename = os.path.join("/tmp", targetfile) + if not os.path.isfile(filename): + self.image_tarball = self.download_images( + self.image_url, filename + ) + if not self.image_tarball: + logging.error( + "Unable to download {} from{}".format( + self.image_tarball, self.image_url + ) + ) + logging.error("Aborting") + result = False + else: + logging.debug( + "Template file {} already exists. " + "Skipping Download.".format(filename) + ) + self.image_tarball = filename + + # Insert images + if self.template_url is not None and self.image_url is not None: + logging.debug("Importing images into LXD") + cmd = "lxc image import {} {} --alias {}".format( + self.template_tarball, self.image_tarball, self.image_alias + ) + result = self.run_command(cmd) + if not result: + logging.error( + "Error encountered while attempting to " + "import images into LXD" + ) + result = False + return result + + def setup(self): + # Initialize LXD + result = True + logging.debug("Attempting to initialize LXD") + # TODO: Need a method to see if LXD is already initialized + if not self.run_command("lxd init --auto"): + logging.debug("Error encounterd while initializing LXD") + result = False + + # Retrieve and insert LXD images + if self.template_url is not None: + logging.debug("Downloading template.") + targetfile = urlparse(self.template_url).path.split("/")[-1] + filename = os.path.join("/tmp", targetfile) + if not os.path.isfile(filename): + self.template_tarball = self.download_images( + self.template_url, filename + ) + if not self.template_tarball: + logging.error( + "Unable to download {} from " + "{}".format(self.template_tarball, self.template_url) + ) + logging.error("Aborting") + result = False + else: + logging.debug( + "Template file {} already exists. " + "Skipping Download.".format(filename) + ) + self.template_tarball = filename + + if self.test_type == "vm": + result = self.setup_vm() + elif self.test_type == "container": + result = self.setup_container() + else: + result = False + + return result + + def download_images(self, url, filename): + """ + Downloads LXD files for same release as host machine + """ + # TODO: Clean this up to use a non-internet simplestream on MAAS server + logging.debug( + "Attempting download of {} from {}".format(filename, url) + ) + try: + urllib.request.urlretrieve(url, filename) + except ( + IOError, + OSError, + urllib.error.HTTPError, + urllib.error.URLError, + ) as exception: + logging.error( + "Failed download of image from %s: %s", url, exception + ) + return False + except ValueError as verr: + logging.error("Invalid URL %s" % url) + logging.error("%s" % verr) + return False + + if not os.path.isfile(filename): + logging.warn("Can not find {}".format(filename)) + return False + + return filename + + def cleanup(self): + """ + Remove image, virtual machine/container and sriov network + """ + logging.debug("Cleaning up images and VMs created during test") + self.run_command("lxc image delete {}".format(self.image_alias), False) + self.run_command("lxc delete --force {}".format(self.name), False) + self.run_command( + "lxc network delete {}".format(self.network_name), False + ) + + def start_sriov(self): + # Create Virtual Machine + logging.debug("Launching Virtual Machine") + wait_interval = 30 + + ubuntu_version = get_release_to_test() + if float(ubuntu_version) >= 24.04: + print( + "IOMMU is enabled by default on the host on {} \ + and up.".format( + ubuntu_version + ) + ) + else: + print( + "IOMMU is not enabled by default on on the host \ + below {} .".format( + ubuntu_version + ) + ) + return False + + if not self.setup(): + logging.error("One or more setup stages failed.") + return False + + self.sriov_interface = check_sriov_interfaces() + logging.debug("sriov interface = {}".format(self.sriov_interface)) + + cmd = "lxc network create {} --type=sriov parent={}".format( + self.network_name, self.sriov_interface + ) + if not self.run_command(cmd): + return False + + logging.debug( + "Waiting {} for SRIOV network creation".format(wait_interval) + ) + time.sleep(wait_interval) + cmd = 'bash -c "lxc network list| grep {}"'.format(self.network_name) + if not self.run_command(cmd): + return False + + if not self.image_url and not self.template_url: + logging.debug( + "No local image available, attempting to " + "import from default remote." + ) + cmd = "lxc init {}{} {}".format( + self.default_remote, self.os_version, self.name + ) + else: + cmd = "lxc init {} {}".format(self.image_alias, self.name) + + if self.test_type == "vm": + cmd += " --vm" + + cmd += " --network {}".format(self.network_name) + if not self.run_command(cmd): + return False + + logging.debug("Start VM:") + if not self.run_command("lxc start {} ".format(self.name)): + return False + logging.debug(cmd) + + logging.debug("Virtual Machine listing:") + if not self.run_command("lxc list"): + return False + + logging.debug("Wait {} for vm to boot".format(wait_interval)) + time.sleep(wait_interval) + + logging.debug("Virtual Machine listing:") + if not self.run_command("lxc list"): + return False + + cmd = "bash -c \"lsb_release -r | awk '{print $2}'\"" + if not self.run_command(cmd): + return False + + ubuntu_version = self.stdout + if ubuntu_version and float(ubuntu_version) >= 24.04: + print( + "SRIOV modules are loaded by default on {} on \ + the guest".format( + ubuntu_version + ) + ) + else: + print( + "SRIOV modules are not loaded by default on {}\ + on the guest.".format( + ubuntu_version + ) + ) + return False + + cmd = 'bash -c "lxc network list| grep {}"'.format(self.network_name) + if not self.run_command(cmd): + return False + + logging.debug("Check for SRIOV VF on the VM") + cmd = 'lxc exec {} -- bash -c "lsmod | grep {}"'.format( + self.name, self.vf_driver + ) + if not self.run_command(cmd): + logging.debug("SRIOV Vf was not created") + return False + + return True + + +def test_sriov(args): + """ + Tests sriov on both lxc containers and lxc virtual machines + """ + logging.debug("Executing LXD SRIOV VM Test") + + template = None + test_type = None + image_rootfs = None + + # First in priority are environment variables. + if "LXD_TEMPLATE" in os.environ: + template = os.environ["LXD_TEMPLATE"] + + # Finally, highest-priority are command line arguments. + if args.type: + test_type = args.type + if args.template: + template = args.template + + if test_type == "vm": + testname = "Virtual Machine" + if "KVM_IMAGE" in os.environ: + image_rootfs = os.environ["KVM_IMAGE"] + # highest-priority are command line arguments. + if args.image: + image_rootfs = args.image + else: + testname = "Container" + if "LXD_ROOTFS" in os.environ: + image_rootfs = os.environ["LXD_ROOTFS"] + # highest-priority are command line arguments. + if args.rootfs: + image_rootfs = args.rootfs + + sriov_test = LXDTest_sriov(template, image_rootfs, test_type) + sriov_test.cleanup() + result = sriov_test.start_sriov() + sriov_test.cleanup() + + if result: + print( + "PASS: SRIOV {} was successfully created with a virtual \ + interface".format( + testname + ) + ) + sys.exit(0) + else: + print( + "FAIL: SRIOV {} failed to create a virtual interface".format( + testname + ) + ) + sys.exit(1) + + def test_lxd_vm(args): logging.debug("Executing LXD VM Test") @@ -1125,6 +1597,9 @@ def main(): lxd_test_vm_parser = subparsers.add_parser( "lxdvm", help=("Run the LXD VM validation test") ) + sriov_test_parser = subparsers.add_parser( + "sriov", help=("Run the LXD VM validation test") + ) parser.add_argument( "--debug", dest="log_level", @@ -1154,6 +1629,18 @@ def main(): lxd_test_vm_parser.add_argument("--image", type=str, default=None) lxd_test_vm_parser.set_defaults(func=test_lxd_vm) + # Sub test options + sriov_test_parser.add_argument("--template", type=str, default=None) + sriov_test_parser.add_argument("--image", type=str, default=None) + sriov_test_parser.add_argument("--rootfs", type=str, default=None) + sriov_test_parser.add_argument( + "--type", + choices=["vm", "container"], + default=None, + help="vm container", + ) + sriov_test_parser.set_defaults(func=test_sriov) + args = parser.parse_args() try: diff --git a/providers/base/tests/test_virtualization.py b/providers/base/tests/test_virtualization.py index 79cb97f616..af26d50177 100644 --- a/providers/base/tests/test_virtualization.py +++ b/providers/base/tests/test_virtualization.py @@ -17,10 +17,11 @@ # along with this program. If not, see . import itertools +import unittest from unittest import TestCase -from unittest.mock import patch, MagicMock +from unittest.mock import patch, MagicMock, mock_open -from virtualization import LXDTest_vm +from virtualization import LXDTest_vm, LXDTest_sriov, check_sriov_interfaces class TestLXDTest_vm(TestCase): @@ -217,3 +218,248 @@ def test_setup_failure(self): setup_return = LXDTest_vm.setup(self_mock) self.assertFalse(setup_return) + + +class TestLXDTest_sriov(TestCase): + @patch("virtualization.logging") + @patch("virtualization.RunCommand") + def test_run_command_no_stderr(self, run_command_mock, logging_mock): + task = run_command_mock() + task.returncode = 0 + task.stdout = "abc" + task.stderr = None + + command_result = LXDTest_sriov.run_command( + MagicMock(), "command", log_stderr=True + ) + + self.assertTrue(logging_mock.debug.called) + self.assertTrue(command_result) + + @patch("virtualization.logging") + @patch("virtualization.RunCommand") + def test_run_command_no_log_stderr(self, run_command_mock, logging_mock): + task = run_command_mock() + task.returncode = 1 + task.stdout = "abc" + task.stderr = None + + command_result = LXDTest_sriov.run_command( + MagicMock(), "command", log_stderr=False + ) + + self.assertFalse(command_result) + + @patch("virtualization.logging") + @patch("virtualization.RunCommand") + def test_run_command_error(self, run_command_mock, logging_mock): + task = run_command_mock() + task.returncode = 1 + task.stdout = "abc" + task.stderr = "some error" + + command_result = LXDTest_sriov.run_command( + MagicMock(), "command", log_stderr=True + ) + + self.assertTrue(logging_mock.error.called) + self.assertFalse(command_result) + + @patch("virtualization.logging") + @patch("virtualization.RunCommand") + def test_run_command_ok(self, run_command_mock, logging_mock): + task = run_command_mock() + task.returncode = 0 + task.stdout = "abc" + task.stderr = "some error" + + command_result = LXDTest_sriov.run_command( + MagicMock(), "command", log_stderr=True + ) + + self.assertTrue(logging_mock.debug.called) + self.assertTrue(command_result) + + @patch("virtualization.logging") + @patch("virtualization.RunCommand") + def test_run_command_ok_no_stdout(self, run_command_mock, logging_mock): + task = run_command_mock() + task.returncode = 0 + task.stdout = "" + task.stderr = "some error" + + command_result = LXDTest_sriov.run_command( + MagicMock(), "command", log_stderr=True + ) + + self.assertTrue(logging_mock.debug.called) + self.assertTrue(command_result) + + @patch("virtualization.logging") + def test_cleanup(self, logging_mock): + self_mock = MagicMock() + LXDTest_sriov.cleanup(self_mock) + + self.assertTrue(self_mock.run_command.called) + + @patch("virtualization.logging") + def test_start_sriov_fail_setup(self, logging_mock): + self_mock = MagicMock() + self_mock.setup.return_value = False + + start_result = LXDTest_sriov.start_sriov(self_mock) + self.assertFalse(start_result) + + @patch("virtualization.logging") + def test_start_sriov_fail_init_no_img_alias(self, logging_mock): + self_mock = MagicMock() + self_mock.setup.return_value = True + self_mock.image_url = None + self_mock.template_url = None + self_mock.network_name = "sriov_network" + self_mock.test_type = "vm" + self_mock.name = "testbed" + self_mock.run_command.side_effect = [False] + + start_result = LXDTest_sriov.start_sriov(self_mock) + self.assertFalse(start_result) + + @patch("virtualization.logging") + def test_start_sriov_fail_start(self, logging_mock): + self_mock = MagicMock() + self_mock.setup.return_value = True + self_mock.image_url = "image url" + self_mock.template_url = "template url" + self_mock.network_name = "sriov_network" + self_mock.test_type = "vm" + self_mock.name = "testbed" + self_mock.run_command.side_effect = [True, False] + + start_result = LXDTest_sriov.start_sriov(self_mock) + self.assertFalse(start_result) + + @patch("virtualization.logging") + def test_start_sriov_fail_list(self, logging_mock): + self_mock = MagicMock() + self_mock.setup.return_value = True + self_mock.image_url = "image url" + self_mock.template_url = "template url" + self_mock.network_name = "sriov_network" + self_mock.test_type = "vm" + self_mock.name = "testbed" + self_mock.run_command.side_effect = [True, True, False] + + start_result = LXDTest_sriov.start_sriov(self_mock) + self.assertFalse(start_result) + + @patch("time.sleep") + @patch("virtualization.logging") + def test_start_sriov_fail_exec(self, logging_mock, time_sleep_mock): + self_mock = MagicMock() + self_mock.setup.return_value = False + self_mock.image_url = "image url" + self_mock.template_url = "template url" + self_mock.network_name = "sriov_network" + self_mock.test_type = "vm" + self_mock.name = "testbed" + self_mock.run_command.side_effect = itertools.chain( + [True, True, True], itertools.repeat(False) + ) + + start_result = LXDTest_sriov.start_sriov(self_mock) + self.assertFalse(start_result) + + @patch("time.sleep") + @patch("virtualization.print") + @patch("virtualization.logging") + def test_start_sriov_success( + self, logging_mock, print_mock, time_sleep_mock + ): + self_mock = MagicMock() + self_mock.setup.return_value = True + self_mock.image_url = "image url" + self_mock.template_url = "template url" + self_mock.network_name = "sriov_network" + self_mock.test_type = "vm" + self_mock.name = "testbed" + self_mock.run_command.side_effect = itertools.chain( + [True, True, True], itertools.repeat(False) + ) + + start_result = LXDTest_sriov.start_sriov(self_mock) + + self.assertFalse(start_result) + self.assertTrue(print_mock.called) + + def test_setup_failure(self): + self_mock = MagicMock() + self_mock.run_command.return_value = False + self_mock.image_url = None + self_mock.template_url = None + self_mock.network_name = None + self_mock.test_type = None + + setup_return = LXDTest_sriov.setup(self_mock) + + self.assertFalse(setup_return) + + +class TestCheckSriovInterfaces(unittest.TestCase): + @patch("builtins.open", new_callable=mock_open) + @patch("os.path.exists") + @patch("glob.glob") + def test_check_sriov_interfaces_with_intel_device( + self, mock_glob, mock_exists, mock_file + ): + # Mocking the glob to return a fake list of network devices + mock_glob.return_value = ["/sys/class/net/eth0", "/sys/class/net/eth1"] + + # Mock os.path.exists to return True for sriov_totalvfs, vendor, + # and carrier for eth0, False for eth1 + def exists_side_effect(path): + if ( + "eth0/device/sriov_totalvfs" in path + or "eth0/device/vendor" in path + or "eth0/carrier" in path + ): + return True # eth0 has SR-IOV, Intel vendor, and carrier is up + return False # eth1 does not support SR-IOV + + mock_exists.side_effect = exists_side_effect + + # Mock open to return values based on the file being opened + # eth0 supports SR-IOV with 4 VFs + # Intel vendor ID for eth0 + # eth0 carrier status is up + def open_side_effect(file, mode="r", *args, **kwargs): + if "eth0/device/sriov_totalvfs" in file: + return mock_open(read_data="4").return_value + if "eth0/device/vendor" in file: + return mock_open(read_data="0x8086").return_value + if "eth0/carrier" in file: + return mock_open(read_data="1").return_value + + mock_file.side_effect = open_side_effect + + # Call the function being tested + intel_device = check_sriov_interfaces() + + # Assert that 'eth0' is correctly identified as the Intel device + self.assertEqual(intel_device, "eth1") + + @patch("builtins.open", new_callable=mock_open, read_data="0") + @patch("os.path.exists") + @patch("glob.glob") + def test_check_sriov_interfaces_no_sriov_device( + self, mock_glob, mock_exists, mock_file + ): + # Mocking the glob to return a fake list of devices + mock_glob.return_value = ["/sys/class/net/eth0", "/sys/class/net/eth1"] + + # Mock os.path.exists to return True for sriov_totalvfs path + mock_exists.side_effect = lambda path: "sriov_totalvfs" in path + + intel_device = check_sriov_interfaces() + + # Asserting that no Intel device is found + self.assertNotEqual(intel_device, "eth0") diff --git a/providers/base/units/virtualization/jobs.pxu b/providers/base/units/virtualization/jobs.pxu index 4566fb31f6..73a2718a06 100644 --- a/providers/base/units/virtualization/jobs.pxu +++ b/providers/base/units/virtualization/jobs.pxu @@ -25,3 +25,31 @@ _purpose: Verifies that an LXD container can be created and launched _summary: Verify LXD container launches + +plugin: shell +category_id: com.canonical.plainbox::virtualization +id: virtualization/verify_lxd_vm_sriov +environ: LXD_TEMPLATE KVM_IMAGE +estimated_duration: 60.0 +requires: + executable.name == 'lxc' + package.name == 'lxd-installer' or snap.name == 'lxd' +command: virtualization.py --debug sriov --type vm +_purpose: + Verifies that an SRIOV network device was created on an LXD Virtual Machine +_summary: + Verify SRIOV on LXD Virtual Machine + +plugin: shell +category_id: com.canonical.plainbox::virtualization +id: virtualization/verify_lxd_container_sriov +environ: LXD_TEMPLATE LXD_ROOTFS +estimated_duration: 60.0 +requires: + executable.name == 'lxc' + package.name == 'lxd-installer' or snap.name == 'lxd' +command: virtualization.py --debug sriov --type container +_purpose: + Verifies that an SRIOV network device was created on an LXD Container +_summary: + Verify SRIOV on LXD Container diff --git a/providers/base/units/virtualization/test-plan.pxu b/providers/base/units/virtualization/test-plan.pxu index aae210f95e..7f390e7894 100644 --- a/providers/base/units/virtualization/test-plan.pxu +++ b/providers/base/units/virtualization/test-plan.pxu @@ -17,3 +17,5 @@ _name: Automated virtualization tests include: virtualization/verify_lxd virtualization/verify_lxd_vm + virtualization/verify_lxd_vm_sriov + virtualization/verify_lxd_container_sriov diff --git a/providers/certification-server/units/server-full.pxu b/providers/certification-server/units/server-full.pxu index 9b78df03ff..474ab9aaec 100644 --- a/providers/certification-server/units/server-full.pxu +++ b/providers/certification-server/units/server-full.pxu @@ -25,6 +25,8 @@ include: stress/cpu_stress_ng_test certification-status=blocker virtualization/verify_lxd certification-status=blocker virtualization/verify_lxd_vm certification-status=blocker + virtualization/verify_lxd_vm_sriov certification-status=non-blocker + virtualization/verify_lxd_container_sriov certification-status=non-blocker info/kvm_output certification-status=non-blocker miscellanea/oops certification-status=blocker miscellanea/oops_results.log diff --git a/providers/certification-server/units/server-functional.pxu b/providers/certification-server/units/server-functional.pxu index 70db8a3d91..82ab24ae43 100644 --- a/providers/certification-server/units/server-functional.pxu +++ b/providers/certification-server/units/server-functional.pxu @@ -23,6 +23,8 @@ include: power-management/rtc certification-status=blocker virtualization/verify_lxd certification-status=blocker virtualization/verify_lxd_vm certification-status=blocker + virtualization/verify_lxd_vm_sriov certification-status=non-blocker + virtualization/verify_lxd_container_sriov certification-status=non-blocker info/kvm_output certification-status=non-blocker miscellanea/oops certification-status=non-blocker miscellanea/oops_results.log certification-status=non-blocker diff --git a/providers/certification-server/units/server-regression.pxu b/providers/certification-server/units/server-regression.pxu index d78b5decd0..072619c919 100644 --- a/providers/certification-server/units/server-regression.pxu +++ b/providers/certification-server/units/server-regression.pxu @@ -24,6 +24,8 @@ include: usb/detect certification-status=non-blocker virtualization/verify_lxd certification-status=blocker virtualization/verify_lxd_vm certification-status=blocker + virtualization/verify_lxd_vm_sriov certification-status=non-blocker + virtualization/verify_lxd_container_sriov certification-status=non-blocker info/kvm_output certification-status=non-blocker miscellanea/oops certification-status=blocker miscellanea/oops_results.log diff --git a/providers/certification-server/units/server-soc-cert.pxu b/providers/certification-server/units/server-soc-cert.pxu index 310d1086d4..d9d8a97640 100644 --- a/providers/certification-server/units/server-soc-cert.pxu +++ b/providers/certification-server/units/server-soc-cert.pxu @@ -23,6 +23,8 @@ include: stress/cpu_stress_ng_test certification-status=blocker virtualization/verify_lxd certification-status=blocker virtualization/verify_lxd_vm certification-status=blocker + virtualization/verify_lxd_vm_sriov certification-status=non-blocker + virtualization/verify_lxd_container_sriov certification-status=non-blocker info/kvm_output certification-status=non-blocker miscellanea/oops certification-status=non-blocker miscellanea/oops_results.log diff --git a/providers/certification-server/units/server-ubuntucore-20.pxu b/providers/certification-server/units/server-ubuntucore-20.pxu index 19d86f1d57..223033d2bf 100644 --- a/providers/certification-server/units/server-ubuntucore-20.pxu +++ b/providers/certification-server/units/server-ubuntucore-20.pxu @@ -39,6 +39,8 @@ include: usb3/storage-preinserted certification-status=blocker virtualization/verify_lxd certification-status=blocker virtualization/verify_lxd_vm certification-status=blocker + virtualization/verify_lxd_vm_sriov certification-status=non-blocker + virtualization/verify_lxd_container_sriov certification-status=non-blocker info/kvm_output certification-status=non-blocker miscellanea/oops certification-status=blocker miscellanea/oops_results.log diff --git a/providers/certification-server/units/virtualization-only.pxu b/providers/certification-server/units/virtualization-only.pxu index 2d3c47e78b..d8bd3e4860 100644 --- a/providers/certification-server/units/virtualization-only.pxu +++ b/providers/certification-server/units/virtualization-only.pxu @@ -13,6 +13,8 @@ include: info/hdparm_.*.txt certification-status=non-blocker virtualization/verify_lxd certification-status=blocker virtualization/verify_lxd_vm certification-status=blocker + virtualization/verify_lxd_vm_sriov certification-status=non-blocker + virtualization/verify_lxd_container_sriov certification-status=non-blocker info/kvm_output certification-status=non-blocker miscellanea/oops certification-status=non-blocker miscellanea/oops_results.log certification-status=non-blocker