Pass on osd settings from clients to osd units

If a client application has requested particular osd settings then
validate them and pass them on to the osd units.

Change-Id: Iad7132b3e6b34038ecd9a61fbb9589b22117396d
This commit is contained in:
Liam Young 2020-03-27 11:13:35 +00:00
parent 544a0e0a38
commit 6afe8288a8
5 changed files with 80 additions and 35 deletions

View File

@ -80,7 +80,10 @@ from charmhelpers.core.sysctl import create as create_sysctl
from charmhelpers.core.templating import render
from charmhelpers.contrib.storage.linux.ceph import (
CephConfContext,
OSD_SETTING_EXCEPTIONS,
enable_pg_autoscale,
get_osd_settings,
send_osd_settings,
)
from utils import (
add_rbd_mirror_features,
@ -682,6 +685,7 @@ def osd_relation(relid=None, unit=None):
notify_radosgws()
notify_client()
notify_rbd_mirrors()
send_osd_settings()
else:
log('mon cluster not in quorum - deferring fsid provision')
@ -853,6 +857,7 @@ def admin_relation_joined(relid=None):
@hooks.hook('client-relation-changed')
@hooks.hook('client-relation-joined')
def client_relation(relid=None, unit=None):
send_osd_settings()
if ready_for_service():
log('mon cluster in quorum and osds bootstrapped '
'- providing client with keys, processing broker requests')
@ -1020,6 +1025,12 @@ def assess_status():
'``default-rbd-features``')
return
try:
get_osd_settings('client')
except OSD_SETTING_EXCEPTIONS as e:
status_set('blocked', str(e))
return
# active - bootstrapped + quorum status check
if ceph.is_bootstrapped() and ceph.is_quorum():
expected_osd_count = config('expected-osd-count') or 3

View File

