You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3380 lines
145 KiB
3380 lines
145 KiB
# Copyright 2012 OpenStack Foundation |
|
# All Rights Reserved. |
|
# |
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may |
|
# not use this file except in compliance with the License. You may obtain |
|
# a copy of the License at |
|
# |
|
# http://www.apache.org/licenses/LICENSE-2.0 |
|
# |
|
# Unless required by applicable law or agreed to in writing, software |
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|
# License for the specific language governing permissions and limitations |
|
# under the License. |
|
|
|
import copy |
|
import os |
|
from unittest import mock |
|
|
|
import netaddr |
|
from neutron_lib.api.definitions import extra_dhcp_opt as edo_ext |
|
from neutron_lib import constants |
|
from neutron_lib import exceptions |
|
from neutron_lib import fixture as lib_fixtures |
|
from oslo_config import cfg |
|
import oslo_messaging |
|
from oslo_utils import fileutils |
|
from oslo_utils import uuidutils |
|
import testtools |
|
|
|
from neutron.agent.linux import dhcp |
|
from neutron.agent.linux import ip_lib |
|
from neutron.cmd import runtime_checks as checks |
|
from neutron.conf.agent import common as config |
|
from neutron.conf.agent import dhcp as dhcp_config |
|
from neutron.conf import common as base_config |
|
from neutron.privileged.agent.linux import dhcp as priv_dhcp |
|
from neutron.tests import base |
|
|
|
|
|
class FakeIPAllocation(object): |
|
def __init__(self, address, subnet_id=None): |
|
self.ip_address = address |
|
self.subnet_id = subnet_id |
|
|
|
|
|
class FakeDNSAssignment(object): |
|
def __init__(self, ip_address, dns_name='', domain='openstacklocal'): |
|
if dns_name: |
|
self.hostname = dns_name |
|
else: |
|
self.hostname = 'host-%s' % ip_address.replace( |
|
'.', '-').replace(':', '-') |
|
self.ip_address = ip_address |
|
self.fqdn = self.hostname |
|
if domain: |
|
self.fqdn = '%s.%s.' % (self.hostname, domain) |
|
|
|
|
|
class DhcpOpt(object): |
|
def __init__(self, **kwargs): |
|
self.__dict__.update(ip_version=constants.IP_VERSION_4) |
|
self.__dict__.update(kwargs) |
|
|
|
def __str__(self): |
|
return str(self.__dict__) |
|
|
|
|
|
# A base class where class attributes can also be accessed by treating |
|
# an instance as a dict. |
|
class Dictable(object): |
|
def __getitem__(self, k): |
|
return self.__class__.__dict__.get(k) |
|
|
|
|
|
class FakeDhcpPort(object): |
|
def __init__(self): |
|
self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa' |
|
self.admin_state_up = True |
|
self.device_owner = constants.DEVICE_OWNER_DHCP |
|
self.fixed_ips = [ |
|
FakeIPAllocation('192.168.0.1', |
|
'dddddddd-dddd-dddd-dddd-dddddddddddd')] |
|
self.mac_address = '00:00:80:aa:bb:ee' |
|
self.device_id = 'fake_dhcp_port' |
|
self.extra_dhcp_opts = [] |
|
|
|
|
|
class FakeReservedPort(object): |
|
def __init__(self, id='reserved-aaaa-aaaa-aaaa-aaaaaaaaaaa'): |
|
self.admin_state_up = True |
|
self.device_owner = constants.DEVICE_OWNER_DHCP |
|
self.fixed_ips = [ |
|
FakeIPAllocation('192.168.0.6', |
|
'dddddddd-dddd-dddd-dddd-dddddddddddd'), |
|
FakeIPAllocation('fdca:3ba5:a17a:4ba3::2', |
|
'ffffffff-ffff-ffff-ffff-ffffffffffff')] |
|
self.mac_address = '00:00:80:aa:bb:ee' |
|
self.device_id = constants.DEVICE_ID_RESERVED_DHCP_PORT |
|
self.extra_dhcp_opts = [] |
|
self.id = id |
|
|
|
|
|
class FakePort1(object): |
|
def __init__(self, domain='openstacklocal'): |
|
self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' |
|
self.admin_state_up = True |
|
self.device_owner = 'foo1' |
|
self.fixed_ips = [ |
|
FakeIPAllocation('192.168.0.2', |
|
'dddddddd-dddd-dddd-dddd-dddddddddddd')] |
|
self.mac_address = '00:00:80:aa:bb:cc' |
|
self.device_id = 'fake_port1' |
|
self.extra_dhcp_opts = [] |
|
self.dns_assignment = [FakeDNSAssignment('192.168.0.2', domain=domain)] |
|
|
|
|
|
class FakePort2(object): |
|
def __init__(self): |
|
self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' |
|
self.admin_state_up = False |
|
self.device_owner = 'foo2' |
|
self.fixed_ips = [ |
|
FakeIPAllocation('192.168.0.3', |
|
'dddddddd-dddd-dddd-dddd-dddddddddddd')] |
|
self.mac_address = '00:00:f3:aa:bb:cc' |
|
self.device_id = 'fake_port2' |
|
self.dns_assignment = [FakeDNSAssignment('192.168.0.3')] |
|
self.extra_dhcp_opts = [] |
|
|
|
|
|
class FakePort3(object): |
|
def __init__(self): |
|
self.id = '44444444-4444-4444-4444-444444444444' |
|
self.admin_state_up = True |
|
self.device_owner = 'foo3' |
|
self.fixed_ips = [ |
|
FakeIPAllocation('192.168.0.4', |
|
'dddddddd-dddd-dddd-dddd-dddddddddddd'), |
|
FakeIPAllocation('192.168.1.2', |
|
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')] |
|
self.dns_assignment = [FakeDNSAssignment('192.168.0.4'), |
|
FakeDNSAssignment('192.168.1.2')] |
|
self.mac_address = '00:00:0f:aa:bb:cc' |
|
self.device_id = 'fake_port3' |
|
self.extra_dhcp_opts = [] |
|
|
|
|
|
class FakePort4(object): |
|
def __init__(self): |
|
self.id = 'gggggggg-gggg-gggg-gggg-gggggggggggg' |
|
self.admin_state_up = False |
|
self.device_owner = 'foo3' |
|
self.fixed_ips = [ |
|
FakeIPAllocation('192.168.0.4', |
|
'dddddddd-dddd-dddd-dddd-dddddddddddd'), |
|
FakeIPAllocation('ffda:3ba5:a17a:4ba3:0216:3eff:fec2:771d', |
|
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')] |
|
self.dns_assignment = [ |
|
FakeDNSAssignment('192.168.0.4'), |
|
FakeDNSAssignment('ffda:3ba5:a17a:4ba3:0216:3eff:fec2:771d')] |
|
self.mac_address = '00:16:3E:C2:77:1D' |
|
self.device_id = 'fake_port4' |
|
self.extra_dhcp_opts = [] |
|
|
|
|
|
class FakePort5(object): |
|
def __init__(self): |
|
self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeee' |
|
self.admin_state_up = True |
|
self.device_owner = 'foo5' |
|
self.fixed_ips = [ |
|
FakeIPAllocation('192.168.0.5', |
|
'dddddddd-dddd-dddd-dddd-dddddddddddd')] |
|
self.dns_assignment = [FakeDNSAssignment('192.168.0.5')] |
|
self.mac_address = '00:00:0f:aa:bb:55' |
|
self.device_id = 'fake_port5' |
|
self.extra_dhcp_opts = [ |
|
DhcpOpt(opt_name=edo_ext.DHCP_OPT_CLIENT_ID, |
|
opt_value='test5')] |
|
|
|
|
|
class FakePort6(object): |
|
def __init__(self): |
|
self.id = 'ccccccccc-cccc-cccc-cccc-ccccccccc' |
|
self.admin_state_up = True |
|
self.device_owner = 'foo6' |
|
self.fixed_ips = [ |
|
FakeIPAllocation('192.168.0.6', |
|
'dddddddd-dddd-dddd-dddd-dddddddddddd')] |
|
self.dns_assignment = [FakeDNSAssignment('192.168.0.6')] |
|
self.mac_address = '00:00:0f:aa:bb:66' |
|
self.device_id = 'fake_port6' |
|
self.extra_dhcp_opts = [ |
|
DhcpOpt(opt_name=edo_ext.DHCP_OPT_CLIENT_ID, |
|
opt_value='test6', |
|
ip_version=constants.IP_VERSION_4), |
|
DhcpOpt(opt_name='dns-server', |
|
opt_value='123.123.123.45', |
|
ip_version=constants.IP_VERSION_4)] |
|
|
|
|
|
class FakeV6Port(object): |
|
def __init__(self, domain='openstacklocal'): |
|
self.id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh' |
|
self.admin_state_up = True |
|
self.device_owner = 'foo3' |
|
self.fixed_ips = [ |
|
FakeIPAllocation('fdca:3ba5:a17a:4ba3::2', |
|
'ffffffff-ffff-ffff-ffff-ffffffffffff')] |
|
self.mac_address = '00:00:f3:aa:bb:cc' |
|
self.device_id = 'fake_port6' |
|
self.extra_dhcp_opts = [] |
|
self.dns_assignment = [FakeDNSAssignment('fdca:3ba5:a17a:4ba3::2', |
|
domain=domain)] |
|
|
|
|
|
class FakeV6PortExtraOpt(object): |
|
def __init__(self): |
|
self.id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh' |
|
self.admin_state_up = True |
|
self.device_owner = 'foo3' |
|
self.fixed_ips = [ |
|
FakeIPAllocation('ffea:3ba5:a17a:4ba3:0216:3eff:fec2:771d', |
|
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')] |
|
self.dns_assignment = [ |
|
FakeDNSAssignment('ffea:3ba5:a17a:4ba3:0216:3eff:fec2:771d')] |
|
self.mac_address = '00:16:3e:c2:77:1d' |
|
self.device_id = 'fake_port6' |
|
self.extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='dns-server', |
|
opt_value='ffea:3ba5:a17a:4ba3::100', |
|
ip_version=constants.IP_VERSION_6)] |
|
|
|
|
|
class FakeV6PortMultipleFixedIpsSameSubnet(object): |
|
def __init__(self, domain='openstacklocal'): |
|
self.id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh' |
|
self.admin_state_up = True |
|
self.device_owner = 'foo3' |
|
self.fixed_ips = [ |
|
FakeIPAllocation('fdca:3ba5:a17a:4ba3::2', |
|
'ffffffff-ffff-ffff-ffff-ffffffffffff'), |
|
FakeIPAllocation('fdca:3ba5:a17a:4ba3::4', |
|
'ffffffff-ffff-ffff-ffff-ffffffffffff')] |
|
self.mac_address = '00:00:f3:aa:bb:cc' |
|
self.device_id = 'fake_port6' |
|
self.extra_dhcp_opts = [] |
|
self.dns_assignment = [FakeDNSAssignment('fdca:3ba5:a17a:4ba3::2', |
|
domain=domain), |
|
FakeDNSAssignment('fdca:3ba5:a17a:4ba3::4', |
|
domain=domain)] |
|
|
|
|
|
class FakeDualPortWithV6ExtraOpt(object): |
|
def __init__(self): |
|
self.id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh' |
|
self.admin_state_up = True |
|
self.device_owner = 'foo3' |
|
self.fixed_ips = [ |
|
FakeIPAllocation('192.168.0.3', |
|
'dddddddd-dddd-dddd-dddd-dddddddddddd'), |
|
FakeIPAllocation('ffea:3ba5:a17a:4ba3:0216:3eff:fec2:771d', |
|
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')] |
|
self.dns_assignment = [ |
|
FakeDNSAssignment('192.168.0.3'), |
|
FakeDNSAssignment('ffea:3ba5:a17a:4ba3:0216:3eff:fec2:771d')] |
|
self.mac_address = '00:16:3e:c2:77:1d' |
|
self.device_id = 'fake_port6' |
|
self.extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='dns-server', |
|
opt_value='ffea:3ba5:a17a:4ba3::100', |
|
ip_version=constants.IP_VERSION_6)] |
|
|
|
|
|
class FakeDualPort(object): |
|
def __init__(self, domain='openstacklocal'): |
|
self.id = 'hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh' |
|
self.admin_state_up = True |
|
self.device_owner = 'foo3' |
|
self.fixed_ips = [ |
|
FakeIPAllocation('192.168.0.3', |
|
'dddddddd-dddd-dddd-dddd-dddddddddddd'), |
|
FakeIPAllocation('fdca:3ba5:a17a:4ba3::3', |
|
'ffffffff-ffff-ffff-ffff-ffffffffffff')] |
|
self.mac_address = '00:00:0f:aa:bb:cc' |
|
self.device_id = 'fake_dual_port' |
|
self.extra_dhcp_opts = [] |
|
self.dns_assignment = [FakeDNSAssignment('192.168.0.3', domain=domain), |
|
FakeDNSAssignment('fdca:3ba5:a17a:4ba3::3', |
|
domain=domain)] |
|
|
|
|
|
class FakeRouterPort(object): |
|
def __init__(self, dev_owner=constants.DEVICE_OWNER_ROUTER_INTF, |
|
ip_address='192.168.0.1', domain='openstacklocal'): |
|
self.id = 'rrrrrrrr-rrrr-rrrr-rrrr-rrrrrrrrrrrr' |
|
self.admin_state_up = True |
|
self.mac_address = '00:00:0f:rr:rr:rr' |
|
self.device_id = 'fake_router_port' |
|
self.dns_assignment = [] |
|
self.extra_dhcp_opts = [] |
|
self.device_owner = dev_owner |
|
self.fixed_ips = [FakeIPAllocation( |
|
ip_address, 'dddddddd-dddd-dddd-dddd-dddddddddddd')] |
|
self.dns_assignment = [FakeDNSAssignment(ip.ip_address, domain=domain) |
|
for ip in self.fixed_ips] |
|
|
|
|
|
class FakeRouterHAPort(object): |
|
def __init__(self): |
|
self.id = 'hahahaha-haha-haha-haha-hahahahahaha' |
|
self.admin_state_up = True |
|
self.device_owner = constants.DEVICE_OWNER_ROUTER_HA_INTF |
|
self.mac_address = '00:00:0f:aa:aa:aa' |
|
self.device_id = 'fake_router_ha_port' |
|
self.dns_assignment = [] |
|
self.extra_dhcp_opts = [] |
|
self.fixed_ips = [FakeIPAllocation( |
|
'169.254.169.20', 'dddddddd-dddd-dddd-dddd-dddddddddddd')] |
|
|
|
|
|
class FakeRouterPortNoDHCP(object): |
|
def __init__(self, dev_owner=constants.DEVICE_OWNER_ROUTER_INTF, |
|
ip_address='192.168.0.1', domain='openstacklocal'): |
|
self.id = 'ssssssss-ssss-ssss-ssss-ssssssssssss' |
|
self.admin_state_up = True |
|
self.mac_address = '00:00:0f:rr:rr:rr' |
|
self.device_id = 'fake_router_port_no_dhcp' |
|
self.dns_assignment = [] |
|
self.extra_dhcp_opts = [] |
|
self.device_owner = dev_owner |
|
self.fixed_ips = [FakeIPAllocation( |
|
ip_address, 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')] |
|
self.dns_assignment = [FakeDNSAssignment(ip.ip_address, domain=domain) |
|
for ip in self.fixed_ips] |
|
|
|
|
|
class FakeRouterPort2(object): |
|
def __init__(self): |
|
self.id = 'rrrrrrrr-rrrr-rrrr-rrrr-rrrrrrrrrrrr' |
|
self.admin_state_up = True |
|
self.device_owner = constants.DEVICE_OWNER_ROUTER_INTF |
|
self.fixed_ips = [ |
|
FakeIPAllocation('192.168.1.1', |
|
'cccccccc-cccc-cccc-cccc-cccccccccccc')] |
|
self.dns_assignment = [FakeDNSAssignment('192.168.1.1')] |
|
self.mac_address = '00:00:0f:rr:rr:r2' |
|
self.device_id = 'fake_router_port2' |
|
self.extra_dhcp_opts = [] |
|
|
|
|
|
class FakeRouterPortSegmentID(object): |
|
def __init__(self): |
|
self.id = 'qqqqqqqq-qqqq-qqqq-qqqq-qqqqqqqqqqqq' |
|
self.admin_state_up = True |
|
self.device_owner = constants.DEVICE_OWNER_ROUTER_INTF |
|
self.fixed_ips = [ |
|
FakeIPAllocation('192.168.2.1', |
|
'iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii')] |
|
self.dns_assignment = [FakeDNSAssignment('192.168.2.1')] |
|
self.mac_address = '00:00:0f:rr:rr:r3' |
|
self.device_id = 'fake_router_port3' |
|
self.extra_dhcp_opts = [] |
|
|
|
|
|
class FakePortMultipleAgents1(object): |
|
def __init__(self): |
|
self.id = 'rrrrrrrr-rrrr-rrrr-rrrr-rrrrrrrrrrrr' |
|
self.admin_state_up = True |
|
self.device_owner = constants.DEVICE_OWNER_DHCP |
|
self.fixed_ips = [ |
|
FakeIPAllocation('192.168.0.5', |
|
'dddddddd-dddd-dddd-dddd-dddddddddddd')] |
|
self.dns_assignment = [FakeDNSAssignment('192.168.0.5')] |
|
self.mac_address = '00:00:0f:dd:dd:dd' |
|
self.device_id = 'fake_multiple_agents_port' |
|
self.extra_dhcp_opts = [] |
|
|
|
|
|
class FakePortMultipleAgents2(object): |
|
def __init__(self): |
|
self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' |
|
self.admin_state_up = True |
|
self.device_owner = constants.DEVICE_OWNER_DHCP |
|
self.fixed_ips = [ |
|
FakeIPAllocation('192.168.0.6', |
|
'dddddddd-dddd-dddd-dddd-dddddddddddd')] |
|
self.dns_assignment = [FakeDNSAssignment('192.168.0.6')] |
|
self.mac_address = '00:00:0f:ee:ee:ee' |
|
self.device_id = 'fake_multiple_agents_port2' |
|
self.extra_dhcp_opts = [] |
|
|
|
|
|
class FakePortWithClientIdNum(object): |
|
def __init__(self): |
|
self.extra_dhcp_opts = [ |
|
DhcpOpt(opt_name=dhcp.DHCP_OPT_CLIENT_ID_NUM, |
|
opt_value='test_client_id_num')] |
|
|
|
|
|
class FakePortWithClientIdNumStr(object): |
|
def __init__(self): |
|
self.extra_dhcp_opts = [ |
|
DhcpOpt(opt_name=str(dhcp.DHCP_OPT_CLIENT_ID_NUM), |
|
opt_value='test_client_id_num')] |
|
|
|
|
|
class FakeV4HostRoute(object): |
|
def __init__(self): |
|
self.destination = '20.0.0.1/24' |
|
self.nexthop = '20.0.0.1' |
|
|
|
|
|
class FakeV4HostRouteGateway(object): |
|
def __init__(self): |
|
self.destination = constants.IPv4_ANY |
|
self.nexthop = '10.0.0.1' |
|
|
|
|
|
class FakeV6HostRoute(object): |
|
def __init__(self): |
|
self.destination = '2001:0200:feed:7ac0::/64' |
|
self.nexthop = '2001:0200:feed:7ac0::1' |
|
|
|
|
|
class FakeV4Subnet(Dictable): |
|
def __init__(self): |
|
self.id = 'dddddddd-dddd-dddd-dddd-dddddddddddd' |
|
self.ip_version = constants.IP_VERSION_4 |
|
self.cidr = '192.168.0.0/24' |
|
self.gateway_ip = '192.168.0.1' |
|
self.enable_dhcp = True |
|
self.host_routes = [FakeV4HostRoute()] |
|
self.dns_nameservers = ['8.8.8.8'] |
|
|
|
|
|
class FakeV4Subnet2(FakeV4Subnet): |
|
def __init__(self): |
|
super(FakeV4Subnet2, self).__init__() |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.cidr = '192.168.1.0/24' |
|
self.gateway_ip = '192.168.1.1' |
|
self.host_routes = [] |
|
|
|
|
|
class FakeV4SubnetSegmentID(FakeV4Subnet): |
|
def __init__(self): |
|
super(FakeV4SubnetSegmentID, self).__init__() |
|
self.id = 'iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii' |
|
self.cidr = '192.168.2.0/24' |
|
self.gateway_ip = '192.168.2.1' |
|
self.host_routes = [] |
|
self.segment_id = 1 |
|
|
|
|
|
class FakeV4SubnetSegmentID2(FakeV4Subnet): |
|
def __init__(self): |
|
super(FakeV4SubnetSegmentID2, self).__init__() |
|
self.id = 'jjjjjjjj-jjjj-jjjj-jjjj-jjjjjjjjjjjj' |
|
self.host_routes = [] |
|
self.segment_id = 2 |
|
|
|
|
|
class FakeV4MetadataSubnet(FakeV4Subnet): |
|
def __init__(self): |
|
super(FakeV4MetadataSubnet, self).__init__() |
|
self.cidr = '169.254.169.254/30' |
|
self.gateway_ip = '169.254.169.253' |
|
self.host_routes = [] |
|
self.dns_nameservers = [] |
|
|
|
|
|
class FakeV4SubnetGatewayRoute(FakeV4Subnet): |
|
def __init__(self): |
|
super(FakeV4SubnetGatewayRoute, self).__init__() |
|
self.host_routes = [FakeV4HostRouteGateway()] |
|
|
|
|
|
class FakeV4SubnetMultipleAgentsWithoutDnsProvided(FakeV4Subnet): |
|
def __init__(self): |
|
super(FakeV4SubnetMultipleAgentsWithoutDnsProvided, self).__init__() |
|
self.dns_nameservers = [] |
|
self.host_routes = [] |
|
|
|
|
|
class FakeV4SubnetAgentWithManyDnsProvided(FakeV4Subnet): |
|
def __init__(self): |
|
super(FakeV4SubnetAgentWithManyDnsProvided, self).__init__() |
|
self.dns_nameservers = ['2.2.2.2', '9.9.9.9', '1.1.1.1', '3.3.3.3'] |
|
self.host_routes = [] |
|
|
|
|
|
class FakeV4SubnetAgentWithNoDnsProvided(FakeV4Subnet): |
|
def __init__(self): |
|
super(FakeV4SubnetAgentWithNoDnsProvided, self).__init__() |
|
self.dns_nameservers = ['0.0.0.0'] |
|
self.host_routes = [] |
|
|
|
|
|
class FakeV4MultipleAgentsWithoutDnsProvided(object): |
|
def __init__(self): |
|
self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' |
|
self.subnets = [FakeV4SubnetMultipleAgentsWithoutDnsProvided()] |
|
self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort(), |
|
FakePortMultipleAgents1(), FakePortMultipleAgents2()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeV4AgentWithoutDnsProvided(object): |
|
def __init__(self): |
|
self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' |
|
self.subnets = [FakeV4SubnetMultipleAgentsWithoutDnsProvided()] |
|
self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort(), |
|
FakePortMultipleAgents1()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeV4AgentWithManyDnsProvided(object): |
|
def __init__(self): |
|
self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' |
|
self.subnets = [FakeV4SubnetAgentWithManyDnsProvided()] |
|
self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort(), |
|
FakePortMultipleAgents1()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeV4AgentWithNoDnsProvided(object): |
|
def __init__(self): |
|
self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' |
|
self.subnets = [FakeV4SubnetAgentWithNoDnsProvided()] |
|
self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort(), |
|
FakePortMultipleAgents1()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeV4SubnetMultipleAgentsWithDnsProvided(FakeV4Subnet): |
|
def __init__(self): |
|
super(FakeV4SubnetMultipleAgentsWithDnsProvided, self).__init__() |
|
self.host_routes = [] |
|
|
|
|
|
class FakeV4MultipleAgentsWithDnsProvided(object): |
|
def __init__(self): |
|
self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' |
|
self.subnets = [FakeV4SubnetMultipleAgentsWithDnsProvided()] |
|
self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort(), |
|
FakePortMultipleAgents1(), FakePortMultipleAgents2()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeV6Subnet(object): |
|
def __init__(self): |
|
self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' |
|
self.ip_version = constants.IP_VERSION_6 |
|
self.cidr = 'fdca:3ba5:a17a:4ba3::/64' |
|
self.gateway_ip = 'fdca:3ba5:a17a:4ba3::1' |
|
self.enable_dhcp = True |
|
self.host_routes = [FakeV6HostRoute()] |
|
self.dns_nameservers = ['2001:0200:feed:7ac0::1'] |
|
self.ipv6_ra_mode = None |
|
self.ipv6_address_mode = None |
|
|
|
|
|
class FakeV4SubnetNoDHCP(object): |
|
def __init__(self): |
|
self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' |
|
self.ip_version = constants.IP_VERSION_4 |
|
self.cidr = '192.168.1.0/24' |
|
self.gateway_ip = '192.168.1.1' |
|
self.enable_dhcp = False |
|
self.host_routes = [] |
|
self.dns_nameservers = [] |
|
|
|
|
|
class FakeV6SubnetDHCPStateful(Dictable): |
|
def __init__(self): |
|
self.id = 'ffffffff-ffff-ffff-ffff-ffffffffffff' |
|
self.ip_version = constants.IP_VERSION_6 |
|
self.cidr = 'fdca:3ba5:a17a:4ba3::/64' |
|
self.gateway_ip = 'fdca:3ba5:a17a:4ba3::1' |
|
self.enable_dhcp = True |
|
self.host_routes = [FakeV6HostRoute()] |
|
self.dns_nameservers = ['2001:0200:feed:7ac0::1'] |
|
self.ipv6_ra_mode = None |
|
self.ipv6_address_mode = constants.DHCPV6_STATEFUL |
|
|
|
|
|
class FakeV6SubnetSlaac(object): |
|
def __init__(self): |
|
self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' |
|
self.ip_version = constants.IP_VERSION_6 |
|
self.cidr = 'ffda:3ba5:a17a:4ba3::/64' |
|
self.gateway_ip = 'ffda:3ba5:a17a:4ba3::1' |
|
self.enable_dhcp = True |
|
self.host_routes = [FakeV6HostRoute()] |
|
self.ipv6_address_mode = constants.IPV6_SLAAC |
|
self.ipv6_ra_mode = None |
|
|
|
|
|
class FakeV6SubnetStateless(object): |
|
def __init__(self): |
|
self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' |
|
self.ip_version = constants.IP_VERSION_6 |
|
self.cidr = 'ffea:3ba5:a17a:4ba3::/64' |
|
self.gateway_ip = 'ffea:3ba5:a17a:4ba3::1' |
|
self.enable_dhcp = True |
|
self.dns_nameservers = [] |
|
self.host_routes = [] |
|
self.ipv6_address_mode = constants.DHCPV6_STATELESS |
|
self.ipv6_ra_mode = None |
|
|
|
|
|
class FakeV6SubnetStatelessNoDnsProvided(object): |
|
def __init__(self): |
|
self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' |
|
self.ip_version = constants.IP_VERSION_6 |
|
self.cidr = 'ffea:3ba5:a17a:4ba3::/64' |
|
self.gateway_ip = 'ffea:3ba5:a17a:4ba3::1' |
|
self.enable_dhcp = True |
|
self.dns_nameservers = ['::'] |
|
self.host_routes = [] |
|
self.ipv6_address_mode = constants.DHCPV6_STATELESS |
|
self.ipv6_ra_mode = None |
|
|
|
|
|
class FakeV6SubnetStatelessBadPrefixLength(object): |
|
def __init__(self): |
|
self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' |
|
self.ip_version = constants.IP_VERSION_6 |
|
self.cidr = 'ffeb:3ba5:a17a:4ba3::/56' |
|
self.gateway_ip = 'ffeb:3ba5:a17a:4ba3::1' |
|
self.enable_dhcp = True |
|
self.dns_nameservers = [] |
|
self.host_routes = [] |
|
self.ipv6_address_mode = constants.DHCPV6_STATELESS |
|
self.ipv6_ra_mode = None |
|
|
|
|
|
class FakeV4SubnetNoGateway(FakeV4Subnet): |
|
def __init__(self): |
|
super(FakeV4SubnetNoGateway, self).__init__() |
|
self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' |
|
self.cidr = '192.168.1.0/24' |
|
self.gateway_ip = None |
|
self.enable_dhcp = True |
|
self.host_routes = [] |
|
self.dns_nameservers = [] |
|
|
|
|
|
class FakeV4SubnetNoRouter(FakeV4Subnet): |
|
def __init__(self): |
|
super(FakeV4SubnetNoRouter, self).__init__() |
|
self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' |
|
self.cidr = '192.168.1.0/24' |
|
self.gateway_ip = '192.168.1.1' |
|
self.host_routes = [] |
|
self.dns_nameservers = [] |
|
|
|
|
|
class FakeV4Network(object): |
|
def __init__(self): |
|
self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' |
|
self.subnets = [FakeV4Subnet()] |
|
self.ports = [FakePort1()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeV4NetworkClientId(object): |
|
def __init__(self): |
|
self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' |
|
self.subnets = [FakeV4Subnet()] |
|
self.ports = [FakePort1(), FakePort5(), FakePort6()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeV4NetworkClientIdNum(object): |
|
def __init__(self): |
|
self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' |
|
self.subnets = [FakeV4Subnet()] |
|
self.ports = [FakePortWithClientIdNum()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeV4NetworkClientIdNumStr(object): |
|
def __init__(self): |
|
self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' |
|
self.subnets = [FakeV4Subnet()] |
|
self.ports = [FakePortWithClientIdNumStr()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeV6Network(object): |
|
def __init__(self): |
|
self.id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' |
|
self.subnets = [FakeV6Subnet()] |
|
self.ports = [FakePort2()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeDualNetwork(object): |
|
def __init__(self, domain='openstacklocal'): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.subnets = [FakeV4Subnet(), FakeV6SubnetDHCPStateful()] |
|
self.namespace = 'qdhcp-ns' |
|
self.ports = [FakePort1(domain=domain), FakeV6Port(domain=domain), |
|
FakeDualPort(domain=domain), |
|
FakeRouterHAPort(), |
|
FakeRouterPort(domain=domain)] |
|
|
|
|
|
class FakeDeviceManagerNetwork(object): |
|
def __init__(self): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.subnets = [FakeV4Subnet(), FakeV6SubnetDHCPStateful()] |
|
self.ports = [FakePort1(), |
|
FakeV6Port(), |
|
FakeDualPort(), |
|
FakeRouterPort()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeDualNetworkReserved(object): |
|
def __init__(self): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.subnets = [FakeV4Subnet(), FakeV6SubnetDHCPStateful()] |
|
self.ports = [FakePort1(), FakeV6Port(), FakeDualPort(), |
|
FakeRouterPort(), FakeReservedPort()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeDualNetworkReserved2(object): |
|
def __init__(self): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.subnets = [FakeV4Subnet(), FakeV6SubnetDHCPStateful()] |
|
self.ports = [FakePort1(), FakeV6Port(), FakeDualPort(), |
|
FakeRouterPort(), FakeReservedPort(), |
|
FakeReservedPort(id='reserved-2')] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeNetworkDhcpPort(object): |
|
def __init__(self): |
|
self.id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' |
|
self.subnets = [FakeV4Subnet()] |
|
self.ports = [FakePort1(), FakeDhcpPort()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeDualNetworkGatewayRoute(object): |
|
def __init__(self): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.subnets = [FakeV4SubnetGatewayRoute(), FakeV6SubnetDHCPStateful()] |
|
self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeDualNetworkSingleDHCP(object): |
|
def __init__(self): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.subnets = [FakeV4Subnet(), FakeV4SubnetNoDHCP()] |
|
self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeDualNetworkSingleDHCPBothAttaced(object): |
|
def __init__(self): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
# dhcp-agent actually can't get the subnet with dhcp disabled |
|
self.subnets = [FakeV4Subnet()] |
|
self.ports = [FakePort1(), FakeRouterPortNoDHCP(), FakeRouterPort()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeDualNetworkDualDHCP(object): |
|
def __init__(self): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.subnets = [FakeV4Subnet(), FakeV4Subnet2()] |
|
self.ports = [FakePort1(), FakeRouterPort(), FakeRouterPort2()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeDualNetworkDualDHCPOnLinkSubnetRoutesDisabled(object): |
|
def __init__(self): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.subnets = [FakeV4Subnet(), FakeV4SubnetSegmentID()] |
|
self.ports = [FakePort1(), FakeRouterPort(), FakeRouterPortSegmentID()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeNonLocalSubnets(object): |
|
def __init__(self): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.subnets = [FakeV4SubnetSegmentID2()] |
|
self.non_local_subnets = [FakeV4SubnetSegmentID()] |
|
self.ports = [FakePort1(), FakeRouterPort(), FakeRouterPortSegmentID()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeDualNetworkTriDHCPOneOnLinkSubnetRoute(object): |
|
def __init__(self): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.subnets = [FakeV4Subnet(), FakeV4Subnet2(), |
|
FakeV4SubnetSegmentID()] |
|
self.ports = [FakePort1(), FakeRouterPort(), FakeRouterPort2(), |
|
FakeRouterPortSegmentID()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeV4NoGatewayNetwork(object): |
|
def __init__(self): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.subnets = [FakeV4SubnetNoGateway()] |
|
self.ports = [FakePort1()] |
|
|
|
|
|
class FakeV4NetworkNoRouter(object): |
|
def __init__(self): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.subnets = [FakeV4SubnetNoRouter()] |
|
self.ports = [FakePort1()] |
|
|
|
|
|
class FakeV4MetadataNetwork(object): |
|
def __init__(self): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.subnets = [FakeV4MetadataSubnet()] |
|
self.ports = [FakeRouterPort(ip_address='169.254.169.253')] |
|
|
|
|
|
class FakeV4NetworkDistRouter(object): |
|
def __init__(self): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.subnets = [FakeV4Subnet()] |
|
self.ports = [FakePort1(), |
|
FakeRouterPort( |
|
dev_owner=constants.DEVICE_OWNER_DVR_INTERFACE)] |
|
|
|
|
|
class FakeDualV4Pxe3Ports(object): |
|
def __init__(self, port_detail="portsSame"): |
|
self.id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' |
|
self.subnets = [FakeV4Subnet(), FakeV4SubnetNoDHCP()] |
|
self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()] |
|
self.namespace = 'qdhcp-ns' |
|
if port_detail == "portsSame": |
|
self.ports[0].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] |
|
self.ports[1].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.1.3'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.1.2'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux2.0')] |
|
self.ports[2].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.1.3'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.1.2'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux3.0')] |
|
else: |
|
self.ports[0].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.2'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] |
|
self.ports[1].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.5'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.5'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux2.0')] |
|
self.ports[2].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.7'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.7'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux3.0')] |
|
|
|
|
|
class FakeV4NetworkPxe2Ports(object): |
|
def __init__(self, port_detail="portsSame"): |
|
self.id = 'dddddddd-dddd-dddd-dddd-dddddddddddd' |
|
self.subnets = [FakeV4Subnet()] |
|
self.ports = [FakePort1(), FakePort2(), FakeRouterPort()] |
|
self.namespace = 'qdhcp-ns' |
|
if port_detail == "portsSame": |
|
self.ports[0].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] |
|
self.ports[1].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] |
|
else: |
|
self.ports[0].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] |
|
self.ports[1].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.5'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.5'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] |
|
|
|
|
|
class FakeV4NetworkPxe3Ports(object): |
|
def __init__(self, port_detail="portsSame"): |
|
self.id = 'dddddddd-dddd-dddd-dddd-dddddddddddd' |
|
self.subnets = [FakeV4Subnet()] |
|
self.ports = [FakePort1(), FakePort2(), FakePort3(), FakeRouterPort()] |
|
self.namespace = 'qdhcp-ns' |
|
if port_detail == "portsSame": |
|
self.ports[0].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] |
|
self.ports[1].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.1.3'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.1.2'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] |
|
self.ports[2].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.1.3'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.1.2'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] |
|
else: |
|
self.ports[0].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0')] |
|
self.ports[1].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.5'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.5'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux2.0')] |
|
self.ports[2].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.7'), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.7'), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux3.0')] |
|
|
|
|
|
class FakeV4NetworkPxePort(object): |
|
def __init__(self): |
|
self.id = 'dddddddd-dddd-dddd-dddd-dddddddddddd' |
|
self.subnets = [FakeV4Subnet()] |
|
self.ports = [FakePort1()] |
|
self.namespace = 'qdhcp-ns' |
|
self.ports[0].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.3', |
|
ip_version=constants.IP_VERSION_4), |
|
DhcpOpt(opt_name='server-ip-address', opt_value='192.168.0.2', |
|
ip_version=constants.IP_VERSION_4), |
|
DhcpOpt(opt_name='nd98', opt_value='option-nondigit-98', |
|
ip_version=constants.IP_VERSION_4), |
|
DhcpOpt(opt_name='99', opt_value='option-99', |
|
ip_version=constants.IP_VERSION_4), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0', |
|
ip_version=constants.IP_VERSION_4)] |
|
|
|
|
|
class FakeV6NetworkPxePort(object): |
|
def __init__(self): |
|
self.id = 'dddddddd-dddd-dddd-dddd-dddddddddddd' |
|
self.subnets = [FakeV6SubnetDHCPStateful()] |
|
self.ports = [FakeV6Port()] |
|
self.namespace = 'qdhcp-ns' |
|
self.ports[0].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='2001:192:168::1', |
|
ip_version=constants.IP_VERSION_6), |
|
DhcpOpt(opt_name='nd98', opt_value='option-nondigit-98', |
|
ip_version=constants.IP_VERSION_6), |
|
DhcpOpt(opt_name='99', opt_value='option-99', |
|
ip_version=constants.IP_VERSION_6), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0', |
|
ip_version=constants.IP_VERSION_6)] |
|
|
|
|
|
class FakeV6NetworkPxePortWrongOptVersion(object): |
|
def __init__(self): |
|
self.id = 'dddddddd-dddd-dddd-dddd-dddddddddddd' |
|
self.subnets = [FakeV6SubnetDHCPStateful()] |
|
self.ports = [FakeV6Port()] |
|
self.namespace = 'qdhcp-ns' |
|
self.ports[0].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tftp-server', opt_value='192.168.0.7', |
|
ip_version=constants.IP_VERSION_4), |
|
DhcpOpt(opt_name='bootfile-name', opt_value='pxelinux.0', |
|
ip_version=constants.IP_VERSION_6)] |
|
|
|
|
|
class FakeDualStackNetworkSingleDHCP(object): |
|
def __init__(self): |
|
self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' |
|
self.subnets = [FakeV4Subnet(), FakeV6SubnetSlaac()] |
|
self.ports = [FakePort1(), FakePort4(), FakeRouterPort()] |
|
|
|
|
|
class FakeDualStackNetworkingSingleDHCPTags(object): |
|
def __init__(self): |
|
self.id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' |
|
self.subnets = [FakeV4Subnet(), FakeV6SubnetSlaac()] |
|
self.ports = [FakePort1(), FakePort4(), FakeRouterPort()] |
|
for port in self.ports: |
|
port.extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tag:ipxe,bootfile-name', |
|
opt_value='pxelinux.0')] |
|
|
|
|
|
class FakeV4NetworkMultipleTags(object): |
|
def __init__(self): |
|
self.id = 'dddddddd-dddd-dddd-dddd-dddddddddddd' |
|
self.subnets = [FakeV4Subnet()] |
|
self.ports = [FakePort1(), FakeRouterPort()] |
|
self.namespace = 'qdhcp-ns' |
|
self.ports[0].extra_dhcp_opts = [ |
|
DhcpOpt(opt_name='tag:ipxe,bootfile-name', opt_value='pxelinux.0')] |
|
|
|
|
|
class FakeV6NetworkStatelessDHCP(object): |
|
def __init__(self): |
|
self.id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' |
|
self.subnets = [FakeV6SubnetStateless()] |
|
self.ports = [FakeV6PortExtraOpt()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeV6NetworkStatelessDHCPNoDnsProvided(object): |
|
def __init__(self): |
|
self.id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' |
|
self.subnets = [FakeV6SubnetStatelessNoDnsProvided()] |
|
self.ports = [FakeV6Port()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeV6NetworkStatelessDHCPBadPrefixLength(object): |
|
def __init__(self): |
|
self.id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' |
|
self.subnets = [FakeV6SubnetStatelessBadPrefixLength()] |
|
self.ports = [FakeV6PortExtraOpt()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeNetworkWithV6SatelessAndV4DHCPSubnets(object): |
|
def __init__(self): |
|
self.id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' |
|
self.subnets = [FakeV6SubnetStateless(), FakeV4Subnet()] |
|
self.ports = [FakeDualPortWithV6ExtraOpt(), FakeRouterPort()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class FakeV6NetworkStatefulDHCPSameSubnetFixedIps(object): |
|
def __init__(self): |
|
self.id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' |
|
self.subnets = [FakeV6SubnetDHCPStateful()] |
|
self.ports = [FakeV6PortMultipleFixedIpsSameSubnet()] |
|
self.namespace = 'qdhcp-ns' |
|
|
|
|
|
class LocalChild(dhcp.DhcpLocalProcess): |
|
PORTS = {4: [4], 6: [6]} |
|
|
|
def __init__(self, *args, **kwargs): |
|
self.process_monitor = mock.Mock() |
|
kwargs['process_monitor'] = self.process_monitor |
|
super(LocalChild, self).__init__(*args, **kwargs) |
|
self.called = [] |
|
|
|
def reload_allocations(self): |
|
self.called.append('reload') |
|
|
|
def spawn_process(self): |
|
self.called.append('spawn') |
|
|
|
|
|
class TestConfBase(base.BaseTestCase): |
|
def setUp(self): |
|
super(TestConfBase, self).setUp() |
|
self.conf = config.setup_conf() |
|
self.conf.register_opts(base_config.core_opts) |
|
self.conf.register_opts(dhcp_config.DHCP_OPTS) |
|
self.conf.register_opts(dhcp_config.DNSMASQ_OPTS) |
|
config.register_external_process_opts(self.conf) |
|
config.register_interface_driver_opts_helper(self.conf) |
|
|
|
|
|
class TestBase(TestConfBase): |
|
def setUp(self): |
|
super(TestBase, self).setUp() |
|
instance = mock.patch("neutron.agent.linux.dhcp.DeviceManager") |
|
self.mock_mgr = instance.start() |
|
self.conf.register_opt(cfg.BoolOpt('enable_isolated_metadata', |
|
default=True)) |
|
self.conf.register_opt(cfg.BoolOpt("force_metadata", |
|
default=False)) |
|
self.conf.register_opt(cfg.BoolOpt('enable_metadata_network', |
|
default=False)) |
|
self.config_parse(self.conf) |
|
self.conf.set_override('state_path', '') |
|
|
|
self.replace_p = mock.patch('neutron_lib.utils.file.replace_file') |
|
self.execute_p = mock.patch('neutron.agent.common.utils.execute') |
|
mock.patch('neutron.agent.linux.utils.execute').start() |
|
self.safe = self.replace_p.start() |
|
self.execute = self.execute_p.start() |
|
|
|
self.makedirs = mock.patch('os.makedirs').start() |
|
self.rmtree = mock.patch('shutil.rmtree').start() |
|
|
|
self.external_process = mock.patch( |
|
'neutron.agent.linux.external_process.ProcessManager').start() |
|
|
|
self.mock_mgr.return_value.driver.bridged = True |
|
|
|
|
|
class TestDhcpBase(TestBase): |
|
|
|
def test_existing_dhcp_networks_abstract_error(self): |
|
self.assertRaises(NotImplementedError, |
|
dhcp.DhcpBase.existing_dhcp_networks, |
|
None) |
|
|
|
def test_check_version_abstract_error(self): |
|
self.assertRaises(NotImplementedError, |
|
dhcp.DhcpBase.check_version) |
|
|
|
def test_base_abc_error(self): |
|
self.assertRaises(TypeError, dhcp.DhcpBase, None) |
|
|
|
def test_restart(self): |
|
class SubClass(dhcp.DhcpBase): |
|
def __init__(self): |
|
dhcp.DhcpBase.__init__(self, cfg.CONF, FakeV4Network(), |
|
mock.Mock(), None) |
|
self.called = [] |
|
|
|
def enable(self): |
|
self.called.append('enable') |
|
|
|
def disable(self, retain_port=False, block=False): |
|
self.called.append('disable %s %s' % (retain_port, block)) |
|
|
|
def reload_allocations(self): |
|
pass |
|
|
|
@property |
|
def active(self): |
|
return True |
|
|
|
c = SubClass() |
|
c.restart() |
|
self.assertEqual(c.called, ['disable True True', 'enable']) |
|
|
|
|
|
class TestDhcpLocalProcess(TestBase): |
|
|
|
def test_get_conf_file_name(self): |
|
tpl = '/dhcp/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/dev' |
|
lp = LocalChild(self.conf, FakeV4Network()) |
|
self.assertEqual(lp.get_conf_file_name('dev'), tpl) |
|
|
|
@mock.patch.object(fileutils, 'ensure_tree') |
|
def test_ensure_dir_called(self, ensure_dir): |
|
LocalChild(self.conf, FakeV4Network()) |
|
ensure_dir.assert_called_once_with( |
|
'/dhcp/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', mode=0o755) |
|
|
|
def test_enable_already_active(self): |
|
with mock.patch.object(LocalChild, 'active') as patched: |
|
patched.__get__ = mock.Mock(side_effect=[True, False]) |
|
lp = LocalChild(self.conf, FakeV4Network()) |
|
with mock.patch.object(ip_lib, 'delete_network_namespace'): |
|
lp.enable() |
|
|
|
self.assertEqual(lp.called, ['spawn']) |
|
self.assertTrue(self.mock_mgr.return_value.setup.called) |
|
|
|
@mock.patch.object(fileutils, 'ensure_tree') |
|
def test_enable(self, ensure_dir): |
|
attrs_to_mock = dict( |
|
(a, mock.DEFAULT) for a in |
|
['active', 'interface_name', 'spawn_process'] |
|
) |
|
|
|
with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks: |
|
mocks['active'].__get__ = mock.Mock(return_value=False) |
|
mocks['interface_name'].__set__ = mock.Mock() |
|
mocks['spawn_process'].side_effect = [ |
|
exceptions.ProcessExecutionError( |
|
returncode=2, message="Test dnsmasq start failed"), |
|
None] |
|
lp = LocalChild(self.conf, |
|
FakeDualNetwork()) |
|
|
|
lp.enable() |
|
|
|
self.mock_mgr.assert_has_calls( |
|
[mock.call(self.conf, None), |
|
mock.call().setup(mock.ANY)]) |
|
self.assertEqual(2, mocks['interface_name'].__set__.call_count) |
|
ensure_dir.assert_has_calls([ |
|
mock.call( |
|
'/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc', mode=0o755), |
|
mock.call( |
|
'/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc', mode=0o755)]) |
|
|
|
def _assert_disabled(self, lp): |
|
self.assertTrue(lp.process_monitor.unregister.called) |
|
self.assertTrue(self.external_process().disable.called) |
|
|
|
def test_disable_not_active(self): |
|
attrs_to_mock = dict((a, mock.DEFAULT) for a in |
|
['active', 'interface_name']) |
|
with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks: |
|
mocks['active'].__get__ = mock.Mock(return_value=False) |
|
mocks['interface_name'].__get__ = mock.Mock(return_value='tap0') |
|
network = FakeDualNetwork() |
|
lp = LocalChild(self.conf, network) |
|
lp.device_manager = mock.Mock() |
|
with mock.patch('neutron.agent.linux.ip_lib.' |
|
'delete_network_namespace') as delete_ns: |
|
lp.disable() |
|
lp.device_manager.destroy.assert_called_once_with( |
|
network, 'tap0') |
|
self._assert_disabled(lp) |
|
|
|
delete_ns.assert_called_with('qdhcp-ns') |
|
|
|
def test_disable_retain_port(self): |
|
attrs_to_mock = dict((a, mock.DEFAULT) for a in |
|
['active', 'interface_name']) |
|
network = FakeDualNetwork() |
|
with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks: |
|
mocks['active'].__get__ = mock.Mock(return_value=True) |
|
mocks['interface_name'].__get__ = mock.Mock(return_value='tap0') |
|
lp = LocalChild(self.conf, network) |
|
lp.disable(retain_port=True) |
|
self._assert_disabled(lp) |
|
|
|
def test_disable(self): |
|
attrs_to_mock = {'active': mock.DEFAULT} |
|
|
|
with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks: |
|
mocks['active'].__get__ = mock.Mock(return_value=False) |
|
lp = LocalChild(self.conf, FakeDualNetwork()) |
|
with mock.patch('neutron.agent.linux.ip_lib.' |
|
'delete_network_namespace') as delete_ns: |
|
lp.disable() |
|
|
|
self._assert_disabled(lp) |
|
|
|
delete_ns.assert_called_with('qdhcp-ns') |
|
|
|
def test_disable_config_dir_removed_after_destroy(self): |
|
parent = mock.MagicMock() |
|
parent.attach_mock(self.rmtree, 'rmtree') |
|
parent.attach_mock(self.mock_mgr, 'DeviceManager') |
|
|
|
lp = LocalChild(self.conf, FakeDualNetwork()) |
|
with mock.patch('neutron.agent.linux.ip_lib.' |
|
'delete_network_namespace') as delete_ns: |
|
lp.disable(retain_port=False) |
|
|
|
expected = [mock.call.DeviceManager().destroy(mock.ANY, mock.ANY), |
|
mock.call.rmtree(mock.ANY, ignore_errors=True)] |
|
parent.assert_has_calls(expected) |
|
delete_ns.assert_called_with('qdhcp-ns') |
|
|
|
def test_get_interface_name(self): |
|
net = FakeDualNetwork() |
|
path = '/dhcp/%s/interface' % net.id |
|
self.useFixture(lib_fixtures.OpenFixture(path, 'tap0')) |
|
lp = LocalChild(self.conf, net) |
|
self.assertEqual(lp.interface_name, 'tap0') |
|
|
|
def test_set_interface_name(self): |
|
with mock.patch('neutron_lib.utils.file.replace_file') as replace: |
|
lp = LocalChild(self.conf, FakeDualNetwork()) |
|
with mock.patch.object(lp, 'get_conf_file_name') as conf_file: |
|
conf_file.return_value = '/interface' |
|
lp.interface_name = 'tap0' |
|
conf_file.assert_called_once_with('interface') |
|
replace.assert_called_once_with(mock.ANY, 'tap0') |
|
|
|
|
|
class TestDnsmasq(TestBase): |
|
|
|
def setUp(self): |
|
super(TestDnsmasq, self).setUp() |
|
self._mock_get_devices_with_ip = mock.patch.object( |
|
ip_lib, 'get_devices_with_ip') |
|
self.mock_get_devices_with_ip = self._mock_get_devices_with_ip.start() |
|
self.addCleanup(self._stop_mocks) |
|
|
|
def _stop_mocks(self): |
|
self._mock_get_devices_with_ip.stop() |
|
|
|
def _get_dnsmasq(self, network, process_monitor=None): |
|
process_monitor = process_monitor or mock.Mock() |
|
return dhcp.Dnsmasq(self.conf, network, |
|
process_monitor=process_monitor) |
|
|
|
def _test_spawn(self, extra_options, network=FakeDualNetwork(), |
|
max_leases=16777216, lease_duration=86400, |
|
has_static=True, no_resolv='--no-resolv', |
|
has_stateless=True, dhcp_t1=0, dhcp_t2=0, |
|
bridged=True): |
|
def mock_get_conf_file_name(kind): |
|
return '/dhcp/%s/%s' % (network.id, kind) |
|
|
|
# Empty string passed to --conf-file in dnsmasq is invalid |
|
# we must force '' to '/dev/null' because the dhcp agent |
|
# does the same. Therefore we allow empty string to |
|
# be passed to neutron but not to dnsmasq. |
|
def check_conf_file_empty(cmd_list): |
|
for i in cmd_list: |
|
conf_file = '' |
|
value = '' |
|
if i.startswith('--conf-file='): |
|
conf_file = i |
|
value = i[12:].strip() |
|
if not value: |
|
idx = cmd_list.index(conf_file) |
|
cmd_list[idx] = '--conf-file=/dev/null' |
|
|
|
# if you need to change this path here, think twice, |
|
# that means pid files will move around, breaking upgrades |
|
# or backwards-compatibility |
|
expected_pid_file = '/dhcp/%s/pid' % network.id |
|
|
|
expected = [ |
|
'dnsmasq', |
|
'--no-hosts', |
|
no_resolv, |
|
'--pid-file=%s' % expected_pid_file, |
|
'--dhcp-hostsfile=/dhcp/%s/host' % network.id, |
|
'--addn-hosts=/dhcp/%s/addn_hosts' % network.id, |
|
'--dhcp-optsfile=/dhcp/%s/opts' % network.id, |
|
'--dhcp-leasefile=/dhcp/%s/leases' % network.id, |
|
'--dhcp-match=set:ipxe,175', |
|
'--dhcp-userclass=set:ipxe6,iPXE', |
|
'--local-service', |
|
'--bind-dynamic', |
|
] |
|
if not bridged: |
|
expected += [ |
|
'--bridge-interface=tap0,tap*' |
|
] |
|
|
|
seconds = '' |
|
if lease_duration == -1: |
|
lease_duration = 'infinite' |
|
else: |
|
seconds = 's' |
|
if has_static: |
|
prefix = '--dhcp-range=set:subnet-%s,%s,static,%s,%s%s' |
|
prefix6 = '--dhcp-range=set:subnet-%s,%s,static,%s,%s%s' |
|
elif has_stateless: |
|
prefix = '--dhcp-range=set:subnet-%s,%s,%s,%s%s' |
|
prefix6 = '--dhcp-range=set:subnet-%s,%s,%s,%s%s' |
|
possible_leases = 0 |
|
for s in network.subnets: |
|
if (s.ip_version != constants.IP_VERSION_6 or |
|
s.ipv6_address_mode == constants.DHCPV6_STATEFUL): |
|
if s.ip_version == constants.IP_VERSION_4: |
|
expected.extend([prefix % ( |
|
s.id, s.cidr.split('/')[0], |
|
netaddr.IPNetwork(s.cidr).netmask, lease_duration, |
|
seconds)]) |
|
else: |
|
expected.extend([prefix6 % ( |
|
s.id, s.cidr.split('/')[0], s.cidr.split('/')[1], |
|
lease_duration, seconds)]) |
|
possible_leases += netaddr.IPNetwork(s.cidr).size |
|
|
|
if hasattr(network, 'mtu'): |
|
expected.append( |
|
'--dhcp-option-force=option:mtu,%s' % network.mtu) |
|
|
|
expected.append('--dhcp-lease-max=%d' % min( |
|
possible_leases, max_leases)) |
|
|
|
if dhcp_t1: |
|
expected.append('--dhcp-option-force=option:T1,%ds' % dhcp_t1) |
|
if dhcp_t2: |
|
expected.append('--dhcp-option-force=option:T2,%ds' % dhcp_t2) |
|
|
|
expected.extend(extra_options) |
|
check_conf_file_empty(expected) |
|
|
|
self.execute.return_value = ('', '') |
|
|
|
attrs_to_mock = dict( |
|
(a, mock.DEFAULT) for a in |
|
['_output_opts_file', 'get_conf_file_name', 'interface_name'] |
|
) |
|
|
|
test_pm = mock.Mock() |
|
|
|
with mock.patch.multiple(dhcp.Dnsmasq, **attrs_to_mock) as mocks: |
|
mocks['get_conf_file_name'].side_effect = mock_get_conf_file_name |
|
mocks['_output_opts_file'].return_value = ( |
|
'/dhcp/%s/opts' % network.id |
|
) |
|
mocks['interface_name'].__get__ = mock.Mock(return_value='tap0') |
|
|
|
dm = self._get_dnsmasq(network, test_pm) |
|
dm.spawn_process() |
|
self.assertTrue(mocks['_output_opts_file'].called) |
|
|
|
self.assertTrue(test_pm.register.called) |
|
self.external_process().enable.assert_called_once_with( |
|
ensure_active=True, reload_cfg=False) |
|
call_kwargs = self.external_process.mock_calls[0][2] |
|
cmd_callback = call_kwargs['default_cmd_callback'] |
|
|
|
result_cmd = cmd_callback(expected_pid_file) |
|
|
|
self.assertEqual(expected, result_cmd) |
|
|
|
def test_spawn(self): |
|
self._test_spawn(['--conf-file=', '--domain=openstacklocal']) |
|
|
|
def test_spawn_not_bridged(self): |
|
self.mock_mgr.return_value.driver.bridged = False |
|
self._test_spawn(['--conf-file=', '--domain=openstacklocal'], |
|
bridged=False) |
|
|
|
def test_spawn_infinite_lease_duration(self): |
|
self.conf.set_override('dhcp_lease_duration', -1) |
|
self._test_spawn(['--conf-file=', '--domain=openstacklocal'], |
|
FakeDualNetwork(), 16777216, -1) |
|
|
|
def test_spawn_cfg_config_file(self): |
|
self.conf.set_override('dnsmasq_config_file', '/foo') |
|
self._test_spawn(['--conf-file=/foo', '--domain=openstacklocal']) |
|
|
|
@mock.patch.object(checks, 'dnsmasq_host_tag_support', autospec=True) |
|
def test_spawn_no_dns_domain(self, mock_tag_support): |
|
mock_tag_support.return_value = False |
|
(exp_host_name, exp_host_data, |
|
exp_addn_name, exp_addn_data) = self._test_no_dns_domain_alloc_data() |
|
self.conf.set_override('dns_domain', '') |
|
network = FakeDualNetwork(domain=self.conf.dns_domain) |
|
self._test_spawn(['--conf-file='], network=network) |
|
self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data), |
|
mock.call(exp_addn_name, exp_addn_data)]) |
|
|
|
@mock.patch.object(checks, 'dnsmasq_host_tag_support', autospec=True) |
|
def test_spawn_no_dns_domain_tag_support(self, mock_tag_support): |
|
mock_tag_support.return_value = True |
|
(exp_host_name, exp_host_data, exp_addn_name, |
|
exp_addn_data) = self._test_no_dns_domain_alloc_data( |
|
tag=dhcp.HOST_DHCPV6_TAG) |
|
self.conf.set_override('dns_domain', '') |
|
network = FakeDualNetwork(domain=self.conf.dns_domain) |
|
self._test_spawn(['--conf-file='], network=network) |
|
self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data), |
|
mock.call(exp_addn_name, exp_addn_data)]) |
|
|
|
def test_spawn_no_dhcp_range(self): |
|
network = FakeV6Network() |
|
subnet = FakeV6SubnetSlaac() |
|
network.subnets = [subnet] |
|
self._test_spawn(['--conf-file=', '--domain=openstacklocal'], |
|
network, has_static=False) |
|
|
|
def test_spawn_no_dhcp_range_bad_prefix_length(self): |
|
network = FakeV6NetworkStatelessDHCPBadPrefixLength() |
|
subnet = FakeV6SubnetStatelessBadPrefixLength() |
|
network.subnets = [subnet] |
|
self._test_spawn(['--conf-file=', '--domain=openstacklocal'], |
|
network, has_static=False, has_stateless=False) |
|
|
|
def test_spawn_cfg_dns_server(self): |
|
self.conf.set_override('dnsmasq_dns_servers', ['8.8.8.8']) |
|
self._test_spawn(['--conf-file=', |
|
'--server=8.8.8.8', |
|
'--domain=openstacklocal']) |
|
|
|
def test_spawn_cfg_multiple_dns_server(self): |
|
self.conf.set_override('dnsmasq_dns_servers', ['8.8.8.8', |
|
'9.9.9.9']) |
|
self._test_spawn(['--conf-file=', |
|
'--server=8.8.8.8', |
|
'--server=9.9.9.9', |
|
'--domain=openstacklocal']) |
|
|
|
def test_spawn_cfg_enable_dnsmasq_log(self): |
|
self.conf.set_override('dnsmasq_base_log_dir', '/tmp') |
|
network = FakeV4Network() |
|
dhcp_dns_log = \ |
|
'/tmp/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/dhcp_dns_log' |
|
|
|
self._test_spawn(['--conf-file=', |
|
'--domain=openstacklocal', |
|
'--log-queries', |
|
'--log-dhcp', |
|
('--log-facility=%s' % dhcp_dns_log)], |
|
network) |
|
self.makedirs.assert_called_with(os.path.join('/tmp', network.id)) |
|
|
|
def test_spawn_cfg_with_local_resolv(self): |
|
self.conf.set_override('dnsmasq_local_resolv', True) |
|
|
|
self._test_spawn(['--conf-file=', '--domain=openstacklocal'], |
|
no_resolv='') |
|
|
|
def test_spawn_cfg_with_local_resolv_overridden(self): |
|
self.conf.set_override('dnsmasq_local_resolv', True) |
|
self.conf.set_override('dnsmasq_dns_servers', ['8.8.8.8']) |
|
|
|
self._test_spawn(['--conf-file=', |
|
'--server=8.8.8.8', |
|
'--domain=openstacklocal']) |
|
|
|
def test_spawn_max_leases_is_smaller_than_cap(self): |
|
self._test_spawn( |
|
['--conf-file=', '--domain=openstacklocal'], |
|
network=FakeV4Network(), |
|
max_leases=256) |
|
|
|
def test_spawn_cfg_broadcast(self): |
|
self.conf.set_override('dhcp_broadcast_reply', True) |
|
self._test_spawn(['--conf-file=', '--domain=openstacklocal', |
|
'--dhcp-broadcast']) |
|
|
|
def test_spawn_cfg_advertise_mtu(self): |
|
network = FakeV4Network() |
|
network.mtu = 1500 |
|
self._test_spawn(['--conf-file=', '--domain=openstacklocal'], |
|
network) |
|
|
|
def test_spawn_cfg_advertise_mtu_plugin_doesnt_pass_mtu_value(self): |
|
network = FakeV4Network() |
|
self._test_spawn(['--conf-file=', '--domain=openstacklocal'], |
|
network) |
|
|
|
def test_spawn_cfg_with_dhcp_timers(self): |
|
self.conf.set_override('dhcp_renewal_time', 30) |
|
self.conf.set_override('dhcp_rebinding_time', 100) |
|
self._test_spawn(['--conf-file=', '--domain=openstacklocal'], |
|
dhcp_t1=30, dhcp_t2=100) |
|
|
|
def _test_output_init_lease_file(self, timestamp): |
|
expected = [ |
|
'00:00:80:aa:bb:cc 192.168.0.2 * *', |
|
'00:00:0f:aa:bb:cc 192.168.0.3 * *', |
|
'00:00:0f:rr:rr:rr 192.168.0.1 * *\n'] |
|
expected = "\n".join(['%s %s' % (timestamp, le) for le in expected]) |
|
with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: |
|
conf_fn.return_value = '/foo/leases' |
|
dm = self._get_dnsmasq(FakeDualNetwork()) |
|
dm._output_init_lease_file() |
|
self.safe.assert_called_once_with('/foo/leases', expected) |
|
|
|
@mock.patch('time.time') |
|
def test_output_init_lease_file(self, tmock): |
|
self.conf.set_override('dhcp_lease_duration', 500) |
|
tmock.return_value = 1000000 |
|
# lease duration should be added to current time |
|
timestamp = 1000000 + 500 |
|
self._test_output_init_lease_file(timestamp) |
|
|
|
def test_output_init_lease_file_infinite_duration(self): |
|
self.conf.set_override('dhcp_lease_duration', -1) |
|
# when duration is infinite, lease db timestamp should be 0 |
|
timestamp = 0 |
|
self._test_output_init_lease_file(timestamp) |
|
|
|
def _test_output_opts_file(self, expected, network, ipm_retval=None): |
|
with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: |
|
conf_fn.return_value = '/foo/opts' |
|
dm = self._get_dnsmasq(network) |
|
if ipm_retval: |
|
with mock.patch.object( |
|
dm, '_make_subnet_interface_ip_map') as ipm: |
|
ipm.return_value = ipm_retval |
|
dm._output_opts_file() |
|
self.assertTrue(ipm.called) |
|
else: |
|
dm._output_opts_file() |
|
self.safe.assert_called_once_with('/foo/opts', expected) |
|
|
|
def test_output_opts_file(self): |
|
fake_v6 = '2001:0200:feed:7ac0::1' |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,20.0.0.1/24,20.0.0.1,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,20.0.0.1/24,20.0.0.1,169.254.169.254/32,192.168.0.1,' |
|
'0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1\n' |
|
'tag:subnet-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option6:dns-server,%s\n' |
|
'tag:subnet-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option6:domain-search,openstacklocal' |
|
).lstrip() % ('[' + fake_v6 + ']') |
|
|
|
self._test_output_opts_file(expected, FakeDualNetwork()) |
|
|
|
def test_output_opts_file_gateway_route(self): |
|
fake_v6 = '2001:0200:feed:7ac0::1' |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1\n' |
|
'tag:subnet-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option6:dns-server,%s\n' |
|
'tag:subnet-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option6:domain-search,openstacklocal' |
|
).lstrip() % ('[' + fake_v6 + ']') |
|
|
|
self._test_output_opts_file(expected, FakeDualNetworkGatewayRoute()) |
|
|
|
def test_output_opts_file_multiple_agents_without_dns_provided(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,192.168.0.5,192.168.0.6').lstrip() |
|
|
|
self._test_output_opts_file(expected, |
|
FakeV4MultipleAgentsWithoutDnsProvided()) |
|
|
|
def test_output_opts_file_agent_dns_provided(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1').lstrip() |
|
|
|
self._test_output_opts_file(expected, |
|
FakeV4AgentWithoutDnsProvided()) |
|
|
|
def test_output_opts_file_agent_with_many_dns_provided(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,2.2.2.2,9.9.9.9,1.1.1.1,3.3.3.3\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1').lstrip() |
|
|
|
self._test_output_opts_file(expected, |
|
FakeV4AgentWithManyDnsProvided()) |
|
|
|
def test_output_opts_file_agent_with_no_dns_provided(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1').lstrip() |
|
|
|
self._test_output_opts_file(expected, |
|
FakeV4AgentWithNoDnsProvided()) |
|
|
|
def test_output_opts_file_multiple_agents_with_dns_provided(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1').lstrip() |
|
|
|
self._test_output_opts_file(expected, |
|
FakeV4MultipleAgentsWithDnsProvided()) |
|
|
|
def test_output_opts_file_single_dhcp(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,192.168.1.0/24,0.0.0.0,' |
|
'20.0.0.1/24,20.0.0.1,169.254.169.254/32,192.168.0.1,' |
|
'0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,192.168.1.0/24,0.0.0.0,20.0.0.1/24,20.0.0.1,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1').lstrip() |
|
|
|
self._test_output_opts_file(expected, FakeDualNetworkSingleDHCP()) |
|
|
|
def test_output_opts_file_single_dhcp_both_not_isolated(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,20.0.0.1/24,20.0.0.1,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,20.0.0.1/24,20.0.0.1,169.254.169.254/32,192.168.0.1,' |
|
'0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1').lstrip() |
|
|
|
self._test_output_opts_file(expected, |
|
FakeDualNetworkSingleDHCPBothAttaced()) |
|
|
|
def test_output_opts_file_dual_dhcp_rfc3442(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,192.168.1.0/24,0.0.0.0,' |
|
'20.0.0.1/24,20.0.0.1,169.254.169.254/32,192.168.0.1,' |
|
'0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,192.168.1.0/24,0.0.0.0,20.0.0.1/24,20.0.0.1,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1\n' |
|
'tag:subnet-cccccccc-cccc-cccc-cccc-cccccccccccc,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-cccccccc-cccc-cccc-cccc-cccccccccccc,' |
|
'option:classless-static-route,192.168.0.0/24,0.0.0.0,' |
|
'169.254.169.254/32,192.168.1.1,0.0.0.0/0,192.168.1.1\n' |
|
'tag:subnet-cccccccc-cccc-cccc-cccc-cccccccccccc,' |
|
'249,192.168.0.0/24,0.0.0.0,169.254.169.254/32,192.168.1.1,' |
|
'0.0.0.0/0,192.168.1.1\n' |
|
'tag:subnet-cccccccc-cccc-cccc-cccc-cccccccccccc,' |
|
'option:router,192.168.1.1').lstrip() |
|
|
|
self._test_output_opts_file(expected, FakeDualNetworkDualDHCP()) |
|
|
|
def test_output_opts_file_dual_dhcp_rfc3442_no_on_link_subnet_routes(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,20.0.0.1/24,20.0.0.1,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,20.0.0.1/24,20.0.0.1,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1\n' |
|
'tag:subnet-iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii,' |
|
'option:classless-static-route,169.254.169.254/32,192.168.2.1,' |
|
'0.0.0.0/0,192.168.2.1\n' |
|
'tag:subnet-iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii,' |
|
'249,169.254.169.254/32,192.168.2.1,0.0.0.0/0,192.168.2.1\n' |
|
'tag:subnet-iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii,' |
|
'option:router,192.168.2.1').lstrip() |
|
|
|
self._test_output_opts_file(expected, |
|
FakeDualNetworkDualDHCPOnLinkSubnetRoutesDisabled()) |
|
|
|
def test_output_opts_file_dual_dhcp_rfc3442_one_on_link_subnet_route(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,192.168.1.0/24,0.0.0.0,' |
|
'20.0.0.1/24,20.0.0.1,169.254.169.254/32,192.168.0.1,' |
|
'0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,192.168.1.0/24,0.0.0.0,20.0.0.1/24,20.0.0.1,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1\n' |
|
'tag:subnet-cccccccc-cccc-cccc-cccc-cccccccccccc,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-cccccccc-cccc-cccc-cccc-cccccccccccc,' |
|
'option:classless-static-route,192.168.0.0/24,0.0.0.0,' |
|
'169.254.169.254/32,192.168.1.1,0.0.0.0/0,192.168.1.1\n' |
|
'tag:subnet-cccccccc-cccc-cccc-cccc-cccccccccccc,' |
|
'249,192.168.0.0/24,0.0.0.0,169.254.169.254/32,192.168.1.1,' |
|
'0.0.0.0/0,192.168.1.1\n' |
|
'tag:subnet-cccccccc-cccc-cccc-cccc-cccccccccccc,' |
|
'option:router,192.168.1.1\n' |
|
'tag:subnet-iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii,' |
|
'option:classless-static-route,169.254.169.254/32,192.168.2.1,' |
|
'0.0.0.0/0,192.168.2.1\n' |
|
'tag:subnet-iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii,' |
|
'249,169.254.169.254/32,192.168.2.1,0.0.0.0/0,192.168.2.1\n' |
|
'tag:subnet-iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii,' |
|
'option:router,192.168.2.1').lstrip() |
|
|
|
self._test_output_opts_file(expected, |
|
FakeDualNetworkTriDHCPOneOnLinkSubnetRoute()) |
|
|
|
def test_output_opts_file_no_gateway(self): |
|
expected = ( |
|
'tag:subnet-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:classless-static-route,169.254.169.254/32,192.168.1.1\n' |
|
'tag:subnet-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'249,169.254.169.254/32,192.168.1.1\n' |
|
'tag:subnet-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:router').lstrip() |
|
|
|
ipm_retval = {FakeV4SubnetNoGateway().id: '192.168.1.1'} |
|
self._test_output_opts_file(expected, FakeV4NoGatewayNetwork(), |
|
ipm_retval=ipm_retval) |
|
|
|
def test_non_local_subnets(self): |
|
expected = ( |
|
'tag:subnet-jjjjjjjj-jjjj-jjjj-jjjj-jjjjjjjjjjjj,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-jjjjjjjj-jjjj-jjjj-jjjj-jjjjjjjjjjjj,' |
|
'option:classless-static-route,169.254.169.254/32,192.168.0.1,' |
|
'0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-jjjjjjjj-jjjj-jjjj-jjjj-jjjjjjjjjjjj,' |
|
'249,169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-jjjjjjjj-jjjj-jjjj-jjjj-jjjjjjjjjjjj,' |
|
'option:router,192.168.0.1\n' |
|
'tag:subnet-iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii,' |
|
'option:classless-static-route,169.254.169.254/32,192.168.2.1,' |
|
'0.0.0.0/0,192.168.2.1\n' |
|
'tag:subnet-iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii,' |
|
'249,169.254.169.254/32,192.168.2.1,0.0.0.0/0,192.168.2.1\n' |
|
'tag:subnet-iiiiiiii-iiii-iiii-iiii-iiiiiiiiiiii,' |
|
'option:router,192.168.2.1').lstrip() |
|
ipm_retval = {FakeV4SubnetSegmentID2().id: '192.168.0.1'} |
|
self._test_output_opts_file(expected, FakeNonLocalSubnets(), |
|
ipm_retval=ipm_retval) |
|
|
|
def test_output_opts_file_no_neutron_router_on_subnet(self): |
|
expected = ( |
|
'tag:subnet-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:classless-static-route,' |
|
'169.254.169.254/32,192.168.1.2,0.0.0.0/0,192.168.1.1\n' |
|
'tag:subnet-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'249,169.254.169.254/32,192.168.1.2,0.0.0.0/0,192.168.1.1\n' |
|
'tag:subnet-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:router,192.168.1.1').lstrip() |
|
|
|
ipm_retval = {FakeV4SubnetNoRouter().id: '192.168.1.2'} |
|
self._test_output_opts_file(expected, FakeV4NetworkNoRouter(), |
|
ipm_retval=ipm_retval) |
|
|
|
def test_output_opts_file_dist_neutron_router_on_subnet(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,20.0.0.1/24,20.0.0.1,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,20.0.0.1/24,20.0.0.1,169.254.169.254/32,192.168.0.1,' |
|
'0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1').lstrip() |
|
|
|
ipm_retval = {FakeV4Subnet().id: '192.168.0.1'} |
|
self._test_output_opts_file(expected, FakeV4NetworkDistRouter(), |
|
ipm_retval=ipm_retval) |
|
|
|
def test_output_opts_file_pxe_2port_1net(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,20.0.0.1/24,20.0.0.1,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,20.0.0.1/24,20.0.0.1,169.254.169.254/32,192.168.0.1,' |
|
'0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1\n' |
|
'tag:port-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:tftp-server,192.168.0.3\n' |
|
'tag:port-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:server-ip-address,192.168.0.2\n' |
|
'tag:port-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:bootfile-name,pxelinux.0\n' |
|
'tag:port-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option:tftp-server,192.168.0.3\n' |
|
'tag:port-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option:server-ip-address,192.168.0.2\n' |
|
'tag:port-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option:bootfile-name,pxelinux.0').lstrip() |
|
|
|
self._test_output_opts_file(expected, FakeV4NetworkPxe2Ports()) |
|
|
|
def test_output_opts_file_pxe_2port_1net_diff_details(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,20.0.0.1/24,20.0.0.1,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,20.0.0.1/24,20.0.0.1,169.254.169.254/32,192.168.0.1,' |
|
'0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1\n' |
|
'tag:port-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:tftp-server,192.168.0.3\n' |
|
'tag:port-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:server-ip-address,192.168.0.2\n' |
|
'tag:port-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:bootfile-name,pxelinux.0\n' |
|
'tag:port-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option:tftp-server,192.168.0.5\n' |
|
'tag:port-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option:server-ip-address,192.168.0.5\n' |
|
'tag:port-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option:bootfile-name,pxelinux.0').lstrip() |
|
|
|
self._test_output_opts_file(expected, |
|
FakeV4NetworkPxe2Ports("portsDiff")) |
|
|
|
def test_output_opts_file_pxe_3port_2net(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,192.168.1.0/24,0.0.0.0,20.0.0.1/24,' |
|
'20.0.0.1,169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,192.168.1.0/24,0.0.0.0,20.0.0.1/24,20.0.0.1,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1\n' |
|
'tag:port-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:tftp-server,192.168.0.3\n' |
|
'tag:port-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:server-ip-address,192.168.0.2\n' |
|
'tag:port-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:bootfile-name,pxelinux.0\n' |
|
'tag:port-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option:tftp-server,192.168.1.3\n' |
|
'tag:port-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option:server-ip-address,192.168.1.2\n' |
|
'tag:port-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option:bootfile-name,pxelinux2.0\n' |
|
'tag:port-44444444-4444-4444-4444-444444444444,' |
|
'option:tftp-server,192.168.1.3\n' |
|
'tag:port-44444444-4444-4444-4444-444444444444,' |
|
'option:server-ip-address,192.168.1.2\n' |
|
'tag:port-44444444-4444-4444-4444-444444444444,' |
|
'option:bootfile-name,pxelinux3.0').lstrip() |
|
|
|
self._test_output_opts_file(expected, FakeDualV4Pxe3Ports()) |
|
|
|
def test_output_opts_file_pxe_port(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,20.0.0.1/24,20.0.0.1,' |
|
'0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,20.0.0.1/24,20.0.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1\n' |
|
'tag:port-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:tftp-server,192.168.0.3\n' |
|
'tag:port-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:server-ip-address,192.168.0.2\n' |
|
'tag:port-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:nd98,option-nondigit-98\n' |
|
'tag:port-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'99,option-99\n' |
|
'tag:port-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'option:bootfile-name,pxelinux.0').lstrip() |
|
|
|
self._test_output_opts_file(expected, FakeV4NetworkPxePort()) |
|
|
|
def test_output_opts_file_multiple_tags(self): |
|
expected = ( |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:dns-server,8.8.8.8\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:classless-static-route,20.0.0.1/24,20.0.0.1,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'249,20.0.0.1/24,20.0.0.1,' |
|
'169.254.169.254/32,192.168.0.1,0.0.0.0/0,192.168.0.1\n' |
|
'tag:subnet-dddddddd-dddd-dddd-dddd-dddddddddddd,' |
|
'option:router,192.168.0.1\n' |
|
'tag:port-eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee,' |
|
'tag:ipxe,option:bootfile-name,pxelinux.0') |
|
expected = expected.lstrip() |
|
|
|
with mock.patch.object(dhcp.Dnsmasq, 'get_conf_file_name') as conf_fn: |
|
conf_fn.return_value = '/foo/opts' |
|
dm = self._get_dnsmasq(FakeV4NetworkMultipleTags()) |
|
dm._output_opts_file() |
|
|
|
self.safe.assert_called_once_with('/foo/opts', expected) |
|
|
|
@mock.patch('neutron.agent.linux.dhcp.Dnsmasq.get_conf_file_name', |
|
return_value='/foo/opts') |
|
def test_output_opts_file_pxe_ipv6_port_with_ipv6_opt(self, |
|
mock_get_conf_fn): |
|
expected = ( |
|
'tag:subnet-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option6:dns-server,[2001:0200:feed:7ac0::1]\n' |
|
'tag:subnet-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option6:domain-search,openstacklocal\n' |
|
'tag:port-hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh,' |
|
'option6:tftp-server,2001:192:168::1\n' |
|
'tag:port-hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh,' |
|
'option6:nd98,option-nondigit-98\n' |
|
'tag:port-hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh,' |
|
'option6:99,option-99\n' |
|
'tag:port-hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh,' |
|
'option6:bootfile-name,pxelinux.0') |
|
expected = expected.lstrip() |
|
|
|
dm = self._get_dnsmasq(FakeV6NetworkPxePort()) |
|
dm._output_opts_file() |
|
|
|
self.safe.assert_called_once_with('/foo/opts', expected) |
|
|
|
@mock.patch('neutron.agent.linux.dhcp.Dnsmasq.get_conf_file_name', |
|
return_value='/foo/opts') |
|
def test_output_opts_file_pxe_ipv6_port_with_ipv4_opt(self, |
|
mock_get_conf_fn): |
|
expected = ( |
|
'tag:subnet-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option6:dns-server,[2001:0200:feed:7ac0::1]\n' |
|
'tag:subnet-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option6:domain-search,openstacklocal\n' |
|
'tag:port-hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh,' |
|
'option6:bootfile-name,pxelinux.0') |
|
expected = expected.lstrip() |
|
|
|
dm = self._get_dnsmasq(FakeV6NetworkPxePortWrongOptVersion()) |
|
dm._output_opts_file() |
|
|
|
self.safe.assert_called_once_with('/foo/opts', expected) |
|
|
|
def test_output_opts_file_ipv6_address_mode_unset(self): |
|
fake_v6 = '2001:0200:feed:7ac0::1' |
|
expected = ( |
|
'tag:subnet-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option6:dns-server,%s\n' |
|
'tag:subnet-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option6:domain-search,openstacklocal').lstrip() % ( |
|
'[' + fake_v6 + ']') |
|
|
|
self._test_output_opts_file(expected, FakeV6Network()) |
|
|
|
def test_output_opts_file_ipv6_address_force_metadata(self): |
|
fake_v6 = '2001:0200:feed:7ac0::1' |
|
expected = ( |
|
'tag:subnet-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option6:dns-server,%s\n' |
|
'tag:subnet-ffffffff-ffff-ffff-ffff-ffffffffffff,' |
|
'option6:domain-search,openstacklocal').lstrip() % ( |
|
'[' + fake_v6 + ']') |
|
self.conf.force_metadata = True |
|
self._test_output_opts_file(expected, FakeV6Network()) |
|
|
|
def _test_no_dns_domain_alloc_data(self, tag=''): |
|
exp_host_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/host' |
|
exp_host_data = ('00:00:80:aa:bb:cc,host-192-168-0-2,' |
|
'192.168.0.2\n' |
|
'00:00:f3:aa:bb:cc,{tag}host-fdca-3ba5-a17a-4ba3--2,' |
|
'[fdca:3ba5:a17a:4ba3::2]\n' |
|
'00:00:0f:aa:bb:cc,host-192-168-0-3,' |
|
'192.168.0.3\n' |
|
'00:00:0f:aa:bb:cc,{tag}host-fdca-3ba5-a17a-4ba3--3,' |
|
'[fdca:3ba5:a17a:4ba3::3]\n' |
|
'00:00:0f:rr:rr:rr,host-192-168-0-1,' |
|
'192.168.0.1\n').format(tag=tag).lstrip() |
|
exp_addn_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/addn_hosts' |
|
exp_addn_data = ( |
|
'192.168.0.2\t' |
|
'host-192-168-0-2 host-192-168-0-2\n' |
|
'fdca:3ba5:a17a:4ba3::2\t' |
|
'host-fdca-3ba5-a17a-4ba3--2 ' |
|
'host-fdca-3ba5-a17a-4ba3--2\n' |
|
'192.168.0.3\thost-192-168-0-3 ' |
|
'host-192-168-0-3\n' |
|
'fdca:3ba5:a17a:4ba3::3\t' |
|
'host-fdca-3ba5-a17a-4ba3--3 ' |
|
'host-fdca-3ba5-a17a-4ba3--3\n' |
|
'192.168.0.1\t' |
|
'host-192-168-0-1 ' |
|
'host-192-168-0-1\n' |
|
).lstrip() |
|
return (exp_host_name, exp_host_data, |
|
exp_addn_name, exp_addn_data) |
|
|
|
def _test_reload_allocation_data(self, tag=''): |
|
exp_host_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/host' |
|
exp_host_data = ('00:00:80:aa:bb:cc,host-192-168-0-2.openstacklocal.,' |
|
'192.168.0.2\n' |
|
'00:00:f3:aa:bb:cc,{tag}host-fdca-3ba5-a17a-4ba3--2.' |
|
'openstacklocal.,[fdca:3ba5:a17a:4ba3::2]\n' |
|
'00:00:0f:aa:bb:cc,host-192-168-0-3.openstacklocal.,' |
|
'192.168.0.3\n' |
|
'00:00:0f:aa:bb:cc,{tag}host-fdca-3ba5-a17a-4ba3--3.' |
|
'openstacklocal.,[fdca:3ba5:a17a:4ba3::3]\n' |
|
'00:00:0f:rr:rr:rr,host-192-168-0-1.openstacklocal.,' |
|
'192.168.0.1\n').format(tag=tag).lstrip() |
|
exp_addn_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/addn_hosts' |
|
exp_addn_data = ( |
|
'192.168.0.2\t' |
|
'host-192-168-0-2.openstacklocal. host-192-168-0-2\n' |
|
'fdca:3ba5:a17a:4ba3::2\t' |
|
'host-fdca-3ba5-a17a-4ba3--2.openstacklocal. ' |
|
'host-fdca-3ba5-a17a-4ba3--2\n' |
|
'192.168.0.3\thost-192-168-0-3.openstacklocal. ' |
|
'host-192-168-0-3\n' |
|
'fdca:3ba5:a17a:4ba3::3\t' |
|
'host-fdca-3ba5-a17a-4ba3--3.openstacklocal. ' |
|
'host-fdca-3ba5-a17a-4ba3--3\n' |