Add get_instance_pci_request_from_vif

This change extends nova/pci/request.py with a method
that retrieves an instance's PCI request from a given VIF
if the given VIF required a PCI allocation during instance
creation.

The PCI request, if retrieved, belongs to a PCI device
in the compute node where the instance is running.

The change is required to facilitate SR-IOV live migration
allowing to claim VIF related PCI resources on
the destination node.

Change-Id: I9ba475e91b8283f063db446de74d3e4b2de002c5
Partial-Implements: blueprint libvirt-neutron-sriov-livemigration
This commit is contained in:
Adrian Chiris 2018-11-25 13:15:23 +02:00
parent a7fae371a7
commit 30295f1a14
3 changed files with 191 additions and 1 deletions

View File

@ -1804,6 +1804,11 @@ class PciConfigInvalidWhitelist(Invalid):
msg_fmt = _("Invalid PCI devices Whitelist config: %(reason)s")
class PciRequestFromVIFNotFound(NotFound):
msg_fmt = _("Failed to locate PCI request associated with the given VIF "
"PCI address: %(pci_slot)s on compute node: %(node_id)s")
# Cannot be templated, msg needs to be constructed when raised.
class InternalError(NovaException):
"""Generic hypervisor errors.

View File

@ -39,6 +39,7 @@
"""
import jsonschema
from oslo_log import log as logging
from oslo_serialization import jsonutils
import six
@ -50,6 +51,7 @@ from nova import objects
from nova.objects import fields as obj_fields
from nova.pci import utils
LOG = logging.getLogger(__name__)
PCI_NET_TAG = 'physical_network'
PCI_TRUSTED_TAG = 'trusted'
PCI_DEVICE_TYPE_TAG = 'dev_type'
@ -171,6 +173,60 @@ def _translate_alias_to_requests(alias_spec):
return pci_requests
def get_instance_pci_request_from_vif(context, instance, vif):
"""Given an Instance, return the PCI request associated
to the PCI device related to the given VIF (if any) on the
compute node the instance is currently running.
In this method we assume a VIF is associated with a PCI device
if 'pci_slot' attribute exists in the vif 'profile' dict.
:param context: security context
:param instance: instance object
:param vif: network VIF model object
:raises: raises PciRequestFromVIFNotFound if a pci device is requested
but not found on current host
:return: instance's PCIRequest object associated with the given VIF
or None if no PCI device is requested
"""
# Get PCI device address for VIF if exists
vif_pci_dev_addr = vif['profile'].get('pci_slot') \
if vif['profile'] else None
if not vif_pci_dev_addr:
return None
try:
cn_id = objects.ComputeNode.get_by_host_and_nodename(
context,
instance.host,
instance.node).id
except exception.NotFound:
LOG.warning("expected to find compute node with host %s "
"and node %s when getting instance PCI request "
"from VIF", instance.host, instance.node)
return None
# Find PCIDevice associated with vif_pci_dev_addr on the compute node
# the instance is running on.
found_pci_dev = None
for pci_dev in instance.pci_devices:
if (pci_dev.compute_node_id == cn_id and
pci_dev.address == vif_pci_dev_addr):
found_pci_dev = pci_dev
break
if not found_pci_dev:
return None
# Find PCIRequest associated with the given PCIDevice in instance
for pci_req in instance.pci_requests.requests:
if pci_req.request_id == found_pci_dev.request_id:
return pci_req
raise exception.PciRequestFromVIFNotFound(
pci_slot=vif_pci_dev_addr,
node_id=cn_id)
def get_pci_requests_from_flavor(flavor):
"""Validate and return PCI requests.

View File

@ -15,9 +15,17 @@
"""Tests for PCI request."""
import mock
from oslo_utils.fixture import uuidsentinel
from nova import context
from nova import exception
from nova.network import model
from nova import objects
from nova.pci import request
from nova import test
from nova.tests.unit.api.openstack import fakes
_fake_alias1 = """{
@ -62,7 +70,35 @@ _fake_alias4 = """{
}"""
class AliasTestCase(test.NoDBTestCase):
class PciRequestTestCase(test.NoDBTestCase):
@staticmethod
def _create_fake_inst_with_pci_devs(pci_req_list, pci_dev_list):
"""Create a fake Instance object with the provided InstancePciRequests
and PciDevices.
:param pci_req_list: a list of InstancePCIRequest objects.
:param pci_dev_list: a list of PciDevice objects, each element
associated (via request_id attribute)with a corresponding
element from pci_req_list.
:return: A fake Instance object associated with the provided
PciRequests and PciDevices.
"""
inst = objects.Instance()
inst.uuid = uuidsentinel.instance1
inst.pci_requests = objects.InstancePCIRequests(
requests=pci_req_list)
inst.pci_devices = objects.PciDeviceList(objects=pci_dev_list)
inst.host = 'fake-host'
inst.node = 'fake-node'
return inst
def setUp(self):
super(PciRequestTestCase, self).setUp()
self.context = context.RequestContext(fakes.FAKE_USER_ID,
fakes.FAKE_PROJECT_ID)
self.mock_inst_cn = mock.Mock()
def test_valid_alias(self):
self.flags(alias=[_fake_alias1], group='pci')
@ -232,6 +268,99 @@ class AliasTestCase(test.NoDBTestCase):
request._translate_alias_to_requests,
"QuicAssistX : 3")
@mock.patch.object(objects.compute_node.ComputeNode,
'get_by_host_and_nodename')
def test_get_instance_pci_request_from_vif_invalid(
self,
cn_get_by_host_and_node):
# Basically make sure we raise an exception if an instance
# has an allocated PCI device without having the its corresponding
# PCIRequest object in instance.pci_requests
self.mock_inst_cn.id = 1
cn_get_by_host_and_node.return_value = self.mock_inst_cn
# Create a fake instance with PCI request and allocated PCI devices
pci_dev1 = objects.PciDevice(request_id=uuidsentinel.pci_req_id1,
address='0000:04:00.0',
compute_node_id=1)
pci_req2 = objects.InstancePCIRequest(
request_id=uuidsentinel.pci_req_id2)
pci_dev2 = objects.PciDevice(request_id=uuidsentinel.pci_req_id2,
address='0000:05:00.0',
compute_node_id=1)
pci_request_list = [pci_req2]
pci_device_list = [pci_dev1, pci_dev2]
inst = PciRequestTestCase._create_fake_inst_with_pci_devs(
pci_request_list,
pci_device_list)
# Create a VIF with pci_dev1 that has no corresponding PCI request
pci_vif = model.VIF(vnic_type=model.VNIC_TYPE_DIRECT,
profile={'pci_slot': '0000:04:00.0'})
self.assertRaises(exception.PciRequestFromVIFNotFound,
request.get_instance_pci_request_from_vif,
self.context,
inst,
pci_vif)
@mock.patch.object(objects.compute_node.ComputeNode,
'get_by_host_and_nodename')
def test_get_instance_pci_request_from_vif(self, cn_get_by_host_and_node):
self.mock_inst_cn.id = 1
cn_get_by_host_and_node.return_value = self.mock_inst_cn
# Create a fake instance with PCI request and allocated PCI devices
pci_req1 = objects.InstancePCIRequest(
request_id=uuidsentinel.pci_req_id1)
pci_dev1 = objects.PciDevice(request_id=uuidsentinel.pci_req_id1,
address='0000:04:00.0',
compute_node_id = 1)
pci_req2 = objects.InstancePCIRequest(
request_id=uuidsentinel.pci_req_id2)
pci_dev2 = objects.PciDevice(request_id=uuidsentinel.pci_req_id2,
address='0000:05:00.0',
compute_node_id=1)
pci_request_list = [pci_req1, pci_req2]
pci_device_list = [pci_dev1, pci_dev2]
inst = PciRequestTestCase._create_fake_inst_with_pci_devs(
pci_request_list,
pci_device_list)
# Create a vif with normal port and make sure no PCI request returned
normal_vif = model.VIF(vnic_type=model.VNIC_TYPE_NORMAL)
self.assertIsNone(request.get_instance_pci_request_from_vif(
self.context,
inst,
normal_vif))
# Create a vif with PCI address under profile, make sure the correct
# PCI request is returned
pci_vif = model.VIF(vnic_type=model.VNIC_TYPE_DIRECT,
profile={'pci_slot': '0000:05:00.0'})
self.assertEqual(uuidsentinel.pci_req_id2,
request.get_instance_pci_request_from_vif(
self.context,
inst,
pci_vif).request_id)
# Create a vif with PCI under profile which is not claimed
# for the instance, i.e no matching pci device in instance.pci_devices
nonclaimed_pci_vif = model.VIF(vnic_type=model.VNIC_TYPE_DIRECT,
profile={'pci_slot': '0000:08:00.0'})
self.assertIsNone(request.get_instance_pci_request_from_vif(
self.context,
inst,
nonclaimed_pci_vif))
# "Move" the instance to another compute node, make sure that no
# matching PCI request against the new compute.
self.mock_inst_cn.id = 2
self.assertIsNone(request.get_instance_pci_request_from_vif(
self.context,
inst,
pci_vif))
def test_get_pci_requests_from_flavor(self):
self.flags(alias=[_fake_alias1, _fake_alias3], group='pci')
expect_request = [