@ -36,7 +36,7 @@ from charmhelpers.contrib.network import ip
from charmhelpers.core import unitdata
from charmhelpers.core.hookenv import (
WL_STATES,
WORKLOAD_STATES,
action_fail,
action_set,
config,
@ -1827,7 +1827,7 @@ def os_application_status_set(check_function):
:type check_function: function
"""
state, message = check_function()
status_set(state, message, application_status=True)
status_set(state, message, application=True)
def enable_memcache(source=None, release=None, package=None):
@ -2295,7 +2295,7 @@ def check_api_unit_ready(check_db_ready=True):
:rtype: (bool, str)
"""
unit_state, msg = get_api_unit_status(check_db_ready=check_db_ready)
return unit_state == WL_STATES.ACTIVE, msg
return unit_state == WORKLOAD_STATES.ACTIVE, msg
def get_api_unit_status(check_db_ready=True):
@ -2306,22 +2306,22 @@ def get_api_unit_status(check_db_ready=True):
:returns: Workload state and message
:rtype: (bool, str)
"""
unit_state = WL_STATES.ACTIVE
unit_state = WORKLOAD_STATES.ACTIVE
msg = 'Unit is ready'
if is_db_maintenance_mode():
unit_state = WL_STATES.MAINTENANCE
unit_state = WORKLOAD_STATES.MAINTENANCE
msg = 'Database in maintenance mode.'
elif is_unit_paused_set():
unit_state = WL_STATES.BLOCKED
unit_state = WORKLOAD_STATES.BLOCKED
msg = 'Unit paused.'
elif check_db_ready and not is_db_ready():
unit_state = WL_STATES.WAITING
unit_state = WORKLOAD_STATES.WAITING
msg = 'Allowed_units list provided but this unit not present'
elif not is_db_initialised():
unit_state = WL_STATES.WAITING
unit_state = WORKLOAD_STATES.WAITING
msg = 'Database not initialised'
elif not is_expected_scale():
unit_state = WL_STATES.WAITING
unit_state = WORKLOAD_STATES.WAITING
msg = 'Charm and its dependencies not yet at expected scale'
juju_log(msg, 'DEBUG')
return unit_state, msg
@ -2334,7 +2334,7 @@ def check_api_application_ready():
:rtype: (bool, str)
"""
app_state, msg = get_api_application_status()
return app_state == WL_STATES.ACTIVE, msg
return app_state == WORKLOAD_STATES.ACTIVE, msg
def get_api_application_status():
@ -2344,9 +2344,9 @@ def get_api_application_status():
:rtype: (bool, str)
"""
app_state, msg = get_api_unit_status()
if app_state == WL_STATES.ACTIVE:
if app_state == WORKLOAD_STATES.ACTIVE:
if are_peers_ready():
return WL_STATES.ACTIVE, 'Application Ready'
return WORKLOAD_STATES.ACTIVE, 'Application Ready'
else:
return WL_STATES.WAITING, 'Some units are not ready'
return WORKLOAD_STATES.WAITING, 'Some units are not ready'
return app_state, msg

View File

@ -59,7 +59,7 @@ RANGE_WARNING = ('Passing NO_PROXY string that includes a cidr. '
'running in your shell.')
class WL_STATES(Enum):
class WORKLOAD_STATES(Enum):
ACTIVE = 'active'
BLOCKED = 'blocked'
MAINTENANCE = 'maintenance'
@ -1097,31 +1097,33 @@ def function_tag():
return os.environ.get('JUJU_FUNCTION_TAG') or action_tag()
def status_set(workload_state, message, application_status=False):
def status_set(workload_state, message, application=False):
"""Set the workload state with a message
Use status-set to set the workload state with a message which is visible
to the user via juju status. If the status-set command is not found then
assume this is juju < 1.23 and juju-log the message instead.
workload_state -- valid juju workload state. str or WL_STATES
message -- status update message
application_status -- Whether this is an application state set
workload_state -- valid juju workload state. str or WORKLOAD_STATES
message -- status update message
application -- Whether this is an application state set
"""
# Extract the value if workload_state is an Enum
try:
workload_state = workload_state.value
except AttributeError:
pass
workload_state = workload_state.lower()
if workload_state not in [s.lower() for s in WL_STATES.__members__.keys()]:
raise ValueError(
'{!r} is not a valid workload state'.format(workload_state)
)
bad_state_msg = '{!r} is not a valid workload state'
if isinstance(workload_state, str):
try:
# Convert string to enum.
workload_state = WORKLOAD_STATES[workload_state.upper()]
except KeyError:
raise ValueError(bad_state_msg.format(workload_state))
if workload_state not in WORKLOAD_STATES:
raise ValueError(bad_state_msg.format(workload_state))
cmd = ['status-set']
if application_status:
if application:
cmd.append('--application')
cmd.extend([workload_state, message])
cmd.extend([workload_state.value, message])
try:
ret = subprocess.call(cmd)
if ret == 0:
@ -1129,7 +1131,7 @@ def status_set(workload_state, message, application_status=False):
except OSError as e:
if e.errno != errno.ENOENT:
raise
log_message = 'status-set failed: {} {}'.format(workload_state,
log_message = 'status-set failed: {} {}'.format(workload_state.value,
message)
log(log_message, level='INFO')

View File

@ -407,6 +407,7 @@ class RelatedUnitsTestCase(unittest.TestCase):
call('osd:23')
])
@patch.object(ceph_hooks, 'send_osd_settings')
@patch.object(ceph_hooks, 'get_rbd_features')
@patch.object(ceph_hooks, 'relation_set')
@patch.object(ceph_hooks, 'handle_broker_request')
@ -423,7 +424,8 @@ class RelatedUnitsTestCase(unittest.TestCase):
_config,
_handle_broker_request,
_relation_set,
_get_rbd_features):
_get_rbd_features,
_send_osd_settings):
_remote_service_name.return_value = 'glance'
config = copy.deepcopy(CHARM_CONFIG)
_config.side_effect = lambda key: config[key]
@ -431,6 +433,7 @@ class RelatedUnitsTestCase(unittest.TestCase):
_get_rbd_features.return_value = None
ceph_hooks.client_relation(relid='rel1', unit='glance/0')
_ready_for_service.assert_called_once_with()
_send_osd_settings.assert_called_once_with()
_get_public_addr.assert_called_once_with()
_get_named_key.assert_called_once_with('glance')
_handle_broker_request.assert_called_once_with(
@ -454,6 +457,7 @@ class RelatedUnitsTestCase(unittest.TestCase):
'rbd-features': 42,
})
@patch.object(ceph_hooks, 'send_osd_settings')
@patch.object(ceph_hooks, 'get_rbd_features')
@patch.object(ceph_hooks, 'config')
@patch.object(ceph_hooks.ceph, 'get_named_key')
@ -479,7 +483,8 @@ class RelatedUnitsTestCase(unittest.TestCase):
get_public_addr,
get_named_key,
_config,
_get_rbd_features):
_get_rbd_features,
_send_osd_settings):
# Check for LP #1738154
ready_for_service.return_value = True
process_requests.return_value = 'AOK'
@ -491,6 +496,7 @@ class RelatedUnitsTestCase(unittest.TestCase):
_config.side_effect = lambda key: config[key]
_get_rbd_features.return_value = None
ceph_hooks.client_relation(relid='rel1', unit='glance/0')
_send_osd_settings.assert_called_once_with()
relation_set.assert_called_once_with(
relation_id='rel1',
relation_settings={

View File

@ -17,6 +17,8 @@ import sys
import test_utils
import charmhelpers.contrib.storage.linux.ceph as ch_ceph
# python-apt is not installed as part of test-requirements but is imported by
# some charmhelpers modules so create a fake import.
mock_apt = mock.MagicMock()
@ -81,32 +83,56 @@ class ServiceStatusTestCase(test_utils.CharmTestCase):
self.status_set.assert_called_with('waiting', mock.ANY)
self.application_version_set.assert_called_with('10.2.2')
@mock.patch.object(hooks, 'get_osd_settings')
@mock.patch.object(hooks, 'has_rbd_mirrors')
@mock.patch.object(hooks, 'sufficient_osds')
@mock.patch.object(hooks, 'get_peer_units')
def test_assess_status_peers_complete_active(self, _peer_units,
_sufficient_osds,
_has_rbd_mirrors):
_has_rbd_mirrors,
_get_osd_settings):
_peer_units.return_value = ENOUGH_PEERS_COMPLETE
_sufficient_osds.return_value = True
self.ceph.is_bootstrapped.return_value = True
self.ceph.is_quorum.return_value = True
_has_rbd_mirrors.return_value = False
_get_osd_settings.return_value = {}
hooks.assess_status()
self.status_set.assert_called_with('active', mock.ANY)
self.application_version_set.assert_called_with('10.2.2')
@mock.patch.object(hooks, 'get_osd_settings')
@mock.patch.object(hooks, 'has_rbd_mirrors')
@mock.patch.object(hooks, 'sufficient_osds')
@mock.patch.object(hooks, 'get_peer_units')
def test_assess_status_invalid_osd_settings(self, _peer_units,
_sufficient_osds,
_has_rbd_mirrors,
_get_osd_settings):
_peer_units.return_value = ENOUGH_PEERS_COMPLETE
_sufficient_osds.return_value = True
self.ceph.is_bootstrapped.return_value = True
self.ceph.is_quorum.return_value = True
_has_rbd_mirrors.return_value = False
_get_osd_settings.side_effect = ch_ceph.OSDSettingConflict(
'conflict in setting foo')
hooks.assess_status()
self.status_set.assert_called_with('blocked', mock.ANY)
@mock.patch.object(hooks, 'get_osd_settings')
@mock.patch.object(hooks, 'has_rbd_mirrors')
@mock.patch.object(hooks, 'sufficient_osds')
@mock.patch.object(hooks, 'get_peer_units')
def test_assess_status_peers_complete_down(self, _peer_units,
_sufficient_osds,
_has_rbd_mirrors):
_has_rbd_mirrors,
_get_osd_settings):
_peer_units.return_value = ENOUGH_PEERS_COMPLETE
_sufficient_osds.return_value = True
self.ceph.is_bootstrapped.return_value = False
self.ceph.is_quorum.return_value = False
_has_rbd_mirrors.return_value = False
_get_osd_settings.return_value = {}
hooks.assess_status()
self.status_set.assert_called_with('blocked', mock.ANY)
self.application_version_set.assert_called_with('10.2.2')