Merge "Report pkt processing capacity on Neutron agent RP"

This commit is contained in:
Zuul 2021-10-20 13:20:08 +00:00 committed by Gerrit Code Review
commit d22237d84e
5 changed files with 216 additions and 3 deletions

View File

@ -81,6 +81,8 @@ class PlacementState(object):
def __init__(self,
rp_bandwidths,
rp_inventory_defaults,
rp_pkt_processing,
rp_pkt_processing_inventory_defaults,
driver_uuid_namespace,
agent_type,
hypervisor_rps,
@ -89,6 +91,8 @@ class PlacementState(object):
client):
self._rp_bandwidths = rp_bandwidths
self._rp_inventory_defaults = rp_inventory_defaults
self._rp_pp = rp_pkt_processing
self._rp_pp_inventory_defaults = rp_pkt_processing_inventory_defaults
self._driver_uuid_namespace = driver_uuid_namespace
self._agent_type = agent_type
self._hypervisor_rps = hypervisor_rps
@ -219,7 +223,36 @@ class PlacementState(object):
return rp_traits
def deferred_update_resource_provider_inventories(self):
def _deferred_update_rp_pp_inventory(self):
agent_rp_inventories = []
for hypervisor, pp_values in self._rp_pp.items():
agent_rp_uuid = place_utils.agent_resource_provider_uuid(
self._driver_uuid_namespace, hypervisor)
inventories = {}
for direction, rp_class in (
(nlib_const.EGRESS_DIRECTION,
orc.NET_PACKET_RATE_EGR_KILOPACKET_PER_SEC),
(nlib_const.INGRESS_DIRECTION,
orc.NET_PACKET_RATE_IGR_KILOPACKET_PER_SEC),
(nlib_const.ANY_DIRECTION,
orc.NET_PACKET_RATE_KILOPACKET_PER_SEC)):
if pp_values.get(direction):
inventory = dict(self._rp_pp_inventory_defaults)
inventory['total'] = pp_values[direction]
inventories[rp_class] = inventory
if inventories:
agent_rp_inventories.append(
DeferredCall(
self._client.update_resource_provider_inventories,
resource_provider_uuid=agent_rp_uuid,
inventories=inventories))
return agent_rp_inventories
def _deferred_update_rp_bw_inventory(self):
rp_inventories = []
for device, bw_values in self._rp_bandwidths.items():
@ -234,7 +267,7 @@ class PlacementState(object):
orc.NET_BW_EGR_KILOBIT_PER_SEC),
(nlib_const.INGRESS_DIRECTION,
orc.NET_BW_IGR_KILOBIT_PER_SEC)):
if bw_values[direction] is not None:
if bw_values.get(direction):
inventory = dict(self._rp_inventory_defaults)
inventory['total'] = bw_values[direction]
inventories[rp_class] = inventory
@ -248,6 +281,15 @@ class PlacementState(object):
return rp_inventories
def deferred_update_resource_provider_inventories(self):
bw_inventory = self._deferred_update_rp_bw_inventory()
pp_inventory = self._deferred_update_rp_pp_inventory()
inventories = []
inventories.extend(bw_inventory)
inventories.extend(pp_inventory)
return inventories
def deferred_sync(self):
state = []
state += self.deferred_update_traits()

View File

@ -18,6 +18,7 @@ from neutron_lib.api.definitions import agent_resources_synced
from neutron_lib.callbacks import events
from neutron_lib.callbacks import registry
from neutron_lib.callbacks import resources
from neutron_lib import constants as n_const
from neutron_lib.placement import client as place_client
from neutron_lib.plugins import directory
from neutron_lib.services import base as service_base
@ -130,6 +131,17 @@ class PlacementReportPlugin(service_base.ServicePluginBase):
'resource_provider_bandwidths'],
rp_inventory_defaults=configurations[
'resource_provider_inventory_defaults'],
# RP_PP_WITHOUT_DIRECTION and RP_PP_WITH_DIRECTION are mutually
# exclusive options. Use the one that was provided or fallback to
# an empty dict.
rp_pkt_processing=(
configurations[n_const.RP_PP_WITHOUT_DIRECTION]
if configurations.get(
n_const.RP_PP_WITHOUT_DIRECTION)
else configurations.get(
n_const.RP_PP_WITH_DIRECTION, {})),
rp_pkt_processing_inventory_defaults=configurations.get(
n_const.RP_PP_INVENTORY_DEFAULTS, {}),
driver_uuid_namespace=uuid_ns,
agent_type=agent['agent_type'],
hypervisor_rps=hypervisor_rps,

