Add function for adding DNS HA resources

Add ability for charms to request DNS entries to be managed by
hacluster via the hacluster interface.

Change-Id: Id51f7553c806a62bbcb49c114fe48c6471642c10
Partial-Bug: #1727376
Depends-On: I1a6cdeffa3aa8657b957ba68cd09face27f93b27
This commit is contained in:
Liam Young 2017-10-26 10:15:34 +01:00
parent 575d729487
commit cc03973a35
3 changed files with 127 additions and 1 deletions

View File

@ -2,12 +2,14 @@ import base64
import contextlib
import os
import random
import re
import shutil
import string
import subprocess
import charmhelpers.contrib.network.ip as ch_ip
import charmhelpers.contrib.openstack.utils as os_utils
import charmhelpers.contrib.openstack.ha as os_ha
import charmhelpers.core.hookenv as hookenv
import charmhelpers.core.host as ch_host
import charmhelpers.fetch as fetch
@ -29,6 +31,7 @@ import charms_openstack.ip as os_ip
VIP_KEY = "vip"
CIDR_KEY = "vip_cidr"
IFACE_KEY = "vip_iface"
DNSHA_KEY = "dns-ha"
APACHE_SSL_VHOST = '/etc/apache2/sites-available/openstack_https_frontend.conf'
SYSTEM_CA_CERTS = '/etc/ssl/certs/ca-certificates.crt'
SNAP_CA_CERTS = '/var/snap/{}/common/etc/ssl/certs/ca-certificates.crt'
@ -421,6 +424,7 @@ class HAOpenStackCharm(OpenStackAPICharm):
RESOURCE_TYPES = {
'vips': self._add_ha_vips_config,
'haproxy': self._add_ha_haproxy_config,
'dnsha': self._add_dnsha_config,
}
if self.ha_resources:
for res_type in self.ha_resources:
@ -432,7 +436,9 @@ class HAOpenStackCharm(OpenStackAPICharm):
@param hacluster instance of interface class HAClusterRequires
"""
for vip in self.config.get(VIP_KEY, '').split():
if not self.config.get(VIP_KEY):
return
for vip in self.config[VIP_KEY].split():
iface = (ch_ip.get_iface_for_address(vip) or
self.config.get(IFACE_KEY))
netmask = (ch_ip.get_netmask_for_address(vip) or
@ -447,6 +453,41 @@ class HAOpenStackCharm(OpenStackAPICharm):
"""
hacluster.add_init_service(self.name, 'haproxy')
def _add_dnsha_config(self, hacluster):
"""Add a DNSHA object to self.resources
@param hacluster instance of interface class HAClusterRequires
"""
if not self.config.get(DNSHA_KEY):
return
settings = ['os-admin-hostname', 'os-internal-hostname',
'os-public-hostname', 'os-access-hostname']
for setting in settings:
hostname = self.config.get(setting)
if hostname is None:
hookenv.log(
'DNS HA: Hostname setting {} is None. Ignoring.'.format(
setting),
hookenv.DEBUG)
continue
m = re.search('os-(.+?)-hostname', setting)
if m:
endpoint_type = m.group(1)
# resolve_address's ADDRESS_MAP uses 'int' not 'internal'
if endpoint_type == 'internal':
endpoint_type = 'int'
else:
msg = (
'Unexpected DNS hostname setting: {}. Cannot determine '
'endpoint_type name'.format(setting))
hookenv.status_set('blocked', msg)
raise os_ha.DNSHAException(msg)
ip = os_ip.resolve_address(
endpoint_type=endpoint_type,
override=False)
hacluster.add_dnsha(self.name, ip, hostname, endpoint_type)
def set_haproxy_stat_password(self):
"""Set a stats password for accessing haproxy statistics"""
if not self.get_state('haproxy.stat.password'):

View File

