Skip to content

Commit d869aff

Browse files
karelyatinmtomaska
authored andcommitted
[DHCP agent] Add route to OVN metadata port if exists
When DHCP agent is deployed with ml2/ovn for baremetal ports, ovn metadata route is not added. This patch adds route via ovn metadata port if exists so baremetal nodes can fetch metadata. Conflicts: neutron/agent/linux/dhcp.py The conflict was just simple indent and formatting which needed manual resolution Closes-Bug: #1982569 Related-Bug: https://bugzilla.redhat.com/show_bug.cgi?id=2213862 Change-Id: I12e496d70bb6db707b317d0aeb6e4edd6c43571e (cherry picked from commit 82f2a21)
1 parent 85b2500 commit d869aff

File tree

4 files changed

+128
-19
lines changed

4 files changed

+128
-19
lines changed

neutron/agent/linux/dhcp.py

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,14 @@ def _output_opts_file(self):
11401140
file_utils.replace_file(name, '\n'.join(options))
11411141
return name
11421142

1143+
def _get_ovn_metadata_port_ip(self, subnet):
1144+
m_ports = [port for port in self.network.ports if
1145+
self._is_ovn_metadata_port(port, self.network.id)]
1146+
if m_ports:
1147+
for fixed_ip in m_ports[0].fixed_ips:
1148+
if fixed_ip.subnet_id == subnet.id:
1149+
return fixed_ip.ip_address
1150+
11431151
def _generate_opts_per_subnet(self):
11441152
options = []
11451153
subnets_without_nameservers = set()
@@ -1193,23 +1201,33 @@ def _generate_opts_per_subnet(self):
11931201
else:
11941202
host_routes.append("%s,%s" % (hr.destination, hr.nexthop))
11951203

1196-
# Add host routes for isolated network segments
1197-
1198-
if ((self.conf.force_metadata or
1199-
(isolated_subnets[subnet.id] and
1200-
self.conf.enable_isolated_metadata)) and
1201-
subnet.ip_version == 4):
1202-
subnet_dhcp_ip = subnet_to_interface_ip.get(subnet.id)
1203-
if subnet_dhcp_ip:
1204+
# Determine metadata port route
1205+
if subnet.ip_version == constants.IP_VERSION_4:
1206+
metadata_route_ip = None
1207+
# NOTE: OVN metadata port IP is used in a case when the DHCP
1208+
# agent is deployed in the ML2/OVN enviroment where the native
1209+
# ovn-controller dhcp is disabled. The ovn metadata route
1210+
# takes precedence over native force_metadata and
1211+
# enable_isolated_metadata routes settings.
1212+
ovn_metadata_port_ip = self._get_ovn_metadata_port_ip(subnet)
1213+
if ovn_metadata_port_ip:
1214+
metadata_route_ip = ovn_metadata_port_ip
1215+
1216+
elif (self.conf.force_metadata or
1217+
(isolated_subnets[subnet.id] and
1218+
self.conf.enable_isolated_metadata)):
1219+
subnet_dhcp_ip = subnet_to_interface_ip.get(subnet.id)
1220+
if subnet_dhcp_ip:
1221+
metadata_route_ip = subnet_dhcp_ip
1222+
1223+
if not isolated_subnets[subnet.id] and gateway:
1224+
metadata_route_ip = gateway
1225+
1226+
if metadata_route_ip:
12041227
host_routes.append(
1205-
'%s,%s' % (constants.METADATA_CIDR, subnet_dhcp_ip)
1228+
'%s,%s' % (constants.METADATA_CIDR, metadata_route_ip)
12061229
)
1207-
elif not isolated_subnets[subnet.id] and gateway:
1208-
host_routes.append(
1209-
'%s,%s' % (constants.METADATA_CIDR, gateway)
1210-
)
12111230

