Enable image features when ``rbd-mirror`` relation present
The RBD Mirroring feature requires ``journaling`` and ``exclusive-lock`` image features to be enabled. Set the appropriate value so new images get these features automatically. Change-Id: Ie36c23b27fb7238814993756cb1d72e86309ab02
This commit is contained in:
parent
463176b276
commit
e899641dae
26
config.yaml
26
config.yaml
|
@ -212,14 +212,24 @@ options:
|
|||
type: int
|
||||
default:
|
||||
description: |
|
||||
Restrict the rbd features used to the specified level. If set, this will
|
||||
inform clients that they should set the config value `rbd default
|
||||
features`, for example:
|
||||
.
|
||||
rbd default features = 1
|
||||
.
|
||||
This needs to be set to 1 when deploying a cloud with the nova-lxd
|
||||
hypervisor.
|
||||
Default RBD Features to use when creating new images. The value of this
|
||||
configuration option will be shared with consumers of the ``ceph-client``
|
||||
interface and client charms may choose to add this to the Ceph
|
||||
configuration file on the units they manage.
|
||||
|
||||
Example:
|
||||
|
||||
rbd default features = 1
|
||||
|
||||
NOTE: If you have clients using the kernel RBD driver you must set this
|
||||
configuration option to a value corrensponding to the features the driver
|
||||
in your kernel supports. The kernel RBD driver tends to be multiple
|
||||
cycles behind the userspace driver available for libvirt/qemu. Nova LXD
|
||||
is among the clients depending on the kernel RBD driver.
|
||||
|
||||
NOTE: If you want to use the RBD Mirroring feature you must either let
|
||||
this configuration option be the default or make sure the value you set
|
||||
includes the ``exclusive-lock`` and ``journaling`` features.
|
||||
no-bootstrap:
|
||||
type: boolean
|
||||
default: False
|
||||
|
|
|
@ -81,10 +81,11 @@ from charmhelpers.core.templating import render
|
|||
from charmhelpers.contrib.storage.linux.ceph import (
|
||||
CephConfContext)
|
||||
from utils import (
|
||||
assert_charm_supports_ipv6,
|
||||
get_cluster_addr,
|
||||
get_networks,
|
||||
get_public_addr,
|
||||
get_cluster_addr,
|
||||
assert_charm_supports_ipv6
|
||||
get_rbd_features,
|
||||
)
|
||||
|
||||
from charmhelpers.contrib.charmsupport import nrpe
|
||||
|
@ -184,8 +185,9 @@ def get_ceph_context():
|
|||
cephcontext['public_addr'] = get_public_addr()
|
||||
cephcontext['cluster_addr'] = get_cluster_addr()
|
||||
|
||||
if config('default-rbd-features'):
|
||||
cephcontext['rbd_features'] = config('default-rbd-features')
|
||||
rbd_features = get_rbd_features()
|
||||
if rbd_features:
|
||||
cephcontext['rbd_features'] = rbd_features
|
||||
|
||||
if config('disable-pg-max-object-skew'):
|
||||
cephcontext['disable_object_skew'] = config(
|
||||
|
|
|
@ -12,27 +12,30 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import socket
|
||||
import re
|
||||
import socket
|
||||
import subprocess
|
||||
|
||||
from charmhelpers.core.hookenv import (
|
||||
unit_get,
|
||||
DEBUG,
|
||||
cached,
|
||||
config,
|
||||
status_set,
|
||||
network_get_primary_address,
|
||||
goal_state,
|
||||
log,
|
||||
DEBUG,
|
||||
network_get_primary_address,
|
||||
related_units,
|
||||
relation_ids,
|
||||
status_set,
|
||||
unit_get,
|
||||
)
|
||||
from charmhelpers.fetch import (
|
||||
apt_install,
|
||||
filter_installed_packages
|
||||
)
|
||||
|
||||
from charmhelpers.core.host import (
|
||||
lsb_release,
|
||||
CompareHostReleases,
|
||||
)
|
||||
|
||||
from charmhelpers.contrib.network.ip import (
|
||||
get_address_in_network,
|
||||
get_ipv6_addr
|
||||
|
@ -152,3 +155,56 @@ def assert_charm_supports_ipv6():
|
|||
if CompareHostReleases(_release) < "trusty":
|
||||
raise Exception("IPv6 is not supported in the charms for Ubuntu "
|
||||
"versions less than Trusty 14.04")
|
||||
|
||||
|
||||
def has_rbd_mirrors():
|
||||
"""Determine if we have or will have ``rbd-mirror`` charms related.
|
||||
|
||||
:returns: True or False
|
||||
:rtype: bool
|
||||
"""
|
||||
try:
|
||||
# NOTE(fnordahl): This optimization will not be useful until we get a
|
||||
# resolution on LP: #1818245
|
||||
raise NotImplementedError
|
||||
gs = goal_state()
|
||||
return 'rbd-mirror' in gs.get('relations', {})
|
||||
except NotImplementedError:
|
||||
for relid in relation_ids('rbd-mirror'):
|
||||
if related_units(relid):
|
||||
return True
|
||||
|
||||
|
||||
def get_default_rbd_features():
|
||||
"""Get default value for ``rbd_default_features``.
|
||||
|
||||
This is retrieved by asking the installed Ceph binary to show its runtime
|
||||
config when using a empty configuration file.
|
||||
|
||||
:returns: Installed Ceph's Default vaule for ``rbd_default_features``
|
||||
:rtype: int
|
||||
:raises: subprocess.CalledProcessError
|
||||
"""
|
||||
output = subprocess.check_output(
|
||||
['ceph', '-c', '/dev/null', '--show-config'],
|
||||
universal_newlines=True)
|
||||
for line in output.splitlines():
|
||||
if 'rbd_default_features' in line:
|
||||
return int(line.split('=')[1].lstrip().rstrip())
|
||||
|
||||
|
||||
def get_rbd_features():
|
||||
"""Determine if we should set, and what the rbd default features should be.
|
||||
|
||||
:returns: None or the apropriate value to use
|
||||
:rtype: Option[int, None]
|
||||
"""
|
||||
RBD_FEATURE_EXCLUSIVE_LOCK = 4
|
||||
RBD_FEATURE_JOURNALING = 64
|
||||
|
||||
rbd_feature_config = config('default-rbd-features')
|
||||
if rbd_feature_config:
|
||||
return int(rbd_feature_config)
|
||||
elif has_rbd_mirrors():
|
||||
return (get_default_rbd_features() |
|
||||
RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING)
|
||||
|
|
|
@ -57,6 +57,7 @@ class CephHooksTestCase(unittest.TestCase):
|
|||
def setUp(self):
|
||||
super(CephHooksTestCase, self).setUp()
|
||||
|
||||
@patch.object(ceph_hooks, 'get_rbd_features', return_value=None)
|
||||
@patch.object(ceph_hooks, 'get_public_addr', lambda *args: "10.0.0.1")
|
||||
@patch.object(ceph_hooks, 'get_cluster_addr', lambda *args: "10.1.0.1")
|
||||
@patch.object(ceph_hooks, 'cmp_pkgrevno', lambda *args: 1)
|
||||
|
@ -66,7 +67,8 @@ class CephHooksTestCase(unittest.TestCase):
|
|||
@patch.object(ceph_hooks, 'leader_get', lambda *args: '1234')
|
||||
@patch.object(ceph, 'config')
|
||||
@patch.object(ceph_hooks, 'config')
|
||||
def test_get_ceph_context(self, mock_config, mock_config2):
|
||||
def test_get_ceph_context(self, mock_config, mock_config2,
|
||||
_get_rbd_features):
|
||||
config = copy.deepcopy(CHARM_CONFIG)
|
||||
mock_config.side_effect = lambda key: config[key]
|
||||
mock_config2.side_effect = lambda key: config[key]
|
||||
|
@ -84,6 +86,7 @@ class CephHooksTestCase(unittest.TestCase):
|
|||
'use_syslog': 'true'}
|
||||
self.assertEqual(ctxt, expected)
|
||||
|
||||
@patch.object(ceph_hooks, 'get_rbd_features', return_value=1)
|
||||
@patch.object(ceph_hooks, 'get_public_addr', lambda *args: "10.0.0.1")
|
||||
@patch.object(ceph_hooks, 'get_cluster_addr', lambda *args: "10.1.0.1")
|
||||
@patch.object(ceph_hooks, 'cmp_pkgrevno',
|
||||
|
@ -94,9 +97,9 @@ class CephHooksTestCase(unittest.TestCase):
|
|||
@patch.object(ceph_hooks, 'leader_get', lambda *args: '1234')
|
||||
@patch.object(ceph, 'config')
|
||||
@patch.object(ceph_hooks, 'config')
|
||||
def test_get_ceph_context_rbd_features(self, mock_config, mock_config2):
|
||||
def test_get_ceph_context_rbd_features(self, mock_config, mock_config2,
|
||||
_get_rbd_features):
|
||||
config = copy.deepcopy(CHARM_CONFIG)
|
||||
config['default-rbd-features'] = 1
|
||||
mock_config.side_effect = lambda key: config[key]
|
||||
mock_config2.side_effect = lambda key: config[key]
|
||||
ctxt = ceph_hooks.get_ceph_context()
|
||||
|
@ -114,6 +117,7 @@ class CephHooksTestCase(unittest.TestCase):
|
|||
'rbd_features': 1}
|
||||
self.assertEqual(ctxt, expected)
|
||||
|
||||
@patch.object(ceph_hooks, 'get_rbd_features', return_value=None)
|
||||
@patch.object(ceph_hooks, 'get_public_addr', lambda *args: "10.0.0.1")
|
||||
@patch.object(ceph_hooks, 'get_cluster_addr', lambda *args: "10.1.0.1")
|
||||
@patch.object(ceph_hooks, 'cmp_pkgrevno', lambda *args: 1)
|
||||
|
@ -123,7 +127,8 @@ class CephHooksTestCase(unittest.TestCase):
|
|||
@patch.object(ceph_hooks, 'leader_get', lambda *args: '1234')
|
||||
@patch.object(ceph, 'config')
|
||||
@patch.object(ceph_hooks, 'config')
|
||||
def test_get_ceph_context_w_config_flags(self, mock_config, mock_config2):
|
||||
def test_get_ceph_context_w_config_flags(self, mock_config, mock_config2,
|
||||
_get_rbd_features):
|
||||
config = copy.deepcopy(CHARM_CONFIG)
|
||||
config['config-flags'] = '{"mon": {"mon sync max retries": 10}}'
|
||||
mock_config.side_effect = lambda key: config[key]
|
||||
|
@ -143,6 +148,7 @@ class CephHooksTestCase(unittest.TestCase):
|
|||
'use_syslog': 'true'}
|
||||
self.assertEqual(ctxt, expected)
|
||||
|
||||
@patch.object(ceph_hooks, 'get_rbd_features', return_value=None)
|
||||
@patch.object(ceph_hooks, 'get_public_addr', lambda *args: "10.0.0.1")
|
||||
@patch.object(ceph_hooks, 'get_cluster_addr', lambda *args: "10.1.0.1")
|
||||
@patch.object(ceph_hooks, 'cmp_pkgrevno', lambda *args: 1)
|
||||
|
@ -153,7 +159,8 @@ class CephHooksTestCase(unittest.TestCase):
|
|||
@patch.object(ceph, 'config')
|
||||
@patch.object(ceph_hooks, 'config')
|
||||
def test_get_ceph_context_w_config_flags_invalid(self, mock_config,
|
||||
mock_config2):
|
||||
mock_config2,
|
||||
_get_rbd_features):
|
||||
config = copy.deepcopy(CHARM_CONFIG)
|
||||
config['config-flags'] = ('{"mon": {"mon sync max retries": 10},'
|
||||
'"foo": "bar"}')
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
# Copyright 2019 Canonical Ltd
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import mock
|
||||
|
||||
import test_utils
|
||||
|
||||
from hooks import utils
|
||||
|
||||
|
||||
class CephUtilsTestCase(test_utils.CharmTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
@mock.patch.object(utils, 'related_units')
|
||||
@mock.patch.object(utils, 'relation_ids')
|
||||
def test_has_rbd_mirrors(self, _relation_ids, _related_units):
|
||||
# NOTE(fnordahl): This optimization will not be useful until we get a
|
||||
# resolution on LP: #1818245
|
||||
# _goal_state.return_value = {'relations': {'rbd-mirror': None}}
|
||||
# self.assertTrue(utils.has_rbd_mirrors())
|
||||
# _goal_state.assert_called_once_with()
|
||||
# _goal_state.side_effect = NotImplementedError
|
||||
_relation_ids.return_value = ['arelid']
|
||||
_related_units.return_value = ['aunit/0']
|
||||
self.assertTrue(utils.has_rbd_mirrors())
|
||||
_relation_ids.assert_called_once_with('rbd-mirror')
|
||||
_related_units.assert_called_once_with('arelid')
|
||||
|
||||
@mock.patch.object(utils.subprocess, 'check_output')
|
||||
def test_get_default_rbd_features(self, _check_output):
|
||||
_check_output.return_value = ('a = b\nrbd_default_features = 61\n'
|
||||
'c = d\n')
|
||||
self.assertEquals(
|
||||
utils.get_default_rbd_features(),
|
||||
61)
|
||||
_check_output.assert_called_once_with(
|
||||
['ceph', '-c', '/dev/null', '--show-config'],
|
||||
universal_newlines=True)
|
||||
|
||||
@mock.patch.object(utils, 'get_default_rbd_features')
|
||||
@mock.patch.object(utils, 'has_rbd_mirrors')
|
||||
@mock.patch.object(utils, 'config')
|
||||
def test_get_rbd_features(self, _config, _has_rbd_mirrors,
|
||||
_get_default_rbd_features):
|
||||
_config.side_effect = \
|
||||
lambda key: {'default-rbd-features': 42}.get(key, None)
|
||||
self.assertEquals(utils.get_rbd_features(), 42)
|
||||
_has_rbd_mirrors.return_value = True
|
||||
_get_default_rbd_features.return_value = 61
|
||||
_config.side_effect = lambda key: {}.get(key, None)
|
||||
self.assertEquals(utils.get_rbd_features(), 125)
|
||||
_has_rbd_mirrors.return_value = False
|
||||
self.assertEquals(utils.get_rbd_features(), None)
|
Loading…
Reference in New Issue