Merge "Report pkt processing capacity on Neutron agent RP"
This commit is contained in:
commit
d22237d84e
|
@ -81,6 +81,8 @@ class PlacementState(object):
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
rp_bandwidths,
|
rp_bandwidths,
|
||||||
rp_inventory_defaults,
|
rp_inventory_defaults,
|
||||||
|
rp_pkt_processing,
|
||||||
|
rp_pkt_processing_inventory_defaults,
|
||||||
driver_uuid_namespace,
|
driver_uuid_namespace,
|
||||||
agent_type,
|
agent_type,
|
||||||
hypervisor_rps,
|
hypervisor_rps,
|
||||||
|
@ -89,6 +91,8 @@ class PlacementState(object):
|
||||||
client):
|
client):
|
||||||
self._rp_bandwidths = rp_bandwidths
|
self._rp_bandwidths = rp_bandwidths
|
||||||
self._rp_inventory_defaults = rp_inventory_defaults
|
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._driver_uuid_namespace = driver_uuid_namespace
|
||||||
self._agent_type = agent_type
|
self._agent_type = agent_type
|
||||||
self._hypervisor_rps = hypervisor_rps
|
self._hypervisor_rps = hypervisor_rps
|
||||||
|
@ -219,7 +223,36 @@ class PlacementState(object):
|
||||||
|
|
||||||
return rp_traits
|
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 = []
|
rp_inventories = []
|
||||||
|
|
||||||
for device, bw_values in self._rp_bandwidths.items():
|
for device, bw_values in self._rp_bandwidths.items():
|
||||||
|
@ -234,7 +267,7 @@ class PlacementState(object):
|
||||||
orc.NET_BW_EGR_KILOBIT_PER_SEC),
|
orc.NET_BW_EGR_KILOBIT_PER_SEC),
|
||||||
(nlib_const.INGRESS_DIRECTION,
|
(nlib_const.INGRESS_DIRECTION,
|
||||||
orc.NET_BW_IGR_KILOBIT_PER_SEC)):
|
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 = dict(self._rp_inventory_defaults)
|
||||||
inventory['total'] = bw_values[direction]
|
inventory['total'] = bw_values[direction]
|
||||||
inventories[rp_class] = inventory
|
inventories[rp_class] = inventory
|
||||||
|
@ -248,6 +281,15 @@ class PlacementState(object):
|
||||||
|
|
||||||
return rp_inventories
|
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):
|
def deferred_sync(self):
|
||||||
state = []
|
state = []
|
||||||
state += self.deferred_update_traits()
|
state += self.deferred_update_traits()
|
||||||
|
|
|
@ -18,6 +18,7 @@ from neutron_lib.api.definitions import agent_resources_synced
|
||||||
from neutron_lib.callbacks import events
|
from neutron_lib.callbacks import events
|
||||||
from neutron_lib.callbacks import registry
|
from neutron_lib.callbacks import registry
|
||||||
from neutron_lib.callbacks import resources
|
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.placement import client as place_client
|
||||||
from neutron_lib.plugins import directory
|
from neutron_lib.plugins import directory
|
||||||
from neutron_lib.services import base as service_base
|
from neutron_lib.services import base as service_base
|
||||||
|
@ -130,6 +131,17 @@ class PlacementReportPlugin(service_base.ServicePluginBase):
|
||||||
'resource_provider_bandwidths'],
|
'resource_provider_bandwidths'],
|
||||||
rp_inventory_defaults=configurations[
|
rp_inventory_defaults=configurations[
|
||||||
'resource_provider_inventory_defaults'],
|
'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,
|
driver_uuid_namespace=uuid_ns,
|
||||||
agent_type=agent['agent_type'],
|
agent_type=agent['agent_type'],
|
||||||
hypervisor_rps=hypervisor_rps,
|
hypervisor_rps=hypervisor_rps,
|
||||||
|
|
|
@ -54,6 +54,8 @@ class PlacementStateTestCase(base.BaseTestCase):
|
||||||
self.kwargs = {
|
self.kwargs = {
|
||||||
'rp_bandwidths': {},
|
'rp_bandwidths': {},
|
||||||
'rp_inventory_defaults': {},
|
'rp_inventory_defaults': {},
|
||||||
|
'rp_pkt_processing': {},
|
||||||
|
'rp_pkt_processing_inventory_defaults': {},
|
||||||
'driver_uuid_namespace': self.driver_uuid_namespace,
|
'driver_uuid_namespace': self.driver_uuid_namespace,
|
||||||
'agent_type': 'fake agent type',
|
'agent_type': 'fake agent type',
|
||||||
'hypervisor_rps': {
|
'hypervisor_rps': {
|
||||||
|
@ -225,7 +227,7 @@ class PlacementStateTestCase(base.BaseTestCase):
|
||||||
set(['CUSTOM_VNIC_TYPE_NORMAL'])],
|
set(['CUSTOM_VNIC_TYPE_NORMAL'])],
|
||||||
actual_traits)
|
actual_traits)
|
||||||
|
|
||||||
def test_deferred_update_resource_provider_inventories(self):
|
def test_deferred_update_resource_provider_inventories_bw(self):
|
||||||
self.kwargs.update({
|
self.kwargs.update({
|
||||||
'device_mappings': {
|
'device_mappings': {
|
||||||
'physnet0': ['eth0'],
|
'physnet0': ['eth0'],
|
||||||
|
@ -255,3 +257,67 @@ class PlacementStateTestCase(base.BaseTestCase):
|
||||||
'total': 100,
|
'total': 100,
|
||||||
'step_size': 10,
|
'step_size': 10,
|
||||||
'max_unit': 50}})
|
'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)
|
||||||
|
|
|
@ -249,6 +249,86 @@ class PlacementReportPluginTestCases(test_plugin.Ml2PluginV2TestCase):
|
||||||
self.assertEqual(1, mock_queue_event.call_count)
|
self.assertEqual(1, mock_queue_event.call_count)
|
||||||
mock_list_rps.assert_called_once_with(name='hypervisor0')
|
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):
|
class PlacementReporterAgentsTestCases(test_plugin.Ml2PluginV2TestCase):
|
||||||
|
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue