Skip to content

Commit fb2310a

Browse files
authored
Merge pull request #43 from stackhpc/upstream/yoga-2023-05-01
Synchronise yoga with upstream
2 parents d4b3efc + 28dd08a commit fb2310a

File tree

7 files changed

+124
-29
lines changed

7 files changed

+124
-29
lines changed

neutron/agent/l3/keepalived_state_change.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ def handle_sigterm(self, signum, frame):
168168
def configure(conf):
169169
config.init(sys.argv[1:])
170170
conf.set_override('log_dir', cfg.CONF.conf_dir)
171-
conf.set_override('debug', True)
172171
conf.set_override('use_syslog', True)
173172
config.setup_logging()
174173
privileged.default.set_client_mode(False)

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

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2321,7 +2321,7 @@ def create_subnet(self, context, subnet, network):
23212321
mport_updated = False
23222322
if subnet['ip_version'] == const.IP_VERSION_4:
23232323
mport_updated = self.update_metadata_port(
2324-
context, network['id'], subnet=subnet)
2324+
context, network, subnet=subnet)
23252325
if subnet['ip_version'] == const.IP_VERSION_6 or not mport_updated:
23262326
# NOTE(ralonsoh): if IPv4 but the metadata port has not been
23272327
# updated, the DHPC options register has not been created.
@@ -2341,7 +2341,7 @@ def update_subnet(self, context, subnet, network, txn=None):
23412341
subnet['id'])['subnet']
23422342

23432343
if subnet['enable_dhcp'] or ovn_subnet:
2344-
self.update_metadata_port(context, network['id'], subnet=subnet)
2344+
self.update_metadata_port(context, network, subnet=subnet)
23452345

23462346
check_rev_cmd = self._nb_idl.check_revision_number(
23472347
subnet['id'], subnet, ovn_const.TYPE_SUBNETS)
@@ -2437,8 +2437,9 @@ def create_metadata_port(self, context, network):
24372437
if not ovn_conf.is_ovn_metadata_enabled():
24382438
return
24392439

2440-
if self._find_metadata_port(context, network['id']):
2441-
return
2440+
metadata_port = self._find_metadata_port(context, network['id'])
2441+
if metadata_port:
2442+
return metadata_port
24422443

24432444
# Create a neutron port for DHCP/metadata services
24442445
filters = {'network_id': [network['id']]}
@@ -2453,16 +2454,19 @@ def create_metadata_port(self, context, network):
24532454
}
24542455
}
24552456
# TODO(boden): rehome create_port into neutron-lib
2456-
p_utils.create_port(self._plugin, context, port)
2457+
return p_utils.create_port(self._plugin, context, port)
24572458

