tests: verify pci passthrough with numa

Adding a functional test to verify the allocation
of pci devices with the numa a topology in play.

Change-Id: I1c6f922bd31937521815758bd0794fe4a28a0ed9
This commit is contained in:
Vladik Romanovsky 2016-08-16 11:07:26 -04:00
parent b0a451d428
commit 61bcb831af
2 changed files with 82 additions and 7 deletions

View File

@ -256,3 +256,69 @@ class SRIOVServersTest(ServersTestBase):
self._delete_server(pf_server['id'])
self._delete_server(vf_server['id'])
@mock.patch('nova.virt.libvirt.LibvirtDriver._create_image')
def test_create_server_with_pci_dev_and_numa(self, img_mock):
"""Verifies that an instance can be booted with cpu pinning and with an
assigned pci device.
"""
host_info = NumaHostInfo(cpu_nodes=2, cpu_sockets=1, cpu_cores=2,
cpu_threads=2, kB_mem=15740000)
pci_info = fakelibvirt.HostPciSRIOVDevicesInfo()
pci_info.create_pci_devices(num_pfs=1, numa_node=1)
fake_connection = self._get_connection(host_info, pci_info)
# Create a flavor
extra_spec = {"pci_passthrough:alias": "%s:1" % self.pfs_alias_name,
'hw:numa_nodes': '1',
'hw:cpu_policy': 'dedicated',
'hw:cpu_thread_policy': 'prefer'}
flavor_id = self._create_flavor(extra_spec=extra_spec)
host_pass_mock = self._get_pci_passthrough_filter_spy()
with test.nested(
mock.patch('nova.virt.libvirt.host.Host.get_connection',
return_value=fake_connection),
mock.patch('nova.scheduler.filters'
'.pci_passthrough_filter.PciPassthroughFilter'
'.host_passes',
side_effect=host_pass_mock)) as (conn_mock,
filter_mock):
pf_server = self._run_build_test(flavor_id, filter_mock)
self._delete_server(pf_server['id'])
@mock.patch('nova.virt.libvirt.LibvirtDriver._create_image')
def test_create_server_with_pci_dev_and_numa_fails(self, img_mock):
"""This test ensures that it is not possible to allocated CPU and
memory resources from one NUMA node and a PCI device from another.
"""
host_info = NumaHostInfo(cpu_nodes=2, cpu_sockets=1, cpu_cores=2,
cpu_threads=2, kB_mem=15740000)
pci_info = fakelibvirt.HostPciSRIOVDevicesInfo()
pci_info.create_pci_devices(num_pfs=1, numa_node=0)
fake_connection = self._get_connection(host_info, pci_info)
# Create a flavor
extra_spec_vm = {'hw:cpu_policy': 'dedicated',
'hw:numa_node': '1'}
extra_spec = {'pci_passthrough:alias': '%s:1' % self.pfs_alias_name,
'hw:numa_nodes': '1',
'hw:cpu_policy': 'dedicated',
'hw:cpu_thread_policy': 'prefer'}
vm_flavor_id = self._create_flavor(vcpu=4, extra_spec=extra_spec_vm)
pf_flavor_id = self._create_flavor(extra_spec=extra_spec)
host_pass_mock = self._get_pci_passthrough_filter_spy()
with test.nested(
mock.patch('nova.virt.libvirt.host.Host.get_connection',
return_value=fake_connection),
mock.patch('nova.scheduler.filters'
'.pci_passthrough_filter.PciPassthroughFilter'
'.host_passes',
side_effect=host_pass_mock)) as (conn_mock,
filter_mock):
vm_server = self._run_build_test(vm_flavor_id, filter_mock)
pf_server = self._run_build_test(pf_flavor_id, filter_mock,
end_status='ERROR')
self._delete_server(vm_server['id'])
self._delete_server(pf_server['id'])

View File

@ -189,7 +189,7 @@ class FakePciDevice(object):
<iommuGroup number='%(group_id)d'>
<address domain='0x0000' bus='0x81' slot='0x%(slot)s' function='0x%(dev)d'/>
</iommuGroup>
<numa node='0'/>
<numa node='%(numa_node)s'/>
<pci-express>
<link validity='cap' port='0' speed='5' width='8'/>
<link validity='sta' speed='5' width='8'/>
@ -197,7 +197,7 @@ class FakePciDevice(object):
</capability>
</device>"""
def __init__(self, dev_type, vf_ratio, group, dev, product_id):
def __init__(self, dev_type, vf_ratio, group, dev, product_id, numa_node):
"""Populate pci devices
:param dev_type: (string) Indicates the type of the device (PF, VF)
@ -205,6 +205,7 @@ class FakePciDevice(object):
:param group: (int) iommu group id
:param dev: (int) function number of the device
:param product_id: (int) Device product ID
:param numa_node: (int) NUMA node of the device
"""
addr_templ = (" <address domain='0x0000' bus='0x81' slot='0x%(slot)s'"
" function='0x%(dev)d'/>")
@ -218,7 +219,7 @@ class FakePciDevice(object):
'prod': product_id, 'group_id': group,
'functions': '\n'.join(pf_caps), 'slot': 0,
'cap_type': PF_CAP_TYPE, 'prod_name': PF_PROD_NAME,
'driver': PF_DRIVER_NAME}
'driver': PF_DRIVER_NAME, 'numa_node': numa_node}
elif dev_type == 'VF':
vf_caps = [addr_templ % {'dev': int(dev / vf_ratio),
'slot': PF_SLOT}]
@ -226,7 +227,7 @@ class FakePciDevice(object):
'prod': product_id, 'group_id': group,
'functions': '\n'.join(vf_caps), 'slot': VF_SLOT,
'cap_type': VF_CAP_TYPE, 'prod_name': VF_PROD_NAME,
'driver': VF_DRIVER_NAME}
'driver': VF_DRIVER_NAME, 'numa_node': numa_node}
def XMLDesc(self, flags):
return self.pci_dev
@ -238,7 +239,8 @@ class HostPciSRIOVDevicesInfo(object):
self.sriov_devices = {}
def create_pci_devices(self, vf_product_id=1515, pf_product_id=1528,
num_pfs=2, num_vfs=8, group=47):
num_pfs=2, num_vfs=8, group=47, numa_node=None,
total_numa_nodes=2):
"""Populate pci devices
:param vf_product_id: (int) Product ID of the Virtual Functions
@ -246,7 +248,12 @@ class HostPciSRIOVDevicesInfo(object):
:param num_pfs: (int) The number of the Physical Functions
:param num_vfs: (int) The number of the Virtual Functions
:param group: (int) Initial group id
:param numa_node: (int) NUMA node of the device, if set all of the
device will be created in the provided node
:param total_numa_nodes: (int) total number of NUMA nodes
"""
def _calc_numa_node(dev):
return dev % total_numa_nodes if numa_node is None else numa_node
vf_ratio = num_vfs / num_pfs
@ -257,7 +264,8 @@ class HostPciSRIOVDevicesInfo(object):
'dev': dev}
self.sriov_devices[pci_dev_name] = FakePciDevice('PF', vf_ratio,
dev_group, dev,
pf_product_id)
pf_product_id,
_calc_numa_node(dev))
# Generate VFs
for dev in range(num_vfs):
@ -266,7 +274,8 @@ class HostPciSRIOVDevicesInfo(object):
'dev': dev}
self.sriov_devices[pci_dev_name] = FakePciDevice('VF', vf_ratio,
dev_group, dev,
vf_product_id)
vf_product_id,
_calc_numa_node(dev))
def get_all_devices(self):
return self.sriov_devices.keys()