Merge "[DHCP agent] Add route to OVN metadata port if exists" into stable/xena
This commit is contained in:
commit
d266c08b86
@ -1132,6 +1132,14 @@ class Dnsmasq(DhcpLocalProcess):
|
|||||||
file_utils.replace_file(name, '\n'.join(options))
|
file_utils.replace_file(name, '\n'.join(options))
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
def _get_ovn_metadata_port_ip(self, subnet):
|
||||||
|
m_ports = [port for port in self.network.ports if
|
||||||
|
self._is_ovn_metadata_port(port, self.network.id)]
|
||||||
|
if m_ports:
|
||||||
|
for fixed_ip in m_ports[0].fixed_ips:
|
||||||
|
if fixed_ip.subnet_id == subnet.id:
|
||||||
|
return fixed_ip.ip_address
|
||||||
|
|
||||||
def _generate_opts_per_subnet(self):
|
def _generate_opts_per_subnet(self):
|
||||||
options = []
|
options = []
|
||||||
subnets_without_nameservers = set()
|
subnets_without_nameservers = set()
|
||||||
@ -1185,23 +1193,33 @@ class Dnsmasq(DhcpLocalProcess):
|
|||||||
else:
|
else:
|
||||||
host_routes.append("%s,%s" % (hr.destination, hr.nexthop))
|
host_routes.append("%s,%s" % (hr.destination, hr.nexthop))
|
||||||
|
|
||||||
# Add host routes for isolated network segments
|
# Determine metadata port route
|
||||||
|
if subnet.ip_version == constants.IP_VERSION_4:
|
||||||
|
metadata_route_ip = None
|
||||||
|
# NOTE: OVN metadata port IP is used in a case when the DHCP
|
||||||
|
# agent is deployed in the ML2/OVN enviroment where the native
|
||||||
|
# ovn-controller dhcp is disabled. The ovn metadata route
|
||||||
|
# takes precedence over native force_metadata and
|
||||||
|
# enable_isolated_metadata routes settings.
|
||||||
|
ovn_metadata_port_ip = self._get_ovn_metadata_port_ip(subnet)
|
||||||
|
if ovn_metadata_port_ip:
|
||||||
|
metadata_route_ip = ovn_metadata_port_ip
|
||||||
|
|
||||||
if ((self.conf.force_metadata or
|
elif (self.conf.force_metadata or
|
||||||
(isolated_subnets[subnet.id] and
|
(isolated_subnets[subnet.id] and
|
||||||
self.conf.enable_isolated_metadata)) and
|
self.conf.enable_isolated_metadata)):
|
||||||
subnet.ip_version == 4):
|
|
||||||
subnet_dhcp_ip = subnet_to_interface_ip.get(subnet.id)
|
subnet_dhcp_ip = subnet_to_interface_ip.get(subnet.id)
|
||||||
if subnet_dhcp_ip:
|
if subnet_dhcp_ip:
|
||||||
|
metadata_route_ip = subnet_dhcp_ip
|
||||||
|
|
||||||
|
if not isolated_subnets[subnet.id] and gateway:
|
||||||
|
metadata_route_ip = gateway
|
||||||
|
|
||||||
|
if metadata_route_ip:
|
||||||
host_routes.append(
|
host_routes.append(
|
||||||
'%s,%s' % (constants.METADATA_CIDR, subnet_dhcp_ip)
|
'%s,%s' % (constants.METADATA_CIDR, metadata_route_ip)
|
||||||
)
|
|
||||||
elif not isolated_subnets[subnet.id] and gateway:
|
|
||||||
host_routes.append(
|
|
||||||
'%s,%s' % (constants.METADATA_CIDR, gateway)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if subnet.ip_version == 4:
|
|
||||||
for s in self._get_all_subnets(self.network):
|
for s in self._get_all_subnets(self.network):
|
||||||
sub_segment_id = getattr(s, 'segment_id', None)
|
sub_segment_id = getattr(s, 'segment_id', None)
|
||||||
if (s.ip_version == 4 and
|
if (s.ip_version == 4 and
|
||||||
@ -1366,13 +1384,21 @@ class Dnsmasq(DhcpLocalProcess):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _is_ovn_metadata_port(port, network_id):
|
||||||
|
return (port.device_id == 'ovnmeta-' + network_id and
|
||||||
|
port.device_owner == constants.DEVICE_OWNER_DISTRIBUTED)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def should_enable_metadata(cls, conf, network):
|
def should_enable_metadata(cls, conf, network):
|
||||||
"""Determine whether the metadata proxy is needed for a network
|
"""Determine whether the metadata proxy is needed for a network
|
||||||
|
|
||||||
This method returns True for truly isolated networks (ie: not attached
|
If the given network contains a ovn metadata port then this method
|
||||||
to a router) when enable_isolated_metadata is True, or for all the
|
assumes that the ovn metadata service is in use and this metadata
|
||||||
networks when the force_metadata flags is True.
|
service is not required, method returns False. For other cases this
|
||||||
|
method returns True for truly isolated networks (ie: not attached to a
|
||||||
|
router) when enable_isolated_metadata is True, or for all the networks
|
||||||
|
when the force_metadata flags is True.
|
||||||
|
|
||||||
This method also returns True when enable_metadata_network is True,
|
This method also returns True when enable_metadata_network is True,
|
||||||
and the network passed as a parameter has a subnet in the link-local
|
and the network passed as a parameter has a subnet in the link-local
|
||||||
@ -1381,6 +1407,10 @@ class Dnsmasq(DhcpLocalProcess):
|
|||||||
providing access to the metadata service via logical routers built
|
providing access to the metadata service via logical routers built
|
||||||
with 3rd party backends.
|
with 3rd party backends.
|
||||||
"""
|
"""
|
||||||
|
for port in network.ports:
|
||||||
|
if cls._is_ovn_metadata_port(port, network.id):
|
||||||
|
return False
|
||||||
|
|
||||||
all_subnets = cls._get_all_subnets(network)
|
all_subnets = cls._get_all_subnets(network)
|
||||||
dhcp_subnets = [s for s in all_subnets if s.enable_dhcp]
|
dhcp_subnets = [s for s in all_subnets if s.enable_dhcp]
|
||||||
if not dhcp_subnets:
|
if not dhcp_subnets:
|
||||||
|
@ -142,10 +142,27 @@ fake_port2 = dhcp.DictModel(id='12345678-1234-aaaa-123456789000',
|
|||||||
|
|
||||||
fake_ipv6_port = dhcp.DictModel(id='12345678-1234-aaaa-123456789000',
|
fake_ipv6_port = dhcp.DictModel(id='12345678-1234-aaaa-123456789000',
|
||||||
device_owner='',
|
device_owner='',
|
||||||
|
device_id='',
|
||||||
mac_address='aa:bb:cc:dd:ee:99',
|
mac_address='aa:bb:cc:dd:ee:99',
|
||||||
network_id=FAKE_NETWORK_UUID,
|
network_id=FAKE_NETWORK_UUID,
|
||||||
fixed_ips=[fake_fixed_ipv6])
|
fixed_ips=[fake_fixed_ipv6])
|
||||||
|
|
||||||
|
fake_ovn_port = dhcp.DictModel(id='12345678-1234-aaaa-123456789000',
|
||||||
|
device_owner='',
|
||||||
|
device_id='',
|
||||||
|
mac_address='aa:bb:cc:dd:ee:98',
|
||||||
|
network_id=FAKE_NETWORK_UUID,
|
||||||
|
fixed_ips=[fake_fixed_ip2])
|
||||||
|
|
||||||
|
fake_ovn_metadata_port = dhcp.DictModel(id='12345678-1234-aaaa-123456789000',
|
||||||
|
device_owner=const.
|
||||||
|
DEVICE_OWNER_DISTRIBUTED,
|
||||||
|
device_id='ovnmeta-{}'.format(
|
||||||
|
FAKE_NETWORK_UUID),
|
||||||
|
mac_address='aa:bb:cc:dd:ee:99',
|
||||||
|
network_id=FAKE_NETWORK_UUID,
|
||||||
|
fixed_ips=[fake_fixed_ip1])
|
||||||
|
|
||||||
fake_meta_port = dhcp.DictModel(id='12345678-1234-aaaa-1234567890ab',
|
fake_meta_port = dhcp.DictModel(id='12345678-1234-aaaa-1234567890ab',
|
||||||
mac_address='aa:bb:cc:dd:ee:ff',
|
mac_address='aa:bb:cc:dd:ee:ff',
|
||||||
network_id=FAKE_NETWORK_UUID,
|
network_id=FAKE_NETWORK_UUID,
|
||||||
@ -175,6 +192,12 @@ fake_network_ipv6 = dhcp.NetModel(id=FAKE_NETWORK_UUID,
|
|||||||
subnets=[fake_ipv6_subnet],
|
subnets=[fake_ipv6_subnet],
|
||||||
ports=[fake_ipv6_port])
|
ports=[fake_ipv6_port])
|
||||||
|
|
||||||
|
fake_ovn_network = dhcp.NetModel(id=FAKE_NETWORK_UUID,
|
||||||
|
project_id=FAKE_TENANT_ID,
|
||||||
|
admin_state_up=True,
|
||||||
|
subnets=[fake_ipv6_subnet],
|
||||||
|
ports=[fake_ovn_metadata_port, fake_ovn_port])
|
||||||
|
|
||||||
fake_network_ipv6_ipv4 = dhcp.NetModel(
|
fake_network_ipv6_ipv4 = dhcp.NetModel(
|
||||||
id=FAKE_NETWORK_UUID,
|
id=FAKE_NETWORK_UUID,
|
||||||
tenant_id=FAKE_TENANT_ID,
|
tenant_id=FAKE_TENANT_ID,
|
||||||
@ -808,7 +831,7 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
default_cmd_callback=mock.ANY)
|
default_cmd_callback=mock.ANY)
|
||||||
|
|
||||||
def _enable_dhcp_helper(self, network, enable_isolated_metadata=False,
|
def _enable_dhcp_helper(self, network, enable_isolated_metadata=False,
|
||||||
is_isolated_network=False):
|
is_isolated_network=False, is_ovn_network=False):
|
||||||
self.dhcp._process_monitor = mock.Mock()
|
self.dhcp._process_monitor = mock.Mock()
|
||||||
if enable_isolated_metadata:
|
if enable_isolated_metadata:
|
||||||
cfg.CONF.set_override('enable_isolated_metadata', True)
|
cfg.CONF.set_override('enable_isolated_metadata', True)
|
||||||
@ -818,7 +841,8 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
mock.call.get_network_info(network.id)])
|
mock.call.get_network_info(network.id)])
|
||||||
self.call_driver.assert_called_once_with('enable', network)
|
self.call_driver.assert_called_once_with('enable', network)
|
||||||
self.cache.assert_has_calls([mock.call.put(network)])
|
self.cache.assert_has_calls([mock.call.put(network)])
|
||||||
if is_isolated_network and enable_isolated_metadata:
|
if (is_isolated_network and enable_isolated_metadata and not
|
||||||
|
is_ovn_network):
|
||||||
self.external_process.assert_has_calls([
|
self.external_process.assert_has_calls([
|
||||||
self._process_manager_constructor_call(),
|
self._process_manager_constructor_call(),
|
||||||
mock.call().enable()], any_order=True)
|
mock.call().enable()], any_order=True)
|
||||||
@ -863,6 +887,21 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
enable_isolated_metadata=True,
|
enable_isolated_metadata=True,
|
||||||
is_isolated_network=False)
|
is_isolated_network=False)
|
||||||
|
|
||||||
|
def test_enable_dhcp_helper_enable_metadata_ovn_network(self):
|
||||||
|
# Metadata should not be enabled when the dhcp agent is used
|
||||||
|
# in ML2/OVN where the ovn metadata agent is responsible for the
|
||||||
|
# metadata service.
|
||||||
|
self._enable_dhcp_helper(fake_ovn_network, is_ovn_network=True)
|
||||||
|
|
||||||
|
def test_enable_dhcp_helper_ovn_network_with_enable_isolated_metadata(
|
||||||
|
self):
|
||||||
|
# Metadata should not be enabled when the dhcp agent is used
|
||||||
|
# in ML2/OVN where the ovn metadata agent is responsible for the
|
||||||
|
# metadata service. Even if the enable_isolated_metadata is enabled
|
||||||
|
self._enable_dhcp_helper(fake_ovn_network,
|
||||||
|
enable_isolated_metadata=True,
|
||||||
|
is_ovn_network=True)
|
||||||
|
|
||||||
def test_enable_dhcp_helper_enable_metadata_empty_network(self):
|
def test_enable_dhcp_helper_enable_metadata_empty_network(self):
|
||||||
self._enable_dhcp_helper(empty_network,
|
self._enable_dhcp_helper(empty_network,
|
||||||
enable_isolated_metadata=True,
|
enable_isolated_metadata=True,
|
||||||
|
@ -87,6 +87,19 @@ class FakeDhcpPort(object):
|
|||||||
self.extra_dhcp_opts = []
|
self.extra_dhcp_opts = []
|
||||||
|
|
||||||
|
|
||||||
|
class FakeOvnMetadataPort(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa'
|
||||||
|
self.admin_state_up = True
|
||||||
|
self.device_owner = constants.DEVICE_OWNER_DISTRIBUTED
|
||||||
|
self.fixed_ips = [
|
||||||
|
FakeIPAllocation('192.168.0.10',
|
||||||
|
'dddddddd-dddd-dddd-dddd-dddddddddddd')]
|
||||||
|
self.mac_address = '00:00:80:aa:bb:ee'
|
||||||
|
self.device_id = 'ovnmeta-aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
|
||||||
|
self.extra_dhcp_opts = []
|
||||||
|
|
||||||
|
|
||||||
class FakeReservedPort(object):
|
class FakeReservedPort(object):
|
||||||
def __init__(self, id='reserved-aaaa-aaaa-aaaa-aaaaaaaaaaa'):
|
def __init__(self, id='reserved-aaaa-aaaa-aaaa-aaaaaaaaaaa'):
|
||||||
self.admin_state_up = True
|
self.admin_state_up = True
|
||||||
@ -754,6 +767,14 @@ class FakeNetworkDhcpPort(object):
|
|||||||
self.namespace = 'qdhcp-ns'
|
self.namespace = 'qdhcp-ns'
|
||||||
|
|
||||||
|
|
||||||
|
class FakeNetworkDhcpandOvnMetadataPort(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
|
||||||
|
self.subnets = [FakeV4Subnet()]
|
||||||
|
self.ports = [FakePort1(), FakeDhcpPort(), FakeOvnMetadataPort()]
|
||||||
|
self.namespace = 'qdhcp-ns'
|
||||||
|
|
||||||
|
|
||||||
class FakeDualNetworkGatewayRoute(object):
|
class FakeDualNetworkGatewayRoute(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
|
||||||
@ -3049,6 +3070,10 @@ class TestDnsmasq(TestBase):
|
|||||||
self.assertFalse(dhcp.Dnsmasq.has_metadata_subnet(
|
self.assertFalse(dhcp.Dnsmasq.has_metadata_subnet(
|
||||||
[FakeV4Subnet()]))
|
[FakeV4Subnet()]))
|
||||||
|
|
||||||
|
def test_should_enable_metadata_ovn_metadata_port_returns_false(self):
|
||||||
|
self.assertFalse(dhcp.Dnsmasq.should_enable_metadata(
|
||||||
|
self.conf, FakeNetworkDhcpandOvnMetadataPort()))
|
||||||
|
|
||||||
def test_should_enable_metadata_isolated_network_returns_true(self):
|
def test_should_enable_metadata_isolated_network_returns_true(self):
|
||||||
self.assertTrue(dhcp.Dnsmasq.should_enable_metadata(
|
self.assertTrue(dhcp.Dnsmasq.should_enable_metadata(
|
||||||
self.conf, FakeV4NetworkNoRouter()))
|
self.conf, FakeV4NetworkNoRouter()))
|
||||||
@ -3097,6 +3122,12 @@ class TestDnsmasq(TestBase):
|
|||||||
'force_metadata': False}
|
'force_metadata': False}
|
||||||
self._test__generate_opts_per_subnet_helper(config, False)
|
self._test__generate_opts_per_subnet_helper(config, False)
|
||||||
|
|
||||||
|
def test__generate_opts_per_subnet_with_metadata_port(self):
|
||||||
|
config = {'enable_isolated_metadata': False,
|
||||||
|
'force_metadata': False}
|
||||||
|
self._test__generate_opts_per_subnet_helper(config, True,
|
||||||
|
network_class=FakeNetworkDhcpandOvnMetadataPort)
|
||||||
|
|
||||||
def test__generate_opts_per_subnet_isolated_metadata_with_router(self):
|
def test__generate_opts_per_subnet_isolated_metadata_with_router(self):
|
||||||
config = {'enable_isolated_metadata': True,
|
config = {'enable_isolated_metadata': True,
|
||||||
'force_metadata': False}
|
'force_metadata': False}
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
Fixed the scenario where the DHCP agent is deployed in conjunction with
|
||||||
|
the OVN metadata agent in order to serve metadata for baremetal nodes.
|
||||||
|
In this scenario, the DHCP agent would not set the route needed for the
|
||||||
|
OVN metadata agent service resulting in baremetal nodes not being able
|
||||||
|
to query the metadata service. For more information see
|
||||||
|
`bug 1982569 <https://bugs.launchpad.net/neutron/+bug/1982569>`_.
|
Loading…
Reference in New Issue
Block a user