View File

@ -54,6 +54,8 @@ class PlacementStateTestCase(base.BaseTestCase):
self.kwargs = {
'rp_bandwidths': {},
'rp_inventory_defaults': {},
'rp_pkt_processing': {},
'rp_pkt_processing_inventory_defaults': {},
'driver_uuid_namespace': self.driver_uuid_namespace,
'agent_type': 'fake agent type',
'hypervisor_rps': {
@ -225,7 +227,7 @@ class PlacementStateTestCase(base.BaseTestCase):
set(['CUSTOM_VNIC_TYPE_NORMAL'])],
actual_traits)
def test_deferred_update_resource_provider_inventories(self):
def test_deferred_update_resource_provider_inventories_bw(self):
self.kwargs.update({
'device_mappings': {
'physnet0': ['eth0'],
@ -255,3 +257,67 @@ class PlacementStateTestCase(base.BaseTestCase):
'total': 100,
'step_size': 10,
'max_unit': 50}})
def test_deferred_update_resource_provider_inventories_pp_direction(self):
self.kwargs.update({
'rp_pkt_processing': {
'fakehost': {'egress': 100, 'ingress': 200},
},
'rp_pkt_processing_inventory_defaults': {
'step_size': 10,
'max_unit': 50,
},
})
state = placement_report.PlacementState(**self.kwargs)
for deferred in state.deferred_update_resource_provider_inventories():
deferred.execute()
# uuid below generated by the following command:
# uuid -v5 '00000000-0000-0000-0000-000000000001' 'fakehost'
expected_calls = [
mock.call(
resource_provider_uuid=uuid.UUID(
'c0b4abe5-516f-54b8-b965-ff94060dcbcc'),
inventories={
# TODO(przszc): Replace hard-coded resource classes names
# with os-resource-classes lib.
'NET_PACKET_RATE_EGR_KILOPACKET_PER_SEC': {
'total': 100,
'step_size': 10,
'max_unit': 50},
'NET_PACKET_RATE_IGR_KILOPACKET_PER_SEC': {
'total': 200,
'step_size': 10,
'max_unit': 50}})]
self.client_mock.update_resource_provider_inventories.assert_has_calls(
expected_calls)
def test_deferred_update_resource_provider_inventories_pp(self):
self.kwargs.update({
'rp_pkt_processing': {
'fakehost': {'any': 300},
},
'rp_pkt_processing_inventory_defaults': {
'step_size': 1,
'max_unit': 5,
},
})
state = placement_report.PlacementState(**self.kwargs)
for deferred in state.deferred_update_resource_provider_inventories():
deferred.execute()
# uuid below generated by the following command:
# uuid -v5 '00000000-0000-0000-0000-000000000001' 'fakehost'
expected_calls = [
mock.call(
resource_provider_uuid=uuid.UUID(
'c0b4abe5-516f-54b8-b965-ff94060dcbcc'),
inventories={
'NET_PACKET_RATE_KILOPACKET_PER_SEC': {
'total': 300,
'step_size': 1,
'max_unit': 5}})]
self.client_mock.update_resource_provider_inventories.assert_has_calls(
expected_calls)

View File