1212-
if subnet.ip_version == 4:
12131231
for s in self._get_all_subnets(self.network):
12141232
sub_segment_id = getattr(s, 'segment_id', None)
12151233
if (s.ip_version == 4 and
@@ -1374,13 +1392,21 @@ def has_metadata_subnet(subnets):
13741392
return True
13751393
return False
13761394

1395+
@staticmethod
1396+
def _is_ovn_metadata_port(port, network_id):
1397+
return (port.device_id == 'ovnmeta-' + network_id and
1398+
port.device_owner == constants.DEVICE_OWNER_DISTRIBUTED)
1399+
13771400
@classmethod
13781401
def should_enable_metadata(cls, conf, network):
13791402
"""Determine whether the metadata proxy is needed for a network
13801403
1381-
This method returns True for truly isolated networks (ie: not attached
1382-
to a router) when enable_isolated_metadata is True, or for all the
1383-
networks when the force_metadata flags is True.
1404+
If the given network contains a ovn metadata port then this method
1405+
assumes that the ovn metadata service is in use and this metadata
1406+
service is not required, method returns False. For other cases this
1407+
method returns True for truly isolated networks (ie: not attached to a
1408+
router) when enable_isolated_metadata is True, or for all the networks
1409+
when the force_metadata flags is True.
13841410
13851411
This method also returns True when enable_metadata_network is True,
13861412
and the network passed as a parameter has a subnet in the link-local
@@ -1389,6 +1415,10 @@ def should_enable_metadata(cls, conf, network):
13891415
providing access to the metadata service via logical routers built
13901416
with 3rd party backends.
13911417
"""
1418+
for port in network.ports:
1419+
if cls._is_ovn_metadata_port(port, network.id):
1420+
return False
1421+
13921422
all_subnets = cls._get_all_subnets(network)
13931423
dhcp_subnets = [s for s in all_subnets if s.enable_dhcp]
13941424
if not dhcp_subnets:

neutron/tests/unit/agent/dhcp/test_agent.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,27 @@
152152

153153
fake_ipv6_port = dhcp.DictModel(id='12345678-1234-aaaa-123456789000',
154154
device_owner='',
155+
device_id='',
155156
mac_address='aa:bb:cc:dd:ee:99',
156157
network_id=FAKE_NETWORK_UUID,
157158
fixed_ips=[fake_fixed_ipv6])
158159

160+
fake_ovn_port = dhcp.DictModel(id='12345678-1234-aaaa-123456789000',
161+
device_owner='',
162+
device_id='',
163+
mac_address='aa:bb:cc:dd:ee:98',
164+
network_id=FAKE_NETWORK_UUID,
165+
fixed_ips=[fake_fixed_ip2])
166+
167+
fake_ovn_metadata_port = dhcp.DictModel(id='12345678-1234-aaaa-123456789000',
168+
device_owner=const.
169+
DEVICE_OWNER_DISTRIBUTED,
170+
device_id='ovnmeta-{}'.format(
171+
FAKE_NETWORK_UUID),
172+
mac_address='aa:bb:cc:dd:ee:99',
173+
network_id=FAKE_NETWORK_UUID,
174+
fixed_ips=[fake_fixed_ip1])
175+
159176
fake_meta_port = dhcp.DictModel(id='12345678-1234-aaaa-1234567890ab',
160177
mac_address='aa:bb:cc:dd:ee:ff',
161178
network_id=FAKE_NETWORK_UUID,
@@ -191,6 +208,12 @@
191208
subnets=[fake_ipv6_subnet],
192209
ports=[fake_ipv6_port])
193210

211+
fake_ovn_network = dhcp.NetModel(id=FAKE_NETWORK_UUID,
212+
project_id=FAKE_PROJECT_ID,
213+
admin_state_up=True,
214+
subnets=[fake_ipv6_subnet],
215+
ports=[fake_ovn_metadata_port, fake_ovn_port])
216+
194217
fake_network_ipv6_ipv4 = dhcp.NetModel(
195218
id=FAKE_NETWORK_UUID,
196219
project_id=FAKE_PROJECT_ID,
@@ -782,7 +805,7 @@ def _process_manager_constructor_call(self, ns=FAKE_NETWORK_DHCP_NS):
782805
default_cmd_callback=mock.ANY)
783806

784807
def _enable_dhcp_helper(self, network, enable_isolated_metadata=False,
785-
is_isolated_network=False):
808+
is_isolated_network=False, is_ovn_network=False):
786809
self.dhcp._process_monitor = mock.Mock()
787810
if enable_isolated_metadata:
788811
cfg.CONF.set_override('enable_isolated_metadata', True)
@@ -792,7 +815,8 @@ def _enable_dhcp_helper(self, network, enable_isolated_metadata=False,
792815
mock.call.get_network_info(network.id)])
793816
self.call_driver.assert_called_once_with('enable', network)
794817
self.cache.assert_has_calls([mock.call.put(network)])
795-
if is_isolated_network and enable_isolated_metadata:
818+
if (is_isolated_network and enable_isolated_metadata and not
819+
is_ovn_network):
796820
self.external_process.assert_has_calls([
797821
self._process_manager_constructor_call(),
798822
mock.call().enable()], any_order=True)
@@ -837,6 +861,21 @@ def test_enable_dhcp_helper_enable_metadata_nonisolated_dist_network(self):
837861
enable_isolated_metadata=True,
838862
is_isolated_network=False)
839863

