from neutron.agent.linux import ip_lib
from neutron.agent.linux import utils
+from neutron.common import constants
from neutron.common import exceptions
from neutron.openstack.common import importutils
from neutron.openstack.common import jsonutils
host_routes.append("%s,%s" % (hr.destination, hr.nexthop))
# Add host routes for isolated network segments
- enable_metadata = (
- self.conf.enable_isolated_metadata
- and not subnet.gateway_ip
- and subnet.ip_version == 4)
- if enable_metadata:
+ if self._enable_metadata(subnet):
subnet_dhcp_ip = subnet_to_interface_ip[subnet.id]
host_routes.append(
'%s/32,%s' % (METADATA_DEFAULT_IP, subnet_dhcp_ip)
return ','.join((set_tag + tag, '%s' % option) + args)
+ def _enable_metadata(self, subnet):
+ '''Determine if the metadata route will be pushed to hosts on subnet.
+
+ If subnet has a Neutron router attached, we want the hosts to get
+ metadata from the router's proxy via their default route instead.
+ '''
+ if self.conf.enable_isolated_metadata and subnet.ip_version == 4:
+ if subnet.gateway_ip is None:
+ return True
+ else:
+ for port in self.network.ports:
+ if port.device_owner == constants.DEVICE_OWNER_ROUTER_INTF:
+ for alloc in port.fixed_ips:
+ if alloc.subnet_id == subnet.id:
+ return False
+ return True
+ else:
+ return False
+
@classmethod
def lease_update(cls):
network_id = os.environ.get(cls.NEUTRON_NETWORK_ID_KEY)
class FakeIPAllocation:
- def __init__(self, address):
+ def __init__(self, address, subnet_id=None):
self.ip_address = address
+ self.subnet_id = subnet_id
class DhcpOpt(object):
class FakePort1:
id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
admin_state_up = True
+ device_owner = 'foo1'
fixed_ips = [FakeIPAllocation('192.168.0.2')]
mac_address = '00:00:80:aa:bb:cc'
class FakePort2:
id = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
admin_state_up = False
+ device_owner = 'foo2'
fixed_ips = [FakeIPAllocation('fdca:3ba5:a17a:4ba3::2')]
mac_address = '00:00:f3:aa:bb:cc'
class FakePort3:
id = '44444444-4444-4444-4444-444444444444'
admin_state_up = True
+ device_owner = 'foo3'
fixed_ips = [FakeIPAllocation('192.168.0.3'),
FakeIPAllocation('fdca:3ba5:a17a:4ba3::3')]
mac_address = '00:00:0f:aa:bb:cc'
self.extra_dhcp_opts = []
+class FakeRouterPort:
+ id = 'rrrrrrrr-rrrr-rrrr-rrrr-rrrrrrrrrrrr'
+ admin_state_up = True
+ device_owner = 'network:router_interface'
+ fixed_ips = [FakeIPAllocation('192.168.0.1',
+ 'dddddddd-dddd-dddd-dddd-dddddddddddd')]
+ mac_address = '00:00:0f:rr:rr:rr'
+
+ def __init__(self):
+ self.extra_dhcp_opts = []
+
+
class FakeV4HostRoute:
destination = '20.0.0.1/24'
nexthop = '20.0.0.1'
dns_nameservers = []
+class FakeV4SubnetNoRouter:
+ id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
+ ip_version = 4
+ cidr = '192.168.1.0/24'
+ gateway_ip = '192.168.1.1'
+ enable_dhcp = True
+ host_routes = []
+ dns_nameservers = []
+
+
class FakeV4Network:
id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
subnets = [FakeV4Subnet()]
class FakeDualNetwork:
id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
subnets = [FakeV4Subnet(), FakeV6Subnet()]
- ports = [FakePort1(), FakePort2(), FakePort3()]
+ ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()]
namespace = 'qdhcp-ns'
class FakeDualNetworkGatewayRoute:
id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
subnets = [FakeV4SubnetGatewayRoute(), FakeV6Subnet()]
- ports = [FakePort1(), FakePort2(), FakePort3()]
+ ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()]
namespace = 'qdhcp-ns'
class FakeDualNetworkSingleDHCP:
id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
subnets = [FakeV4Subnet(), FakeV4SubnetNoDHCP()]
- ports = [FakePort1(), FakePort2(), FakePort3()]
+ ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()]
namespace = 'qdhcp-ns'
ports = [FakePort1()]
+class FakeV4NetworkNoRouter:
+ id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
+ subnets = [FakeV4SubnetNoRouter()]
+ ports = [FakePort1()]
+
+
class FakeDualV4Pxe3Ports:
id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
subnets = [FakeV4Subnet(), FakeV4SubnetNoDHCP()]
- ports = [FakePort1(), FakePort2(), FakePort3()]
+ ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()]
namespace = 'qdhcp-ns'
def __init__(self, port_detail="portsSame"):
class FakeV4NetworkPxe2Ports:
id = 'dddddddd-dddd-dddd-dddd-dddddddddddd'
subnets = [FakeV4Subnet()]
- ports = [FakePort1(), FakePort2()]
+ ports = [FakePort1(), FakePort2(), FakeRouterPort()]
namespace = 'qdhcp-ns'
def __init__(self, port_detail="portsSame"):
class FakeV4NetworkPxe3Ports:
id = 'dddddddd-dddd-dddd-dddd-dddddddddddd'
subnets = [FakeV4Subnet()]
- ports = [FakePort1(), FakePort2(), FakePort3()]
+ ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()]
namespace = 'qdhcp-ns'
def __init__(self, port_detail="portsSame"):
self.safe.assert_called_once_with('/foo/opts', expected)
+ def test_output_opts_file_no_neutron_router_on_subnet(self):
+ expected = """
+tag:tag0,option:classless-static-route,169.254.169.254/32,192.168.1.2
+tag:tag0,249,169.254.169.254/32,192.168.1.2
+tag:tag0,option:router,192.168.1.1""".lstrip()
+
+ with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn:
+ conf_fn.return_value = '/foo/opts'
+ dm = dhcp.Dnsmasq(self.conf, FakeV4NetworkNoRouter(),
+ version=float(2.59))
+ with mock.patch.object(dm, '_make_subnet_interface_ip_map') as ipm:
+ ipm.return_value = {FakeV4SubnetNoRouter.id: '192.168.1.2'}
+
+ dm._output_opts_file()
+ self.assertTrue(ipm.called)
+
+ self.safe.assert_called_once_with('/foo/opts', expected)
+
def test_release_lease(self):
dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), version=float(2.59))
dm.release_lease(mac_address=FakePort2.mac_address,
'00:00:0f:aa:bb:cc,host-192-168-0-3.openstacklocal,'
'192.168.0.3\n'
'00:00:0f:aa:bb:cc,host-fdca-3ba5-a17a-4ba3--3.'
- 'openstacklocal,fdca:3ba5:a17a:4ba3::3\n').lstrip()
+ 'openstacklocal,fdca:3ba5:a17a:4ba3::3\n'
+ '00:00:0f:rr:rr:rr,host-192-168-0-1.openstacklocal,'
+ '192.168.0.1\n').lstrip()
exp_opt_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts'
exp_opt_data = "tag:tag0,option:router,192.168.0.1"
fake_v6 = 'gdca:3ba5:a17a:4ba3::1'
'00:00:0f:aa:bb:cc,host-192-168-0-3.openstacklocal,'
'192.168.0.3\n'
'00:00:0f:aa:bb:cc,host-fdca-3ba5-a17a-4ba3--3.'
- 'openstacklocal,fdca:3ba5:a17a:4ba3::3\n').lstrip()
+ 'openstacklocal,fdca:3ba5:a17a:4ba3::3\n'
+ '00:00:0f:rr:rr:rr,host-192-168-0-1.openstacklocal,'
+ '192.168.0.1\n').lstrip()
exp_host_data.replace('\n', '')
exp_opt_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts'
exp_opt_data = "tag:tag0,option:router,192.168.0.1"