2458-
def update_metadata_port(self, context, network_id, subnet=None):
2459+
def update_metadata_port(self, context, network, subnet=None):
24592460
"""Update metadata port.
24602461
24612462
This function will allocate an IP address for the metadata port of
24622463
the given network in all its IPv4 subnets or the given subnet. Returns
24632464
"True" if the metadata port has been updated and "False" if OVN
2464-
metadata is disabled or the metadata port does not exist.
2465+
metadata is disabled or the metadata port does not exist or
2466+
cannot be created.
24652467
"""
2468+
network_id = network['id']
2469+
24662470
def update_metadata_port_fixed_ips(metadata_port, add_subnet_ids,
24672471
del_subnet_ids):
24682472
wanted_fixed_ips = [
@@ -2481,11 +2485,11 @@ def update_metadata_port_fixed_ips(metadata_port, add_subnet_ids,
24812485
if not ovn_conf.is_ovn_metadata_enabled():
24822486
return False
24832487

2484-
# Retrieve the metadata port of this network
2485-
metadata_port = self._find_metadata_port(context, network_id)
2488+
# Retrieve or create the metadata port of this network
2489+
metadata_port = self.create_metadata_port(context, network)
24862490
if not metadata_port:
2487-
LOG.error("Metadata port couldn't be found for network %s",
2488-
network_id)
2491+
LOG.error("Metadata port could not be found or created "
2492+
"for network %s", network_id)
24892493
return False
24902494

24912495
port_subnet_ids = set(ip['subnet_id'] for ip in

neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_db_sync.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -951,7 +951,7 @@ def _sync_metadata_ports(self, ctx, db_ports):
951951
try:
952952
# Make sure that this port has an IP address in all the
953953
# subnets
954-
self._ovn_client.update_metadata_port(ctx, net['id'])
954+
self._ovn_client.update_metadata_port(ctx, net)
955955
except n_exc.IpAddressGenerationFailure:
956956
LOG.error('Could not allocate IP addresses for '
957957
'metadata port in network %s', net['id'])

neutron/tests/functional/agent/l3/test_keepalived_state_change.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ def _generate_cmd_opts(self, monitor_interface=None, cidr=None):
8585
'--pid_file=%s' % self.pid_file,
8686
'--state_path=%s' % self.conf_dir,
8787
'--user=%s' % os.geteuid(),
88-
'--group=%s' % os.getegid()]
88+
'--group=%s' % os.getegid(),
89+
'--debug',
90+
]
8991

9092
def _search_in_file(self, file_name, text):
9193
def text_in_file():

neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,7 @@ class TestMetadataPorts(base.TestOVNFunctionalBase):
995995

996996
def setUp(self, *args, **kwargs):
997997
super().setUp(*args, **kwargs)
998+
self.plugin = self.mech_driver._plugin
998999
self._ovn_client = self.mech_driver._ovn_client
9991000
self.meta_regex = re.compile(r'%s,(\d+\.\d+\.\d+\.\d+)' %
10001001
constants.METADATA_V4_CIDR)
@@ -1017,7 +1018,7 @@ def _list_ports_ovn(self, net_id=None):
10171018
res = self._list_ports(self.fmt, net_id=net_id)
10181019
return self.deserialize(self.fmt, res)['ports']
10191020

1020-
def _check_metadata_port(self, net_id, fixed_ip):
1021+
def _check_metadata_port(self, net_id, fixed_ip, fail=True):
10211022
for port in self._list_ports_ovn(net_id=net_id):
10221023
if ovn_client.OVNClient.is_metadata_port(port):
10231024
self.assertEqual(net_id, port['network_id'])
@@ -1027,13 +1028,17 @@ def _check_metadata_port(self, net_id, fixed_ip):
10271028
self.assertEqual([], port['fixed_ips'])
10281029
return port['id']
10291030

1030-
self.fail('Metadata port is not present in network %s or data is not '
1031-
'correct' % self.n1_id)
1031+
if fail:
1032+
self.fail('Metadata port is not present in network %s or data is '
1033+
'not correct' % self.n1_id)
10321034

10331035
def _check_subnet_dhcp_options(self, subnet_id, cidr):
1034-
# This method checks the DHCP options CIDR and returns, if exits, the
1035-
# metadata port IP address, included in the classless static routes.
1036+
# This method checks DHCP options for a subnet ID, and if they exist,
1037+
# verifies the CIDR matches. Returns the metadata port IP address
1038+
# if it is included in the classless static routes, else returns None.
10361039
dhcp_opts = self._ovn_client._nb_idl.get_subnet_dhcp_options(subnet_id)
1040+
if not dhcp_opts['subnet']:
1041+
return
10371042
self.assertEqual(cidr, dhcp_opts['subnet']['cidr'])
10381043
routes = dhcp_opts['subnet']['options'].get('classless_static_route')
10391044
if not routes:
@@ -1062,6 +1067,35 @@ def test_subnet_ipv4(self):
10621067
fixed_ip = {'subnet_id': subnet['id'], 'ip_address': metatada_ip}
10631068
self._check_metadata_port(self.n1_id, fixed_ip)
10641069

1070+
def test_update_subnet_ipv4(self):
1071+
self._create_network_ovn(metadata_enabled=True)
1072+
subnet = self._create_subnet_ovn('10.0.0.0/24')
1073+
metatada_ip = self._check_subnet_dhcp_options(subnet['id'],
1074+
'10.0.0.0/24')
1075+
fixed_ip = {'subnet_id': subnet['id'], 'ip_address': metatada_ip}
1076+
port_id = self._check_metadata_port(self.n1_id, fixed_ip)
1077+
1078+
# Disable DHCP, port should still be present
1079+
subnet['enable_dhcp'] = False
1080+
self._ovn_client.update_subnet(self.context, subnet,
1081+
self.n1['network'])
1082+
port_id = self._check_metadata_port(self.n1_id, None)
1083+
self.assertIsNone(self._check_subnet_dhcp_options(subnet['id'], []))
1084+
1085+
# Delete metadata port
1086+
self.plugin.delete_port(self.context, port_id)
1087+
port_id = self._check_metadata_port(self.n1_id, None, fail=False)
1088+
self.assertIsNone(port_id)
1089+
1090+
# Enable DHCP, metadata port should have been re-created
1091+
subnet['enable_dhcp'] = True
1092+
self._ovn_client.update_subnet(self.context, subnet,
1093+
self.n1['network'])
1094+
metatada_ip = self._check_subnet_dhcp_options(subnet['id'],
1095+
'10.0.0.0/24')
1096+
fixed_ip = {'subnet_id': subnet['id'], 'ip_address': metatada_ip}
1097+
port_id = self._check_metadata_port(self.n1_id, fixed_ip)
1098+
10651099
def test_subnet_ipv4_no_metadata(self):
10661100
self._create_network_ovn(metadata_enabled=False)
10671101
subnet = self._create_subnet_ovn('10.0.0.0/24')

neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from neutron_lib import context
3333
from neutron_lib import exceptions as n_exc
3434
from neutron_lib.plugins import directory
35+
from neutron_lib.plugins import utils as p_utils
3536
from neutron_lib.tests import tools
3637
from neutron_lib.utils import net as n_net
3738
from oslo_concurrency import processutils
@@ -1736,7 +1737,8 @@ def test_update_subnet_postcommit_enable_dhcp(self):
17361737
self.mech_driver.update_subnet_postcommit(context)
17371738
esd.assert_called_once_with(
17381739
context.current, context.network.current, mock.ANY)
1739-
umd.assert_called_once_with(mock.ANY, 'id', subnet=subnet)
1740+
umd.assert_called_once_with(mock.ANY, context.network.current,
1741+
subnet=subnet)
17401742

17411743
def test_update_subnet_postcommit_disable_dhcp(self):
17421744
self.mech_driver.nb_ovn.get_subnet_dhcp_options.return_value = {
@@ -1752,7 +1754,8 @@ def test_update_subnet_postcommit_disable_dhcp(self):
17521754
'update_metadata_port') as umd:
17531755
self.mech_driver.update_subnet_postcommit(context)
17541756
dsd.assert_called_once_with(context.current['id'], mock.ANY)
1755-
umd.assert_called_once_with(mock.ANY, 'id', subnet=subnet)
1757+
umd.assert_called_once_with(mock.ANY, context.network.current,
1758+
subnet=subnet)
17561759

17571760
def test_update_subnet_postcommit_update_dhcp(self):
17581761
self.mech_driver.nb_ovn.get_subnet_dhcp_options.return_value = {
@@ -1769,7 +1772,8 @@ def test_update_subnet_postcommit_update_dhcp(self):
17691772
self.mech_driver.update_subnet_postcommit(context)
17701773
usd.assert_called_once_with(
17711774
context.current, context.network.current, mock.ANY)
1772-
umd.assert_called_once_with(mock.ANY, 'id', subnet=subnet)
1775+
umd.assert_called_once_with(mock.ANY, context.network.current,
1776+
subnet=subnet)
17731777

17741778
def test__get_port_options(self):
17751779
with mock.patch.object(self.mech_driver._plugin, 'get_subnets') as \
@@ -1955,9 +1959,10 @@ def test_update_metadata_port_with_subnet(self):
19551959
mock_metaport.return_value = {'fixed_ips': fixed_ips,
19561960
'id': 'metadata_id'}
19571961
mock_get_subnets.return_value = [{'id': 'subnet1'}]
1962+
network = {'id': 'net_id'}
19581963
subnet = {'id': 'subnet1', 'enable_dhcp': True}
19591964
self.mech_driver._ovn_client.update_metadata_port(
1960-
self.context, 'net_id', subnet=subnet)
1965+
self.context, network, subnet=subnet)
19611966
mock_update_port.assert_not_called()
19621967

19631968
# Subnet without DHCP, present in port.
@@ -1967,7 +1972,7 @@ def test_update_metadata_port_with_subnet(self):
19671972
mock_get_subnets.return_value = [{'id': 'subnet1'}]
19681973
subnet = {'id': 'subnet1', 'enable_dhcp': False}
19691974
self.mech_driver._ovn_client.update_metadata_port(
1970-
self.context, 'net_id', subnet=subnet)
1975+
self.context, network, subnet=subnet)
19711976
port = {'id': 'metadata_id',
19721977
'port': {'network_id': 'net_id', 'fixed_ips': []}}
19731978
mock_update_port.assert_called_once_with(mock.ANY, 'metadata_id',
@@ -1980,7 +1985,7 @@ def test_update_metadata_port_with_subnet(self):
19801985
mock_get_subnets.return_value = []
19811986
subnet = {'id': 'subnet1', 'enable_dhcp': True}
19821987
self.mech_driver._ovn_client.update_metadata_port(
1983-
self.context, 'net_id', subnet=subnet)
1988+
self.context, network, subnet=subnet)
19841989
fixed_ips = [{'subnet_id': 'subnet1'}]
19851990
port = {'id': 'metadata_id',
19861991
'port': {'network_id': 'net_id', 'fixed_ips': fixed_ips}}
@@ -1994,7 +1999,7 @@ def test_update_metadata_port_with_subnet(self):
19941999
mock_get_subnets.return_value = []
19952000
subnet = {'id': 'subnet1', 'enable_dhcp': False}
19962001
self.mech_driver._ovn_client.update_metadata_port(
1997-
self.context, 'net_id', subnet=subnet)
2002+
self.context, network, subnet=subnet)
19982003
mock_update_port.assert_not_called()
19992004