@ -249,6 +249,86 @@ class PlacementReportPluginTestCases(test_plugin.Ml2PluginV2TestCase):
self.assertEqual(1, mock_queue_event.call_count)
mock_list_rps.assert_called_once_with(name='hypervisor0')
def test__sync_placement_state_rp_pkt_processing_with_direction(self):
agent = {
'agent_type': 'test_mechanism_driver_agent',
'configurations': {
'resource_provider_bandwidths': {},
'resource_provider_inventory_defaults': {},
'resource_provider_packet_processing_with_direction': {
'fake host': {'egress': 1, 'ingress': 2}
},
'resource_provider_packet_processing_inventory_defaults': {
'allocation_ratio': 1, 'min_unit': 1, 'step_size': 1
},
},
'host': 'fake host',
}
agent_db = mock.Mock()
mock_state = mock.Mock(return_value=[])
with mock.patch.object(self.service_plugin._batch_notifier,
'queue_event') as mock_queue_event, \
mock.patch('neutron.agent.common.placement_report.PlacementState',
return_value=mock_state) as mock_placement_state:
self.service_plugin._sync_placement_state(agent, agent_db)
self.assertEqual(1, mock_queue_event.call_count)
mock_placement_state.assert_called_once_with(
rp_pkt_processing={'fake host': {'egress': 1, 'ingress': 2}},
rp_pkt_processing_inventory_defaults={
'allocation_ratio': 1, 'min_unit': 1, 'step_size': 1},
rp_bandwidths=mock.ANY,
rp_inventory_defaults=mock.ANY,
driver_uuid_namespace=mock.ANY,
agent_type=mock.ANY,
hypervisor_rps=mock.ANY,
device_mappings=mock.ANY,
supported_vnic_types=mock.ANY,
client=mock.ANY)
mock_state.deferred_sync.assert_called_once()
def test__sync_placement_state_rp_pkt_processing_without_direction(self):
agent = {
'agent_type': 'test_mechanism_driver_agent',
'configurations': {
'resource_provider_bandwidths': {},
'resource_provider_inventory_defaults': {},
'resource_provider_packet_processing_without_direction': {
'fake host': {'any': 1}
},
'resource_provider_packet_processing_inventory_defaults': {
'allocation_ratio': 1, 'min_unit': 1, 'step_size': 1
},
},
'host': 'fake host',
}
agent_db = mock.Mock()
mock_state = mock.Mock(return_value=[])
with mock.patch.object(self.service_plugin._batch_notifier,
'queue_event') as mock_queue_event, \
mock.patch('neutron.agent.common.placement_report.PlacementState',
return_value=mock_state) as mock_placement_state:
self.service_plugin._sync_placement_state(agent, agent_db)
self.assertEqual(1, mock_queue_event.call_count)
mock_placement_state.assert_called_once_with(
rp_pkt_processing={'fake host': {'any': 1}},
rp_pkt_processing_inventory_defaults={
'allocation_ratio': 1, 'min_unit': 1, 'step_size': 1},
rp_bandwidths=mock.ANY,
rp_inventory_defaults=mock.ANY,
driver_uuid_namespace=mock.ANY,
agent_type=mock.ANY,
hypervisor_rps=mock.ANY,
device_mappings=mock.ANY,
supported_vnic_types=mock.ANY,
client=mock.ANY)
mock_state.deferred_sync.assert_called_once()
class PlacementReporterAgentsTestCases(test_plugin.Ml2PluginV2TestCase):

View File

@ -0,0 +1,13 @@
---
features:
- |
Report packet processing capacity on the OVS agent resource provider as
the new ``NET_PACKET_RATE_KILOPACKET_PER_SEC``,
``NET_PACKET_RATE_EGR_KILOPACKET_PER_SEC`` or
``NET_PACKET_RATE_IGR_KILOPACKET_PER_SEC`` resource inventory. This is
similar to how the bandwidth resource is reported today. The former
is used for non-hardware-offloaded OVS deployments, where packets
processed from both ingress and egress directions are handled by the same
set of CPU cores. Remaining inventories are used for hardware-offloaded
OVS, where the incoming and outgoing packets are handled by independent
hardware resources.