From f822742af06b4e0e7e9729ad600102258df0ddad Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Wed, 15 Mar 2023 06:29:39 +0100 Subject: [PATCH] [OVN] Explicitly define the fixed IPs for the metadata port The metadata port fixed IPs depend on the subnets "enabled_dhcp" flag. If the subnet has this flag disabled, the metadata port doesn't receive an IP on the subnet CIDR. The method ``create_metadata_port`` should explicitly define what fixed IPs should request the metadata port during the creating depending on the subnets "enabled_dhcp" flag. Closes-Bug: #2011724 Change-Id: If362fab20ac03f8b62471b60c031f9349171ce93 (cherry picked from commit 9704dca84ea3ad21ecf9730eea03692daeddf382) --- .../ovn/mech_driver/ovsdb/ovn_client.py | 27 +++++--- .../ovn/mech_driver/ovsdb/test_ovn_client.py | 68 +++++++++++++++++++ 2 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_client.py diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py index 96b7b61360c..02e71ad09d4 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py @@ -2395,17 +2395,26 @@ class OVNClient(object): return fixed_ip['ip_address'] def create_metadata_port(self, context, network): - if ovn_conf.is_ovn_metadata_enabled(): - metadata_port = self._find_metadata_port(context, network['id']) - if not metadata_port: - # Create a neutron port for DHCP/metadata services - port = {'port': - {'network_id': network['id'], + if not ovn_conf.is_ovn_metadata_enabled(): + return + + if self._find_metadata_port(context, network['id']): + return + + # Create a neutron port for DHCP/metadata services + filters = {'network_id': [network['id']]} + subnets = self._plugin.get_subnets(context, filters=filters) + fixed_ips = [{'subnet_id': s['id']} + for s in subnets if s['enable_dhcp']] + port = {'port': {'network_id': network['id'], 'tenant_id': network['project_id'], 'device_owner': const.DEVICE_OWNER_DISTRIBUTED, - 'device_id': 'ovnmeta-%s' % network['id']}} - # TODO(boden): rehome create_port into neutron-lib - p_utils.create_port(self._plugin, context, port) + 'device_id': 'ovnmeta-%s' % network['id'], + 'fixed_ips': fixed_ips, + } + } + # TODO(boden): rehome create_port into neutron-lib + p_utils.create_port(self._plugin, context, port) def update_metadata_port(self, context, network_id, subnet=None): """Update metadata port. diff --git a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_client.py b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_client.py new file mode 100644 index 00000000000..6a9b5a0938a --- /dev/null +++ b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_client.py @@ -0,0 +1,68 @@ +# Copyright 2023 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from neutron_lib import constants + +from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf as ovn_config +from neutron.tests.functional import base + + +class TestOVNClient(base.TestOVNFunctionalBase): + + def test_create_metadata_port(self): + def check_metadata_port(enable_dhcp): + ports = self.plugin.get_ports( + self.context, filters={'network_id': [network['id']]}) + self.assertEqual(1, len(ports)) + if enable_dhcp: + self.assertEqual(1, len(ports[0]['fixed_ips'])) + else: + self.assertEqual(0, len(ports[0]['fixed_ips'])) + return ports + + ovn_config.cfg.CONF.set_override('ovn_metadata_enabled', True, + group='ovn') + ovn_client = self.mech_driver._ovn_client + for enable_dhcp in (True, False): + network_args = {'tenant_id': 'project_1', + 'name': 'test_net_1', + 'admin_state_up': True, + 'shared': False, + 'status': constants.NET_STATUS_ACTIVE} + network = self.plugin.create_network(self.context, + {'network': network_args}) + subnet_args = {'tenant_id': 'project_1', + 'name': 'test_snet_1', + 'network_id': network['id'], + 'ip_version': constants.IP_VERSION_4, + 'cidr': '10.210.10.0/28', + 'enable_dhcp': enable_dhcp, + 'gateway_ip': constants.ATTR_NOT_SPECIFIED, + 'allocation_pools': constants.ATTR_NOT_SPECIFIED, + 'dns_nameservers': constants.ATTR_NOT_SPECIFIED, + 'host_routes': constants.ATTR_NOT_SPECIFIED} + self.plugin.create_subnet(self.context, {'subnet': subnet_args}) + + # The metadata port has been created during the network creation. + ports = check_metadata_port(enable_dhcp) + + # Force the deletion and creation the metadata port. + self.plugin.delete_port(self.context, ports[0]['id']) + ovn_client.create_metadata_port(self.context, network) + check_metadata_port(enable_dhcp) + + # Call again the "create_metadata_port" method as is idempotent + # because it checks first if the metadata port exists. + ovn_client.create_metadata_port(self.context, network) + check_metadata_port(enable_dhcp)