20002005
def test_update_metadata_port_no_subnet(self):
@@ -2011,10 +2016,11 @@ def test_update_metadata_port_no_subnet(self):
20112016
mock_get_subnets.return_value = [{'id': 'subnet1'},
20122017
{'id': 'subnet2'}]
20132018
fixed_ips = [{'subnet_id': 'subnet1', 'ip_address': 'ip_add1'}]
2019+
network = {'id': 'net_id'}
20142020
mock_metaport.return_value = {'fixed_ips': fixed_ips,
20152021
'id': 'metadata_id'}
20162022
self.mech_driver._ovn_client.update_metadata_port(self.context,
2017-
'net_id')
2023+
network)
20182024
port = {'id': 'metadata_id',
20192025
'port': {'network_id': 'net_id', 'fixed_ips': fixed_ips}}
20202026
fixed_ips.append({'subnet_id': 'subnet2'})
@@ -2025,10 +2031,11 @@ def test_update_metadata_port_no_subnet(self):
20252031
# Port with IP in subnet1; subnet1 with DHCP, subnet2 without DHCP.
20262032
mock_get_subnets.return_value = [{'id': 'subnet1'}]
20272033
fixed_ips = [{'subnet_id': 'subnet1', 'ip_address': 'ip_add1'}]
2034+
network = {'id': 'net_id'}
20282035
mock_metaport.return_value = {'fixed_ips': fixed_ips,
20292036
'id': 'metadata_id'}
20302037
self.mech_driver._ovn_client.update_metadata_port(self.context,
2031-
'net_id')
2038+
network)
20322039
mock_update_port.assert_not_called()
20332040

