From 1ea26616b41335b24b9c162062740ee3de78372d Mon Sep 17 00:00:00 2001 From: Przemyslaw Szczerbik Date: Tue, 29 Jun 2021 09:11:45 +0200 Subject: [PATCH] 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 --- neutron/conf/plugins/ml2/drivers/ovs_conf.py | 43 +++++++++++++++++++ .../openvswitch/agent/ovs_neutron_agent.py | 31 +++++++++++++ .../agent/test_ovs_neutron_agent.py | 29 +++++++++++++ .../pps-config-ovs-036b5940694f786c.yaml | 15 +++++++ 4 files changed, 118 insertions(+) create mode 100644 releasenotes/notes/pps-config-ovs-036b5940694f786c.yaml diff --git a/neutron/conf/plugins/ml2/drivers/ovs_conf.py b/neutron/conf/plugins/ml2/drivers/ovs_conf.py index 206cdaa1b38..30bc86c723f 100644 --- a/neutron/conf/plugins/ml2/drivers/ovs_conf.py +++ b/neutron/conf/plugins/ml2/drivers/ovs_conf.py @@ -88,6 +88,35 @@ ovs_opts = [ "the resource_provider_default_hypervisor config " "option value as known by the nova-compute managing " "that hypervisor.")), + cfg.ListOpt('resource_provider_packet_processing_without_direction', + default=[], + help=_("Comma-separated list of " + ": 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 " + ":: " + "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', help=_("The default hypervisor name used to locate the parent " "of the resource provider. If this option is not set, " @@ -106,6 +135,20 @@ ovs_opts = [ "See also: " "https://docs.openstack.org/api-ref/placement/" "#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, choices=[constants.OVS_DATAPATH_SYSTEM, constants.OVS_DATAPATH_NETDEV], diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py index 87859ae23b9..ce07db74d53 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py @@ -216,12 +216,30 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, ovs_conf.bridge_mappings) self.rp_bandwidths = place_utils.parse_rp_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()) n_utils.validate_rp_bandwidth(self.rp_bandwidths, br_set) self.rp_inventory_defaults = place_utils.parse_rp_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( ovs_conf.resource_provider_hypervisors, {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_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': self.rp_hypervisors, 'integration_bridge': @@ -2777,6 +2801,13 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, network_id, self.agent_id, self.conf.host) 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): """Verify if the ip exists on the agent's host.""" diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py index 0027be87838..e3b565bebbf 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py @@ -2566,6 +2566,35 @@ class TestOvsNeutronAgent(object): self.assertIn(n_const.RP_BANDWIDTHS, 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): self.assertIn(n_const.RP_INVENTORY_DEFAULTS, self.agent.agent_state['configurations']) diff --git a/releasenotes/notes/pps-config-ovs-036b5940694f786c.yaml b/releasenotes/notes/pps-config-ovs-036b5940694f786c.yaml new file mode 100644 index 00000000000..436897ca2e4 --- /dev/null +++ b/releasenotes/notes/pps-config-ovs-036b5940694f786c.yaml @@ -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.