From f7104a681d2e808642ea65132a2a3ad6997a614f Mon Sep 17 00:00:00 2001 From: James Parker Date: Wed, 15 Dec 2021 14:28:27 -0500 Subject: [PATCH] Add initial vdpa smoke test Add vdpa smoke test for [1]. The network, subnet, port creation, and verification had a lot of similarities to the test_sriov.SRIOVBase class. Moved these common methods to the base.BaseWhiteboxComputeTest class and updated impacted tests accordingly. In addition the sriov tests were reliant on provider_net_base_segmentation_id for configuring the vlan id for the network. To allow the vdpa and sriov networks their own vlan, moved the vlan id configuration to whitebox_hardware via sriov_vlan_id and vdpa_vlan_id. [1] https://specs.openstack.org/openstack/nova-specs/specs/wallaby/implemented/libvirt-vdpa-support.html Change-Id: Ia823d0503dbfb97eda35c6d3cf09a38407802fed --- whitebox_tempest_plugin/api/compute/base.py | 180 ++++++++++++ .../api/compute/test_sriov.py | 275 ++++-------------- .../api/compute/test_vdpa.py | 85 ++++++ whitebox_tempest_plugin/config.py | 12 + 4 files changed, 328 insertions(+), 224 deletions(-) create mode 100644 whitebox_tempest_plugin/api/compute/test_vdpa.py diff --git a/whitebox_tempest_plugin/api/compute/base.py b/whitebox_tempest_plugin/api/compute/base.py index a419a27c..cfe6b324 100644 --- a/whitebox_tempest_plugin/api/compute/base.py +++ b/whitebox_tempest_plugin/api/compute/base.py @@ -258,3 +258,183 @@ class BaseWhiteboxComputeTest(base.BaseV2ComputeAdminTest): cipher=cipher, control_location=control_location) return self.create_volume(volume_type=volume_type['name']) + + def _get_expected_xml_interface_type(self, port): + """Return expected domain xml interface type based on port vnic_type + + :param port: dictionary with port details + :return xml_vnic_type: the vnic_type as it is expected to be + represented in a guest's XML + """ + VNIC_MAPPING_DICT = { + 'vdpa': 'vdpa', + 'direct': 'hostdev', + 'macvtap': 'direct' + } + vnic_type = port['port']['binding:vnic_type'] + # NOTE: SR-IOV Port binding vnic type has been known to cause confusion + # when mapping the value to the underlying instance XML. A vnic_type + # that is direct is a 'hostdev' or Host device assignment that is + # is passing the device directly from the host to the guest. A + # vnic_type that is macvtap or 'direct' in the guest xml, is using the + # macvtap driver to attach a guests NIC directly to a specified + # physical interface on the host. + + return VNIC_MAPPING_DICT.get(vnic_type) + + def _get_xml_interface_device(self, server_id, port_id): + """Returns xml interface element that matches provided port mac + and interface type. It is technically possible to have multiple ports + with the same MAC address in an instance, so method functionality may + break in the future. + + :param server_id: str, id of the instance to analyze + :param port_id: str, port id to request from the ports client + :return xml_network_deivce: The xml network device delement that match + the port search criteria + """ + port_info = self.os_admin.ports_client.show_port(port_id) + interface_type = self._get_expected_xml_interface_type(port_info) + root = self.get_server_xml(server_id) + mac = port_info['port']['mac_address'] + interface_list = root.findall( + "./devices/interface[@type='%s']/mac[@address='%s'].." + % (interface_type, mac) + ) + self.assertEqual(len(interface_list), 1, 'Expect to find one ' + 'and only one instance of interface but ' + 'instead found %d instances' % + len(interface_list)) + return interface_list[0] + + def _get_port_attribute(self, port_id, attribute): + """Get a specific attribute for provided port id + + :param port_id: str the port id to search for + :param attribute: str the attribute or key to check from the returned + port dictionary + :return port_attribute: the requested port attribute value + """ + body = self.os_admin.ports_client.show_port(port_id) + port = body['port'] + return port.get(attribute) + + def _create_net_from_physical_network(self, vlan_id, physical_net): + """Create an IPv4 L2 vlan network. Physical network provider comes + from sriov_physnet provided in tempest config + + :return net A dictionary describing details about the created network + """ + name_net = data_utils.rand_name(self.__class__.__name__) + net_dict = { + 'provider:network_type': 'vlan', + 'provider:physical_network': physical_net, + 'provider:segmentation_id': vlan_id, + 'shared': True + } + net = self.os_admin.networks_client.create_network( + name=name_net, + **net_dict) + self.addCleanup(self.os_admin.networks_client.delete_network, + net['network']['id']) + return net + + def _create_subnet(self, network_id): + """Create an IPv4 L2 vlan network. Physical network provider comes + from sriov_physnet provided in tempest config + + :param network_id: str, network id subnet will be associated with + :return net A dictionary describing details about the created network + """ + name_subnet = data_utils.rand_name(self.__class__.__name__) + subnet = self.os_admin.subnets_client.create_subnet( + name=name_subnet, + network_id=network_id, + cidr=CONF.network.project_network_cidr, + ip_version=4 + ) + self.addCleanup( + self.os_admin.subnets_client.delete_subnet, + subnet['subnet']['id'] + ) + return subnet + + def _create_port_from_vnic_type(self, net, vnic_type, + numa_affinity_policy=None): + """Create an sr-iov port based on the provided vnic type + + :param net: dictionary with network details + :param vnic_type: str, representing the vnic type to use with creating + the sriov port, e.g. direct, macvtap, etc. + :return port: dictionary with details about newly created port provided + by neutron ports client + """ + vnic_params = {'binding:vnic_type': vnic_type} + if numa_affinity_policy: + vnic_params['numa_affinity_policy'] = numa_affinity_policy + port = self.os_primary.ports_client.create_port( + network_id=net['network']['id'], + **vnic_params) + self.addCleanup(self.os_primary.ports_client.delete_port, + port['port']['id']) + return port + + def _search_pci_devices(self, column, value): + """Returns all pci_device's address, status, and dev_type that match + query criteria. + + :param column: str, the column in the pci_devices table to search + :param value: str, the specific value in the column to query for + return query_match: json, all pci_devices that match specified query + """ + db_client = clients.DatabaseClient() + db = CONF.whitebox_database.nova_cell1_db_name + with db_client.cursor(db) as cursor: + cursor.execute( + 'SELECT address,status,dev_type FROM ' + 'pci_devices WHERE %s = "%s"' % (column, value)) + data = cursor.fetchall() + return data + + def _verify_neutron_port_binding(self, server_id, port_id): + """Verifies db metrics are accurate for the state of the provided + port_id + + :param port_id str, the port id to request from the ports client + :param server_id str, the guest id to check + """ + binding_profile = self._get_port_attribute(port_id, 'binding:profile') + pci_info = self._search_pci_devices('instance_uuid', server_id) + vnic_type = self._get_port_attribute(port_id, 'binding:vnic_type') + for pci_device in pci_info: + self.assertEqual( + "allocated", pci_device['status'], 'PCI function %s is ' + 'status %s and not status allocated' % + (pci_device['address'], pci_device['status'])) + self.assertEqual( + pci_device['address'], + binding_profile['pci_slot'], 'PCI device ' + 'information in Nova and and Binding profile information in ' + 'Neutron mismatch') + if vnic_type == 'vdpa': + self.assertEqual(pci_device['dev_type'], 'vdpa') + elif vnic_type == 'direct-physical': + self.assertEqual(pci_device['dev_type'], 'type-PF') + else: + # vnic_type direct, macvtap or virtio-forwarder can use VF or + # type pci devices. + self.assertIn(pci_device['dev_type'], ['type-VF', 'type-PCI']) + + def _get_pci_status_count(self, status): + """Return the number of pci devices that match the status argument + + :param status: str, value to query from the pci_devices table + return int, the number of rows that match the provided status + """ + db_client = clients.DatabaseClient() + db = CONF.whitebox_database.nova_cell1_db_name + with db_client.cursor(db) as cursor: + cursor.execute('select COUNT(*) from pci_devices WHERE ' + 'status = "%s"' % status) + data = cursor.fetchall() + return data[0]['COUNT(*)'] diff --git a/whitebox_tempest_plugin/api/compute/test_sriov.py b/whitebox_tempest_plugin/api/compute/test_sriov.py index d3318a78..f640b944 100644 --- a/whitebox_tempest_plugin/api/compute/test_sriov.py +++ b/whitebox_tempest_plugin/api/compute/test_sriov.py @@ -19,7 +19,6 @@ from tempest.common import compute from tempest.common.utils.linux import remote_client from tempest import config from tempest import exceptions as tempest_exc -from tempest.lib.common.utils import data_utils from tempest.lib import exceptions as lib_exc from whitebox_tempest_plugin.api.compute import base @@ -43,95 +42,13 @@ class SRIOVBase(base.BaseWhiteboxComputeTest): raise cls.skipException('Requires sriov_physnet parameter ' 'to be set in order to execute test ' 'cases.') - if getattr(CONF.network_feature_enabled, - 'provider_net_base_segmentation_id', None) is None: + if getattr(CONF.whitebox_hardware, + 'sriov_vlan_id', None) is None: raise cls.skipException('Requires ' - 'provider_net_base_segmentation_id ' - 'parameter to be set in order to execute ' + 'sriov_vlan_id parameter ' + 'to be set in order to execute ' 'test cases.') - def _get_expected_xml_interface_type(self, port): - """Return expected domain xml interface type based on port vnic_type - - :param port: dictionary with port details - :return xml_vnic_type: the vnic_type as it is expected to be - represented in a guest's XML - """ - vnic_type = port['port']['binding:vnic_type'] - # NOTE: SR-IOV Port binding vnic type has been known to cause confusion - # when mapping the value to the underlying instance XML. A vnic_type - # that is direct is a 'hostdev' or Host device assignment that is - # is passing the device directly from the host to the guest. A - # vnic_type that is macvtap or 'direct' in the guest xml, is using the - # macvtap driver to attach a guests NIC directly to a specified - # physical interface on the host. - if vnic_type == 'direct': - return 'hostdev' - elif vnic_type == 'macvtap': - return 'direct' - - def _create_sriov_net(self): - """Create an IPv4 L2 vlan network. Physical network provider comes - from sriov_physnet provided in tempest config - - :return net A dictionary describing details about the created network - """ - name_net = data_utils.rand_name(self.__class__.__name__) - vlan_id = \ - CONF.network_feature_enabled.provider_net_base_segmentation_id - physical_net = CONF.whitebox_hardware.sriov_physnet - net_dict = { - 'provider:network_type': 'vlan', - 'provider:physical_network': physical_net, - 'provider:segmentation_id': vlan_id, - 'shared': True - } - net = self.os_admin.networks_client.create_network( - name=name_net, - **net_dict) - self.addCleanup(self.os_admin.networks_client.delete_network, - net['network']['id']) - return net - - def _create_sriov_subnet(self, network_id): - """Create an IPv4 L2 vlan network. Physical network provider comes - from sriov_physnet provided in tempest config - - :param network_id: str, network id subnet will be associated with - :return net A dictionary describing details about the created network - """ - name_subnet = data_utils.rand_name(self.__class__.__name__) - subnet = self.os_admin.subnets_client.create_subnet( - name=name_subnet, - network_id=network_id, - cidr=CONF.network.project_network_cidr, - ip_version=4 - ) - self.addCleanup( - self.os_admin.subnets_client.delete_subnet, - subnet['subnet']['id'] - ) - return subnet - - def _create_sriov_port(self, net, vnic_type, numa_affinity_policy=None): - """Create an sr-iov port based on the provided vnic type - - :param net: dictionary with network details - :param vnic_type: str, representing the vnic type to use with creating - the sriov port, e.g. direct, macvtap, etc. - :return port: dictionary with details about newly created port provided - by neutron ports client - """ - vnic_params = {'binding:vnic_type': vnic_type} - if numa_affinity_policy: - vnic_params['numa_affinity_policy'] = numa_affinity_policy - port = self.os_primary.ports_client.create_port( - network_id=net['network']['id'], - **vnic_params) - self.addCleanup(self.os_primary.ports_client.delete_port, - port['port']['id']) - return port - def _validate_pf_pci_address_in_xml(self, port_id, host_dev_xml): """Validates pci address matches between port info and guest XML @@ -165,31 +82,6 @@ class SRIOVBase(base.BaseWhiteboxComputeTest): len(hostdev_list)) return hostdev_list[0] - def _get_xml_interface_device(self, server_id, port_id): - """Returns xml interface element that matches provided port mac - and interface type. It is technically possible to have multiple ports - with the same MAC address in an instance, so method functionality may - break in the future. - - :param server_id: str, id of the instance to analyze - :param port_id: str, port id to request from the ports client - :return xml_network_deivce: The xml network device element that matches - the port search criteria - """ - port_info = self.os_primary.ports_client.show_port(port_id) - interface_type = self._get_expected_xml_interface_type(port_info) - root = self.get_server_xml(server_id) - mac = port_info['port']['mac_address'] - interface_list = root.findall( - "./devices/interface[@type='%s']/mac[@address='%s'].." - % (interface_type, mac) - ) - self.assertEqual(len(interface_list), 1, 'Expect to find one ' - 'and only one instance of interface but ' - 'instead found %d instances' % - len(interface_list)) - return interface_list[0] - def _validate_port_xml_vlan_tag(self, port_xml_element, expected_vlan): """Validates port count and vlan are accurate in server's XML @@ -202,62 +94,6 @@ class SRIOVBase(base.BaseWhiteboxComputeTest): 'tag %s but instead it is tagged with %s' % (expected_vlan, interface_vlan)) - def _get_port_attribute(self, port_id, attribute): - """Get a specific attribute for provided port id - - :param port_id: str the port id to search for - :param attribute: str the attribute or key to check from the returned - port dictionary - :return port_attribute: the requested port attribute value - """ - body = self.os_admin.ports_client.show_port(port_id) - port = body['port'] - return port.get(attribute) - - def _search_pci_devices(self, column, value): - """Returns all pci_device's address, status, and dev_type that match - query criteria. - - :param column: str, the column in the pci_devices table to search - :param value: str, the specific value in the column to query for - return query_match: json, all pci_devices that match specified query - """ - db_client = clients.DatabaseClient() - db = CONF.whitebox_database.nova_cell1_db_name - with db_client.cursor(db) as cursor: - cursor.execute( - 'SELECT address,status,dev_type FROM ' - 'pci_devices WHERE %s = "%s"' % (column, value)) - data = cursor.fetchall() - return data - - def _verify_neutron_port_binding(self, server_id, port_id): - """Verifies db metrics are accurate for the state of the provided - port_id - - :param port_id str, the port id to request from the ports client - :param server_id str, the guest id to check - """ - binding_profile = self._get_port_attribute(port_id, 'binding:profile') - vnic_type = self._get_port_attribute(port_id, 'binding:vnic_type') - pci_info = self._search_pci_devices('instance_uuid', server_id) - for pci_device in pci_info: - self.assertEqual( - "allocated", pci_device['status'], 'Physical function %s is ' - 'in status %s and not in status allocated' % - (pci_device['address'], pci_device['status'])) - self.assertEqual( - pci_device['address'], - binding_profile['pci_slot'], 'PCI device ' - 'information in Nova and and Binding profile information in ' - 'Neutron mismatch') - if vnic_type == 'direct-physical': - self.assertEqual(pci_device['dev_type'], 'type-PF') - else: - # vnic_type direct, macvtap or virtio-forwarder can use VF or - # type pci devices. - self.assertIn(pci_device['dev_type'], ['type-VF', 'type-PCI']) - class SRIOVNumaAffinity(SRIOVBase, numa_helper.NUMAHelperMixin): @@ -295,14 +131,17 @@ class SRIOVNumaAffinity(SRIOVBase, numa_helper.NUMAHelperMixin): def setUp(self): super(SRIOVNumaAffinity, self).setUp() - + self.vlan_id = \ + CONF.whitebox_hardware.sriov_vlan_id self.dedicated_cpus_per_numa = \ CONF.whitebox_hardware.dedicated_cpus_per_numa self.affinity_node = str(CONF.whitebox_hardware.physnet_numa_affinity) - - self.network = self._create_sriov_net() - self._create_sriov_subnet(self.network['network']['id']) + self.physical_net = CONF.whitebox_hardware.sriov_physnet + self.network = self._create_net_from_physical_network( + self.vlan_id, + self.physical_net) + self._create_subnet(self.network['network']['id']) self.flavor = self.create_flavor( vcpus=self.dedicated_cpus_per_numa, extra_specs={'hw:cpu_policy': 'dedicated'} @@ -363,8 +202,6 @@ class SRIOVNumaAffinity(SRIOVBase, numa_helper.NUMAHelperMixin): # Validate servers A and B have correct sr-iov interface # information in the xml. Its type and vlan should be accurate. - net_vlan = \ - CONF.network_feature_enabled.provider_net_base_segmentation_id for server, port in zip([server_a, server_b], [port_a, port_b]): interface_xml_element = self._get_xml_interface_device( @@ -373,7 +210,7 @@ class SRIOVNumaAffinity(SRIOVBase, numa_helper.NUMAHelperMixin): ) self._validate_port_xml_vlan_tag( interface_xml_element, - net_vlan) + self.vlan_id) def _required_test_procedure(self, flavor, port_a, port_b, image_id): @@ -418,13 +255,11 @@ class SRIOVNumaAffinity(SRIOVBase, numa_helper.NUMAHelperMixin): # Validate server A has correct sr-iov interface information # in the xml. Its type and vlan should be accurate. - net_vlan = \ - CONF.network_feature_enabled.provider_net_base_segmentation_id interface_xml_element = self._get_xml_interface_device( server_a['id'], port_a['port']['id'] ) - self._validate_port_xml_vlan_tag(interface_xml_element, net_vlan) + self._validate_port_xml_vlan_tag(interface_xml_element, self.vlan_id) class SRIOVNumaAffinityWithFlavor(SRIOVNumaAffinity): @@ -451,10 +286,10 @@ class SRIOVNumaAffinityWithFlavor(SRIOVNumaAffinity): vcpus=self.dedicated_cpus_per_numa, extra_specs=self.preferred ) - port_a = self._create_sriov_port( + port_a = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type) - port_b = self._create_sriov_port( + port_b = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type) self._preferred_test_procedure(flavor, port_a, port_b, self.image_ref) @@ -490,10 +325,10 @@ class SRIOVNumaAffinityWithFlavor(SRIOVNumaAffinity): vcpus=self.dedicated_cpus_per_numa, extra_specs=self.required ) - port_a = self._create_sriov_port( + port_a = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type) - port_b = self._create_sriov_port( + port_b = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type) self._required_test_procedure(flavor, port_a, port_b, self.image_ref) @@ -528,10 +363,10 @@ class SRIOVNumaAffinityWithImagePolicy(SRIOVNumaAffinity): """ image_id = self.copy_default_image( hw_pci_numa_affinity_policy='preferred') - port_a = self._create_sriov_port( + port_a = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type) - port_b = self._create_sriov_port( + port_b = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type) self._preferred_test_procedure( @@ -558,10 +393,10 @@ class SRIOVNumaAffinityWithImagePolicy(SRIOVNumaAffinity): """ image_id = self.copy_default_image( hw_pci_numa_affinity_policy='required') - port_a = self._create_sriov_port( + port_a = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type) - port_b = self._create_sriov_port( + port_b = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type) self._required_test_procedure( @@ -594,11 +429,11 @@ class SRIOVNumaAffinityWithPortPolicy(SRIOVNumaAffinity): guests """ - port_a = self._create_sriov_port( + port_a = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type, numa_affinity_policy='preferred') - port_b = self._create_sriov_port( + port_b = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type, numa_affinity_policy='preferred') @@ -623,11 +458,11 @@ class SRIOVNumaAffinityWithPortPolicy(SRIOVNumaAffinity): guests """ - port_a = self._create_sriov_port( + port_a = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type, numa_affinity_policy='required') - port_b = self._create_sriov_port( + port_b = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type, numa_affinity_policy='preferred') @@ -651,11 +486,11 @@ class SRIOVNumaAffinityWithPortPolicy(SRIOVNumaAffinity): guest """ - port_a = self._create_sriov_port( + port_a = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type, numa_affinity_policy='required') - port_b = self._create_sriov_port( + port_b = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type, numa_affinity_policy='required') @@ -684,11 +519,11 @@ class SRIOVNumaAffinityWithPortPolicy(SRIOVNumaAffinity): required_flavor = self.create_flavor( vcpus=self.dedicated_cpus_per_numa, extra_specs=self.required) - port_a = self._create_sriov_port( + port_a = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type, numa_affinity_policy='preferred') - port_b = self._create_sriov_port( + port_b = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type, numa_affinity_policy='preferred') @@ -716,11 +551,11 @@ class SRIOVNumaAffinityWithPortPolicy(SRIOVNumaAffinity): image_id = self.copy_default_image( hw_pci_numa_affinity_policy='required') - port_a = self._create_sriov_port( + port_a = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type, numa_affinity_policy='preferred') - port_b = self._create_sriov_port( + port_b = self._create_port_from_vnic_type( net=self.network, vnic_type=CONF.network.port_vnic_type, numa_affinity_policy='preferred') @@ -737,8 +572,13 @@ class SRIOVMigration(SRIOVBase): def setUp(self): super(SRIOVMigration, self).setUp() - self.network = self._create_sriov_net() - self._create_sriov_subnet(self.network['network']['id']) + self.vlan_id = \ + CONF.whitebox_hardware.sriov_vlan_id + self.physical_net = CONF.whitebox_hardware.sriov_physnet + self.network = self._create_net_from_physical_network( + self.vlan_id, + self.physical_net) + self._create_subnet(self.network['network']['id']) @classmethod def skip_checks(cls): @@ -746,20 +586,6 @@ class SRIOVMigration(SRIOVBase): if (CONF.compute.min_compute_nodes < 2): raise cls.skipException('Need 2 or more compute nodes.') - def _get_pci_status_count(self, status): - """Return the number of pci devices that match the status argument - - :param status: str, value to query from the pci_devices table - return int, the number of rows that match the provided status - """ - db_client = clients.DatabaseClient() - db = CONF.whitebox_database.nova_cell1_db_name - with db_client.cursor(db) as cursor: - cursor.execute('select COUNT(*) from pci_devices WHERE ' - 'status REGEXP "%s"' % status) - data = cursor.fetchall() - return data[0]['COUNT(*)'] - def _base_test_live_migration(self, vnic_type): """Parent test class that perform sr-iov live migration @@ -770,11 +596,9 @@ class SRIOVMigration(SRIOVBase): else: pci_device_status_regex = 'allocated|claimed' - net_vlan = \ - CONF.network_feature_enabled.provider_net_base_segmentation_id flavor = self.create_flavor() - port = self._create_sriov_port( + port = self._create_port_from_vnic_type( net=self.network, vnic_type=vnic_type ) @@ -797,7 +621,7 @@ class SRIOVMigration(SRIOVBase): ) # Validate the vlan tag persisted in instance's XML after migration - self._validate_port_xml_vlan_tag(interface_xml_element, net_vlan) + self._validate_port_xml_vlan_tag(interface_xml_element, self.vlan_id) # Confirm dev_type, allocation status, and pci address information are # correct in pci_devices table of openstack DB @@ -829,7 +653,7 @@ class SRIOVMigration(SRIOVBase): # Confirm vlan tag in interface XML, dev_type, allocation status, and # pci address information are correct in pci_devices table of openstack # DB after second migration - self._validate_port_xml_vlan_tag(interface_xml_element, net_vlan) + self._validate_port_xml_vlan_tag(interface_xml_element, self.vlan_id) self._verify_neutron_port_binding( server['id'], port['port']['id'] @@ -858,8 +682,13 @@ class SRIOVAttachAndDetach(SRIOVBase): def setUp(self): super(SRIOVAttachAndDetach, self).setUp() - self.sriov_network = self._create_sriov_net() - self._create_sriov_subnet(self.sriov_network['network']['id']) + self.vlan_id = \ + CONF.whitebox_hardware.sriov_vlan_id + self.physical_net = CONF.whitebox_hardware.sriov_physnet + self.network = self._create_net_from_physical_network( + self.vlan_id, + self.physical_net) + self._create_subnet(self.sriov_network['network']['id']) @classmethod def skip_checks(cls): @@ -999,11 +828,9 @@ class SRIOVAttachAndDetach(SRIOVBase): # Gather SR-IOV network vlan, create two guests, and create an SR-IOV # port based on the provided vnic_type - net_vlan = \ - CONF.network_feature_enabled.provider_net_base_segmentation_id servers = [self.create_server_and_ssh(), self.create_server_and_ssh()] - port = self._create_sriov_port( + port = self._create_port_from_vnic_type( net=self.sriov_network, vnic_type=vnic_type ) @@ -1032,7 +859,7 @@ class SRIOVAttachAndDetach(SRIOVBase): # Verify the port's VLAN tag is present in the XML self._validate_port_xml_vlan_tag(interface_xml_element, - net_vlan) + self.vlan_id) # Confirm the vendor and vf product id are present in the guest self._check_device_in_guest( @@ -1086,7 +913,7 @@ class SRIOVAttachAndDetach(SRIOVBase): # direct-physical servers = [self.create_server_and_ssh(), self.create_server_and_ssh()] - port = self._create_sriov_port( + port = self._create_port_from_vnic_type( net=self.sriov_network, vnic_type='direct-physical' ) diff --git a/whitebox_tempest_plugin/api/compute/test_vdpa.py b/whitebox_tempest_plugin/api/compute/test_vdpa.py new file mode 100644 index 00000000..117d49b8 --- /dev/null +++ b/whitebox_tempest_plugin/api/compute/test_vdpa.py @@ -0,0 +1,85 @@ +# Copyright 2020 Red Hat Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from tempest import config + +from whitebox_tempest_plugin.api.compute import base + +from oslo_log import log as logging + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class VDPASmokeTests(base.BaseWhiteboxComputeTest): + + @classmethod + def skip_checks(cls): + super(VDPASmokeTests, cls).skip_checks() + if getattr(CONF.whitebox_hardware, + 'vdpa_physnet', None) is None: + raise cls.skipException('Requires vdpa_physnet parameter ' + 'to be set in order to execute test ' + 'cases.') + if getattr(CONF.whitebox_hardware, + 'vdpa_vlan_id', None) is None: + raise cls.skipException('Requires ' + 'vdpa_vlan_id parameter to be set in ' + 'order to execute test cases.') + + def setUp(self): + super(VDPASmokeTests, self).setUp() + self.vlan_id = \ + CONF.whitebox_hardware.vdpa_vlan_id + self.physical_net = CONF.whitebox_hardware.vdpa_physnet + + self.network = self._create_net_from_physical_network( + self.vlan_id, + self.physical_net) + self._create_subnet(self.network['network']['id']) + + def test_guest_creation_with_vdpa_port(self): + """Creates a guest that requires a vdpa port""" + flavor = self.create_flavor() + + port = self._create_port_from_vnic_type( + net=self.network, + vnic_type='vdpa' + ) + + server = self.create_test_server( + flavor=flavor['id'], + networks=[{'port': port['port']['id']}], + wait_until='ACTIVE' + ) + + interface_xml_element = self._get_xml_interface_device( + server['id'], + port['port']['id'], + ) + if CONF.whitebox.rx_queue_size: + driver = interface_xml_element.find("./driver[@name='vhost']") + self.assertEqual( + str(CONF.whitebox.rx_queue_size), + driver.attrib['rx_queue_size'], + "VDPA rx_queue_size equaling %s not found" % + str(CONF.whitebox.rx_queue_size)) + + # Confirm dev_type, allocation status, and pci address information are + # correct in pci_devices table of openstack DB + self._verify_neutron_port_binding( + server['id'], + port['port']['id'] + ) diff --git a/whitebox_tempest_plugin/config.py b/whitebox_tempest_plugin/config.py index 59c325f5..f0aff2a1 100644 --- a/whitebox_tempest_plugin/config.py +++ b/whitebox_tempest_plugin/config.py @@ -269,6 +269,18 @@ hardware_opts = [ 'sriov_physnet', default=None, help='The physnet to use when creating sr-iov ports'), + cfg.IntOpt( + 'sriov_vlan_id', + default=None, + help='The vlan id associated with the sriov port'), + cfg.StrOpt( + 'vdpa_physnet', + default=None, + help='The physnet to use when creating vdpa ports'), + cfg.IntOpt( + 'vdpa_vlan_id', + default=None, + help='The vlan id associated with the vdpa port'), cfg.IntOpt( 'physnet_numa_affinity', default=None,