objects: Store InstancePCIRequest.numa_policy in DB

In change I9360fe29908, we added the 'numa_policy' field to the
'InstancePCIRequest' object. Unfortunately we did not update the
(de)serialization logic for the 'InstancePCIRequests' object, meaning
this field was never saved to the database. As a result, claiming will
always fail [1].

The resolution is simple - add the (de)serialization logic and tests to
prevent regression.

[1] https://github.com/openstack/nova/blob/18.0.0/nova/compute/resource_tracker.py#L214-L215

Change-Id: Id4d8ecb8fee46b21590ebcc62a2850030cef6508
Closes-Bug: #1805891
This commit is contained in:
Stephen Finucane 2018-12-11 16:01:38 +00:00
parent 6304bcf781
commit 59d9463351
4 changed files with 105 additions and 35 deletions

View File

@ -90,6 +90,8 @@ class InstancePCIRequests(base.NovaObject,
request_obj = InstancePCIRequest(
count=request['count'], spec=request['spec'],
alias_name=request['alias_name'], is_new=False,
numa_policy=request.get('numa_policy',
fields.PCINUMAAffinityPolicy.LEGACY),
request_id=request['request_id'],
requester_id=request.get('requester_id'))
request_obj.obj_reset_changes()
@ -142,6 +144,7 @@ class InstancePCIRequests(base.NovaObject,
'spec': x.spec,
'alias_name': x.alias_name,
'is_new': x.is_new,
'numa_policy': x.numa_policy,
'request_id': x.request_id,
'requester_id': x.requester_id} for x in self.requests]
return jsonutils.dumps(blob)

View File

@ -26,27 +26,32 @@ CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class SRIOVServersTest(base.ServersTestBase):
class _PCIServersTestBase(base.ServersTestBase):
vfs_alias_name = 'vfs'
pfs_alias_name = 'pfs'
pci_passthrough_whitelist = [
'{"vendor_id":"8086", "product_id":"1528"}',
'{"vendor_id":"8086", "product_id":"1515"}',
]
# PFs will be removed from pools unless they are specifically
# requested, so we explicitly request them with the 'device_type'
# attribute
pci_alias = [
'{"vendor_id":"8086", "product_id":"1528", "name":"%s", '
'"device_type":"%s"}' % (
pfs_alias_name, fields.PciDeviceType.SRIOV_PF),
'{"vendor_id":"8086", "product_id":"1515", "name":"%s"}' % (
vfs_alias_name),
]
def setUp(self):
white_list = ['{"vendor_id":"8086","product_id":"1528"}',
'{"vendor_id":"8086","product_id":"1515"}']
self.flags(passthrough_whitelist=white_list, group='pci')
self.flags(passthrough_whitelist=self.pci_passthrough_whitelist,
alias=self.pci_alias,
group='pci')
# PFs will be removed from pools, unless these has been specifically
# requested. This is especially needed in cases where PFs and VFs have
# the same vendor/product id
pci_alias = ['{"vendor_id":"8086", "product_id":"1528", "name":"%s",'
' "device_type":"%s"}' % (self.pfs_alias_name,
fields.PciDeviceType.SRIOV_PF),
'{"vendor_id":"8086", "product_id":"1515", "name":"%s"}' %
self.vfs_alias_name]
self.flags(alias=pci_alias, group='pci')
super(SRIOVServersTest, self).setUp()
super(_PCIServersTestBase, self).setUp()
self.compute_started = False
@ -63,10 +68,12 @@ class SRIOVServersTest(base.ServersTestBase):
def _setup_scheduler_service(self):
# Enable the 'NUMATopologyFilter', 'PciPassthroughFilter'
enabled_filters = CONF.filter_scheduler.enabled_filters + [
'NUMATopologyFilter', 'PciPassthroughFilter']
self.flags(driver='filter_scheduler', group='scheduler')
self.flags(enabled_filters=CONF.filter_scheduler.enabled_filters
+ ['NUMATopologyFilter', 'PciPassthroughFilter'],
group='filter_scheduler')
self.flags(enabled_filters=enabled_filters, group='filter_scheduler')
return self.start_service('scheduler')
def _run_build_test(self, flavor_id, end_status='ACTIVE'):
@ -103,6 +110,9 @@ class SRIOVServersTest(base.ServersTestBase):
self.addCleanup(self._delete_server, created_server_id)
return created_server
class SRIOVServersTest(_PCIServersTestBase):
@mock.patch('nova.virt.libvirt.LibvirtDriver._create_image')
def test_create_server_with_VF(self, img_mock):
@ -177,6 +187,9 @@ class SRIOVServersTest(base.ServersTestBase):
self._run_build_test(flavor_id_vfs)
self._run_build_test(flavor_id_pfs, end_status='ERROR')
class PCIServersTest(_PCIServersTestBase):
@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
@ -190,11 +203,11 @@ class SRIOVServersTest(base.ServersTestBase):
fake_connection = self._get_connection(host_info, pci_info)
self.mock_conn.return_value = fake_connection
# 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'}
# create a flavor
extra_spec = {
'hw:cpu_policy': 'dedicated',
'pci_passthrough:alias': '%s:1' % self.pfs_alias_name,
}
flavor_id = self._create_flavor(extra_spec=extra_spec)
self._run_build_test(flavor_id)
@ -212,15 +225,64 @@ class SRIOVServersTest(base.ServersTestBase):
fake_connection = self._get_connection(host_info, pci_info)
self.mock_conn.return_value = fake_connection
# 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)
# boot one instance with no PCI device to "fill up" NUMA node 0
extra_spec = {
'hw:cpu_policy': 'dedicated',
}
flavor_id = self._create_flavor(vcpu=4, extra_spec=extra_spec)
self._run_build_test(vm_flavor_id)
self._run_build_test(pf_flavor_id, end_status='ERROR')
self._run_build_test(flavor_id)
# now boot one with a PCI device, which should fail to boot
extra_spec['pci_passthrough:alias'] = '%s:1' % self.pfs_alias_name
flavor_id = self._create_flavor(extra_spec=extra_spec)
self._run_build_test(flavor_id, end_status='ERROR')
class PCIServersWithNUMAPoliciesTest(_PCIServersTestBase):
# PFs will be removed from pools unless they are specifically
# requested, so we explicitly request them with the 'device_type'
# attribute
pci_alias = [
'{"vendor_id":"8086", "product_id":"1528", "name":"%s", '
'"device_type":"%s", "numa_policy":"%s"}' % (
_PCIServersTestBase.pfs_alias_name,
fields.PciDeviceType.SRIOV_PF,
fields.PCINUMAAffinityPolicy.PREFERRED
),
'{"vendor_id":"8086", "product_id":"1515", "name":"%s"}' % (
_PCIServersTestBase.vfs_alias_name),
]
@mock.patch('nova.virt.libvirt.LibvirtDriver._create_image')
def test_create_server_with_pci_dev_and_numa(self, img_mock):
"""Validate behavior of 'preferred' PCI NUMA policy.
This test ensures that it *is* possible to allocate CPU and memory
resources from one NUMA node and a PCI device from another *if* PCI
NUMA policies are in use.
"""
host_info = fakelibvirt.NUMAHostInfo(cpu_nodes=2, cpu_sockets=1,
cpu_cores=2, cpu_threads=2,
kB_mem=15740000)
pci_info = fakelibvirt.HostPciSRIOVDevicesInfo(num_pfs=1, numa_node=0)
fake_connection = self._get_connection(host_info, pci_info)
self.mock_conn.return_value = fake_connection
# boot one instance with no PCI device to "fill up" NUMA node 0
extra_spec = {
'hw:cpu_policy': 'dedicated',
}
flavor_id = self._create_flavor(vcpu=4, extra_spec=extra_spec)
self._run_build_test(flavor_id)
# now boot one with a PCI device, which should succeed thanks to the
# use of the PCI policy
extra_spec['pci_passthrough:alias'] = '%s:1' % self.pfs_alias_name
flavor_id = self._create_flavor(extra_spec=extra_spec)
self._run_build_test(flavor_id)

