diff --git a/hooks/ceph_hooks.py b/hooks/ceph_hooks.py index 993166a7..174e0f45 100755 --- a/hooks/ceph_hooks.py +++ b/hooks/ceph_hooks.py @@ -85,6 +85,7 @@ from utils import ( import_osd_upgrade_key, import_osd_removal_key, import_client_crash_key, + import_pending_key, get_host_ip, get_networks, assert_charm_supports_ipv6, @@ -738,8 +739,21 @@ def get_bdev_enable_discard(): "bdev-enable-discard: %s") % bdev_enable_discard) +def handle_pending_key(pending_key): + for osd_id, key in json.loads(pending_key).items(): + if not os.path.exists('/var/lib/ceph/osd/ceph-%s' % osd_id): + continue + import_pending_key(key, osd_id) + service_restart('ceph-osd@%s' % osd_id) + + @hooks.hook('mon-relation-changed') def mon_relation(): + pending_key = relation_get('pending_key') + if pending_key: + handle_pending_key(pending_key) + return + bootstrap_key = relation_get('osd_bootstrap_key') upgrade_key = relation_get('osd_upgrade_key') removal_key = relation_get('osd_disk_removal_key') diff --git a/hooks/utils.py b/hooks/utils.py index a86b99eb..b293e6a0 100644 --- a/hooks/utils.py +++ b/hooks/utils.py @@ -85,15 +85,17 @@ def is_osd_bootstrap_ready(): return os.path.exists(_bootstrap_keyring) -def _import_key(key, path, name): - if not os.path.exists(path): +def _import_key(key, path, name, override=False): + exists = os.path.exists(path) + if not exists or override: + create = ['--create-keyring'] if not exists else [] cmd = [ 'sudo', '-u', ceph.ceph_user(), 'ceph-authtool', - path, - '--create-keyring', + path + ] + create + [ '--name={}'.format(name), '--add-key={}'.format(key) ] @@ -140,6 +142,19 @@ def import_client_crash_key(key): _import_key(key, _client_crash_keyring, 'client.crash') +def import_pending_key(key, osd_id): + """ + Import a pending key, used for key rotation. + + :param key: The pending cephx key that will replace the current one. + :type key: str + :param osd_id: The OSD id whose key will be replaced. + :type osd_id: str + :raises: subprocess.CalledProcessError""" + _import_key(key, '/var/lib/ceph/osd/ceph-%s/keyring' % osd_id, + 'osd.%s' % osd_id, override=True) + + def render_template(template_name, context, template_dir=TEMPLATES_DIR): """Render Jinja2 template. diff --git a/unit_tests/test_ceph_hooks.py b/unit_tests/test_ceph_hooks.py index b4cceea0..a135aeb9 100644 --- a/unit_tests/test_ceph_hooks.py +++ b/unit_tests/test_ceph_hooks.py @@ -835,6 +835,18 @@ class CephHooksTestCase(unittest.TestCase): level=ceph_hooks.ERROR, ) + @patch.object(ceph_hooks, 'service_restart') + @patch.object(ceph_hooks, 'import_pending_key') + @patch.object(ceph_hooks.os.path, 'exists') + def test_handle_pending_key(self, exists, import_pending_key, + service_restart): + exists.return_value = True + pending_key = '0:some-key' + ceph_hooks.handle_pending_key(pending_key) + exists.assert_called_with('/var/lib/ceph/osd/ceph-0') + import_pending_key.assert_called_with('some-key', '0') + service_restart.assert_called_with('ceph-osd@0') + @patch.object(ceph_hooks, 'local_unit') @patch.object(ceph_hooks, 'relation_get') diff --git a/unit_tests/test_ceph_utils.py b/unit_tests/test_ceph_utils.py index f0fbabd6..2ae572aa 100644 --- a/unit_tests/test_ceph_utils.py +++ b/unit_tests/test_ceph_utils.py @@ -352,3 +352,15 @@ cset.uuid 57add9da-e5de-47c6-8f39-3e16aafb8d31 }] }''' self.assertEqual(utils.get_parent_device('/dev/loop1p1'), '/dev/loop1') + + @patch.object(utils.ceph, 'ceph_user') + @patch.object(utils.subprocess, 'check_call') + @patch.object(utils.os.path, 'exists') + def test_import_pending_key(self, exists, check_call, ceph_user): + ceph_user.return_value = 'ceph' + exists.return_value = True + utils.import_pending_key('some-key', '0') + exists.assert_called_with('/var/lib/ceph/osd/ceph-0/keyring') + check_call.assert_called_with(['sudo', '-u', 'ceph', 'ceph-authtool', + '/var/lib/ceph/osd/ceph-0/keyring', + '--name=osd.0', '--add-key=some-key'])