864+
def test_enable_dhcp_helper_enable_metadata_ovn_network(self):
865+
# Metadata should not be enabled when the dhcp agent is used
866+
# in ML2/OVN where the ovn metadata agent is responsible for the
867+
# metadata service.
868+
self._enable_dhcp_helper(fake_ovn_network, is_ovn_network=True)
869+
870+
def test_enable_dhcp_helper_ovn_network_with_enable_isolated_metadata(
871+
self):
872+
# Metadata should not be enabled when the dhcp agent is used
873+
# in ML2/OVN where the ovn metadata agent is responsible for the
874+
# metadata service. Even if the enable_isolated_metadata is enabled
875+
self._enable_dhcp_helper(fake_ovn_network,
876+
enable_isolated_metadata=True,
877+
is_ovn_network=True)
878+
840879
def test_enable_dhcp_helper_enable_metadata_empty_network(self):
841880
self._enable_dhcp_helper(empty_network,
842881
enable_isolated_metadata=True,

neutron/tests/unit/agent/linux/test_dhcp.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,19 @@ def __init__(self):
8888
self.extra_dhcp_opts = []
8989

9090

91+
class FakeOvnMetadataPort(object):
92+
def __init__(self):
93+
self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa'
94+
self.admin_state_up = True
95+
self.device_owner = constants.DEVICE_OWNER_DISTRIBUTED
96+
self.fixed_ips = [
97+
FakeIPAllocation('192.168.0.10',
98+
'dddddddd-dddd-dddd-dddd-dddddddddddd')]
99+
self.mac_address = '00:00:80:aa:bb:ee'
100+
self.device_id = 'ovnmeta-aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
101+
self.extra_dhcp_opts = []
102+
103+
91104
class FakeReservedPort(object):
92105
def __init__(self, id='reserved-aaaa-aaaa-aaaa-aaaaaaaaaaa'):
93106
self.admin_state_up = True
@@ -755,6 +768,14 @@ def __init__(self):
755768
self.namespace = 'qdhcp-ns'
756769

757770

771+
class FakeNetworkDhcpandOvnMetadataPort(object):
772+
def __init__(self):
773+
self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
774+
self.subnets = [FakeV4Subnet()]
775+
self.ports = [FakePort1(), FakeDhcpPort(), FakeOvnMetadataPort()]
776+
self.namespace = 'qdhcp-ns'
777+
778+
758779
class FakeDualNetworkGatewayRoute(object):
759780
def __init__(self):
760781
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
@@ -3050,6 +3071,10 @@ def test_has_metadata_subnet_returns_false(self):
30503071
self.assertFalse(dhcp.Dnsmasq.has_metadata_subnet(
30513072
[FakeV4Subnet()]))
30523073

3074+
def test_should_enable_metadata_ovn_metadata_port_returns_false(self):
3075+
self.assertFalse(dhcp.Dnsmasq.should_enable_metadata(
3076+
self.conf, FakeNetworkDhcpandOvnMetadataPort()))
3077+
30533078
def test_should_enable_metadata_isolated_network_returns_true(self):
30543079
self.assertTrue(dhcp.Dnsmasq.should_enable_metadata(
30553080
self.conf, FakeV4NetworkNoRouter()))
@@ -3098,6 +3123,12 @@ def test__generate_opts_per_subnet_no_metadata(self):
30983123
'force_metadata': False}
30993124
self._test__generate_opts_per_subnet_helper(config, False)
31003125

3126+
def test__generate_opts_per_subnet_with_metadata_port(self):
3127+
config = {'enable_isolated_metadata': False,
3128+
'force_metadata': False}
3129+
self._test__generate_opts_per_subnet_helper(config, True,
3130+
network_class=FakeNetworkDhcpandOvnMetadataPort)
3131+
31013132
def test__generate_opts_per_subnet_isolated_metadata_with_router(self):
31023133
config = {'enable_isolated_metadata': True,
31033134
'force_metadata': False}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
fixes:
3+
- |
4+
Fixed the scenario where the DHCP agent is deployed in conjunction with
5+
the OVN metadata agent in order to serve metadata for baremetal nodes.
6+
In this scenario, the DHCP agent would not set the route needed for the
7+
OVN metadata agent service resulting in baremetal nodes not being able
8+
to query the metadata service. For more information see
9+
`bug 1982569 <https://bugs.launchpad.net/neutron/+bug/1982569>`_.

0 commit comments

Comments
 (0)