Merge "Change MAAS DNS OCF to read IP from file"
This commit is contained in:
commit
0bdf97fa9b
|
@ -69,6 +69,7 @@ from utils import (
|
|||
disable_lsb_services,
|
||||
disable_upstart_services,
|
||||
get_ipv6_addr,
|
||||
get_ip_addr_from_resource_params,
|
||||
validate_dns_ha,
|
||||
setup_maas_api,
|
||||
setup_ocf_files,
|
||||
|
@ -77,6 +78,8 @@ from utils import (
|
|||
kill_legacy_ocf_daemon_process,
|
||||
try_pcmk_wait,
|
||||
maintenance_mode,
|
||||
needs_maas_dns_migration,
|
||||
write_maas_dns_address,
|
||||
)
|
||||
|
||||
from charmhelpers.contrib.charmsupport import nrpe
|
||||
|
@ -174,10 +177,34 @@ def config_changed():
|
|||
maintenance_mode(cfg['maintenance-mode'])
|
||||
|
||||
|
||||
def migrate_maas_dns():
|
||||
"""
|
||||
Migrates the MAAS DNS HA configuration to write local IP address
|
||||
information to files.
|
||||
"""
|
||||
if not needs_maas_dns_migration():
|
||||
log("MAAS DNS migration is not necessary.", INFO)
|
||||
return
|
||||
|
||||
for relid in relation_ids('ha'):
|
||||
for unit in related_units(relid):
|
||||
resources = parse_data(relid, unit, 'resources')
|
||||
resource_params = parse_data(relid, unit, 'resource_params')
|
||||
|
||||
if True in [ra.startswith('ocf:maas')
|
||||
for ra in resources.values()]:
|
||||
for resource in resource_params.keys():
|
||||
if resource.endswith("_hostname"):
|
||||
res_ipaddr = get_ip_addr_from_resource_params(
|
||||
resource_params[resource])
|
||||
log("Migrating MAAS DNS resource %s" % resource, INFO)
|
||||
write_maas_dns_address(resource, res_ipaddr)
|
||||
|
||||
|
||||
@hooks.hook()
|
||||
def upgrade_charm():
|
||||
install()
|
||||
|
||||
migrate_maas_dns()
|
||||
update_nrpe_config()
|
||||
|
||||
|
||||
|
@ -258,10 +285,13 @@ def ha_relation_changed():
|
|||
# credentials
|
||||
for resource in resource_params.keys():
|
||||
if resource.endswith("_hostname"):
|
||||
res_ipaddr = get_ip_addr_from_resource_params(
|
||||
resource_params[resource])
|
||||
resource_params[resource] += (
|
||||
' maas_url="{}" maas_credentials="{}"'
|
||||
''.format(config('maas_url'),
|
||||
config('maas_credentials')))
|
||||
write_maas_dns_address(resource, res_ipaddr)
|
||||
else:
|
||||
msg = ("DNS HA is requested but maas_url "
|
||||
"or maas_credentials are not set")
|
||||
|
|
|
@ -110,6 +110,9 @@ SYSTEMD_OVERRIDES_DIR = '/etc/systemd/system/{}.service.d'
|
|||
SYSTEMD_OVERRIDES_FILE = '{}/overrides.conf'
|
||||
|
||||
|
||||
MAAS_DNS_CONF_DIR = '/etc/maas_dns'
|
||||
|
||||
|
||||
class MAASConfigIncomplete(Exception):
|
||||
pass
|
||||
|
||||
|
@ -561,6 +564,16 @@ def configure_cluster_global():
|
|||
pcmk.commit(cmd)
|
||||
|
||||
|
||||
def get_ip_addr_from_resource_params(params):
|
||||
"""Returns the IP address in the resource params provided
|
||||
|
||||
:return: the IP address in the params or None if not found
|
||||
"""
|
||||
reg_ex = r'.* ip_address="([a-fA-F\d\:\.]+)".*'
|
||||
res = re.search(reg_ex, params)
|
||||
return res.group(1) if res else None
|
||||
|
||||
|
||||
def restart_corosync_on_change():
|
||||
"""Simple decorator to restart corosync if any of its config changes"""
|
||||
def wrap(f):
|
||||
|
@ -710,6 +723,33 @@ def setup_ocf_files():
|
|||
rsync('ocf/maas/maasclient/', '/usr/lib/heartbeat/maasclient/')
|
||||
|
||||
|
||||
def write_maas_dns_address(resource_name, resource_addr):
|
||||
"""Writes the specified IP address to the resource file for MAAS dns.
|
||||
|
||||
:param resource_name: the name of the resource the address belongs to.
|
||||
This is the name of the file that will be written in /etc/maas_dns.
|
||||
:param resource_addr: the IP address for the resource. This will be
|
||||
written to the resource_name file.
|
||||
"""
|
||||
mkdir(MAAS_DNS_CONF_DIR)
|
||||
write_file(os.path.join(MAAS_DNS_CONF_DIR, resource_name),
|
||||
content=resource_addr)
|
||||
|
||||
|
||||
def needs_maas_dns_migration():
|
||||
"""Determines if the MAAS DNS ocf resources need migration.
|
||||
|
||||
:return: True if migration is necessary, False otherwise.
|
||||
"""
|
||||
try:
|
||||
subprocess.check_call(['grep', 'OCF_RESOURCE_INSTANCE',
|
||||
'/usr/lib/ocf/resource.d/maas/dns'])
|
||||
return True
|
||||
except subprocess.CalledProcessError:
|
||||
# check_call will raise an exception if grep doesn't find the string
|
||||
return False
|
||||
|
||||
|
||||
def is_in_standby_mode(node_name):
|
||||
"""Check if node is in standby mode in pacemaker
|
||||
|
||||
|
|
66
ocf/maas/dns
66
ocf/maas/dns
|
@ -42,6 +42,7 @@
|
|||
# OCF_RESKEY_ttl
|
||||
# OCF_RESKEY_maas_url
|
||||
# OCF_RESKEY_maas_credentials
|
||||
# OCF_RESKEY_cfg_dir
|
||||
|
||||
# Defaults
|
||||
|
||||
|
@ -53,6 +54,28 @@ Expects to have a fully populated OCF RA-compliant environment set.
|
|||
END
|
||||
}
|
||||
|
||||
# Retrieve the local IP for the current resource
|
||||
#
|
||||
# returns:
|
||||
# ip address contained in $OCF_RESKEY_cfg_dir/$OCF_RESOURCE_INSTANCE
|
||||
# no = nothing or no file
|
||||
my_ip() {
|
||||
if [ ! -r $ipaddrfile ]
|
||||
then
|
||||
echo "no"
|
||||
return 0
|
||||
fi
|
||||
|
||||
ip_addr=`cat $OCF_RESKEY_cfg_dir/$OCF_RESOURCE_INSTANCE`
|
||||
if [ "x$ip_addr" != "x" ]
|
||||
then
|
||||
echo $ip_addr
|
||||
return 0
|
||||
else
|
||||
echo "no"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Do we already serve this IP address on the given $NIC?
|
||||
#
|
||||
|
@ -66,7 +89,17 @@ dns_served() {
|
|||
target=`dig +short $OCF_RESKEY_fqdn`
|
||||
if [ "x$target" != "x" ]
|
||||
then
|
||||
if test "$OCF_RESKEY_ip_address" = "$target"
|
||||
ip_address=`my_ip`
|
||||
# The $ip_address should be set as it is ensured in the validate
|
||||
# function, but this is a sanity check. The maas_dns.log file should
|
||||
# contain the error that the $ip_address file is not found.
|
||||
if test "$ip_address" = "no"
|
||||
then
|
||||
echo "no"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if test "$ip_address" = "$target"
|
||||
then
|
||||
echo "ok"
|
||||
return 0
|
||||
|
@ -95,8 +128,13 @@ maas_dns_start() {
|
|||
if [ "$dns_status" = "ok" ]; then
|
||||
exit $OCF_SUCCESS
|
||||
fi
|
||||
local myipaddr=`my_ip`
|
||||
if [ "$myipaddr" = "no" ]; then
|
||||
ocf_log err "No ip address found in $ipaddrfile"
|
||||
exit $OCF_ERR_GENERIC
|
||||
fi
|
||||
|
||||
cmd="python3 $binfile --fqdn=$OCF_RESKEY_fqdn --ip_address=$OCF_RESKEY_ip_address --maas_server=$OCF_RESKEY_maas_url --maas_credentials=$OCF_RESKEY_maas_credentials "
|
||||
cmd="python3 $binfile --fqdn=$OCF_RESKEY_fqdn --ip_address=$myipaddr --maas_server=$OCF_RESKEY_maas_url --maas_credentials=$OCF_RESKEY_maas_credentials "
|
||||
if [ -n "$OCF_RESKEY_ttl" ]; then
|
||||
cmd="$cmd --ttl=$OCF_RESKEY_ttl"
|
||||
fi
|
||||
|
@ -133,7 +171,6 @@ maas_dns_monitor() {
|
|||
return $OCF_SUCCESS
|
||||
;;
|
||||
no)
|
||||
exit 7
|
||||
exit $OCF_NOT_RUNNING
|
||||
;;
|
||||
*)
|
||||
|
@ -148,6 +185,7 @@ binfile="$HA_BIN/maas_dns.py"
|
|||
logfile="$OCF_RESKEY_logfile"
|
||||
errlogfile="$OCF_RESKEY_errlogfile"
|
||||
user="$OCF_RESKEY_user"
|
||||
ipaddrfile="$OCF_RESKEY_cfg_dir/$OCF_RESOURCE_INSTANCE"
|
||||
[ -z "$user" ] && user=root
|
||||
|
||||
maas_dns_validate() {
|
||||
|
@ -171,6 +209,16 @@ maas_dns_validate() {
|
|||
}
|
||||
fi
|
||||
done
|
||||
if [ ! -r $ipaddrfile ]
|
||||
then
|
||||
ocf_log err "$ipaddrfile does not exist or cannot be read"
|
||||
exit $OCF_ERR_INSTALLED
|
||||
fi
|
||||
if [ `my_ip` = "no" ]
|
||||
then
|
||||
ocf_log err "IP address is not found in $ipaddrfile"
|
||||
exit $OCF_ERR_INSTALLED
|
||||
fi
|
||||
return $OCF_SUCCESS
|
||||
}
|
||||
|
||||
|
@ -192,9 +240,9 @@ The fully qualified domain name for the DNS entry.
|
|||
<shortdesc lang="en">Fully qualified domain name</shortdesc>
|
||||
<content type="string" default=""/>
|
||||
</parameter>
|
||||
<parameter name="ip_address" required="1">
|
||||
<parameter name="ip_address" required="0">
|
||||
<longdesc lang="en">
|
||||
The IP address for the DNS entry
|
||||
The IP address for the DNS entry. Deprecated option, do not use.
|
||||
</longdesc>
|
||||
<shortdesc lang="en">IP Address</shortdesc>
|
||||
<content type="string" />
|
||||
|
@ -227,6 +275,14 @@ File to write STDERR to
|
|||
<shortdesc lang="en">File to write STDERR to</shortdesc>
|
||||
<content type="string" />
|
||||
</parameter>
|
||||
<parameter name="cfg_dir" required="0">
|
||||
<longdesc lang="en">
|
||||
Directory containing resource config files containing IP address information
|
||||
for the resource running on the local server.
|
||||
</longdesc>
|
||||
<shortdesc lang="en">IP address config file directory</shortdesc>
|
||||
<content type="string" default="/etc/maas_dns"/>
|
||||
</parameter>
|
||||
</parameters>
|
||||
<actions>
|
||||
<action name="start" timeout="20s" />
|
||||
|
|
|
@ -32,6 +32,7 @@ class TestCorosyncConf(unittest.TestCase):
|
|||
def setUp(self):
|
||||
self.tmpdir = tempfile.mkdtemp()
|
||||
|
||||
@mock.patch.object(hooks, 'write_maas_dns_address')
|
||||
@mock.patch('pcmk.wait_for_pcmk')
|
||||
@mock.patch.object(hooks, 'peer_units')
|
||||
@mock.patch('pcmk.crm_opt_exists')
|
||||
|
@ -54,7 +55,7 @@ class TestCorosyncConf(unittest.TestCase):
|
|||
configure_stonith, configure_monitor_host,
|
||||
configure_cluster_global, configure_corosync,
|
||||
oldest_peer, crm_opt_exists, peer_units,
|
||||
wait_for_pcmk):
|
||||
wait_for_pcmk, write_maas_dns_address):
|
||||
crm_opt_exists.return_value = False
|
||||
oldest_peer.return_value = True
|
||||
related_units.return_value = ['ha/0', 'ha/1', 'ha/2']
|
||||
|
@ -90,6 +91,7 @@ class TestCorosyncConf(unittest.TestCase):
|
|||
configure_monitor_host.assert_called_with()
|
||||
configure_cluster_global.assert_called_with()
|
||||
configure_corosync.assert_called_with()
|
||||
write_maas_dns_address.assert_not_called()
|
||||
|
||||
for kw, key in [('location', 'locations'),
|
||||
('clone', 'clones'),
|
||||
|
@ -108,6 +110,7 @@ class TestCorosyncConf(unittest.TestCase):
|
|||
commit.assert_any_call(
|
||||
'crm -w -F configure %s %s %s' % (kw, name, params))
|
||||
|
||||
@mock.patch.object(hooks, 'write_maas_dns_address')
|
||||
@mock.patch.object(hooks, 'setup_maas_api')
|
||||
@mock.patch.object(hooks, 'validate_dns_ha')
|
||||
@mock.patch('pcmk.wait_for_pcmk')
|
||||
|
@ -135,7 +138,7 @@ class TestCorosyncConf(unittest.TestCase):
|
|||
configure_corosync, oldest_peer,
|
||||
crm_opt_exists, peer_units,
|
||||
wait_for_pcmk, validate_dns_ha,
|
||||
setup_maas_api):
|
||||
setup_maas_api, write_maas_dns_addr):
|
||||
validate_dns_ha.return_value = True
|
||||
crm_opt_exists.return_value = False
|
||||
oldest_peer.return_value = True
|
||||
|
@ -158,7 +161,9 @@ class TestCorosyncConf(unittest.TestCase):
|
|||
'groups': {'grp_foo': 'res_foo'},
|
||||
'colocations': {'co_foo': 'inf: grp_foo cl_foo'},
|
||||
'resources': {'res_foo_hostname': 'ocf:maas:dns'},
|
||||
'resource_params': {'res_foo_hostname': 'params bar'},
|
||||
'resource_params': {
|
||||
'res_foo_hostname': 'params bar '
|
||||
'ip_address="172.16.0.1"'},
|
||||
'ms': {'ms_foo': 'res_foo meta notify=true'},
|
||||
'orders': {'foo_after': 'inf: res_foo ms_foo'}}
|
||||
|
||||
|
@ -170,10 +175,12 @@ class TestCorosyncConf(unittest.TestCase):
|
|||
hooks.ha_relation_changed()
|
||||
self.assertTrue(validate_dns_ha.called)
|
||||
self.assertTrue(setup_maas_api.called)
|
||||
write_maas_dns_addr.assert_called_with('res_foo_hostname',
|
||||
'172.16.0.1')
|
||||
# Validate maas_credentials and maas_url are added to params
|
||||
commit.assert_any_call(
|
||||
'crm -w -F configure primitive res_foo_hostname ocf:maas:dns '
|
||||
'params bar maas_url="http://maas/MAAAS/" '
|
||||
'params bar ip_address="172.16.0.1" maas_url="http://maas/MAAAS/" '
|
||||
'maas_credentials="secret"')
|
||||
|
||||
@mock.patch.object(hooks, 'setup_maas_api')
|
||||
|
@ -279,3 +286,37 @@ class TestHooks(test_utils.CharmTestCase):
|
|||
self.test_config.set('maintenance-mode', False)
|
||||
hooks.config_changed()
|
||||
mock_maintenance_mode.assert_called_with(False)
|
||||
|
||||
@mock.patch.object(hooks, 'needs_maas_dns_migration')
|
||||
@mock.patch.object(hooks, 'relation_ids')
|
||||
def test_migrate_maas_dns_no_migration(self, relation_ids,
|
||||
needs_maas_dns_migration):
|
||||
needs_maas_dns_migration.return_value = False
|
||||
hooks.migrate_maas_dns()
|
||||
relation_ids.assert_not_called()
|
||||
|
||||
@mock.patch.object(hooks, 'needs_maas_dns_migration')
|
||||
@mock.patch.object(hooks, 'write_maas_dns_address')
|
||||
@mock.patch.object(hooks, 'relation_ids')
|
||||
@mock.patch.object(hooks, 'related_units')
|
||||
@mock.patch.object(hooks, 'parse_data')
|
||||
def test_migrate_maas_dns_(self, parse_data, related_units, relation_ids,
|
||||
write_maas_dns_address,
|
||||
needs_maas_dns_migration):
|
||||
needs_maas_dns_migration.return_value = True
|
||||
related_units.return_value = 'keystone/0'
|
||||
relation_ids.return_value = 'ha:4'
|
||||
|
||||
def mock_parse_data(relid, unit, key):
|
||||
if key == 'resources':
|
||||
return {'res_keystone_public_hostname': 'ocf:maas:dns'}
|
||||
elif key == 'resource_params':
|
||||
return {'res_keystone_public_hostname':
|
||||
'params fqdn="keystone.maas" ip_address="172.16.0.1"'}
|
||||
else:
|
||||
raise KeyError("unexpected key {}".format(key))
|
||||
|
||||
parse_data.side_effect = mock_parse_data
|
||||
hooks.migrate_maas_dns()
|
||||
write_maas_dns_address.assert_called_with(
|
||||
"res_keystone_public_hostname", "172.16.0.1")
|
||||
|
|
|
@ -381,3 +381,30 @@ class UtilsTestCase(unittest.TestCase):
|
|||
utils.maintenance_mode(False)
|
||||
mock_get_property.assert_called_with('maintenance-mode')
|
||||
mock_set_property.assert_not_called()
|
||||
|
||||
@mock.patch('subprocess.check_call')
|
||||
def test_needs_maas_dns_migration(self, check_call):
|
||||
ret = utils.needs_maas_dns_migration()
|
||||
self.assertEqual(True, ret)
|
||||
|
||||
check_call.side_effect = subprocess.CalledProcessError(1, '')
|
||||
ret = utils.needs_maas_dns_migration()
|
||||
self.assertEqual(False, ret)
|
||||
|
||||
def test_get_ip_addr_from_resource_params(self):
|
||||
param_str = 'params fqdn="keystone.maas" ip_address="{}" '
|
||||
for addr in ("172.16.0.4", "2001:0db8:85a3:0000:0000:8a2e:0370:7334"):
|
||||
ip = utils.get_ip_addr_from_resource_params(param_str.format(addr))
|
||||
self.assertEqual(addr, ip)
|
||||
|
||||
ip = utils.get_ip_addr_from_resource_params("no_ip_addr")
|
||||
self.assertEqual(None, ip)
|
||||
|
||||
@mock.patch.object(utils, 'write_file')
|
||||
@mock.patch.object(utils, 'mkdir')
|
||||
def test_write_maas_dns_address(self, mkdir, write_file):
|
||||
utils.write_maas_dns_address("res_keystone_public_hostname",
|
||||
"172.16.0.1")
|
||||
mkdir.assert_called_once_with("/etc/maas_dns")
|
||||
write_file.assert_called_once_with(
|
||||
"/etc/maas_dns/res_keystone_public_hostname", content="172.16.0.1")
|
||||
|
|
Loading…
Reference in New Issue