Filter computes without remote-managed ports early
Add a pre-filter for requests that contain VNIC_TYPE_REMOTE_MANAGED ports in them: hosts that do not have either the relevant compute driver capability COMPUTE_REMOTE_MANAGED_PORTS or PCI device pools with "remote_managed" devices are filtered out early. Presence of devices actually available for allocation is checked at a later point by the PciPassthroughFilter. Change-Id: I168d3ccc914f25a3d4255c9b319ee6b91a2f66e2 Implements: blueprint integration-with-off-path-network-backends
This commit is contained in:
parent
d1e9ecb443
commit
c487c730d0
|
@ -1126,6 +1126,28 @@ class ResourceTracker(object):
|
|||
LOG.error('Unable to find services table record for nova-compute '
|
||||
'host %s', self.host)
|
||||
|
||||
def _should_expose_remote_managed_ports_trait(self,
|
||||
is_supported: bool):
|
||||
"""Determine whether COMPUTE_REMOTE_MANAGED_PORTS should be exposed.
|
||||
|
||||
Determines if the COMPUTE_REMOTE_MANAGED_PORTS trait needs to be
|
||||
exposed based on the respective compute driver capability and
|
||||
the presence of remote managed devices on a given host. Whether such
|
||||
devices are present or not depends on the Whitelist configuration
|
||||
(presence of a remote_managed tag association with some PCI devices)
|
||||
and their physical presence (plugged in, enumerated by the OS).
|
||||
|
||||
The aim of having this check is to optimize host lookup by prefiltering
|
||||
hosts that have compute driver support but no hardware. The check
|
||||
does not consider free device count - just the presence of device
|
||||
pools since device availability may change between a prefilter check
|
||||
and a later check in PciPassthroughFilter.
|
||||
|
||||
:param bool is_supported: Is the trait supported by the compute driver
|
||||
"""
|
||||
return (is_supported and
|
||||
self.pci_tracker.pci_stats.has_remote_managed_device_pools())
|
||||
|
||||
def _get_traits(self, context, nodename, provider_tree):
|
||||
"""Synchronizes internal and external traits for the node provider.
|
||||
|
||||
|
@ -1149,7 +1171,11 @@ class ResourceTracker(object):
|
|||
# traits that are missing, and remove any existing set traits
|
||||
# that are not currently supported.
|
||||
for trait, supported in self.driver.capabilities_as_traits().items():
|
||||
if supported:
|
||||
add_trait = supported
|
||||
if trait == os_traits.COMPUTE_REMOTE_MANAGED_PORTS:
|
||||
add_trait &= self._should_expose_remote_managed_ports_trait(
|
||||
supported)
|
||||
if add_trait:
|
||||
traits.add(trait)
|
||||
elif trait in traits:
|
||||
traits.remove(trait)
|
||||
|
|
|
@ -106,6 +106,7 @@ VNIC_TYPE_VIRTIO_FORWARDER = 'virtio-forwarder'
|
|||
VNIC_TYPE_VDPA = 'vdpa'
|
||||
VNIC_TYPE_ACCELERATOR_DIRECT = 'accelerator-direct'
|
||||
VNIC_TYPE_ACCELERATOR_DIRECT_PHYSICAL = 'accelerator-direct-physical'
|
||||
VNIC_TYPE_REMOTE_MANAGED = "remote-managed"
|
||||
|
||||
# Define list of ports which needs pci request.
|
||||
# Note: The macvtap port needs a PCI request as it is a tap interface
|
||||
|
@ -121,14 +122,15 @@ VNIC_TYPE_ACCELERATOR_DIRECT_PHYSICAL = 'accelerator-direct-physical'
|
|||
# selected compute node.
|
||||
VNIC_TYPES_SRIOV = (
|
||||
VNIC_TYPE_DIRECT, VNIC_TYPE_MACVTAP, VNIC_TYPE_DIRECT_PHYSICAL,
|
||||
VNIC_TYPE_VIRTIO_FORWARDER, VNIC_TYPE_VDPA)
|
||||
VNIC_TYPE_VIRTIO_FORWARDER, VNIC_TYPE_VDPA, VNIC_TYPE_REMOTE_MANAGED)
|
||||
|
||||
# Define list of ports which are passthrough to the guest
|
||||
# and need a special treatment on snapshot and suspend/resume
|
||||
VNIC_TYPES_DIRECT_PASSTHROUGH = (VNIC_TYPE_DIRECT,
|
||||
VNIC_TYPE_DIRECT_PHYSICAL,
|
||||
VNIC_TYPE_ACCELERATOR_DIRECT,
|
||||
VNIC_TYPE_ACCELERATOR_DIRECT_PHYSICAL)
|
||||
VNIC_TYPE_ACCELERATOR_DIRECT_PHYSICAL,
|
||||
VNIC_TYPE_REMOTE_MANAGED)
|
||||
|
||||
# Define list of ports which contains devices managed by cyborg.
|
||||
VNIC_TYPES_ACCELERATOR = (
|
||||
|
|
|
@ -2123,6 +2123,27 @@ class API:
|
|||
# the port binding profile and we can handle it as a boolean.
|
||||
return strutils.bool_from_string(value)
|
||||
|
||||
@staticmethod
|
||||
def _is_remote_managed(vnic_type):
|
||||
"""Determine if the port is remote_managed or not by VNIC type.
|
||||
|
||||
:param str vnic_type: The VNIC type to assess.
|
||||
:return: A boolean indicator whether the NIC is remote managed or not.
|
||||
:rtype: bool
|
||||
"""
|
||||
return vnic_type == network_model.VNIC_TYPE_REMOTE_MANAGED
|
||||
|
||||
def is_remote_managed_port(self, context, port_id):
|
||||
"""Determine if a port has a REMOTE_MANAGED VNIC type.
|
||||
|
||||
:param context: The request context
|
||||
:param port_id: The id of the Neutron port
|
||||
"""
|
||||
port = self.show_port(context, port_id)['port']
|
||||
return self._is_remote_managed(
|
||||
port.get('binding:vnic_type', network_model.VNIC_TYPE_NORMAL)
|
||||
)
|
||||
|
||||
# NOTE(sean-k-mooney): we might want to have this return a
|
||||
# nova.network.model.VIF object instead in the future.
|
||||
def _get_port_vnic_info(self, context, neutron, port_id):
|
||||
|
|
|
@ -729,3 +729,16 @@ class PciDeviceStats(object):
|
|||
"""Return the contents of the pools as a PciDevicePoolList object."""
|
||||
stats = [x for x in self]
|
||||
return pci_device_pool.from_pci_stats(stats)
|
||||
|
||||
def has_remote_managed_device_pools(self) -> bool:
|
||||
"""Determine whether remote managed device pools are present on a host.
|
||||
|
||||
The check is pool-based, not free device-based and is NUMA cell
|
||||
agnostic.
|
||||
"""
|
||||
dummy_req = objects.InstancePCIRequest(
|
||||
count=0,
|
||||
spec=[{'remote_managed': True}]
|
||||
)
|
||||
pools = self._filter_pools_for_spec(self.pools, dummy_req)
|
||||
return bool(pools)
|
||||
|
|
|
@ -365,6 +365,33 @@ def routed_networks_filter(
|
|||
return True
|
||||
|
||||
|
||||
@trace_request_filter
|
||||
def remote_managed_ports_filter(
|
||||
context: nova_context.RequestContext,
|
||||
request_spec: 'objects.RequestSpec',
|
||||
) -> bool:
|
||||
"""Filter out hosts without remote managed port support (driver or hw).
|
||||
|
||||
If a request spec contains VNIC_TYPE_REMOTE_MANAGED ports then a
|
||||
remote-managed port trait (COMPUTE_REMOTE_MANAGED_PORTS) is added to
|
||||
the request in order to pre-filter hosts that do not use compute
|
||||
drivers supporting remote managed ports and the ones that do not have
|
||||
the device pools providing remote-managed ports (actual device
|
||||
availability besides a pool presence check is done at the time of
|
||||
PciPassthroughFilter execution).
|
||||
"""
|
||||
if request_spec.requested_networks:
|
||||
network_api = neutron.API()
|
||||
for request_net in request_spec.requested_networks:
|
||||
if request_net.port_id and network_api.is_remote_managed_port(
|
||||
context, request_net.port_id):
|
||||
request_spec.root_required.add(
|
||||
os_traits.COMPUTE_REMOTE_MANAGED_PORTS)
|
||||
LOG.debug('remote_managed_ports_filter request filter added '
|
||||
f'trait {os_traits.COMPUTE_REMOTE_MANAGED_PORTS}')
|
||||
return True
|
||||
|
||||
|
||||
ALL_REQUEST_FILTERS = [
|
||||
require_tenant_aggregate,
|
||||
map_az_to_placement_aggregate,
|
||||
|
@ -374,6 +401,7 @@ ALL_REQUEST_FILTERS = [
|
|||
transform_image_metadata,
|
||||
accelerators_filter,
|
||||
routed_networks_filter,
|
||||
remote_managed_ports_filter,
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -45,7 +45,6 @@ class ProviderTreeTests(integrated_helpers.ProviderUsageBaseTestCase):
|
|||
os_traits.COMPUTE_VOLUME_EXTEND,
|
||||
os_traits.COMPUTE_VOLUME_MULTI_ATTACH,
|
||||
os_traits.COMPUTE_TRUSTED_CERTS,
|
||||
os_traits.COMPUTE_REMOTE_MANAGED_PORTS,
|
||||
]
|
||||
])
|
||||
|
||||
|
|
|
@ -1577,9 +1577,13 @@ class TestUpdateComputeNode(BaseTestCase):
|
|||
self.rt._update(mock.sentinel.ctx, new_compute)
|
||||
save_mock.assert_called_once_with()
|
||||
|
||||
@mock.patch(
|
||||
'nova.pci.stats.PciDeviceStats.has_remote_managed_device_pools',
|
||||
return_value=True)
|
||||
@mock.patch('nova.compute.resource_tracker.ResourceTracker.'
|
||||
'_sync_compute_service_disabled_trait')
|
||||
def test_existing_node_capabilities_as_traits(self, mock_sync_disabled):
|
||||
def test_existing_node_capabilities_as_traits(
|
||||
self, mock_sync_disabled, mock_has_remote_managed_device_pools):
|
||||
"""The capabilities_as_traits() driver method returns traits
|
||||
information for a node/provider.
|
||||
"""
|
||||
|
@ -1587,6 +1591,15 @@ class TestUpdateComputeNode(BaseTestCase):
|
|||
rc = self.rt.reportclient
|
||||
rc.set_traits_for_provider = mock.MagicMock()
|
||||
|
||||
# TODO(dmitriis): Remove once the PCI tracker is always created
|
||||
# upon the resource tracker initialization.
|
||||
with mock.patch.object(
|
||||
objects.PciDeviceList, 'get_by_compute_node',
|
||||
return_value=objects.PciDeviceList()
|
||||
):
|
||||
self.rt.pci_tracker = pci_manager.PciDevTracker(
|
||||
mock.sentinel.ctx, _COMPUTE_NODE_FIXTURES[0])
|
||||
|
||||
# Emulate a driver that has implemented the update_from_provider_tree()
|
||||
# virt driver method
|
||||
self.driver_mock.update_provider_tree = mock.Mock()
|
||||
|
|
|
@ -3566,6 +3566,23 @@ class TestAPI(TestAPIBase):
|
|||
self.assertFalse(tunneled)
|
||||
self.assertIsNone(physnet_name)
|
||||
|
||||
def test_is_remote_managed(self):
|
||||
cases = {
|
||||
(model.VNIC_TYPE_NORMAL, False),
|
||||
(model.VNIC_TYPE_DIRECT, False),
|
||||
(model.VNIC_TYPE_MACVTAP, False),
|
||||
(model.VNIC_TYPE_DIRECT_PHYSICAL, False),
|
||||
(model.VNIC_TYPE_BAREMETAL, False),
|
||||
(model.VNIC_TYPE_VIRTIO_FORWARDER, False),
|
||||
(model.VNIC_TYPE_VDPA, False),
|
||||
(model.VNIC_TYPE_ACCELERATOR_DIRECT, False),
|
||||
(model.VNIC_TYPE_ACCELERATOR_DIRECT_PHYSICAL, False),
|
||||
(model.VNIC_TYPE_REMOTE_MANAGED, True),
|
||||
}
|
||||
|
||||
for vnic_type, expected in cases:
|
||||
self.assertEqual(self.api._is_remote_managed(vnic_type), expected)
|
||||
|
||||
def _test_get_port_vnic_info(
|
||||
self, mock_get_client, binding_vnic_type, expected_vnic_type,
|
||||
port_resource_request=None, numa_policy=None
|
||||
|
|
Loading…
Reference in New Issue