View File

@ -574,7 +574,7 @@ class _TestInstanceObject(object):
expected_json = ('[{"count": 1, "alias_name": null, "is_new": false,'
'"request_id": null, "requester_id": null,'
'"spec": [{"vendor_id": "8086", '
'"product_id": "1502"}]}]')
'"product_id": "1502"}], "numa_policy": null}]')
inst = objects.Instance()
inst = objects.Instance._from_db_object(self.context, inst,

View File

@ -32,12 +32,14 @@ fake_pci_requests = [
'device_id': '1502'}],
'alias_name': 'alias_1',
'is_new': False,
'numa_policy': 'preferred',
'request_id': FAKE_REQUEST_UUID},
{'count': 2,
'spec': [{'vendor_id': '6502',
'device_id': '07B5'}],
'alias_name': 'alias_2',
'is_new': True,
'numa_policy': 'preferred',
'request_id': FAKE_REQUEST_UUID,
'requester_id': uuids.requester_id},
]
@ -71,6 +73,8 @@ class _TestInstancePCIRequests(object):
request.count)
self.assertEqual(fake_pci_requests[index]['spec'],
[dict(x.items()) for x in request.spec])
self.assertEqual(fake_pci_requests[index]['numa_policy'],
request.numa_policy)
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance_uuid')
def test_get_by_instance_current(self, mock_get):
@ -118,6 +122,7 @@ class _TestInstancePCIRequests(object):
self.assertEqual(FAKE_UUID, req.instance_uuid)
self.assertEqual(2, len(req.requests))
self.assertEqual('alias_1', req.requests[0].alias_name)
self.assertEqual('preferred', req.requests[0].numa_policy)
self.assertIsNone(None, req.requests[0].requester_id)
self.assertEqual(uuids.requester_id, req.requests[1].requester_id)