Reject devname based device_spec config

We agreed not to support 'devname' based [pci]device_spec configuration
in the new PCI Placement tracking code. So this patch adds a check to
reject those.

blueprint: pci-device-tracking-in-placement
Change-Id: Ifa0dd34506ebc25cfe5bafd6952b72b0008fc741
This commit is contained in:
Balazs Gibizer 2022-06-18 15:15:20 +02:00
parent 10ba714125
commit 01d7a39e00
5 changed files with 58 additions and 0 deletions

View File

@ -386,4 +386,12 @@ be added to the resource provider representing the matching PCI devices.
enforce the same rule for the configuration as well and will refuse to
start if both the parent PF and its VFs are configured.
.. important::
While nova supported configuring PCI devices by device name via the
``devname`` parameter in :oslo.config:option:`pci.device_spec` in the past,
this proved to be problematic as the netdev name of a PCI device could
change for multiple reasons during hypervisor reboot. So since nova 26.0.0
(Zed) the nova-compute service will refuse to start with such configuration.
It is suggested to use the PCI address of the device instead.
For deeper technical details please read the `nova specification. <https://specs.openstack.org/openstack/nova-specs/specs/zed/approved/pci-device-tracking-in-placement.html>`_

View File

@ -22,6 +22,7 @@ from nova import exception
from nova.i18n import _
from nova.objects import fields
from nova.objects import pci_device
from nova.pci import devspec
from nova.pci import manager as pci_manager
@ -278,6 +279,18 @@ class PlacementView:
rp.update_provider_tree(provider_tree)
def ensure_no_dev_spec_with_devname(dev_specs: ty.List[devspec.PciDeviceSpec]):
for dev_spec in dev_specs:
if dev_spec.dev_spec_conf.get("devname"):
msg = _(
"Invalid [pci]device_spec configuration. PCI Placement "
"reporting does not support 'devname' based device "
"specification but we got %(dev_spec)s. "
"Please use PCI address in the configuration instead."
) % {"dev_spec": dev_spec.dev_spec_conf}
raise exception.PlacementPciException(error=msg)
def update_provider_tree_for_pci(
provider_tree: provider_tree.ProviderTree,
nodename: str,
@ -317,6 +330,8 @@ def update_provider_tree_for_pci(
# If tracking is not enabled we just return without touching anything
return False
ensure_no_dev_spec_with_devname(pci_tracker.dev_filter.specs)
LOG.debug(
'Collecting PCI inventories and allocations to track them in Placement'
)

View File

@ -12,6 +12,7 @@
# under the License.
import abc
import copy
import re
import string
import typing as ty
@ -267,6 +268,10 @@ class WhitelistPciAddress(object):
class PciDeviceSpec(PciAddressSpec):
def __init__(self, dev_spec: ty.Dict[str, str]) -> None:
# stored for better error reporting
self.dev_spec_conf = copy.deepcopy(dev_spec)
# the non tag fields (i.e. address, devname) will be removed by
# _init_dev_details
self.tags = dev_spec
self._init_dev_details()

View File

@ -51,6 +51,12 @@ class PlacementPCIReportingTests(test_pci_sriov_servers._PCIServersTestBase):
# These tests should not depend on the host's sysfs
self.useFixture(
fixtures.MockPatch('nova.pci.utils.is_physical_function'))
self.useFixture(
fixtures.MockPatch(
'nova.pci.utils.get_function_by_ifname',
return_value=(None, False)
)
)
@staticmethod
def _to_device_spec_conf(spec_list):
@ -309,3 +315,26 @@ class PlacementPCIReportingTests(test_pci_sriov_servers._PCIServersTestBase):
inventories={},
traits={},
)
def test_devname_based_dev_spec_rejected(self):
device_spec = self._to_device_spec_conf(
[
{
"devname": "eth0",
},
]
)
self.flags(group='pci', device_spec=device_spec)
ex = self.assertRaises(
exception.PlacementPciException,
self.start_compute,
hostname="compute1",
)
self.assertIn(
" Invalid [pci]device_spec configuration. PCI Placement reporting "
"does not support 'devname' based device specification but we got "
"{'devname': 'eth0'}. Please use PCI address in the configuration "
"instead.",
str(ex)
)

View File

@ -50,6 +50,7 @@ class TestTranslator(test.NoDBTestCase):
)
# So we have a device but there is no spec for it
pci_tracker.dev_filter.get_devspec = mock.Mock(return_value=None)
pci_tracker.dev_filter.specs = []
# we expect that the provider_tree is not touched as the device without
# spec is skipped, we assert that with the NonCallableMock
provider_tree = mock.NonCallableMock()