Rename [pci]passthrough_whitelist to device_spec

A later patch in the pci-device-tracking-in-placement work
will extend the existing [pci]passthrough_whitelist config syntax.
So we take the opportunity here to deprecate the old non inclusive
passthrough_whitelist name and introduce a better one.

All the usage of CONF.pci.passthrough_whitelist is now changed over to
the new device_spec config. Also the in tree documentation is updated
accordinly.

However the nova code still has a bunch of references to the
"whitelist" terminology. That will be handled in subsequent patches.

blueprint: pci-device-tracking-in-placement
Change-Id: I843032e113642416114f169069eebf6a56ed78dd
This commit is contained in:
Balazs Gibizer 2022-05-30 12:01:29 +02:00
parent 57c253a609
commit 14e68ac6e9
17 changed files with 174 additions and 118 deletions

View File

@ -46,7 +46,7 @@ A full guide on configuring and using SR-IOV is provided in the
**Limitations**
* Only VFs are supported and they must be tagged in the Nova Compute
configuration in the ``passthrough_whitelist`` option as
configuration in the :oslo.config:option:`pci.device_spec` option as
``remote_managed: "true"``. There is no auto-discovery of this based
on vendor and product IDs;
* Either VF or its respective PF must expose a PCI VPD capability with a

View File

@ -92,15 +92,15 @@ Configure ``nova-compute``
Once PCI passthrough has been configured for the host, :program:`nova-compute`
must be configured to allow the PCI device to pass through to VMs. This is done
using the :oslo.config:option:`pci.passthrough_whitelist` option. For example,
using the :oslo.config:option:`pci.device_spec` option. For example,
assuming our sample PCI device has a PCI address of ``41:00.0`` on each host:
.. code-block:: ini
[pci]
passthrough_whitelist = { "address": "0000:41:00.0" }
device_spec = { "address": "0000:41:00.0" }
Refer to :oslo.config:option:`pci.passthrough_whitelist` for syntax information.
Refer to :oslo.config:option:`pci.device_spec` for syntax information.
Alternatively, to enable passthrough of all devices with the same product and
vendor ID:
@ -108,7 +108,7 @@ vendor ID:
.. code-block:: ini
[pci]
passthrough_whitelist = { "vendor_id": "8086", "product_id": "154d" }
device_spec = { "vendor_id": "8086", "product_id": "154d" }
If using vendor and product IDs, all PCI devices matching the ``vendor_id`` and
``product_id`` are added to the pool of PCI devices available for passthrough
@ -159,7 +159,7 @@ Once configured, restart the :program:`nova-compute` service.
Special Tags
^^^^^^^^^^^^
When specified in :oslo.config:option:`pci.passthrough_whitelist` some tags
When specified in :oslo.config:option:`pci.device_spec` some tags
have special meaning:
``physical_network``

View File

@ -1475,15 +1475,15 @@ class ComputeManager(manager.Manager):
def init_host(self):
"""Initialization for a standalone compute service."""
if CONF.pci.passthrough_whitelist:
# Simply loading the PCI passthrough whitelist will do a bunch of
if CONF.pci.device_spec:
# Simply loading the PCI passthrough spec will do a bunch of
# validation that would otherwise wait until the PciDevTracker is
# constructed when updating available resources for the compute
# node(s) in the resource tracker, effectively killing that task.
# So load up the whitelist when starting the compute service to
# flush any invalid configuration early so we can kill the service
# So load up the spec when starting the compute service to
# flush any invalid configuration early, so we can kill the service
# if the configuration is wrong.
whitelist.Whitelist(CONF.pci.passthrough_whitelist)
whitelist.Whitelist(CONF.pci.device_spec)
nova.conf.neutron.register_dynamic_opts(CONF)
# Even if only libvirt uses them, make it available for all drivers

View File

