Fix "_sync_metadata_ports" with no DHCP subnets

When a subnet does not have DHCP configured, the metadata port does
not have an IP address on this CIDR. The method
"OvnNbSynchronizer.sync_networks_ports_and_dhcp_opts", was always
setting an IP address for the metadata ports, regardless of the subnet
configuration (with or without DHCP).

The method "_sync_metadata_ports", in charge of synchronizing the
metadata ports, now filters the subnets by the parameter "enable_dhcp".

In case of having a subnet with DHCP enabled, if the metadata port is
missing the subnet IP addresses, the method adds them.

In case of having a subnet without DHCP enabled, if the metadata port
has an IP address on the subnet, the method removes it.

Closes-Bug: #1939726

Conflicts:
    neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py

Change-Id: I09cc14dff6933aae63cbd43a29f9221f405ecede
(cherry picked from commit 5e32dddc11)
This commit is contained in:
Rodolfo Alonso Hernandez 2021-09-30 16:28:19 +00:00
parent 5418912b70
commit fe9e596d3e
2 changed files with 46 additions and 14 deletions

View File

@ -2092,12 +2092,15 @@ class OVNClient(object):
This function will allocate an IP address for the metadata port of This function will allocate an IP address for the metadata port of
the given network in all its IPv4 subnets or the given subnet. the given network in all its IPv4 subnets or the given subnet.
""" """
def update_metadata_port_fixed_ips(metadata_port, subnet_ids): def update_metadata_port_fixed_ips(metadata_port, add_subnet_ids,
del_subnet_ids):
wanted_fixed_ips = [ wanted_fixed_ips = [
{'subnet_id': fixed_ip['subnet_id'], {'subnet_id': fixed_ip['subnet_id'],
'ip_address': fixed_ip['ip_address']} for fixed_ip in 'ip_address': fixed_ip['ip_address']} for fixed_ip in
metadata_port['fixed_ips']] metadata_port['fixed_ips'] if
wanted_fixed_ips.extend({'subnet_id': s_id} for s_id in subnet_ids) fixed_ip['subnet_id'] not in del_subnet_ids]
wanted_fixed_ips.extend({'subnet_id': s_id} for s_id in
add_subnet_ids)
port = {'id': metadata_port['id'], port = {'id': metadata_port['id'],
'port': {'network_id': network_id, 'port': {'network_id': network_id,
'fixed_ips': wanted_fixed_ips}} 'fixed_ips': wanted_fixed_ips}}
@ -2122,12 +2125,13 @@ class OVNClient(object):
# metadata port. # metadata port.
if subnet_id: if subnet_id:
if subnet_id not in port_subnet_ids: if subnet_id not in port_subnet_ids:
update_metadata_port_fixed_ips(metadata_port, [subnet_id]) update_metadata_port_fixed_ips(metadata_port, [subnet_id], [])
return return
# Retrieve all subnets in this network # Retrieve all subnets in this network
subnets = self._plugin.get_subnets(context, filters=dict( subnets = self._plugin.get_subnets(context, filters=dict(
network_id=[network_id], ip_version=[4])) network_id=[network_id], ip_version=[const.IP_VERSION_4],
enable_dhcp=[True]))
subnet_ids = set(s['id'] for s in subnets) subnet_ids = set(s['id'] for s in subnets)
@ -2135,7 +2139,8 @@ class OVNClient(object):
# allocate one. # allocate one.
if subnet_ids != port_subnet_ids: if subnet_ids != port_subnet_ids:
update_metadata_port_fixed_ips(metadata_port, update_metadata_port_fixed_ips(metadata_port,
subnet_ids - port_subnet_ids) subnet_ids - port_subnet_ids,
port_subnet_ids - subnet_ids)
def get_parent_port(self, port_id): def get_parent_port(self, port_id):
return self._nb_idl.get_parent_port(port_id) return self._nb_idl.get_parent_port(port_id)

View File

@ -1682,22 +1682,49 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
def test_update_metadata_port_no_subnet(self): def test_update_metadata_port_no_subnet(self):
ovn_conf.cfg.CONF.set_override('ovn_metadata_enabled', True, ovn_conf.cfg.CONF.set_override('ovn_metadata_enabled', True,
group='ovn') group='ovn')
fixed_ips = [{'subnet_id': 'subnet1', 'ip_address': 'ip_add1'}]
with mock.patch.object( with mock.patch.object(
self.mech_driver._ovn_client, '_find_metadata_port', self.mech_driver._ovn_client, '_find_metadata_port') as \
return_value={'fixed_ips': fixed_ips, 'id': 'metadata_id'}), \ mock_metaport, \
mock.patch.object(self.mech_driver._plugin, 'get_subnets', mock.patch.object(self.mech_driver._plugin, 'get_subnets') as \
return_value=[{'id': 'subnet1'}, mock_get_subnets, \
{'id': 'subnet2'}]), \
mock.patch.object(self.mech_driver._plugin, 'update_port') as \ mock.patch.object(self.mech_driver._plugin, 'update_port') as \
mock_update_port: mock_update_port:
# Port with IP in subnet1; subnet1 and subnet2 with DHCP.
mock_get_subnets.return_value = [{'id': 'subnet1'},
{'id': 'subnet2'}]
fixed_ips = [{'subnet_id': 'subnet1', 'ip_address': 'ip_add1'}]
mock_metaport.return_value = {'fixed_ips': fixed_ips,
'id': 'metadata_id'}
self.mech_driver._ovn_client.update_metadata_port(self.context, self.mech_driver._ovn_client.update_metadata_port(self.context,
'net_id') 'net_id')
port = {'id': 'metadata_id',
'port': {'network_id': 'net_id', 'fixed_ips': fixed_ips}}
fixed_ips.append({'subnet_id': 'subnet2'}) fixed_ips.append({'subnet_id': 'subnet2'})
port = {'id': 'metadata_id', 'port': {
'network_id': 'net_id', 'fixed_ips': fixed_ips}}
mock_update_port.assert_called_once_with( mock_update_port.assert_called_once_with(
mock.ANY, 'metadata_id', port) mock.ANY, 'metadata_id', port)
mock_update_port.reset_mock()
# Port with IP in subnet1; subnet1 with DHCP, subnet2 without DHCP.
mock_get_subnets.return_value = [{'id': 'subnet1'}]
fixed_ips = [{'subnet_id': 'subnet1', 'ip_address': 'ip_add1'}]
mock_metaport.return_value = {'fixed_ips': fixed_ips,
'id': 'metadata_id'}
self.mech_driver._ovn_client.update_metadata_port(self.context,
'net_id')
mock_update_port.assert_not_called()
# Port with IP in subnet1; subnet1 without DHCP.
mock_get_subnets.return_value = []
fixed_ips = [{'subnet_id': 'subnet1', 'ip_address': 'ip_add1'}]
mock_metaport.return_value = {'fixed_ips': fixed_ips,
'id': 'metadata_id'}
self.mech_driver._ovn_client.update_metadata_port(self.context,
'net_id')
port = {'id': 'metadata_id',
'port': {'network_id': 'net_id', 'fixed_ips': []}}
mock_update_port.assert_called_once_with(
mock.ANY, 'metadata_id', port)
mock_update_port.reset_mock()
@mock.patch.object(provisioning_blocks, 'is_object_blocked') @mock.patch.object(provisioning_blocks, 'is_object_blocked')
@mock.patch.object(provisioning_blocks, 'provisioning_complete') @mock.patch.object(provisioning_blocks, 'provisioning_complete')