From 61bcb831afa93585782f91538cadb54f60bdf816 Mon Sep 17 00:00:00 2001 From: Vladik Romanovsky Date: Tue, 16 Aug 2016 11:07:26 -0400 Subject: [PATCH] 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 --- .../libvirt/test_pci_sriov_servers.py | 66 +++++++++++++++++++ nova/tests/unit/virt/libvirt/fakelibvirt.py | 23 +++++-- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/nova/tests/functional/libvirt/test_pci_sriov_servers.py b/nova/tests/functional/libvirt/test_pci_sriov_servers.py index 6bd62679e689..3209dc6d9b08 100644 --- a/nova/tests/functional/libvirt/test_pci_sriov_servers.py +++ b/nova/tests/functional/libvirt/test_pci_sriov_servers.py @@ -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']) diff --git a/nova/tests/unit/virt/libvirt/fakelibvirt.py b/nova/tests/unit/virt/libvirt/fakelibvirt.py index 92c514dec14e..a83fac1368fa 100644 --- a/nova/tests/unit/virt/libvirt/fakelibvirt.py +++ b/nova/tests/unit/virt/libvirt/fakelibvirt.py @@ -189,7 +189,7 @@ class FakePciDevice(object):
- + @@ -197,7 +197,7 @@ class FakePciDevice(object): """ - 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 = ("
") @@ -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()