[OVN] Update metadata port ony for requested subnet

When a subnet is updated or created, the metadata port is updated too,
to add the fixed IP address of the new subnet. In this case, the port
should update only the IP address of this specific subnet.

Change-Id: I05394e49077a72199bbc80c8cb622ec2b17f2fa7
Closes-Bug: #1890432
(cherry picked from commit 93225e016b)
This commit is contained in:
Rodolfo Alonso Hernandez 2020-08-07 13:33:29 +00:00 committed by Corey Bryant
parent 04e1a63979
commit 9c43b4e7a0
2 changed files with 91 additions and 24 deletions

View File

@ -1951,7 +1951,8 @@ class OVNClient(object):
def create_subnet(self, context, subnet, network):
if subnet['enable_dhcp']:
if subnet['ip_version'] == 4:
self.update_metadata_port(context, network['id'])
self.update_metadata_port(context, network['id'],
subnet_id=subnet['id'])
self._add_subnet_dhcp_options(subnet, network)
db_rev.bump_revision(context, subnet, ovn_const.TYPE_SUBNETS)
@ -1968,7 +1969,8 @@ class OVNClient(object):
subnet['id'])['subnet']
if subnet['enable_dhcp'] or ovn_subnet:
self.update_metadata_port(context, network['id'])
self.update_metadata_port(context, network['id'],
subnet_id=subnet['id'])
check_rev_cmd = self._nb_idl.check_revision_number(
subnet['id'], subnet, ovn_const.TYPE_SUBNETS)
@ -2076,12 +2078,24 @@ class OVNClient(object):
# TODO(boden): rehome create_port into neutron-lib
p_utils.create_port(self._plugin, context, port)
def update_metadata_port(self, context, network_id):
def update_metadata_port(self, context, network_id, subnet_id=None):
"""Update metadata port.
This function will allocate an IP address for the metadata port of
the given network in all its IPv4 subnets.
the given network in all its IPv4 subnets or the given subnet.
"""
def update_metadata_port_fixed_ips(metadata_port, subnet_ids):
wanted_fixed_ips = [
{'subnet_id': fixed_ip['subnet_id'],
'ip_address': fixed_ip['ip_address']} for fixed_ip in
metadata_port['fixed_ips']]
wanted_fixed_ips.extend({'subnet_id': s_id} for s_id in subnet_ids)
port = {'id': metadata_port['id'],
'port': {'network_id': network_id,
'fixed_ips': wanted_fixed_ips}}
self._plugin.update_port(n_context.get_admin_context(),
metadata_port['id'], port)
if not ovn_conf.is_ovn_metadata_enabled():
return
@ -2092,31 +2106,28 @@ class OVNClient(object):
network_id)
return
port_subnet_ids = set(ip['subnet_id'] for ip in
metadata_port['fixed_ips'])
# If this method is called from "create_subnet" or "update_subnet",
# only the fixed IP address from this subnet should be updated in the
# metadata port.
if subnet_id:
if subnet_id not in port_subnet_ids:
update_metadata_port_fixed_ips(metadata_port, [subnet_id])
return
# Retrieve all subnets in this network
subnets = self._plugin.get_subnets(context, filters=dict(
network_id=[network_id], ip_version=[4]))
subnet_ids = set(s['id'] for s in subnets)
port_subnet_ids = set(ip['subnet_id'] for ip in
metadata_port['fixed_ips'])
# Find all subnets where metadata port doesn't have an IP in and
# allocate one.
if subnet_ids != port_subnet_ids:
wanted_fixed_ips = []
for fixed_ip in metadata_port['fixed_ips']:
wanted_fixed_ips.append(
{'subnet_id': fixed_ip['subnet_id'],
'ip_address': fixed_ip['ip_address']})
wanted_fixed_ips.extend(
dict(subnet_id=s)
for s in subnet_ids - port_subnet_ids)
port = {'id': metadata_port['id'],
'port': {'network_id': network_id,
'fixed_ips': wanted_fixed_ips}}
self._plugin.update_port(n_context.get_admin_context(),
metadata_port['id'], port)
update_metadata_port_fixed_ips(metadata_port,
subnet_ids - port_subnet_ids)
def get_parent_port(self, port_id):
return self._nb_idl.get_parent_port(port_id)

View File