20342041
# Port with IP in subnet1; subnet1 without DHCP.
@@ -2037,13 +2044,51 @@ def test_update_metadata_port_no_subnet(self):
20372044
mock_metaport.return_value = {'fixed_ips': fixed_ips,
20382045
'id': 'metadata_id'}
20392046
self.mech_driver._ovn_client.update_metadata_port(self.context,
2040-
'net_id')
2047+
network)
20412048
port = {'id': 'metadata_id',
20422049
'port': {'network_id': 'net_id', 'fixed_ips': []}}
20432050
mock_update_port.assert_called_once_with(
20442051
mock.ANY, 'metadata_id', port)
20452052
mock_update_port.reset_mock()
20462053

2054+
def test_update_metadata_port_no_port(self):
2055+
ovn_conf.cfg.CONF.set_override('ovn_metadata_enabled', True,
2056+
group='ovn')
2057+
2058+
with mock.patch.object(
2059+
self.mech_driver._ovn_client, '_find_metadata_port') as \
2060+
mock_find_metaport, \
2061+
mock.patch.object(self.mech_driver._plugin, 'get_subnets') as \
2062+
mock_get_subnets, \
2063+
mock.patch.object(p_utils, 'create_port') as \
2064+
mock_create_port:
2065+
# Subnet with DHCP, no port, port created.
2066+
network = {'id': 'net_id', 'project_id': 'project_id-foo'}
2067+
subnet = {'id': 'subnet1', 'enable_dhcp': True}
2068+
fixed_ips = [{'subnet_id': 'subnet1', 'ip_address': 'ip_add1'}]
2069+
port = {'id': 'metadata_id',
2070+
'network_id': 'net_id',
2071+
'device_owner': const.DEVICE_OWNER_DISTRIBUTED,
2072+
'device_id': 'ovnmeta-%s' % 'net_id',
2073+
'fixed_ips': fixed_ips}
2074+
mock_get_subnets.return_value = [subnet]
2075+
mock_find_metaport.return_value = None
2076+
2077+
# Subnet with DHCP, no port, port create failure.
2078+
mock_create_port.return_value = None
2079+
ret_status = self.mech_driver._ovn_client.update_metadata_port(
2080+
self.context, network, subnet=subnet)
2081+
self.assertFalse(ret_status)
2082+
mock_create_port.assert_called_once()
2083+
2084+
# Subnet with DHCP, no port, port created successfully.
2085+
mock_create_port.reset_mock()
2086+
mock_create_port.return_value = port
2087+
ret_status = self.mech_driver._ovn_client.update_metadata_port(
2088+
self.context, network, subnet=subnet)
2089+
self.assertTrue(ret_status)
2090+
mock_create_port.assert_called_once()
2091+
20472092
@mock.patch.object(provisioning_blocks, 'is_object_blocked')
20482093
@mock.patch.object(provisioning_blocks, 'provisioning_complete')
20492094
def test_notify_dhcp_updated(self, mock_prov_complete, mock_is_obj_block):
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
fixes:
3+
- |
4+
Fix an issue in the OVN driver where network metadata could
5+
become unavailable if the metadata port was ever deleted, even
6+
if accidental. To re-create the port, a user can now disable,
7+
then enable, DHCP for one of the subnets associated with the
8+
network using the Neutron API. This will try and create the
9+
port, similar to what happens in the DHCP agent for ML2/OVS.
10+
For more information, see bug `2015377
11+
<https://bugs.launchpad.net/ubuntu/+source/neutron/+bug/2015377>`_.

0 commit comments

Comments
 (0)