diff --git a/neutron/objects/subnet.py b/neutron/objects/subnet.py index 540c6e559e8..8a2b30f9c7b 100644 --- a/neutron/objects/subnet.py +++ b/neutron/objects/subnet.py @@ -366,12 +366,17 @@ class Subnet(base.NeutronDbObject): :raises: FixedIpsSubnetsNotOnSameSegment """ segment_ids = [] + subnets = query.all() + for fixed_ip in fixed_ips: subnet = None if 'subnet_id' in fixed_ip: try: - subnet = query.filter( - cls.db_model.id == fixed_ip['subnet_id']).all()[0] + subnet = [ + sub + for sub in subnets + if sub['id'] == fixed_ip['subnet_id'] + ][0] except IndexError: # NOTE(hjensas): The subnet is invalid for the network, # return all subnets. This will be detected in following @@ -380,7 +385,7 @@ class Subnet(base.NeutronDbObject): elif 'ip_address' in fixed_ip: ip = netaddr.IPNetwork(fixed_ip['ip_address']) - for s in query.all(): + for s in subnets: if ip in netaddr.IPNetwork(s.cidr): subnet = s break 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 8a1c4b31222..d29b99d1d1e 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 @@ -230,27 +230,46 @@ class OVNClient(object): parent_name = binding_prof.get('parent_name', []) tag = binding_prof.get('tag', []) address = port['mac_address'] - for ip in port.get('fixed_ips', []): - try: - subnet = self._plugin.get_subnet(context, ip['subnet_id']) - except n_exc.SubnetNotFound: - continue - ip_addr = ip['ip_address'] - address += ' ' + ip_addr - cidrs += ' {}/{}'.format(ip['ip_address'], - subnet['cidr'].split('/')[1]) - # Check if the port being created is a virtual port - parents = utils.get_virtual_port_parents( - self._nb_idl, ip_addr, port['network_id'], port['id']) - if not parents: - continue + ip_subnets = port.get('fixed_ips', []) + subnet_ids = [ + ip['subnet_id'] + for ip in ip_subnets + if 'subnet_id' in ip + ] + subnets = self._plugin.get_subnets( + context, filters={'id': subnet_ids}) + if subnets: + for ip in ip_subnets: + ip_addr = ip['ip_address'] + address += ' ' + ip_addr + subnet = None - port_type = ovn_const.LSP_TYPE_VIRTUAL - options[ovn_const.LSP_OPTIONS_VIRTUAL_IP_KEY] = ip_addr - options[ovn_const.LSP_OPTIONS_VIRTUAL_PARENTS_KEY] = ( - ','.join(parents)) - break + try: + subnet = [ + sub + for sub in subnets + if sub["id"] == ip["subnet_id"] + ][0] + except IndexError: + LOG.debug('Subnet not found for ip address %s', + ip_addr) + continue + + cidrs += ' {}/{}'.format(ip['ip_address'], + subnet['cidr'].split('/')[1]) + + # Check if the port being created is a virtual port + parents = utils.get_virtual_port_parents( + self._nb_idl, ip_addr, port['network_id'], port['id']) + if not parents: + continue + + port_type = ovn_const.LSP_TYPE_VIRTUAL + options[ovn_const.LSP_OPTIONS_VIRTUAL_IP_KEY] = ip_addr + options[ovn_const.LSP_OPTIONS_VIRTUAL_PARENTS_KEY] = ( + ','.join(parents)) + break # Only adjust the OVN type if the port is not owned by Neutron # DHCP agents. @@ -497,9 +516,14 @@ class OVNClient(object): if self.is_metadata_port(port): context = n_context.get_admin_context() network = self._plugin.get_network(context, port['network_id']) - subnet_ids = set(_ip['subnet_id'] for _ip in port['fixed_ips']) - for subnet_id in subnet_ids: - subnet = self._plugin.get_subnet(context, subnet_id) + subnet_ids = [ + _ip['subnet_id'] + for _ip in port['fixed_ips'] + if 'subnet_id' in _ip + ] + + for subnet in self._plugin.get_subnets( + context, filters={'id': subnet_ids}): if not subnet['enable_dhcp']: continue self._update_subnet_dhcp_options(subnet, network, txn) diff --git a/neutron/tests/unit/objects/test_subnet.py b/neutron/tests/unit/objects/test_subnet.py index 5aa7c99b560..e0174ca9907 100644 --- a/neutron/tests/unit/objects/test_subnet.py +++ b/neutron/tests/unit/objects/test_subnet.py @@ -268,6 +268,26 @@ class SubnetDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, self.assertEqual([service_type_obj.service_type], obj1.service_types) + def test_find_candidate_subnets(self): + network = self._create_test_network() + subnet_data = dict(self.obj_fields[0]) + subnet_data['network_id'] = network['id'] + subnet_net = self._make_object(subnet_data) + subnet2_data = dict(self.obj_fields[0]) + subnet2_data['id'] = uuidutils.generate_uuid() + subnet2_data['network_id'] = network['id'] + subnet2_net = self._make_object(subnet2_data) + subnet_net.create() + subnet2_net.create() + fixed_ips = [ + {'subnet_id': subnet_data['id'], 'ip_address': '10.0.0.2'}, + {'subnet_id': subnet2_data['id'], 'ip_address': '10.0.1.2'}] + candidate_subnet = subnet.Subnet.find_candidate_subnets( + self.context, network['id'], None, None, True, fixed_ips) + self.assertEqual(2, len(candidate_subnet)) + self.assertNotEqual( + candidate_subnet[0]['id'], candidate_subnet[1]['id']) + class NetworkSubnetLockTestCase(obj_test_base.BaseObjectIfaceTestCase): diff --git a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py index a3b98d1a581..b90f11abab5 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py +++ b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py @@ -1608,6 +1608,65 @@ class TestOVNMechanismDriver(test_plugin.Ml2PluginV2TestCase): context.current, context.network.current, mock.ANY) umd.assert_called_once_with(mock.ANY, 'id', subnet=subnet) + def test__get_port_options(self): + with mock.patch.object(self.mech_driver._plugin, 'get_subnets') as \ + mock_get_subnets: + port = {'id': 'virt-port', + 'mac_address': '00:00:00:00:00:00', + 'device_owner': 'device_owner', + 'network_id': 'foo', + 'fixed_ips': [{'subnet_id': 'subnet-1', + 'ip_address': '10.0.0.55'}, + {'subnet_id': 'subnet-2', + 'ip_address': '10.0.1.55'}, + ]} + subnet_ids = [ + ip['subnet_id'] + for ip in port.get('fixed_ips') + ] + self.mech_driver._ovn_client._get_port_options(port) + mock_get_subnets.assert_called_once_with( + mock.ANY, + filters={'id': subnet_ids}) + + def test_update_port(self): + with mock.patch.object( + self.mech_driver._ovn_client, 'is_metadata_port') as \ + mock_is_metadata_port, \ + mock.patch.object(self.mech_driver._plugin, 'get_subnets') as \ + mock_get_subnets, \ + mock.patch.object(self.mech_driver._plugin, 'get_network') as \ + mock_get_network: + net_attrs = {} + fake_net = ( + fakes.FakeNetwork.create_one_network(attrs=net_attrs).info()) + port = {'id': 'virt-port', + 'mac_address': '00:00:00:00:00:00', + 'name': 'port-foo', + 'device_id': 'device_id-foo', + 'project_id': 'project_id-foo', + 'device_owner': 'device_owner', + 'network_id': 'foo', + 'admin_state_up': True, + 'fixed_ips': [{'subnet_id': 'subnet-1', + 'ip_address': '10.0.0.55'}, + {'subnet_id': 'subnet-2', + 'ip_address': '10.0.1.55'}, + ]} + subnet_ids = [ + ip['subnet_id'] + for ip in port.get('fixed_ips') + ] + + mock_is_metadata_port.return_value = [True] + mock_get_network.return_value = fake_net + self.mech_driver._ovn_client.update_port( + self.context, port) + self.assertEqual(mock_get_subnets.call_count, 2) + mock_get_subnets.assert_called_with( + mock.ANY, + filters={'id': subnet_ids}) + def test_update_metadata_port_with_subnet(self): ovn_conf.cfg.CONF.set_override('ovn_metadata_enabled', True, group='ovn')