Merge "Add initial vdpa smoke test"
This commit is contained in:
commit
81a677f54f
@ -258,3 +258,183 @@ class BaseWhiteboxComputeTest(base.BaseV2ComputeAdminTest):
|
|||||||
cipher=cipher,
|
cipher=cipher,
|
||||||
control_location=control_location)
|
control_location=control_location)
|
||||||
return self.create_volume(volume_type=volume_type['name'])
|
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(*)']
|
||||||
|
@ -19,7 +19,6 @@ from tempest.common import compute
|
|||||||
from tempest.common.utils.linux import remote_client
|
from tempest.common.utils.linux import remote_client
|
||||||
from tempest import config
|
from tempest import config
|
||||||
from tempest import exceptions as tempest_exc
|
from tempest import exceptions as tempest_exc
|
||||||
from tempest.lib.common.utils import data_utils
|
|
||||||
from tempest.lib import exceptions as lib_exc
|
from tempest.lib import exceptions as lib_exc
|
||||||
|
|
||||||
from whitebox_tempest_plugin.api.compute import base
|
from whitebox_tempest_plugin.api.compute import base
|
||||||
@ -43,95 +42,13 @@ class SRIOVBase(base.BaseWhiteboxComputeTest):
|
|||||||
raise cls.skipException('Requires sriov_physnet parameter '
|
raise cls.skipException('Requires sriov_physnet parameter '
|
||||||
'to be set in order to execute test '
|
'to be set in order to execute test '
|
||||||
'cases.')
|
'cases.')
|
||||||
if getattr(CONF.network_feature_enabled,
|
if getattr(CONF.whitebox_hardware,
|
||||||
'provider_net_base_segmentation_id', None) is None:
|
'sriov_vlan_id', None) is None:
|
||||||
raise cls.skipException('Requires '
|
raise cls.skipException('Requires '
|
||||||
'provider_net_base_segmentation_id '
|
'sriov_vlan_id parameter '
|
||||||
'parameter to be set in order to execute '
|
'to be set in order to execute '
|
||||||
'test cases.')
|
'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):
|
def _validate_pf_pci_address_in_xml(self, port_id, host_dev_xml):
|
||||||
"""Validates pci address matches between port info and guest XML
|
"""Validates pci address matches between port info and guest XML
|
||||||
|
|
||||||
@ -165,31 +82,6 @@ class SRIOVBase(base.BaseWhiteboxComputeTest):
|
|||||||
len(hostdev_list))
|
len(hostdev_list))
|
||||||
return hostdev_list[0]
|
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):
|
def _validate_port_xml_vlan_tag(self, port_xml_element, expected_vlan):
|
||||||
"""Validates port count and vlan are accurate in server's XML
|
"""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' %
|
'tag %s but instead it is tagged with %s' %
|
||||||
(expected_vlan, interface_vlan))
|
(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):
|
class SRIOVNumaAffinity(SRIOVBase, numa_helper.NUMAHelperMixin):
|
||||||
|
|
||||||
@ -295,14 +131,17 @@ class SRIOVNumaAffinity(SRIOVBase, numa_helper.NUMAHelperMixin):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(SRIOVNumaAffinity, self).setUp()
|
super(SRIOVNumaAffinity, self).setUp()
|
||||||
|
self.vlan_id = \
|
||||||
|
CONF.whitebox_hardware.sriov_vlan_id
|
||||||
self.dedicated_cpus_per_numa = \
|
self.dedicated_cpus_per_numa = \
|
||||||
CONF.whitebox_hardware.dedicated_cpus_per_numa
|
CONF.whitebox_hardware.dedicated_cpus_per_numa
|
||||||
|
|
||||||
self.affinity_node = str(CONF.whitebox_hardware.physnet_numa_affinity)
|
self.affinity_node = str(CONF.whitebox_hardware.physnet_numa_affinity)
|
||||||
|
self.physical_net = CONF.whitebox_hardware.sriov_physnet
|
||||||
self.network = self._create_sriov_net()
|
self.network = self._create_net_from_physical_network(
|
||||||
self._create_sriov_subnet(self.network['network']['id'])
|
self.vlan_id,
|
||||||
|
self.physical_net)
|
||||||
|
self._create_subnet(self.network['network']['id'])
|
||||||
self.flavor = self.create_flavor(
|
self.flavor = self.create_flavor(
|
||||||
vcpus=self.dedicated_cpus_per_numa,
|
vcpus=self.dedicated_cpus_per_numa,
|
||||||
extra_specs={'hw:cpu_policy': 'dedicated'}
|
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
|
# Validate servers A and B have correct sr-iov interface
|
||||||
# information in the xml. Its type and vlan should be accurate.
|
# 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],
|
for server, port in zip([server_a, server_b],
|
||||||
[port_a, port_b]):
|
[port_a, port_b]):
|
||||||
interface_xml_element = self._get_xml_interface_device(
|
interface_xml_element = self._get_xml_interface_device(
|
||||||
@ -373,7 +210,7 @@ class SRIOVNumaAffinity(SRIOVBase, numa_helper.NUMAHelperMixin):
|
|||||||
)
|
)
|
||||||
self._validate_port_xml_vlan_tag(
|
self._validate_port_xml_vlan_tag(
|
||||||
interface_xml_element,
|
interface_xml_element,
|
||||||
net_vlan)
|
self.vlan_id)
|
||||||
|
|
||||||
def _required_test_procedure(self, flavor, port_a, port_b, image_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
|
# Validate server A has correct sr-iov interface information
|
||||||
# in the xml. Its type and vlan should be accurate.
|
# 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(
|
interface_xml_element = self._get_xml_interface_device(
|
||||||
server_a['id'],
|
server_a['id'],
|
||||||
port_a['port']['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):
|
class SRIOVNumaAffinityWithFlavor(SRIOVNumaAffinity):
|
||||||
@ -451,10 +286,10 @@ class SRIOVNumaAffinityWithFlavor(SRIOVNumaAffinity):
|
|||||||
vcpus=self.dedicated_cpus_per_numa,
|
vcpus=self.dedicated_cpus_per_numa,
|
||||||
extra_specs=self.preferred
|
extra_specs=self.preferred
|
||||||
)
|
)
|
||||||
port_a = self._create_sriov_port(
|
port_a = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type)
|
vnic_type=CONF.network.port_vnic_type)
|
||||||
port_b = self._create_sriov_port(
|
port_b = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type)
|
vnic_type=CONF.network.port_vnic_type)
|
||||||
self._preferred_test_procedure(flavor, port_a, port_b, self.image_ref)
|
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,
|
vcpus=self.dedicated_cpus_per_numa,
|
||||||
extra_specs=self.required
|
extra_specs=self.required
|
||||||
)
|
)
|
||||||
port_a = self._create_sriov_port(
|
port_a = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type)
|
vnic_type=CONF.network.port_vnic_type)
|
||||||
port_b = self._create_sriov_port(
|
port_b = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type)
|
vnic_type=CONF.network.port_vnic_type)
|
||||||
self._required_test_procedure(flavor, port_a, port_b, self.image_ref)
|
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(
|
image_id = self.copy_default_image(
|
||||||
hw_pci_numa_affinity_policy='preferred')
|
hw_pci_numa_affinity_policy='preferred')
|
||||||
port_a = self._create_sriov_port(
|
port_a = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type)
|
vnic_type=CONF.network.port_vnic_type)
|
||||||
port_b = self._create_sriov_port(
|
port_b = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type)
|
vnic_type=CONF.network.port_vnic_type)
|
||||||
self._preferred_test_procedure(
|
self._preferred_test_procedure(
|
||||||
@ -558,10 +393,10 @@ class SRIOVNumaAffinityWithImagePolicy(SRIOVNumaAffinity):
|
|||||||
"""
|
"""
|
||||||
image_id = self.copy_default_image(
|
image_id = self.copy_default_image(
|
||||||
hw_pci_numa_affinity_policy='required')
|
hw_pci_numa_affinity_policy='required')
|
||||||
port_a = self._create_sriov_port(
|
port_a = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type)
|
vnic_type=CONF.network.port_vnic_type)
|
||||||
port_b = self._create_sriov_port(
|
port_b = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type)
|
vnic_type=CONF.network.port_vnic_type)
|
||||||
self._required_test_procedure(
|
self._required_test_procedure(
|
||||||
@ -594,11 +429,11 @@ class SRIOVNumaAffinityWithPortPolicy(SRIOVNumaAffinity):
|
|||||||
guests
|
guests
|
||||||
"""
|
"""
|
||||||
|
|
||||||
port_a = self._create_sriov_port(
|
port_a = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type,
|
vnic_type=CONF.network.port_vnic_type,
|
||||||
numa_affinity_policy='preferred')
|
numa_affinity_policy='preferred')
|
||||||
port_b = self._create_sriov_port(
|
port_b = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type,
|
vnic_type=CONF.network.port_vnic_type,
|
||||||
numa_affinity_policy='preferred')
|
numa_affinity_policy='preferred')
|
||||||
@ -623,11 +458,11 @@ class SRIOVNumaAffinityWithPortPolicy(SRIOVNumaAffinity):
|
|||||||
guests
|
guests
|
||||||
"""
|
"""
|
||||||
|
|
||||||
port_a = self._create_sriov_port(
|
port_a = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type,
|
vnic_type=CONF.network.port_vnic_type,
|
||||||
numa_affinity_policy='required')
|
numa_affinity_policy='required')
|
||||||
port_b = self._create_sriov_port(
|
port_b = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type,
|
vnic_type=CONF.network.port_vnic_type,
|
||||||
numa_affinity_policy='preferred')
|
numa_affinity_policy='preferred')
|
||||||
@ -651,11 +486,11 @@ class SRIOVNumaAffinityWithPortPolicy(SRIOVNumaAffinity):
|
|||||||
guest
|
guest
|
||||||
"""
|
"""
|
||||||
|
|
||||||
port_a = self._create_sriov_port(
|
port_a = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type,
|
vnic_type=CONF.network.port_vnic_type,
|
||||||
numa_affinity_policy='required')
|
numa_affinity_policy='required')
|
||||||
port_b = self._create_sriov_port(
|
port_b = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type,
|
vnic_type=CONF.network.port_vnic_type,
|
||||||
numa_affinity_policy='required')
|
numa_affinity_policy='required')
|
||||||
@ -684,11 +519,11 @@ class SRIOVNumaAffinityWithPortPolicy(SRIOVNumaAffinity):
|
|||||||
required_flavor = self.create_flavor(
|
required_flavor = self.create_flavor(
|
||||||
vcpus=self.dedicated_cpus_per_numa,
|
vcpus=self.dedicated_cpus_per_numa,
|
||||||
extra_specs=self.required)
|
extra_specs=self.required)
|
||||||
port_a = self._create_sriov_port(
|
port_a = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type,
|
vnic_type=CONF.network.port_vnic_type,
|
||||||
numa_affinity_policy='preferred')
|
numa_affinity_policy='preferred')
|
||||||
port_b = self._create_sriov_port(
|
port_b = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type,
|
vnic_type=CONF.network.port_vnic_type,
|
||||||
numa_affinity_policy='preferred')
|
numa_affinity_policy='preferred')
|
||||||
@ -716,11 +551,11 @@ class SRIOVNumaAffinityWithPortPolicy(SRIOVNumaAffinity):
|
|||||||
|
|
||||||
image_id = self.copy_default_image(
|
image_id = self.copy_default_image(
|
||||||
hw_pci_numa_affinity_policy='required')
|
hw_pci_numa_affinity_policy='required')
|
||||||
port_a = self._create_sriov_port(
|
port_a = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type,
|
vnic_type=CONF.network.port_vnic_type,
|
||||||
numa_affinity_policy='preferred')
|
numa_affinity_policy='preferred')
|
||||||
port_b = self._create_sriov_port(
|
port_b = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=CONF.network.port_vnic_type,
|
vnic_type=CONF.network.port_vnic_type,
|
||||||
numa_affinity_policy='preferred')
|
numa_affinity_policy='preferred')
|
||||||
@ -737,8 +572,13 @@ class SRIOVMigration(SRIOVBase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(SRIOVMigration, self).setUp()
|
super(SRIOVMigration, self).setUp()
|
||||||
self.network = self._create_sriov_net()
|
self.vlan_id = \
|
||||||
self._create_sriov_subnet(self.network['network']['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
|
@classmethod
|
||||||
def skip_checks(cls):
|
def skip_checks(cls):
|
||||||
@ -746,20 +586,6 @@ class SRIOVMigration(SRIOVBase):
|
|||||||
if (CONF.compute.min_compute_nodes < 2):
|
if (CONF.compute.min_compute_nodes < 2):
|
||||||
raise cls.skipException('Need 2 or more compute nodes.')
|
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):
|
def _base_test_live_migration(self, vnic_type):
|
||||||
"""Parent test class that perform sr-iov live migration
|
"""Parent test class that perform sr-iov live migration
|
||||||
|
|
||||||
@ -770,11 +596,9 @@ class SRIOVMigration(SRIOVBase):
|
|||||||
else:
|
else:
|
||||||
pci_device_status_regex = 'allocated|claimed'
|
pci_device_status_regex = 'allocated|claimed'
|
||||||
|
|
||||||
net_vlan = \
|
|
||||||
CONF.network_feature_enabled.provider_net_base_segmentation_id
|
|
||||||
flavor = self.create_flavor()
|
flavor = self.create_flavor()
|
||||||
|
|
||||||
port = self._create_sriov_port(
|
port = self._create_port_from_vnic_type(
|
||||||
net=self.network,
|
net=self.network,
|
||||||
vnic_type=vnic_type
|
vnic_type=vnic_type
|
||||||
)
|
)
|
||||||
@ -797,7 +621,7 @@ class SRIOVMigration(SRIOVBase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Validate the vlan tag persisted in instance's XML after migration
|
# 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
|
# Confirm dev_type, allocation status, and pci address information are
|
||||||
# correct in pci_devices table of openstack DB
|
# 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
|
# Confirm vlan tag in interface XML, dev_type, allocation status, and
|
||||||
# pci address information are correct in pci_devices table of openstack
|
# pci address information are correct in pci_devices table of openstack
|
||||||
# DB after second migration
|
# 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(
|
self._verify_neutron_port_binding(
|
||||||
server['id'],
|
server['id'],
|
||||||
port['port']['id']
|
port['port']['id']
|
||||||
@ -858,8 +682,13 @@ class SRIOVAttachAndDetach(SRIOVBase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(SRIOVAttachAndDetach, self).setUp()
|
super(SRIOVAttachAndDetach, self).setUp()
|
||||||
self.sriov_network = self._create_sriov_net()
|
self.vlan_id = \
|
||||||
self._create_sriov_subnet(self.sriov_network['network']['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
|
@classmethod
|
||||||
def skip_checks(cls):
|
def skip_checks(cls):
|
||||||
@ -999,11 +828,9 @@ class SRIOVAttachAndDetach(SRIOVBase):
|
|||||||
|
|
||||||
# Gather SR-IOV network vlan, create two guests, and create an SR-IOV
|
# Gather SR-IOV network vlan, create two guests, and create an SR-IOV
|
||||||
# port based on the provided vnic_type
|
# port based on the provided vnic_type
|
||||||
net_vlan = \
|
|
||||||
CONF.network_feature_enabled.provider_net_base_segmentation_id
|
|
||||||
servers = [self.create_server_and_ssh(),
|
servers = [self.create_server_and_ssh(),
|
||||||
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,
|
net=self.sriov_network,
|
||||||
vnic_type=vnic_type
|
vnic_type=vnic_type
|
||||||
)
|
)
|
||||||
@ -1032,7 +859,7 @@ class SRIOVAttachAndDetach(SRIOVBase):
|
|||||||
|
|
||||||
# Verify the port's VLAN tag is present in the XML
|
# Verify the port's VLAN tag is present in the XML
|
||||||
self._validate_port_xml_vlan_tag(interface_xml_element,
|
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
|
# Confirm the vendor and vf product id are present in the guest
|
||||||
self._check_device_in_guest(
|
self._check_device_in_guest(
|
||||||
@ -1086,7 +913,7 @@ class SRIOVAttachAndDetach(SRIOVBase):
|
|||||||
# direct-physical
|
# direct-physical
|
||||||
servers = [self.create_server_and_ssh(),
|
servers = [self.create_server_and_ssh(),
|
||||||
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,
|
net=self.sriov_network,
|
||||||
vnic_type='direct-physical'
|
vnic_type='direct-physical'
|
||||||
)
|
)
|
||||||
|
85
whitebox_tempest_plugin/api/compute/test_vdpa.py
Normal file
85
whitebox_tempest_plugin/api/compute/test_vdpa.py
Normal file
@ -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']
|
||||||
|
)
|
@ -269,6 +269,18 @@ hardware_opts = [
|
|||||||
'sriov_physnet',
|
'sriov_physnet',
|
||||||
default=None,
|
default=None,
|
||||||
help='The physnet to use when creating sr-iov ports'),
|
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(
|
cfg.IntOpt(
|
||||||
'physnet_numa_affinity',
|
'physnet_numa_affinity',
|
||||||
default=None,
|
default=None,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user