@ -1504,13 +1504,13 @@ class TestOVNMechanismDriver(test_plugin.Ml2PluginV2TestCase):
self.mech_driver.update_subnet_postcommit(context)
esd.assert_called_once_with(
context.current, context.network.current, mock.ANY)
umd.assert_called_once_with(mock.ANY, 'id')
umd.assert_called_once_with(mock.ANY, 'id', subnet_id='subnet_id')
def test_update_subnet_postcommit_disable_dhcp(self):
self.mech_driver._nb_ovn.get_subnet_dhcp_options.return_value = {
'subnet': mock.sentinel.subnet, 'ports': []}
context = fakes.FakeSubnetContext(
subnet={'enable_dhcp': False, 'id': 'fake_id', 'ip_version': 4,
subnet={'enable_dhcp': False, 'id': 'subnet_id', 'ip_version': 4,
'network_id': 'id'},
network={'id': 'id'})
with mock.patch.object(
@ -1521,7 +1521,7 @@ class TestOVNMechanismDriver(test_plugin.Ml2PluginV2TestCase):
'update_metadata_port') as umd:
self.mech_driver.update_subnet_postcommit(context)
dsd.assert_called_once_with(context.current['id'], mock.ANY)
umd.assert_called_once_with(mock.ANY, 'id')
umd.assert_called_once_with(mock.ANY, 'id', subnet_id='subnet_id')
def test_update_subnet_postcommit_update_dhcp(self):
self.mech_driver._nb_ovn.get_subnet_dhcp_options.return_value = {
@ -1539,7 +1539,63 @@ class TestOVNMechanismDriver(test_plugin.Ml2PluginV2TestCase):
self.mech_driver.update_subnet_postcommit(context)
usd.assert_called_once_with(
context.current, context.network.current, mock.ANY)
umd.assert_called_once_with(mock.ANY, 'id')
umd.assert_called_once_with(mock.ANY, 'id', subnet_id='subnet_id')
def test_update_metadata_port_with_subnet_present_in_port(self):
ovn_conf.cfg.CONF.set_override('ovn_metadata_enabled', True,
group='ovn')
fixed_ips = [{'subnet_id': 'subnet1', 'ip_address': 'ip_add1'}]
with mock.patch.object(
self.mech_driver._ovn_client, '_find_metadata_port',
return_value={'fixed_ips': fixed_ips, 'id': 'metadata_id'}), \
mock.patch.object(self.mech_driver._plugin, 'get_subnets',
return_value=[{'id': 'subnet1'},
{'id': 'subnet2'}]), \
mock.patch.object(self.mech_driver._plugin, 'update_port') as \
mock_update_port:
self.mech_driver._ovn_client.update_metadata_port(
self.context, 'net_id', subnet_id='subnet1')
mock_update_port.assert_not_called()
def test_update_metadata_port_with_subnet_not_present_in_port(self):
ovn_conf.cfg.CONF.set_override('ovn_metadata_enabled', True,
group='ovn')
fixed_ips = [{'subnet_id': 'subnet1', 'ip_address': 'ip_add1'}]
with mock.patch.object(
self.mech_driver._ovn_client, '_find_metadata_port',
return_value={'fixed_ips': fixed_ips, 'id': 'metadata_id'}), \
mock.patch.object(self.mech_driver._plugin, 'get_subnets',
return_value=[{'id': 'subnet1'},
{'id': 'subnet2'}]), \
mock.patch.object(self.mech_driver._plugin, 'update_port') as \
mock_update_port:
self.mech_driver._ovn_client.update_metadata_port(
self.context, 'net_id', subnet_id='subnet3')
fixed_ips.append({'subnet_id': 'subnet3'})
port = {'id': 'metadata_id', 'port': {
'network_id': 'net_id', 'fixed_ips': fixed_ips}}
mock_update_port.assert_called_once_with(
mock.ANY, 'metadata_id', port)
def test_update_metadata_port_no_subnet(self):
ovn_conf.cfg.CONF.set_override('ovn_metadata_enabled', True,
group='ovn')
fixed_ips = [{'subnet_id': 'subnet1', 'ip_address': 'ip_add1'}]
with mock.patch.object(
self.mech_driver._ovn_client, '_find_metadata_port',
return_value={'fixed_ips': fixed_ips, 'id': 'metadata_id'}), \
mock.patch.object(self.mech_driver._plugin, 'get_subnets',
return_value=[{'id': 'subnet1'},
{'id': 'subnet2'}]), \
mock.patch.object(self.mech_driver._plugin, 'update_port') as \
mock_update_port:
self.mech_driver._ovn_client.update_metadata_port(self.context,
'net_id')
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.ANY, 'metadata_id', port)
@mock.patch.object(provisioning_blocks, 'is_object_blocked')
@mock.patch.object(provisioning_blocks, 'provisioning_complete')