Add pacemaker authkey
To work with pacemaker remotes all pacemaker nodes (including the remotes) need to share a common key in the same way that corosync does. This change allows a user to set a pacemaker key via config in the same way as corosync. If the pacemaker key value is unset then the corosync key is user. Change-Id: I75247e7f3af29fc0907a94ae8e1678bdb9ee64e2
This commit is contained in:
parent
bca864f33f
commit
f3873fe67f
12
config.yaml
12
config.yaml
|
@ -53,6 +53,18 @@ options:
|
|||
.
|
||||
This configuration element is mandatory and the service will fail on
|
||||
install if it is not provided. The value must be base64 encoded.
|
||||
pacemaker_key:
|
||||
type: string
|
||||
default:
|
||||
description: |
|
||||
This value will become the Pacemaker authentication key. To generate
|
||||
a suitable value use:
|
||||
.
|
||||
dd if=/dev/urandom of=/tmp/authkey bs=2048 count=1
|
||||
cat /tmp/authkey | base64 -w 0
|
||||
.
|
||||
If this configuration element is not set then the corosync key will be
|
||||
reused as the pacemaker key.
|
||||
maintenance-mode:
|
||||
type: boolean
|
||||
default: false
|
||||
|
|
|
@ -108,6 +108,8 @@ COROSYNC_CONF_FILES = [
|
|||
COROSYNC_HACLUSTER_ACL,
|
||||
]
|
||||
SUPPORTED_TRANSPORTS = ['udp', 'udpu', 'multicast', 'unicast']
|
||||
|
||||
PCMKR_AUTHKEY = '/etc/pacemaker/authkey'
|
||||
PCMKR_MAX_RETRIES = 3
|
||||
PCMKR_SLEEP_SECS = 5
|
||||
|
||||
|
@ -346,6 +348,11 @@ def emit_corosync_conf():
|
|||
return False
|
||||
|
||||
|
||||
def get_pcmkr_key():
|
||||
"""Return the pacemaker auth key"""
|
||||
return config('pacemaker_key') or config('corosync_key')
|
||||
|
||||
|
||||
def emit_base_conf():
|
||||
if not os.path.isdir(COROSYNC_HACLUSTER_ACL_DIR):
|
||||
os.mkdir(COROSYNC_HACLUSTER_ACL_DIR)
|
||||
|
@ -362,6 +369,12 @@ def emit_base_conf():
|
|||
write_file(path=COROSYNC_AUTHKEY,
|
||||
content=b64decode(corosync_key),
|
||||
perms=0o400)
|
||||
pcmkr_key = get_pcmkr_key()
|
||||
write_file(path=PCMKR_AUTHKEY,
|
||||
owner='root',
|
||||
group='haclient',
|
||||
content=b64decode(pcmkr_key),
|
||||
perms=0o440)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
|
@ -442,3 +442,137 @@ class UtilsTestCase(unittest.TestCase):
|
|||
relation_get.assert_has_calls([
|
||||
mock.call('json_testkey', 'neutron-api/0', 'hacluster:1'),
|
||||
])
|
||||
|
||||
@mock.patch.object(utils, 'render_template')
|
||||
@mock.patch.object(utils.os.path, 'isdir')
|
||||
@mock.patch.object(utils.os, 'mkdir')
|
||||
@mock.patch.object(utils, 'write_file')
|
||||
@mock.patch.object(utils, 'config')
|
||||
def test_emit_base_conf(self, config, write_file, mkdir, isdir,
|
||||
render_template):
|
||||
cfg = {
|
||||
'corosync_key': 'Y29yb3N5bmNrZXkK',
|
||||
'pacemaker_key': 'cGFjZW1ha2Vya2V5Cg==',
|
||||
}
|
||||
config.side_effect = lambda x: cfg.get(x)
|
||||
isdir.return_value = False
|
||||
render = {
|
||||
'corosync': 'corosync etc default config',
|
||||
'hacluster.acl': 'hacluster acl file',
|
||||
}
|
||||
render_template.side_effect = lambda x, y: render[x]
|
||||
expect_write_calls = [
|
||||
mock.call(
|
||||
content='corosync etc default config',
|
||||
path='/etc/default/corosync'),
|
||||
mock.call(
|
||||
content='hacluster acl file',
|
||||
path='/etc/corosync/uidgid.d/hacluster'),
|
||||
mock.call(
|
||||
content=b'corosynckey\n',
|
||||
path='/etc/corosync/authkey',
|
||||
perms=256),
|
||||
mock.call(
|
||||
content=b'pacemakerkey\n',
|
||||
path='/etc/pacemaker/authkey',
|
||||
perms=288,
|
||||
group='haclient',
|
||||
owner='root')
|
||||
]
|
||||
expect_render_calls = [
|
||||
mock.call(
|
||||
'corosync',
|
||||
{'corosync_enabled': 'yes'}),
|
||||
mock.call(
|
||||
'hacluster.acl',
|
||||
{})
|
||||
]
|
||||
self.assertTrue(utils.emit_base_conf())
|
||||
write_file.assert_has_calls(expect_write_calls)
|
||||
render_template.assert_has_calls(expect_render_calls)
|
||||
mkdir.assert_called_once_with('/etc/corosync/uidgid.d')
|
||||
|
||||
@mock.patch.object(utils, 'render_template')
|
||||
@mock.patch.object(utils.os.path, 'isdir')
|
||||
@mock.patch.object(utils.os, 'mkdir')
|
||||
@mock.patch.object(utils, 'write_file')
|
||||
@mock.patch.object(utils, 'config')
|
||||
def test_emit_base_conf_no_pcmkr_key(self, config, write_file, mkdir,
|
||||
isdir, render_template):
|
||||
cfg = {
|
||||
'corosync_key': 'Y29yb3N5bmNrZXkK',
|
||||
}
|
||||
config.side_effect = lambda x: cfg.get(x)
|
||||
isdir.return_value = False
|
||||
render = {
|
||||
'corosync': 'corosync etc default config',
|
||||
'hacluster.acl': 'hacluster acl file',
|
||||
}
|
||||
render_template.side_effect = lambda x, y: render[x]
|
||||
expect_write_calls = [
|
||||
mock.call(
|
||||
content='corosync etc default config',
|
||||
path='/etc/default/corosync'),
|
||||
mock.call(
|
||||
content='hacluster acl file',
|
||||
path='/etc/corosync/uidgid.d/hacluster'),
|
||||
mock.call(
|
||||
content=b'corosynckey\n',
|
||||
path='/etc/corosync/authkey',
|
||||
perms=256),
|
||||
mock.call(
|
||||
content=b'corosynckey\n',
|
||||
path='/etc/pacemaker/authkey',
|
||||
perms=288,
|
||||
group='haclient',
|
||||
owner='root')
|
||||
]
|
||||
expect_render_calls = [
|
||||
mock.call(
|
||||
'corosync',
|
||||
{'corosync_enabled': 'yes'}),
|
||||
mock.call(
|
||||
'hacluster.acl',
|
||||
{})
|
||||
]
|
||||
self.assertTrue(utils.emit_base_conf())
|
||||
write_file.assert_has_calls(expect_write_calls)
|
||||
render_template.assert_has_calls(expect_render_calls)
|
||||
mkdir.assert_called_once_with('/etc/corosync/uidgid.d')
|
||||
|
||||
@mock.patch.object(utils, 'render_template')
|
||||
@mock.patch.object(utils.os.path, 'isdir')
|
||||
@mock.patch.object(utils.os, 'mkdir')
|
||||
@mock.patch.object(utils, 'write_file')
|
||||
@mock.patch.object(utils, 'config')
|
||||
def test_emit_base_conf_no_coro_key(self, config, write_file, mkdir,
|
||||
isdir, render_template):
|
||||
cfg = {
|
||||
}
|
||||
config.side_effect = lambda x: cfg.get(x)
|
||||
isdir.return_value = False
|
||||
render = {
|
||||
'corosync': 'corosync etc default config',
|
||||
'hacluster.acl': 'hacluster acl file',
|
||||
}
|
||||
render_template.side_effect = lambda x, y: render[x]
|
||||
expect_write_calls = [
|
||||
mock.call(
|
||||
content='corosync etc default config',
|
||||
path='/etc/default/corosync'),
|
||||
mock.call(
|
||||
content='hacluster acl file',
|
||||
path='/etc/corosync/uidgid.d/hacluster'),
|
||||
]
|
||||
expect_render_calls = [
|
||||
mock.call(
|
||||
'corosync',
|
||||
{'corosync_enabled': 'yes'}),
|
||||
mock.call(
|
||||
'hacluster.acl',
|
||||
{})
|
||||
]
|
||||
self.assertFalse(utils.emit_base_conf())
|
||||
write_file.assert_has_calls(expect_write_calls)
|
||||
render_template.assert_has_calls(expect_render_calls)
|
||||
mkdir.assert_called_once_with('/etc/corosync/uidgid.d')
|
||||
|
|
Loading…
Reference in New Issue