@ -85,16 +85,18 @@ Possible Values:
"numa_policy": "required"
}
"""),
cfg.MultiStrOpt('passthrough_whitelist',
cfg.MultiStrOpt('device_spec',
default=[],
deprecated_name='pci_passthrough_whitelist',
deprecated_group='DEFAULT',
deprecated_opts=[
cfg.DeprecatedOpt('passthrough_whitelist', group='pci'),
cfg.DeprecatedOpt('pci_passthrough_whitelist', group='DEFAULT'),
],
help="""
White list of PCI devices available to VMs.
Specify the PCI devices available to VMs.
Possible values:
* A JSON dictionary which describe a whitelisted PCI device. It should take
* A JSON dictionary which describe a PCI device. It should take
the following format::
["vendor_id": "<id>",] ["product_id": "<id>",]
@ -146,57 +148,57 @@ Possible values:
Valid examples are::
passthrough_whitelist = {"devname":"eth0",
"physical_network":"physnet"}
passthrough_whitelist = {"address":"*:0a:00.*"}
passthrough_whitelist = {"address":":0a:00.",
"physical_network":"physnet1"}
passthrough_whitelist = {"vendor_id":"1137",
"product_id":"0071"}
passthrough_whitelist = {"vendor_id":"1137",
"product_id":"0071",
"address": "0000:0a:00.1",
"physical_network":"physnet1"}
passthrough_whitelist = {"address":{"domain": ".*",
"bus": "02", "slot": "01",
"function": "[2-7]"},
"physical_network":"physnet1"}
passthrough_whitelist = {"address":{"domain": ".*",
"bus": "02", "slot": "0[1-2]",
"function": ".*"},
"physical_network":"physnet1"}
passthrough_whitelist = {"devname": "eth0", "physical_network":"physnet1",
"trusted": "true"}
passthrough_whitelist = {"vendor_id":"a2d6",
"product_id":"15b3",
"remote_managed": "true"}
passthrough_whitelist = {"vendor_id":"a2d6",
"product_id":"15b3",
"address": "0000:82:00.0",
"physical_network":"physnet1",
"remote_managed": "true"}
device_spec = {"devname":"eth0",
"physical_network":"physnet"}
device_spec = {"address":"*:0a:00.*"}
device_spec = {"address":":0a:00.",
"physical_network":"physnet1"}
device_spec = {"vendor_id":"1137",
"product_id":"0071"}
device_spec = {"vendor_id":"1137",
"product_id":"0071",
"address": "0000:0a:00.1",
"physical_network":"physnet1"}
device_spec = {"address":{"domain": ".*",
"bus": "02", "slot": "01",
"function": "[2-7]"},
"physical_network":"physnet1"}
device_spec = {"address":{"domain": ".*",
"bus": "02", "slot": "0[1-2]",
"function": ".*"},
"physical_network":"physnet1"}
device_spec = {"devname": "eth0", "physical_network":"physnet1",
"trusted": "true"}
device_spec = {"vendor_id":"a2d6",
"product_id":"15b3",
"remote_managed": "true"}
device_spec = {"vendor_id":"a2d6",
"product_id":"15b3",
"address": "0000:82:00.0",
"physical_network":"physnet1",
"remote_managed": "true"}
The following are invalid, as they specify mutually exclusive options::
passthrough_whitelist = {"devname":"eth0",
"physical_network":"physnet",
"address":"*:0a:00.*"}
device_spec = {"devname":"eth0",
"physical_network":"physnet",
"address":"*:0a:00.*"}
The following example is invalid because it specifies the ``remote_managed``
tag for a PF - it will result in an error during config validation at the
Nova Compute service startup::
passthrough_whitelist = {"address": "0000:82:00.0",
"product_id": "a2d6",
"vendor_id": "15b3",
"physical_network": null,
"remote_managed": "true"}
device_spec = {"address": "0000:82:00.0",
"product_id": "a2d6",
"vendor_id": "15b3",
"physical_network": null,
"remote_managed": "true"}
* A JSON list of JSON dictionaries corresponding to the above format. For
example::
passthrough_whitelist = [{"product_id":"0001", "vendor_id":"8086"},
{"product_id":"0002", "vendor_id":"8086"}]
device_spec = [{"product_id":"0001", "vendor_id":"8086"},
{"product_id":"0002", "vendor_id":"8086"}]
""")
]

View File

@ -300,7 +300,7 @@ class API:
self.last_neutron_extension_sync = None
self.extensions = {}
self.pci_whitelist = pci_whitelist.Whitelist(
CONF.pci.passthrough_whitelist)
CONF.pci.device_spec)
def _update_port_with_migration_profile(
self, instance, port_id, port_profile, admin_client):
@ -1638,7 +1638,7 @@ class API:
# through the logs since it is generated per request.
LOG.error('Unable to find PCI device using PCI request ID in '
'list of claimed instance PCI devices: %s. Is the '
'[pci]/passthrough_whitelist configuration correct?',
'[pci]device_spec configuration correct?',
# Convert to a primitive list to stringify it.
list(instance.pci_devices), instance=instance)
raise exception.PciDeviceNotFound(

View File

@ -41,7 +41,7 @@ PCISpecAddressType = ty.Union[ty.Dict[str, str], str]
class PciAddressSpec(metaclass=abc.ABCMeta):
"""Abstract class for all PCI address spec styles
This class checks the address fields of the pci.passthrough_whitelist
This class checks the address fields of the pci.device_spec
"""
def __init__(self, pci_addr: str) -> None:
@ -195,19 +195,19 @@ class PciAddressRegexSpec(PciAddressSpec):
class WhitelistPciAddress(object):
"""Manages the address fields of the whitelist.
This class checks the address fields of the pci.passthrough_whitelist
This class checks the address fields of the pci.device_spec
configuration option, validating the address fields.
Example configs:
| [pci]
| passthrough_whitelist = {"address":"*:0a:00.*",
| "physical_network":"physnet1"}
| passthrough_whitelist = {"address": {"domain": ".*",
"bus": "02",
"slot": "01",
"function": "[0-2]"},
"physical_network":"net1"}
| passthrough_whitelist = {"vendor_id":"1137","product_id":"0071"}
| device_spec = {"address":"*:0a:00.*",
| "physical_network":"physnet1"}
| device_spec = {"address": {"domain": ".*",
"bus": "02",
"slot": "01",
"function": "[0-2]"},
"physical_network":"net1"}
| device_spec = {"vendor_id":"1137","product_id":"0071"}
"""
@ -254,7 +254,7 @@ class WhitelistPciAddress(object):
# Try to match on the parent PCI address if the PciDeviceSpec is a
# PF (sriov is available) and the device to match is a VF. This
# makes it possible to specify the PCI address of a PF in the
# pci.passthrough_whitelist to match any of its VFs' PCI addresses.
# pci.device_spec to match any of its VFs' PCI addresses.
if self.is_physical_function and pci_phys_addr:
pci_phys_addr_obj = PhysicalPciAddress(pci_phys_addr)
if self.pci_address_spec.match(pci_phys_addr_obj):

View File

@ -69,7 +69,7 @@ class PciDevTracker(object):
"""
self.stale: ty.Dict[str, objects.PciDevice] = {}
self.node_id: str = compute_node.id
self.dev_filter = whitelist.Whitelist(CONF.pci.passthrough_whitelist)
self.dev_filter = whitelist.Whitelist(CONF.pci.device_spec)
numa_topology = compute_node.numa_topology
if numa_topology:
# For legacy reasons, the NUMATopology is stored as a JSON blob.
@ -224,7 +224,7 @@ class PciDevTracker(object):
LOG.warning("Unable to remove device with status "
"'%(status)s' and ownership %(instance_uuid)s "
"because of %(pci_exception)s. "
"Check your [pci]passthrough_whitelist "
"Check your [pci]device_spec "
"configuration to make sure this allocated "
"device is whitelisted. If you have removed "
"the device from the whitelist intentionally "

View File

@ -77,7 +77,7 @@ class PciDeviceStats(object):
)
self.pools.sort(key=lambda item: len(item))
self.dev_filter = dev_filter or whitelist.Whitelist(
CONF.pci.passthrough_whitelist)
CONF.pci.device_spec)
def _equal_properties(
self, dev: Pool, entry: Pool, matching_keys: ty.List[str],

View File

@ -44,7 +44,7 @@ class Whitelist(object):
:param whitelist_spec: A JSON string for a dictionary or list thereof.
Each dictionary specifies the pci device properties requirement.
See the definition of ``passthrough_whitelist`` in
See the definition of ``device_spec`` in
``nova.conf.pci`` for details and examples.
"""
if whitelist_spec:

View File

@ -47,9 +47,11 @@ class _PCIServersTestBase(base.ServersTestBase):
def setUp(self):
self.ctxt = context.get_admin_context()
self.flags(passthrough_whitelist=self.PCI_PASSTHROUGH_WHITELIST,
alias=self.PCI_ALIAS,
group='pci')
self.flags(
device_spec=self.PCI_PASSTHROUGH_WHITELIST,
alias=self.PCI_ALIAS,
group='pci'
)
super(_PCIServersTestBase, self).setUp()
@ -1652,9 +1654,11 @@ class PCIServersWithSRIOVAffinityPoliciesTest(_PCIServersTestBase):
}
)]
self.flags(passthrough_whitelist=self.PCI_PASSTHROUGH_WHITELIST,
alias=alias,
group='pci')
self.flags(
device_spec=self.PCI_PASSTHROUGH_WHITELIST,
alias=alias,
group='pci'
)
self._test_policy(pci_numa_node, status, 'required')
@ -1870,9 +1874,11 @@ class PCIServersWithPortNUMAPoliciesTest(_PCIServersTestBase):
}
)]
self.flags(passthrough_whitelist=self.PCI_PASSTHROUGH_WHITELIST,
alias=alias,
group='pci')
self.flags(
device_spec=self.PCI_PASSTHROUGH_WHITELIST,
alias=alias,
group='pci'
)
self._test_policy(pci_numa_node, status, 'required')

View File

@ -459,7 +459,7 @@ class PortResourceRequestBasedSchedulingTestBase(
def _create_sriov_networking_rp_tree(self, hostname, compute_rp_uuid):
# Create a matching RP tree in placement for the PCI devices added to
# the passthrough_whitelist config during setUp() and PCI devices
# the device_spec config during setUp() and PCI devices
# present in the FakeDriverWithPciResources virt driver.
#
# * PF1 represents the PCI device 0000:01:00, it will be mapped to
@ -1362,7 +1362,7 @@ class PortResourceRequestBasedSchedulingTest(
does not have resource request can be allocated to PF2 or PF3.
For the detailed compute host config see the FakeDriverWithPciResources
class. For the necessary passthrough_whitelist config see the setUp of
class. For the necessary device_spec config see the setUp of
the PortResourceRequestBasedSchedulingTestBase class.
"""

View File

@ -5064,13 +5064,16 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase,
msg = mock_log.warning.call_args_list[0]
self.assertIn('appears to not be owned by this host', msg[0][0])
def test_init_host_pci_passthrough_whitelist_validation_failure(self):
# Tests that we fail init_host if there is a pci.passthrough_whitelist
def test_init_host_pci_device_spec_validation_failure(self):
# Tests that we fail init_host if there is a pci.device_spec
# configured incorrectly.
self.flags(passthrough_whitelist=[
# it's invalid to specify both in the same devspec
jsonutils.dumps({'address': 'foo', 'devname': 'bar'})],
group='pci')
self.flags(
device_spec=[
# it's invalid to specify both in the same devspec
jsonutils.dumps({'address': 'foo', 'devname': 'bar'})
],
group='pci'
)
self.assertRaises(exception.PciDeviceInvalidDeviceName,
self.compute.init_host)

View File

@ -8091,13 +8091,21 @@ class TestAPIPortbinding(TestAPIBase):
)
@mock.patch.object(pci_manager, 'get_instance_pci_devs')
def test_pci_parse_whitelist_called_once(
self, mock_get_instance_pci_devs):
white_list = [
'{"address":"0000:0a:00.1","physical_network":"default"}']
cfg.CONF.set_override('passthrough_whitelist', white_list, 'pci')
self, mock_get_instance_pci_devs
):
device_spec = [
jsonutils.dumps(
{
"address": "0000:0a:00.1",
"physical_network": "default",
}
)
]
cfg.CONF.set_override(
'device_spec', device_spec, 'pci')
# NOTE(takashin): neutronapi.API must be initialized
# after the 'passthrough_whitelist' is set in this test case.
# after the 'device_spec' is set in this test case.
api = neutronapi.API()
host_id = 'my_host_id'
instance = {'host': host_id}
@ -8110,7 +8118,7 @@ class TestAPIPortbinding(TestAPIBase):
'dev_type': obj_fields.PciDeviceType.SRIOV_VF,
}
whitelist = pci_whitelist.Whitelist(CONF.pci.passthrough_whitelist)
whitelist = pci_whitelist.Whitelist(CONF.pci.device_spec)
with mock.patch.object(pci_whitelist.Whitelist,
'_parse_white_list_from_config',
wraps=whitelist._parse_white_list_from_config

View File

@ -235,7 +235,7 @@ class PciDevTrackerTestCase(test.NoDBTestCase):
self, mock_debug):
self.flags(
group='pci',
passthrough_whitelist=[
device_spec=[
'{"product_id":"2032", "vendor_id":"8086"}'])
# There are systems where 32 bit PCI domain is used. See bug 1897528
# for example. While nova (and qemu) does not support assigning such

View File

@ -16,6 +16,7 @@
from unittest import mock
from oslo_config import cfg
from oslo_serialization import jsonutils
from nova import exception
from nova import objects
@ -444,9 +445,9 @@ class PciDeviceStatsTestCase(test.NoDBTestCase):
@mock.patch(
'nova.pci.whitelist.Whitelist._parse_white_list_from_config')
def test_white_list_parsing(self, mock_whitelist_parse):
white_list = '{"product_id":"0001", "vendor_id":"8086"}'
CONF.set_override('passthrough_whitelist', white_list, 'pci')
def test_device_spec_parsing(self, mock_whitelist_parse):
device_spec = {"product_id": "0001", "vendor_id": "8086"}
CONF.set_override('device_spec', jsonutils.dumps(device_spec), 'pci')
pci_stats = stats.PciDeviceStats(objects.NUMATopology())
pci_stats.add_device(self.fake_dev_2)
pci_stats.remove_device(self.fake_dev_2)
@ -457,16 +458,34 @@ class PciDeviceStatsWithTagsTestCase(test.NoDBTestCase):
def setUp(self):
super(PciDeviceStatsWithTagsTestCase, self).setUp()
white_list = ['{"vendor_id":"1137","product_id":"0071",'
'"address":"*:0a:00.*","physical_network":"physnet1"}',
'{"vendor_id":"1137","product_id":"0072"}',
'{"vendor_id":"15b3","product_id":"101e", '
'"remote_managed": "true"}',
'{"vendor_id":"15b3","product_id":"101c"}',
'{"vendor_id":"15b3","product_id":"1018", '
'"remote_managed": "false"}']
self.flags(passthrough_whitelist=white_list, group='pci')
dev_filter = whitelist.Whitelist(white_list)
device_spec = [
jsonutils.dumps(
{
"vendor_id": "1137",
"product_id": "0071",
"address": "*:0a:00.*",
"physical_network": "physnet1",
}
),
jsonutils.dumps({"vendor_id": "1137", "product_id": "0072"}),
jsonutils.dumps(
{
"vendor_id": "15b3",
"product_id": "101e",
"remote_managed": "true",
}
),
jsonutils.dumps({"vendor_id": "15b3", "product_id": "101c"}),
jsonutils.dumps(
{
"vendor_id": "15b3",
"product_id": "1018",
"remote_managed": "false",
}
),
]
self.flags(device_spec=device_spec, group="pci")
dev_filter = whitelist.Whitelist(device_spec)
self.pci_stats = stats.PciDeviceStats(
objects.NUMATopology(),
dev_filter=dev_filter)
@ -704,13 +723,25 @@ class PciDeviceVFPFStatsTestCase(test.NoDBTestCase):
def setUp(self):
super(PciDeviceVFPFStatsTestCase, self).setUp()
white_list = ['{"vendor_id":"8086","product_id":"1528"}',
'{"vendor_id":"8086","product_id":"1515"}',
'{"vendor_id":"15b3","product_id":"a2d6", '
'"remote_managed": "false"}',
'{"vendor_id":"15b3","product_id":"101e", '
'"remote_managed": "true"}']
self.flags(passthrough_whitelist=white_list, group='pci')
device_spec = [
jsonutils.dumps({"vendor_id": "8086", "product_id": "1528"}),
jsonutils.dumps({"vendor_id": "8086", "product_id": "1515"}),
jsonutils.dumps(
{
"vendor_id": "15b3",
"product_id": "a2d6",
"remote_managed": "false",
}
),
jsonutils.dumps(
{
"vendor_id": "15b3",
"product_id": "101e",
"remote_managed": "true",
}
),
]
self.flags(device_spec=device_spec, group='pci')
self.pci_stats = stats.PciDeviceStats(objects.NUMATopology())
def _create_pci_devices(self, vf_product_id=1515, pf_product_id=1528):

View File

@ -936,7 +936,7 @@ class FakeDriverWithPciResources(SmallFakeDriver):
def setUp(self):
super(FakeDriverWithPciResources.
FakeDriverWithPciResourcesConfigFixture, self).setUp()
# Set passthrough_whitelist before the compute node starts to match
# Set device_spec before the compute node starts to match
# with the PCI devices reported by this fake driver.
# NOTE(gibi): 0000:01:00 is tagged to physnet1 and therefore not a
@ -951,7 +951,7 @@ class FakeDriverWithPciResources(SmallFakeDriver):
# Having two PFs on the same physnet will allow us to test the
# placement allocation - physical allocation matching based on the
# bandwidth allocation in the future.
CONF.set_override('passthrough_whitelist', override=[
CONF.set_override('device_spec', override=[
jsonutils.dumps(
{
"address": {

View File

@ -0,0 +1,6 @@
---
deprecations:
- |
The [pci]passthrough_whitelist config option is renamed to
[pci]device_spec. The old name is deprecated and aliased to the new one.
The old name will be removed in a future release.