ovs-agent: Report pkt processing info in heartbeat

OVS agent configuration is extended to support new configuration
options:
  - 'resource_provider_packet_processing_without_direction'
  - 'resource_provider_packet_processing_with_direction'
  - 'resource_provider_packet_processing_inventory_defaults'

OVS agent RPC hearthbeat now reports this information to neutron
server in 'configuration' field .

Example config:

ml2_conf.ini:
[ovs]
resource_provider_packet_processing_with_direction = :1000:1000

Partial-Bug: #1922237
See-Also: https://review.opendev.org/785236
Change-Id: Ief554bc445dfd93ea6995bb42b4d010674c7a091
This commit is contained in:
Przemyslaw Szczerbik 2021-06-29 09:11:45 +02:00
parent 56044db26d
commit 1ea26616b4
4 changed files with 118 additions and 0 deletions

View File

@ -88,6 +88,35 @@ ovs_opts = [
"the resource_provider_default_hypervisor config " "the resource_provider_default_hypervisor config "
"option value as known by the nova-compute managing " "option value as known by the nova-compute managing "
"that hypervisor.")), "that hypervisor.")),
cfg.ListOpt('resource_provider_packet_processing_without_direction',
default=[],
help=_("Comma-separated list of "
"<hypervisor>:<packet_rate> tuples, defining the "
"minimum packet rate the OVS backend can guarantee in "
"kilo (1000) packet per second. The hypervisor name is "
"used to locate the parent of the resource provider "
"tree. Only needs to be set in the rare case when the "
"hypervisor name is different from the DEFAULT.host "
"config option value as known by the nova-compute "
"managing that hypervisor or if multiple hypervisors "
"are served by the same OVS backend. The default is :0 "
"which means no packet processing capacity is "
"guaranteed on the hypervisor named according to "
"DEFAULT.host.")),
cfg.ListOpt('resource_provider_packet_processing_with_direction',
default=[],
help=_("Similar to the "
"resource_provider_packet_processing_without_direction "
"but used in case the OVS backend has hardware offload "
"capabilities. In this case the format is "
"<hypervisor>:<egress_pkt_rate>:<ingress_pkt_rate> "
"which allows defining packet processing capacity per "
"traffic direction. The direction is meant from the VM "
"perspective. Note that the "
"resource_provider_packet_processing_without_direction "
"and the "
"resource_provider_packet_processing_with_direction "
"are mutually exclusive options.")),
cfg.StrOpt('resource_provider_default_hypervisor', cfg.StrOpt('resource_provider_default_hypervisor',
help=_("The default hypervisor name used to locate the parent " help=_("The default hypervisor name used to locate the parent "
"of the resource provider. If this option is not set, " "of the resource provider. If this option is not set, "
@ -106,6 +135,20 @@ ovs_opts = [
"See also: " "See also: "
"https://docs.openstack.org/api-ref/placement/" "https://docs.openstack.org/api-ref/placement/"
"#update-resource-provider-inventories")), "#update-resource-provider-inventories")),
cfg.DictOpt('resource_provider_packet_processing_inventory_defaults',
default={'allocation_ratio': 1.0,
'min_unit': 1,
'step_size': 1,
'reserved': 0},
help=_("Key:value pairs to specify defaults used "
"while reporting packet rate inventories. "
"Possible keys with their types: "
"allocation_ratio:float, "
"max_unit:int, min_unit:int, "
"reserved:int, step_size:int, "
"See also: "
"https://docs.openstack.org/api-ref/placement/"
"#update-resource-provider-inventories")),
cfg.StrOpt('datapath_type', default=constants.OVS_DATAPATH_SYSTEM, cfg.StrOpt('datapath_type', default=constants.OVS_DATAPATH_SYSTEM,
choices=[constants.OVS_DATAPATH_SYSTEM, choices=[constants.OVS_DATAPATH_SYSTEM,
constants.OVS_DATAPATH_NETDEV], constants.OVS_DATAPATH_NETDEV],

View File

@ -216,12 +216,30 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
ovs_conf.bridge_mappings) ovs_conf.bridge_mappings)
self.rp_bandwidths = place_utils.parse_rp_bandwidths( self.rp_bandwidths = place_utils.parse_rp_bandwidths(
ovs_conf.resource_provider_bandwidths) ovs_conf.resource_provider_bandwidths)
self.rp_pp_without_direction = (
place_utils.parse_rp_pp_without_direction(
ovs_conf.resource_provider_packet_processing_without_direction,
self.host))
self.rp_pp_with_direction = place_utils.parse_rp_pp_with_direction(
ovs_conf.resource_provider_packet_processing_with_direction,
self.host)
self._validate_rp_pkt_processing_cfg()
br_set = set(self.bridge_mappings.values()) br_set = set(self.bridge_mappings.values())
n_utils.validate_rp_bandwidth(self.rp_bandwidths, n_utils.validate_rp_bandwidth(self.rp_bandwidths,
br_set) br_set)
self.rp_inventory_defaults = place_utils.parse_rp_inventory_defaults( self.rp_inventory_defaults = place_utils.parse_rp_inventory_defaults(
ovs_conf.resource_provider_inventory_defaults) ovs_conf.resource_provider_inventory_defaults)
# At the moment the format of
# resource_provider_packet_processing_inventory_defaults is exactly
# the same as resource_provider_inventory_defaults, so we can simply
# use parse_rp_inventory_defaults() to parse it. However, if at some
# point the formats become different, we'll have to add a dedicated
# function to parse this option.
self.rp_pp_inventory_defaults = (
place_utils.parse_rp_inventory_defaults(
ovs_conf.resource_provider_packet_processing_inventory_defaults
))
self.rp_hypervisors = utils.default_rp_hypervisors( self.rp_hypervisors = utils.default_rp_hypervisors(
ovs_conf.resource_provider_hypervisors, ovs_conf.resource_provider_hypervisors,
{k: [v] for k, v in self.bridge_mappings.items()}, {k: [v] for k, v in self.bridge_mappings.items()},
@ -326,6 +344,12 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
n_const.RP_BANDWIDTHS: self.rp_bandwidths, n_const.RP_BANDWIDTHS: self.rp_bandwidths,
n_const.RP_INVENTORY_DEFAULTS: n_const.RP_INVENTORY_DEFAULTS:
self.rp_inventory_defaults, self.rp_inventory_defaults,
n_const.RP_PP_WITHOUT_DIRECTION:
self.rp_pp_without_direction,
n_const.RP_PP_WITH_DIRECTION:
self.rp_pp_with_direction,
n_const.RP_PP_INVENTORY_DEFAULTS:
self.rp_pp_inventory_defaults,
'resource_provider_hypervisors': 'resource_provider_hypervisors':
self.rp_hypervisors, self.rp_hypervisors,
'integration_bridge': 'integration_bridge':
@ -2777,6 +2801,13 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
network_id, self.agent_id, self.conf.host) network_id, self.agent_id, self.conf.host)
return port_network['mtu'] return port_network['mtu']
def _validate_rp_pkt_processing_cfg(self):
if self.rp_pp_with_direction and self.rp_pp_without_direction:
raise ValueError(_(
'%s and %s configuration options are mutually exclusive.') %
(n_const.RP_PP_WITHOUT_DIRECTION,
n_const.RP_PP_WITH_DIRECTION))
def validate_local_ip(local_ip): def validate_local_ip(local_ip):
"""Verify if the ip exists on the agent's host.""" """Verify if the ip exists on the agent's host."""

View File

@ -2566,6 +2566,35 @@ class TestOvsNeutronAgent(object):
self.assertIn(n_const.RP_BANDWIDTHS, self.assertIn(n_const.RP_BANDWIDTHS,
self.agent.agent_state['configurations']) self.agent.agent_state['configurations'])
def test_configurations_has_rp_pp_without_direction(self):
self.assertIn(n_const.RP_PP_WITHOUT_DIRECTION,
self.agent.agent_state['configurations'])
def test_configurations_has_rp_pp_with_direction(self):
self.assertIn(n_const.RP_PP_WITH_DIRECTION,
self.agent.agent_state['configurations'])
def test_configurations_has_rp_pp_default_inventory(self):
self.assertIn(n_const.RP_PP_INVENTORY_DEFAULTS,
self.agent.agent_state['configurations'])
rp_inv_defaults = \
self.agent.agent_state['configurations'][
n_const.RP_INVENTORY_DEFAULTS]
self.assertListEqual(
sorted(['reserved', 'min_unit', 'allocation_ratio', 'step_size']),
sorted(list(rp_inv_defaults)))
self.assertEqual(1.0, rp_inv_defaults['allocation_ratio'])
self.assertEqual(1, rp_inv_defaults['min_unit'])
self.assertEqual(1, rp_inv_defaults['step_size'])
self.assertEqual(0, rp_inv_defaults['reserved'])
def test__validate_rp_pkt_processing_cfg(self):
cfg.CONF.set_override(n_const.RP_PP_WITHOUT_DIRECTION,
[':0'], 'OVS')
cfg.CONF.set_override(n_const.RP_PP_WITH_DIRECTION,
[':0:0'], 'OVS')
self.assertRaises(ValueError, self._make_agent)
def test_configurations_has_rp_default_inventory(self): def test_configurations_has_rp_default_inventory(self):
self.assertIn(n_const.RP_INVENTORY_DEFAULTS, self.assertIn(n_const.RP_INVENTORY_DEFAULTS,
self.agent.agent_state['configurations']) self.agent.agent_state['configurations'])

View File

@ -0,0 +1,15 @@
---
features:
- |
New configuration options for neutron-ovs-agent under section ``[ovs]``:
``resource_provider_packet_processing_without_direction``,
``resource_provider_packet_processing_with_direction`` and
``resource_provider_packet_processing_inventory_defaults``.
``resource_provider_packet_processing_without_direction`` controls the
minimum packet rate the OVS backend can guarantee in kilo (1000) packet per
second.
``resource_provider_packet_processing_with_direction`` is similar to the
first option, but used in case the OVS backend has hardware offload
capabilities. The last option can be used to tune the other fields
(``allocation_ratio``, ``min_unit``, ``max_unit``, ``reserved``,
``step_size``) of resource provider inventories.