plugins/ceph: Add support for core Ceph charms

Also adds missing release combinations to package_codenames.

Change-Id: I6fbde855ba8f83ef5e265bd5b5dfb0d01eae830b
Related-Bug: #1879072
This commit is contained in:
Frode Nordahl 2020-06-09 12:20:43 +02:00
parent f0a486c59d
commit e03f6a6a1c
No known key found for this signature in database
GPG Key ID: 6A5D59A3BA48373F
2 changed files with 105 additions and 12 deletions

View File

@ -13,6 +13,7 @@
# limitations under the License.
import collections
import enum
import os
import shutil
import socket
@ -50,9 +51,27 @@ class BaseOpenStackCephCharm(object):
# ceph service name is determined from `application_name` property.
# If this does not fit your use case you can override.
ceph_service_name_override = ''
# Unless you are writing a charm providing Ceph mon|osd|mgr|mds services
# this should probably be left as-is.
ceph_service_type = 'client'
class CephServiceType(enum.Enum):
"""Ceph service type."""
client = 'client'
mds = 'mds'
mgr = 'mgr'
mon = 'mon'
osd = 'osd'
def __str__(self):
"""Return string representation of value.
:returns: string representation of value.
:rtype: str
"""
return self.value
ceph_service_type = CephServiceType.client
# Path prefix to where the Ceph keyring should be stored.
ceph_keyring_path_prefix = '/etc/ceph'
@ -102,9 +121,13 @@ class BaseOpenStackCephCharm(object):
:returns: Ceph key name
:rtype: str
"""
base_key_name = '{}.{}'.format(
self.ceph_service_type,
self.ceph_service_name)
if self.ceph_service_type == self.CephServiceType.client:
base_key_name = '{}.{}'.format(
self.ceph_service_type,
self.ceph_service_name)
else:
base_key_name = self.ceph_service_name
if self.ceph_key_per_unit_name:
return '{}.{}'.format(
base_key_name,
@ -131,9 +154,13 @@ class BaseOpenStackCephCharm(object):
:returns: Absolute path to keyring file
:rtype: str
"""
keyring_name = ('{}.{}.keyring'
.format(cluster_name or self.ceph_cluster_name,
self.ceph_key_name))
if self.ceph_service_type == self.CephServiceType.client:
keyring_name = ('{}.{}.keyring'
.format(cluster_name or self.ceph_cluster_name,
self.ceph_key_name))
else:
keyring_name = 'keyring'
keyring_absolute_path = os.path.join(self.ceph_keyring_path,
keyring_name)
return keyring_absolute_path
@ -216,6 +243,8 @@ class CephCharm(charms_openstack.charm.OpenStackCharm,
('10', 'mitaka'), # 10.2.x Jewel
('12', 'pike'), # 12.2.x Luminous
('13', 'rocky'), # 13.2.x Mimic
('14', 'train'), # 14.2.x Nautilus
('15', 'ussuri'), # 15.2.x Octopus
]),
}
@ -250,6 +279,11 @@ class CephCharm(charms_openstack.charm.OpenStackCharm,
# Path prefix to where the Ceph keyring should be stored.
ceph_keyring_path_prefix = '/var/lib/ceph'
def __init__(self, **kwargs):
"""Initialize class."""
super().__init__(**kwargs)
self.hostname = socket.gethostname()
@property
def ceph_keyring_path(self):
"""Provide a path to where the Ceph keyring should be stored.
@ -257,14 +291,33 @@ class CephCharm(charms_openstack.charm.OpenStackCharm,
:returns: Path to directory
:rtype: str
"""
return os.path.join(self.snap_path_prefix,
self.ceph_keyring_path_prefix,
self.ceph_service_name)
keyring_path_components = (
self.snap_path_prefix,
self.ceph_keyring_path_prefix,
self.ceph_service_name)
if self.ceph_service_type != self.CephServiceType.client:
keyring_path_components = (
*keyring_path_components,
'{}-{}'.format(self.ceph_cluster_name,
self.hostname))
return os.path.join(*keyring_path_components)
def configure_ceph_keyring(self, key, cluster_name=None):
"""Override parent function to add symlink in ``/etc/ceph``."""
"""Override parent method for Ceph service providing charms.
:param cluster_name: (Optional) Name of Ceph cluster to operate on.
Defaults to value of ``self.ceph_cluster_name``.
:type cluster_name: str
:raises: OSError
"""
keyring_absolute_path = super().configure_ceph_keyring(
key, cluster_name=cluster_name)
if self.ceph_service_type != self.CephServiceType.client:
return
# If the service is a client-type sevice (sych as RBD Mirror) add
# symlink to key in ``/etc/ceph``.
symlink_absolute_path = os.path.join(
'/etc/ceph',
os.path.basename(keyring_absolute_path))

View File

@ -14,9 +14,19 @@ TEST_CONFIG = {'config': True,
class FakeOpenStackCephConsumingCharm(
chm.OpenStackCharm,
cpl.BaseOpenStackCephCharm):
abstract_class = True
class FakeCephCharm(cpl.CephCharm):
abstract_class = True
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hostname = 'somehost'
class TestOpenStackCephConsumingCharm(BaseOpenStackCharmTest):
def setUp(self):
@ -127,7 +137,7 @@ class TestOpenStackCephConsumingCharm(BaseOpenStackCharmTest):
class TestCephCharm(BaseOpenStackCharmTest):
def setUp(self):
super(TestCephCharm, self).setUp(cpl.CephCharm, {'source': None})
super(TestCephCharm, self).setUp(FakeCephCharm, {'source': None})
def test_ceph_keyring_path(self):
self.patch_object(cpl.ch_core.hookenv, 'application_name',
@ -140,6 +150,16 @@ class TestCephCharm(BaseOpenStackCharmTest):
self.target.ceph_keyring_path,
os.path.join(cpl.SNAP_PATH_PREFIX_FORMAT.format('gnocchi'),
'/var/lib/ceph/charmname'))
self.target.snaps = []
self.target.ceph_service_type = self.target.CephServiceType.mds
self.assertEqual(
self.target.ceph_keyring_path,
'/var/lib/ceph/charmname/ceph-somehost')
self.target.snaps = ['somecephsnap']
self.assertEqual(
self.target.ceph_keyring_path,
os.path.join(cpl.SNAP_PATH_PREFIX_FORMAT.format('gnocchi'),
'/var/lib/ceph/charmname/ceph-somehost'))
def test_configure_ceph_keyring(self):
self.patch_object(cpl.os.path, 'isdir', return_value=False)
@ -154,6 +174,21 @@ class TestCephCharm(BaseOpenStackCharmTest):
self.patch_object(cpl.os, 'readlink')
self.patch_object(cpl.os, 'remove')
self.readlink.side_effect = OSError
self.target.ceph_service_type = self.target.CephServiceType.mds
self.target.configure_ceph_keyring(key)
self.isdir.assert_called_with('/var/lib/ceph/sarepta/ceph-somehost')
self.mkdir.assert_called_with('/var/lib/ceph/sarepta/ceph-somehost',
owner='root', group='root', perms=0o750)
self.check_call.assert_called_with([
'ceph-authtool',
'/var/lib/ceph/sarepta/ceph-somehost/keyring',
'--create-keyring', '--name=sarepta', '--add-key', 'KEY',
'--mode', '0600',
])
self.exists.assert_not_called()
self.readlink.assert_not_called()
self.symlink.assert_not_called()
self.target.ceph_service_type = self.target.CephServiceType.client
self.target.configure_ceph_keyring(key)
self.isdir.assert_called_with('/var/lib/ceph/sarepta')
self.mkdir.assert_called_with('/var/lib/ceph/sarepta',
@ -184,6 +219,11 @@ class TestCephCharm(BaseOpenStackCharmTest):
self.target.delete_ceph_keyring()
self.remove.assert_called_once_with(
'/var/lib/ceph/sarepta/ceph.client.sarepta.keyring')
self.remove.reset_mock()
self.target.ceph_service_type = self.target.CephServiceType.mds
self.target.delete_ceph_keyring()
self.remove.assert_called_once_with(
'/var/lib/ceph/sarepta/ceph-somehost/keyring')
def test_install(self):
self.patch_object(cpl.subprocess, 'check_output', return_value=b'\n')