2b2c4b745f
Adds a dns-servers config option for specifying the forwarding dns servers to be used by the dnsmasq services on the neutron dhcp agent. This enables services using internal dns to also specify the forwarding dns servers in order to resolve hosts outside of the neutron network space. Change-Id: I3cd608b1491a45f565d5147894b8285e638eeaa7 Implements: blueprint internal-dns Closes-Bug: #1713721
381 lines
16 KiB
Python
381 lines
16 KiB
Python
from mock import (
|
|
Mock,
|
|
MagicMock,
|
|
patch
|
|
)
|
|
import neutron_contexts
|
|
import sys
|
|
from contextlib import contextmanager
|
|
|
|
from test_utils import (
|
|
CharmTestCase
|
|
)
|
|
|
|
TO_PATCH = [
|
|
'apt_install',
|
|
'config',
|
|
'eligible_leader',
|
|
'unit_get',
|
|
'network_get_primary_address',
|
|
]
|
|
|
|
|
|
@contextmanager
|
|
def patch_open():
|
|
'''Patch open() to allow mocking both open() itself and the file that is
|
|
yielded.
|
|
|
|
Yields the mock for "open" and "file", respectively.'''
|
|
mock_open = MagicMock(spec=open)
|
|
mock_file = MagicMock(spec=file)
|
|
|
|
@contextmanager
|
|
def stub_open(*args, **kwargs):
|
|
mock_open(*args, **kwargs)
|
|
yield mock_file
|
|
|
|
with patch('__builtin__.open', stub_open):
|
|
yield mock_open, mock_file
|
|
|
|
|
|
class DummyNeutronAPIContext():
|
|
|
|
def __init__(self, return_value):
|
|
self.return_value = return_value
|
|
|
|
def __call__(self):
|
|
return self.return_value
|
|
|
|
|
|
class TestL3AgentContext(CharmTestCase):
|
|
|
|
def setUp(self):
|
|
super(TestL3AgentContext, self).setUp(neutron_contexts,
|
|
TO_PATCH)
|
|
self.network_get_primary_address.side_effect = NotImplementedError
|
|
self.config.side_effect = self.test_config.get
|
|
|
|
@patch('neutron_contexts.NeutronAPIContext')
|
|
def test_new_ext_network(self, _NeutronAPIContext):
|
|
_NeutronAPIContext.return_value = \
|
|
DummyNeutronAPIContext(return_value={'enable_dvr': False,
|
|
'report_interval': 30,
|
|
'rpc_response_timeout': 60,
|
|
})
|
|
self.test_config.set('run-internal-router', 'none')
|
|
self.test_config.set('external-network-id', '')
|
|
self.eligible_leader.return_value = False
|
|
self.assertEquals(neutron_contexts.L3AgentContext()(),
|
|
{'agent_mode': 'legacy',
|
|
'report_interval': 30,
|
|
'rpc_response_timeout': 60,
|
|
'external_configuration_new': True,
|
|
'handle_internal_only_router': False,
|
|
'plugin': 'ovs'})
|
|
|
|
@patch('neutron_contexts.NeutronAPIContext')
|
|
def test_old_ext_network(self, _NeutronAPIContext):
|
|
_NeutronAPIContext.return_value = \
|
|
DummyNeutronAPIContext(return_value={'enable_dvr': False,
|
|
'report_interval': 30,
|
|
'rpc_response_timeout': 60,
|
|
})
|
|
self.test_config.set('run-internal-router', 'none')
|
|
self.test_config.set('ext-port', 'eth1')
|
|
self.eligible_leader.return_value = False
|
|
self.assertEquals(neutron_contexts.L3AgentContext()(),
|
|
{'agent_mode': 'legacy',
|
|
'report_interval': 30,
|
|
'rpc_response_timeout': 60,
|
|
'handle_internal_only_router': False,
|
|
'plugin': 'ovs'})
|
|
|
|
@patch('neutron_contexts.NeutronAPIContext')
|
|
def test_hior_leader(self, _NeutronAPIContext):
|
|
_NeutronAPIContext.return_value = \
|
|
DummyNeutronAPIContext(return_value={'enable_dvr': False,
|
|
'report_interval': 30,
|
|
'rpc_response_timeout': 60,
|
|
})
|
|
self.test_config.set('run-internal-router', 'leader')
|
|
self.test_config.set('external-network-id', 'netid')
|
|
self.eligible_leader.return_value = True
|
|
self.assertEquals(neutron_contexts.L3AgentContext()(),
|
|
{'agent_mode': 'legacy',
|
|
'report_interval': 30,
|
|
'rpc_response_timeout': 60,
|
|
'handle_internal_only_router': True,
|
|
'ext_net_id': 'netid',
|
|
'plugin': 'ovs'})
|
|
|
|
@patch('neutron_contexts.NeutronAPIContext')
|
|
def test_hior_all(self, _NeutronAPIContext):
|
|
_NeutronAPIContext.return_value = \
|
|
DummyNeutronAPIContext(return_value={'enable_dvr': False,
|
|
'report_interval': 30,
|
|
'rpc_response_timeout': 60,
|
|
})
|
|
self.test_config.set('run-internal-router', 'all')
|
|
self.test_config.set('external-network-id', 'netid')
|
|
self.eligible_leader.return_value = True
|
|
self.assertEquals(neutron_contexts.L3AgentContext()(),
|
|
{'agent_mode': 'legacy',
|
|
'report_interval': 30,
|
|
'rpc_response_timeout': 60,
|
|
'handle_internal_only_router': True,
|
|
'ext_net_id': 'netid',
|
|
'plugin': 'ovs'})
|
|
|
|
@patch('neutron_contexts.NeutronAPIContext')
|
|
def test_dvr(self, _NeutronAPIContext):
|
|
_NeutronAPIContext.return_value = \
|
|
DummyNeutronAPIContext(return_value={'enable_dvr': True,
|
|
'report_interval': 30,
|
|
'rpc_response_timeout': 60,
|
|
})
|
|
self.assertEquals(neutron_contexts.L3AgentContext()()['agent_mode'],
|
|
'dvr_snat')
|
|
|
|
|
|
class TestNeutronGatewayContext(CharmTestCase):
|
|
|
|
def setUp(self):
|
|
super(TestNeutronGatewayContext, self).setUp(neutron_contexts,
|
|
TO_PATCH)
|
|
self.config.side_effect = self.test_config.get
|
|
self.maxDiff = None
|
|
|
|
@patch('charmhelpers.contrib.openstack.context.relation_get')
|
|
@patch('charmhelpers.contrib.openstack.context.related_units')
|
|
@patch('charmhelpers.contrib.openstack.context.relation_ids')
|
|
@patch.object(neutron_contexts, 'get_shared_secret')
|
|
def test_all(self, _secret, _rids, _runits, _rget):
|
|
rdata = {'l2-population': 'True',
|
|
'enable-dvr': 'True',
|
|
'overlay-network-type': 'gre',
|
|
'enable-l3ha': 'True',
|
|
'network-device-mtu': 9000,
|
|
'dns-domain': 'openstack.example.'}
|
|
self.test_config.set('plugin', 'ovs')
|
|
self.test_config.set('debug', False)
|
|
self.test_config.set('verbose', True)
|
|
self.test_config.set('instance-mtu', 1420)
|
|
self.test_config.set('dnsmasq-flags', 'dhcp-userclass=set:ipxe,iPXE,'
|
|
'dhcp-match=set:ipxe,175')
|
|
self.test_config.set('dns-servers', '8.8.8.8,4.4.4.4')
|
|
self.test_config.set('vlan-ranges',
|
|
'physnet1:1000:2000 physnet2:2001:3000')
|
|
self.test_config.set('flat-network-providers', 'physnet3 physnet4')
|
|
self.network_get_primary_address.side_effect = NotImplementedError
|
|
self.unit_get.return_value = '10.5.0.1'
|
|
# Provided by neutron-api relation
|
|
_rids.return_value = ['neutron-plugin-api:0']
|
|
_runits.return_value = ['neutron-api/0']
|
|
_rget.side_effect = lambda *args, **kwargs: rdata
|
|
_secret.return_value = 'testsecret'
|
|
ctxt = neutron_contexts.NeutronGatewayContext()()
|
|
self.assertEquals(ctxt, {
|
|
'shared_secret': 'testsecret',
|
|
'enable_dvr': True,
|
|
'enable_l3ha': True,
|
|
'dns_servers': '8.8.8.8,4.4.4.4',
|
|
'dns_domain': 'openstack.example.',
|
|
'local_ip': '10.5.0.1',
|
|
'instance_mtu': 1420,
|
|
'core_plugin': "ml2",
|
|
'plugin': 'ovs',
|
|
'debug': False,
|
|
'verbose': True,
|
|
'l2_population': True,
|
|
'overlay_network_type': 'gre',
|
|
'report_interval': 30,
|
|
'rpc_response_timeout': 60,
|
|
'bridge_mappings': 'physnet1:br-data',
|
|
'network_providers': 'physnet3,physnet4',
|
|
'vlan_ranges': 'physnet1:1000:2000,physnet2:2001:3000',
|
|
'network_device_mtu': 9000,
|
|
'veth_mtu': 9000,
|
|
'enable_isolated_metadata': False,
|
|
'enable_metadata_network': False,
|
|
'dnsmasq_flags': {
|
|
'dhcp-userclass': 'set:ipxe,iPXE',
|
|
'dhcp-match': 'set:ipxe,175'
|
|
}
|
|
})
|
|
|
|
@patch('charmhelpers.contrib.openstack.context.relation_get')
|
|
@patch('charmhelpers.contrib.openstack.context.related_units')
|
|
@patch('charmhelpers.contrib.openstack.context.relation_ids')
|
|
@patch.object(neutron_contexts, 'get_shared_secret')
|
|
def test_all_network_spaces(self, _secret, _rids, _runits, _rget):
|
|
rdata = {'l2-population': 'True',
|
|
'enable-dvr': 'True',
|
|
'overlay-network-type': 'gre',
|
|
'enable-l3ha': 'True',
|
|
'network-device-mtu': 9000,
|
|
'dns-domain': 'openstack.example.'}
|
|
self.test_config.set('plugin', 'ovs')
|
|
self.test_config.set('debug', False)
|
|
self.test_config.set('verbose', True)
|
|
self.test_config.set('instance-mtu', 1420)
|
|
self.test_config.set('dnsmasq-flags', 'dhcp-userclass=set:ipxe,iPXE,'
|
|
'dhcp-match=set:ipxe,175')
|
|
self.test_config.set('vlan-ranges',
|
|
'physnet1:1000:2000 physnet2:2001:3000')
|
|
self.test_config.set('flat-network-providers', 'physnet3 physnet4')
|
|
self.network_get_primary_address.return_value = '192.168.20.2'
|
|
self.unit_get.return_value = '10.5.0.1'
|
|
# Provided by neutron-api relation
|
|
_rids.return_value = ['neutron-plugin-api:0']
|
|
_runits.return_value = ['neutron-api/0']
|
|
_rget.side_effect = lambda *args, **kwargs: rdata
|
|
_secret.return_value = 'testsecret'
|
|
ctxt = neutron_contexts.NeutronGatewayContext()()
|
|
self.assertEquals(ctxt, {
|
|
'shared_secret': 'testsecret',
|
|
'enable_dvr': True,
|
|
'enable_l3ha': True,
|
|
'dns_servers': None,
|
|
'dns_domain': 'openstack.example.',
|
|
'local_ip': '192.168.20.2',
|
|
'instance_mtu': 1420,
|
|
'core_plugin': "ml2",
|
|
'plugin': 'ovs',
|
|
'debug': False,
|
|
'verbose': True,
|
|
'l2_population': True,
|
|
'overlay_network_type': 'gre',
|
|
'report_interval': 30,
|
|
'rpc_response_timeout': 60,
|
|
'bridge_mappings': 'physnet1:br-data',
|
|
'network_providers': 'physnet3,physnet4',
|
|
'vlan_ranges': 'physnet1:1000:2000,physnet2:2001:3000',
|
|
'network_device_mtu': 9000,
|
|
'veth_mtu': 9000,
|
|
'enable_isolated_metadata': False,
|
|
'enable_metadata_network': False,
|
|
'dnsmasq_flags': {
|
|
'dhcp-userclass': 'set:ipxe,iPXE',
|
|
'dhcp-match': 'set:ipxe,175'
|
|
}
|
|
})
|
|
|
|
@patch('charmhelpers.contrib.openstack.context.relation_get')
|
|
@patch('charmhelpers.contrib.openstack.context.related_units')
|
|
@patch('charmhelpers.contrib.openstack.context.relation_ids')
|
|
@patch.object(neutron_contexts, 'get_shared_secret')
|
|
def test_dhcp_settings(self, _secret, _rids, _runits, _rget):
|
|
self.test_config.set('enable-isolated-metadata', True)
|
|
self.test_config.set('enable-metadata-network', True)
|
|
self.network_get_primary_address.return_value = '192.168.20.2'
|
|
self.unit_get.return_value = '10.5.0.1'
|
|
ctxt = neutron_contexts.NeutronGatewayContext()()
|
|
self.assertTrue(ctxt['enable_isolated_metadata'])
|
|
self.assertTrue(ctxt['enable_metadata_network'])
|
|
|
|
@patch('charmhelpers.contrib.openstack.context.relation_get')
|
|
@patch('charmhelpers.contrib.openstack.context.related_units')
|
|
@patch('charmhelpers.contrib.openstack.context.relation_ids')
|
|
@patch.object(neutron_contexts, 'get_shared_secret')
|
|
def test_dhcp_setting_plug_override(self, _secret, _rids, _runits, _rget):
|
|
self.test_config.set('plugin', 'nsx')
|
|
self.test_config.set('enable-isolated-metadata', False)
|
|
self.test_config.set('enable-metadata-network', False)
|
|
self.network_get_primary_address.return_value = '192.168.20.2'
|
|
self.unit_get.return_value = '10.5.0.1'
|
|
ctxt = neutron_contexts.NeutronGatewayContext()()
|
|
self.assertTrue(ctxt['enable_isolated_metadata'])
|
|
self.assertTrue(ctxt['enable_metadata_network'])
|
|
|
|
|
|
class TestSharedSecret(CharmTestCase):
|
|
|
|
def setUp(self):
|
|
super(TestSharedSecret, self).setUp(neutron_contexts,
|
|
TO_PATCH)
|
|
self.config.side_effect = self.test_config.get
|
|
self.network_get_primary_address.side_effect = NotImplementedError
|
|
|
|
@patch('os.path')
|
|
@patch('uuid.uuid4')
|
|
def test_secret_created_stored(self, _uuid4, _path):
|
|
_path.exists.return_value = False
|
|
_uuid4.return_value = 'secret_thing'
|
|
with patch_open() as (_open, _file):
|
|
self.assertEquals(neutron_contexts.get_shared_secret(),
|
|
'secret_thing')
|
|
_open.assert_called_with(
|
|
neutron_contexts.SHARED_SECRET.format('neutron'), 'w')
|
|
_file.write.assert_called_with('secret_thing')
|
|
|
|
@patch('os.path')
|
|
def test_secret_retrieved(self, _path):
|
|
_path.exists.return_value = True
|
|
with patch_open() as (_open, _file):
|
|
_file.read.return_value = 'secret_thing\n'
|
|
self.assertEquals(neutron_contexts.get_shared_secret(),
|
|
'secret_thing')
|
|
_open.assert_called_with(
|
|
neutron_contexts.SHARED_SECRET.format('neutron'), 'r')
|
|
|
|
|
|
class TestHostIP(CharmTestCase):
|
|
|
|
def setUp(self):
|
|
super(TestHostIP, self).setUp(neutron_contexts,
|
|
TO_PATCH)
|
|
self.config.side_effect = self.test_config.get
|
|
self.network_get_primary_address.side_effect = NotImplementedError
|
|
# Save and inject
|
|
self.mods = {'dns': None, 'dns.resolver': None}
|
|
for mod in self.mods:
|
|
if mod not in sys.modules:
|
|
sys.modules[mod] = Mock()
|
|
else:
|
|
del self.mods[mod]
|
|
|
|
def tearDown(self):
|
|
super(TestHostIP, self).tearDown()
|
|
# Cleanup
|
|
for mod in self.mods.keys():
|
|
del sys.modules[mod]
|
|
|
|
def test_get_host_ip_already_ip(self):
|
|
self.assertEquals(neutron_contexts.get_host_ip('10.5.0.1'),
|
|
'10.5.0.1')
|
|
|
|
def test_get_host_ip_noarg(self):
|
|
self.unit_get.return_value = "10.5.0.1"
|
|
self.assertEquals(neutron_contexts.get_host_ip(),
|
|
'10.5.0.1')
|
|
|
|
@patch('dns.resolver.query')
|
|
def test_get_host_ip_hostname_unresolvable(self, _query):
|
|
class NXDOMAIN(Exception):
|
|
pass
|
|
_query.side_effect = NXDOMAIN()
|
|
self.assertRaises(NXDOMAIN, neutron_contexts.get_host_ip,
|
|
'missing.example.com')
|
|
|
|
@patch('dns.resolver.query')
|
|
def test_get_host_ip_hostname_resolvable(self, _query):
|
|
data = MagicMock()
|
|
data.address = '10.5.0.1'
|
|
_query.return_value = [data]
|
|
self.assertEquals(neutron_contexts.get_host_ip('myhost.example.com'),
|
|
'10.5.0.1')
|
|
_query.assert_called_with('myhost.example.com', 'A')
|
|
|
|
|
|
class TestMisc(CharmTestCase):
|
|
|
|
def setUp(self):
|
|
super(TestMisc,
|
|
self).setUp(neutron_contexts,
|
|
TO_PATCH)
|
|
|
|
def test_core_plugin_ml2(self):
|
|
self.config.return_value = 'ovs'
|
|
self.assertEquals(neutron_contexts.core_plugin(),
|
|
neutron_contexts.NEUTRON_ML2_PLUGIN)
|