@ -28,6 +28,8 @@ sys.modules['charmhelpers.core.templating'] = charmhelpers.core.templating
sys.modules['charmhelpers.core.unitdata'] = charmhelpers.core.unitdata
sys.modules['charmhelpers.contrib'] = charmhelpers.contrib
sys.modules['charmhelpers.contrib.openstack'] = charmhelpers.contrib.openstack
sys.modules['charmhelpers.contrib.openstack.ha'] = (
charmhelpers.contrib.openstack.ha)
sys.modules['charmhelpers.contrib.openstack.utils'] = (
charmhelpers.contrib.openstack.utils)
sys.modules['charmhelpers.contrib.openstack.templating'] = (

View File

@ -479,6 +479,13 @@ class TestHAOpenStackCharm(BaseOpenStackCharmTest):
mock.call('myservice', 'vip2', 'user_iface', 'user_cidr')]
interface_mock.add_vip.assert_has_calls(calls)
def test__add_ha_vips_config_novip(self):
config = {'vip': None}
self.patch_target('config', new=config)
interface_mock = mock.Mock()
self.target._add_ha_vips_config(interface_mock)
self.assertFalse(interface_mock.add_vip.called)
def test__add_ha_haproxy_config(self):
self.patch_target('name', new='myservice')
interface_mock = mock.Mock()
@ -487,6 +494,82 @@ class TestHAOpenStackCharm(BaseOpenStackCharmTest):
'myservice',
'haproxy')
def test__add_dnsha_config_single_dns_entry(self):
config = {
'dns-ha': True,
'os-admin-hostname': 'myservice-admin.maas'}
self.patch_target('config', new=config)
self.patch_target('name', new='myservice')
self.patch_object(chm.os_ip, 'resolve_address', '10.0.0.10')
interface_mock = mock.Mock()
self.target._add_dnsha_config(interface_mock)
interface_mock.add_dnsha.assert_called_once_with(
'myservice',
'10.0.0.10',
'myservice-admin.maas',
'admin')
def test__add_dnsha_config_multi_dns_entries(self):
config = {
'dns-ha': True,
'os-public-hostname': 'myservice-public.maas',
'os-admin-hostname': 'myservice-admin.maas'}
addr = {
'public': '10.10.0.10',
'admin': '10.0.0.10'}
self.patch_target('config', new=config)
self.patch_target('name', new='myservice')
self.patch_object(
chm.os_ip,
'resolve_address',
new=lambda endpoint_type, override=False: addr[endpoint_type])
interface_mock = mock.Mock()
self.target._add_dnsha_config(interface_mock)
calls = [
mock.call(
'myservice',
'10.0.0.10',
'myservice-admin.maas',
'admin'),
mock.call(
'myservice',
'10.10.0.10',
'myservice-public.maas',
'public')]
interface_mock.add_dnsha.assert_has_calls(calls)
def test__add_dnsha_config_single_internal_dns_entry(self):
config = {
'dns-ha': True,
'os-internal-hostname': 'myservice-internal.maas'}
self.patch_target('config', new=config)
self.patch_target('name', new='myservice')
self.patch_object(chm.os_ip, 'resolve_address', '10.0.0.10')
interface_mock = mock.Mock()
self.target._add_dnsha_config(interface_mock)
interface_mock.add_dnsha.assert_called_once_with(
'myservice',
'10.0.0.10',
'myservice-internal.maas',
'int')
def test__add_dnsha_config_dns_ha_false(self):
config = {
'os-internal-hostname': 'myservice-internal.maas'
}
self.patch_target('config', new=config)
interface_mock = mock.Mock()
self.target._add_dnsha_config(interface_mock)
self.assertFalse(interface_mock.add_dnsha.called)
config['dns-ha'] = None
interface_mock.reset_mock()
self.target._add_dnsha_config(interface_mock)
self.assertFalse(interface_mock.add_dnsha.called)
config['dns-ha'] = False
interface_mock.reset_mock()
self.target._add_dnsha_config(interface_mock)
self.assertFalse(interface_mock.add_dnsha.called)
def test_set_haproxy_stat_password(self):
self.patch_object(chm.reactive.bus, 'get_state')
self.patch_object(chm.reactive.bus, 'set_state')