Merge "Enable internal DNS resolution"
This commit is contained in:
commit
ccda317d36
25
README.md
25
README.md
@ -66,6 +66,31 @@ os-{admin,internal,public}-hostname(s) are set
|
||||
|
||||
This charm only support deployment with OpenStack Icehouse or better.
|
||||
|
||||
# Internal DNS for Cloud Guests
|
||||
|
||||
The charm supports enabling internal DNS resolution for cloud guests in
|
||||
accordance with the OpenStack DNS integration guide. To enable internal
|
||||
DNS resolution, the 'enable-ml2-dns' option must be set to True. When
|
||||
enabled, the domain name specified in the 'dns-domain' will be advertised
|
||||
as the nameserver search path by the dhcp agents.
|
||||
|
||||
The Nova compute service will leverage this functionality when enabled.
|
||||
When ports are allocated by the compute service, the dns_name of the port
|
||||
is populated with a DNS sanitized version of the instance's display name.
|
||||
The Neutron DHCP agents will then create host entries in the dnsmasq's
|
||||
configuration files matching the dns_name of the port to the IP address
|
||||
associated with the port.
|
||||
|
||||
Note that the DNS nameserver provided to the instance by the DHCP agent
|
||||
depends on the tenant's network setup. The Neutron DHCP agent only advertises
|
||||
itself as a nameserver when the Neutron subnet does not have nameservers
|
||||
configured. If additional nameservers are needed and internal DNS is desired,
|
||||
then the IP address of the DHCP port should be added to the subnet's
|
||||
list of configured nameservers.
|
||||
|
||||
For more information refer to the OpenStack documentation on
|
||||
[DNS Integration](https://docs.openstack.org/ocata/networking-guide/config-dns-int.html).
|
||||
|
||||
# Network Space support
|
||||
|
||||
This charm supports the use of Juju Network Spaces, allowing the charm
|
||||
|
16
config.yaml
16
config.yaml
@ -550,6 +550,13 @@ options:
|
||||
type: boolean
|
||||
default: False
|
||||
description: Enable port security extension for ML2 plugin (>= kilo).
|
||||
enable-ml2-dns:
|
||||
type: boolean
|
||||
default: False
|
||||
description: |
|
||||
Enables the Neutron DNS extension driver (>= mitaka). When enabled,
|
||||
ports attached to Nova instances will have DNS names assigned based
|
||||
on the instance name.
|
||||
haproxy-server-timeout:
|
||||
type: int
|
||||
default:
|
||||
@ -649,3 +656,12 @@ options:
|
||||
when using an overlay/tunnel protocol. This option allows
|
||||
specifying a physical network MTU value that differs from the
|
||||
default global-physnet-mtu value.
|
||||
dns-domain:
|
||||
type: string
|
||||
default: openstack.example.
|
||||
description: |
|
||||
Specifies the dns domain name that should be used for building instance
|
||||
hostnames. An empty option or the value of 'openstacklocal' will cause
|
||||
the dhcp agents to broadcast the default domain of openstacklocal and
|
||||
will not enable internal cloud dns resolution. This value should end
|
||||
with a '.', e.g. 'cloud.example.org.'.
|
||||
|
@ -13,6 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
import ast
|
||||
import re
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
@ -22,6 +23,8 @@ from charmhelpers.core.hookenv import (
|
||||
related_units,
|
||||
relation_get,
|
||||
log,
|
||||
DEBUG,
|
||||
ERROR,
|
||||
)
|
||||
from charmhelpers.contrib.openstack import context
|
||||
from charmhelpers.contrib.hahelpers.cluster import (
|
||||
@ -42,6 +45,16 @@ OVERLAY_NET_TYPES = [VXLAN, GRE]
|
||||
NON_OVERLAY_NET_TYPES = [VLAN, FLAT, LOCAL]
|
||||
TENANT_NET_TYPES = [VXLAN, GRE, VLAN, FLAT, LOCAL]
|
||||
|
||||
EXTENSION_DRIVER_PORT_SECURITY = 'port_security'
|
||||
EXTENSION_DRIVER_DNS = 'dns'
|
||||
|
||||
# Domain name validation regex which is used to certify that
|
||||
# the domain-name consists only of valid characters, is not
|
||||
# longer than 63 characters in length for any name segment,
|
||||
# and each segment does not begin or end with a hyphen.
|
||||
DOMAIN_NAME_REGEX = re.compile(r'^(?!-)[A-Z\d-]{1,63}(?<!-)$',
|
||||
re.IGNORECASE)
|
||||
|
||||
|
||||
def get_l2population():
|
||||
plugin = config('neutron-plugin')
|
||||
@ -121,6 +134,44 @@ def get_dvr():
|
||||
return False
|
||||
|
||||
|
||||
def get_dns_domain():
|
||||
if not config('enable-ml2-dns'):
|
||||
log('ML2 DNS Extensions are not enabled.', DEBUG)
|
||||
return ""
|
||||
|
||||
dns_domain = config('dns-domain')
|
||||
if not dns_domain:
|
||||
log('No dns-domain has been configured', DEBUG)
|
||||
return dns_domain
|
||||
|
||||
release = os_release('neutron-server')
|
||||
if CompareOpenStackReleases(release) < 'mitaka':
|
||||
log('Internal DNS resolution is not supported before Mitaka')
|
||||
return ""
|
||||
|
||||
# Strip any trailing . at the end
|
||||
if dns_domain[-1] == '.':
|
||||
dns_domain = dns_domain[:-1]
|
||||
|
||||
# Ensure that the dns name is only a valid name. Valid entries include
|
||||
# a-z, A-Z, 0-9, ., and -. No particular name may be longer than 63
|
||||
# characters, each part cannot begin/end with a -. Validate this here in
|
||||
# order to prevent other chaos which may prevent neutron services from
|
||||
# functioning properly.
|
||||
# Note: intentionally not validating the length of the domain name because
|
||||
# this is practically difficult to validate reasonably well.
|
||||
for level in dns_domain.split('.'):
|
||||
if not DOMAIN_NAME_REGEX.match(level):
|
||||
msg = "dns-domain '%s' is an invalid domain name." % dns_domain
|
||||
log(msg, ERROR)
|
||||
raise ValueError(msg)
|
||||
|
||||
# Make sure it ends with a .
|
||||
dns_domain += '.'
|
||||
|
||||
return dns_domain
|
||||
|
||||
|
||||
class ApacheSSLContext(context.ApacheSSLContext):
|
||||
|
||||
interfaces = ['https']
|
||||
@ -303,7 +354,17 @@ class NeutronCCContext(context.NeutronContext):
|
||||
if vni_ranges:
|
||||
ctxt['vni_ranges'] = ','.join(vni_ranges.split())
|
||||
|
||||
ctxt['enable_ml2_port_security'] = config('enable-ml2-port-security')
|
||||
extension_drivers = []
|
||||
if config('enable-ml2-port-security'):
|
||||
extension_drivers.append(EXTENSION_DRIVER_PORT_SECURITY)
|
||||
|
||||
dns_domain = get_dns_domain()
|
||||
if dns_domain:
|
||||
extension_drivers.append(EXTENSION_DRIVER_DNS)
|
||||
ctxt['dns_domain'] = dns_domain
|
||||
if extension_drivers:
|
||||
ctxt['extension_drivers'] = ','.join(extension_drivers)
|
||||
|
||||
ctxt['enable_sriov'] = config('enable-sriov')
|
||||
|
||||
if cmp_release == 'kilo' or cmp_release >= 'mitaka':
|
||||
|
@ -87,6 +87,7 @@ from neutron_api_utils import (
|
||||
setup_ipv6,
|
||||
)
|
||||
from neutron_api_context import (
|
||||
get_dns_domain,
|
||||
get_dvr,
|
||||
get_l3ha,
|
||||
get_l2population,
|
||||
@ -502,6 +503,10 @@ def neutron_plugin_api_relation_joined(rid=None):
|
||||
'region': config('region'),
|
||||
})
|
||||
|
||||
dns_domain = get_dns_domain()
|
||||
if dns_domain:
|
||||
relation_data['dns-domain'] = dns_domain
|
||||
|
||||
if is_api_ready(CONFIGS):
|
||||
relation_data['neutron-api-ready'] = "yes"
|
||||
else:
|
||||
|
@ -4,8 +4,8 @@
|
||||
# Configuration file maintained by Juju. Local changes may be overwritten.
|
||||
###############################################################################
|
||||
[ml2]
|
||||
{% if enable_ml2_port_security -%}
|
||||
extension_drivers=port_security
|
||||
{% if extension_drivers -%}
|
||||
extension_drivers={{ extension_drivers }}
|
||||
{% endif -%}
|
||||
|
||||
{% if neutron_plugin == 'Calico' -%}
|
||||
|
@ -4,8 +4,8 @@
|
||||
# Configuration file maintained by Juju. Local changes may be overwritten.
|
||||
###############################################################################
|
||||
[ml2]
|
||||
{% if enable_ml2_port_security -%}
|
||||
extension_drivers=port_security
|
||||
{% if extension_drivers -%}
|
||||
extension_drivers={{ extension_drivers }}
|
||||
{% endif -%}
|
||||
|
||||
{% if neutron_plugin == 'Calico' -%}
|
||||
|
@ -18,6 +18,10 @@ rpc_workers = {{ workers }}
|
||||
|
||||
router_distributed = {{ enable_dvr }}
|
||||
|
||||
{% if dns_domain -%}
|
||||
dns_domain = {{ dns_domain }}
|
||||
{% endif -%}
|
||||
|
||||
l3_ha = {{ l3_ha }}
|
||||
{% if l3_ha -%}
|
||||
max_l3_agents_per_router = {{ max_l3_agents_per_router }}
|
||||
|
@ -18,6 +18,10 @@ rpc_workers = {{ workers }}
|
||||
|
||||
router_distributed = {{ enable_dvr }}
|
||||
|
||||
{% if dns_domain -%}
|
||||
dns_domain = {{ dns_domain }}
|
||||
{% endif -%}
|
||||
|
||||
l3_ha = {{ l3_ha }}
|
||||
{% if l3_ha -%}
|
||||
max_l3_agents_per_router = {{ max_l3_agents_per_router }}
|
||||
|
@ -203,6 +203,27 @@ class GeneralTests(CharmTestCase):
|
||||
self.os_release.return_value = 'juno'
|
||||
self.assertEquals(context.get_dvr(), False)
|
||||
|
||||
def test_get_dns_domain(self):
|
||||
self.test_config.set('dns-domain', 'example.org.')
|
||||
self.test_config.set('enable-ml2-dns', True)
|
||||
self.os_release.return_value = 'mitaka'
|
||||
self.assertEquals(context.get_dns_domain(), 'example.org.')
|
||||
|
||||
def test_get_dns_domain_bad_values(self):
|
||||
self.os_release.return_value = 'mitaka'
|
||||
self.test_config.set('enable-ml2-dns', True)
|
||||
bad_values = ['example@foo.org',
|
||||
'exclamation!marks.notwelcom.ed',
|
||||
'%s.way.too.long' % ('x' * 64),
|
||||
'-hyphen.in.front',
|
||||
'hypen-.in.back',
|
||||
'no_.under_scor.es',
|
||||
]
|
||||
|
||||
for value in bad_values:
|
||||
self.test_config.set('dns-domain', value)
|
||||
self.assertRaises(ValueError, context.get_dns_domain)
|
||||
|
||||
|
||||
class IdentityServiceContext(CharmTestCase):
|
||||
|
||||
@ -385,7 +406,7 @@ class NeutronCCContextTest(CharmTestCase):
|
||||
'quota_vip': 10,
|
||||
'vlan_ranges': 'physnet1:1000:2000',
|
||||
'vni_ranges': '1001:2000',
|
||||
'enable_ml2_port_security': True,
|
||||
'extension_drivers': 'port_security',
|
||||
'enable_hyperv': False
|
||||
}
|
||||
napi_ctxt = context.NeutronCCContext()
|
||||
@ -393,6 +414,50 @@ class NeutronCCContextTest(CharmTestCase):
|
||||
with patch.object(napi_ctxt, '_ensure_packages'):
|
||||
self.assertEquals(ctxt_data, napi_ctxt())
|
||||
|
||||
@patch.object(context.NeutronCCContext, 'network_manager')
|
||||
@patch.object(context.NeutronCCContext, 'plugin')
|
||||
def test_neutroncc_context_dns_setting(self, plugin, nm):
|
||||
plugin.return_value = None
|
||||
self.test_config.set('enable-ml2-dns', True)
|
||||
self.test_config.set('dns-domain', 'example.org.')
|
||||
self.os_release.return_value = 'mitaka'
|
||||
napi_ctxt = context.NeutronCCContext()
|
||||
with patch.object(napi_ctxt, '_ensure_packages'):
|
||||
ctxt = napi_ctxt()
|
||||
self.assertEqual('example.org.', ctxt['dns_domain'])
|
||||
self.assertEqual('port_security,dns', ctxt['extension_drivers'])
|
||||
|
||||
@patch.object(context.NeutronCCContext, 'network_manager')
|
||||
@patch.object(context.NeutronCCContext, 'plugin')
|
||||
def test_neutroncc_context_dns_no_port_security_setting(self,
|
||||
plugin, nm):
|
||||
"""Verify extension drivers without port security."""
|
||||
plugin.return_value = None
|
||||
self.test_config.set('enable-ml2-port-security', False)
|
||||
self.test_config.set('enable-ml2-dns', True)
|
||||
self.test_config.set('dns-domain', 'example.org.')
|
||||
self.os_release.return_value = 'mitaka'
|
||||
napi_ctxt = context.NeutronCCContext()
|
||||
with patch.object(napi_ctxt, '_ensure_packages'):
|
||||
ctxt = napi_ctxt()
|
||||
self.assertEquals('example.org.', ctxt['dns_domain'])
|
||||
self.assertEquals('dns', ctxt['extension_drivers'])
|
||||
|
||||
@patch.object(context.NeutronCCContext, 'network_manager')
|
||||
@patch.object(context.NeutronCCContext, 'plugin')
|
||||
def test_neutroncc_context_dns_kilo(self, plugin, nm):
|
||||
"""Verify dns extension and domain are not specified in kilo."""
|
||||
plugin.return_value = None
|
||||
self.test_config.set('enable-ml2-port-security', False)
|
||||
self.test_config.set('enable-ml2-dns', True)
|
||||
self.test_config.set('dns-domain', 'example.org.')
|
||||
self.os_release.return_value = 'kilo'
|
||||
napi_ctxt = context.NeutronCCContext()
|
||||
with patch.object(napi_ctxt, '_ensure_packages'):
|
||||
ctxt = napi_ctxt()
|
||||
self.assertFalse('dns_domain' in ctxt)
|
||||
self.assertFalse('extension_drivers' in ctxt)
|
||||
|
||||
@patch.object(context.NeutronCCContext, 'network_manager')
|
||||
@patch.object(context.NeutronCCContext, 'plugin')
|
||||
@patch('__builtin__.__import__')
|
||||
@ -427,7 +492,7 @@ class NeutronCCContextTest(CharmTestCase):
|
||||
'vlan_ranges': 'physnet1:1000:2000',
|
||||
'vni_ranges': '1001:2000,3001:4000',
|
||||
'network_providers': 'physnet2,physnet3',
|
||||
'enable_ml2_port_security': True,
|
||||
'extension_drivers': 'port_security',
|
||||
'enable_hyperv': False
|
||||
}
|
||||
napi_ctxt = context.NeutronCCContext()
|
||||
@ -472,7 +537,7 @@ class NeutronCCContextTest(CharmTestCase):
|
||||
'quota_vip': 10,
|
||||
'vlan_ranges': 'physnet1:1000:2000',
|
||||
'vni_ranges': '1001:2000',
|
||||
'enable_ml2_port_security': True,
|
||||
'extension_drivers': 'port_security',
|
||||
'enable_hyperv': False
|
||||
}
|
||||
napi_ctxt = context.NeutronCCContext()
|
||||
@ -524,7 +589,7 @@ class NeutronCCContextTest(CharmTestCase):
|
||||
'quota_vip': 10,
|
||||
'vlan_ranges': 'physnet1:1000:2000',
|
||||
'vni_ranges': '1001:2000',
|
||||
'enable_ml2_port_security': True,
|
||||
'extension_drivers': 'port_security',
|
||||
'enable_hyperv': False
|
||||
}
|
||||
napi_ctxt = context.NeutronCCContext()
|
||||
|
@ -60,6 +60,7 @@ TO_PATCH = [
|
||||
'l3ha_router_present',
|
||||
'execd_preinstall',
|
||||
'filter_installed_packages',
|
||||
'get_dns_domain',
|
||||
'get_dvr',
|
||||
'get_l3ha',
|
||||
'get_l2population',
|
||||
@ -538,6 +539,7 @@ class NeutronAPIHooksTests(CharmTestCase):
|
||||
port = 1234
|
||||
_canonical_url.return_value = host
|
||||
self.api_port.return_value = port
|
||||
self.get_dns_domain.return_value = ""
|
||||
self.is_relation_made = True
|
||||
neutron_url = '%s:%s' % (host, port)
|
||||
_relation_data = {
|
||||
@ -620,6 +622,7 @@ class NeutronAPIHooksTests(CharmTestCase):
|
||||
self.get_l3ha.return_value = False
|
||||
self.get_l2population.return_value = False
|
||||
self.get_overlay_network_type.return_value = 'vxlan'
|
||||
self.get_dns_domain.return_value = ''
|
||||
self._call_hook('neutron-plugin-api-relation-joined')
|
||||
self.relation_set.assert_called_with(
|
||||
relation_id=None,
|
||||
@ -653,6 +656,7 @@ class NeutronAPIHooksTests(CharmTestCase):
|
||||
self.get_l3ha.return_value = False
|
||||
self.get_l2population.return_value = True
|
||||
self.get_overlay_network_type.return_value = 'vxlan'
|
||||
self.get_dns_domain.return_value = ''
|
||||
self._call_hook('neutron-plugin-api-relation-joined')
|
||||
self.relation_set.assert_called_with(
|
||||
relation_id=None,
|
||||
@ -686,6 +690,7 @@ class NeutronAPIHooksTests(CharmTestCase):
|
||||
self.get_l3ha.return_value = True
|
||||
self.get_l2population.return_value = False
|
||||
self.get_overlay_network_type.return_value = 'vxlan'
|
||||
self.get_dns_domain.return_value = ''
|
||||
self._call_hook('neutron-plugin-api-relation-joined')
|
||||
self.relation_set.assert_called_with(
|
||||
relation_id=None,
|
||||
@ -721,6 +726,42 @@ class NeutronAPIHooksTests(CharmTestCase):
|
||||
self.get_l3ha.return_value = True
|
||||
self.get_l2population.return_value = False
|
||||
self.get_overlay_network_type.return_value = 'vxlan'
|
||||
self.get_dns_domain.return_value = ''
|
||||
self._call_hook('neutron-plugin-api-relation-joined')
|
||||
self.relation_set.assert_called_with(
|
||||
relation_id=None,
|
||||
**_relation_data
|
||||
)
|
||||
|
||||
def test_neutron_plugin_api_relation_joined_dns(self):
|
||||
self.unit_get.return_value = '172.18.18.18'
|
||||
self.IdentityServiceContext.return_value = \
|
||||
DummyContext(return_value={})
|
||||
_relation_data = {
|
||||
'neutron-security-groups': False,
|
||||
'enable-dvr': False,
|
||||
'enable-l3ha': False,
|
||||
'addr': '172.18.18.18',
|
||||
'l2-population': False,
|
||||
'overlay-network-type': 'vxlan',
|
||||
'service_protocol': None,
|
||||
'auth_protocol': None,
|
||||
'service_tenant': None,
|
||||
'service_port': None,
|
||||
'region': 'RegionOne',
|
||||
'service_password': None,
|
||||
'auth_port': None,
|
||||
'auth_host': None,
|
||||
'service_username': None,
|
||||
'service_host': None,
|
||||
'neutron-api-ready': 'no',
|
||||
'dns-domain': 'openstack.example.'
|
||||
}
|
||||
self.get_dvr.return_value = False
|
||||
self.get_l3ha.return_value = False
|
||||
self.get_l2population.return_value = False
|
||||
self.get_overlay_network_type.return_value = 'vxlan'
|
||||
self.get_dns_domain.return_value = 'openstack.example.'
|
||||
self._call_hook('neutron-plugin-api-relation-joined')
|
||||
self.relation_set.assert_called_with(
|
||||
relation_id=None,
|
||||
|
Loading…
Reference in New Issue
Block a user