Add apply_osd_settings

Add apply_osd_settings for applying osd settings to running osd
daemons.

Change-Id: If8fa9020d9cbe2c8fcf981abd737653173926341
This commit is contained in:
Liam Young 2020-03-30 13:02:39 +00:00
parent 341b5c5bf4
commit 4fc32cc401
2 changed files with 157 additions and 0 deletions

View File

@ -3067,3 +3067,57 @@ def osd_noout(enable):
except subprocess.CalledProcessError as e:
log(e)
raise
class OSDConfigSetError(Exception):
"""Error occured applying OSD settings."""
pass
def apply_osd_settings(settings):
"""Applies the provided osd settings
Apply the provided settings to all local OSD unless settings are already
present. Settings stop being applied on encountering an error.
:param settings: dict. Dictionary of settings to apply.
:returns: bool. True if commands ran succesfully.
:raises: OSDConfigSetError
"""
current_settings = {}
base_cmd = 'ceph daemon osd.{osd_id} config --format=json'
get_cmd = base_cmd + ' get {key}'
set_cmd = base_cmd + ' set {key} {value}'
def _get_cli_key(key):
return(key.replace(' ', '_'))
# Retrieve the current values to check keys are correct and to make this a
# noop if setting are already applied.
for osd_id in get_local_osd_ids():
for key, value in sorted(settings.items()):
cli_key = _get_cli_key(key)
cmd = get_cmd.format(osd_id=osd_id, key=cli_key)
out = json.loads(
subprocess.check_output(cmd.split()).decode('UTF-8'))
if 'error' in out:
log("Error retrieving osd setting: {}".format(out['error']),
level=ERROR)
return False
current_settings[key] = out[cli_key]
settings_diff = {
k: v
for k, v in settings.items()
if str(v) != str(current_settings[k])}
for key, value in sorted(settings_diff.items()):
log("Setting {} to {}".format(key, value), level=DEBUG)
cmd = set_cmd.format(
osd_id=osd_id,
key=_get_cli_key(key),
value=value)
out = json.loads(
subprocess.check_output(cmd.split()).decode('UTF-8'))
if 'error' in out:
log("Error applying osd setting: {}".format(out['error']),
level=ERROR)
raise OSDConfigSetError
return True

View File

@ -1101,6 +1101,109 @@ class CephTestCase(unittest.TestCase):
})
class CephApplyOSDSettingsTestCase(unittest.TestCase):
def setUp(self):
super(CephApplyOSDSettingsTestCase, self).setUp()
self.base_cmd = 'ceph daemon osd.{osd_id} config --format=json'
self.get_cmd = self.base_cmd + ' get {key}'
self.set_cmd = self.base_cmd + ' set {key} {value}'
self.grace = 'osd_heartbeat_grace'
self.interval = 'osd_heartbeat_interval'
@patch.object(utils, 'get_local_osd_ids')
@patch.object(utils.subprocess, 'check_output')
def test_apply_osd_settings(self, _check_output, _get_local_osd_ids):
_get_local_osd_ids.return_value = ['0']
output = {
self.get_cmd.format(osd_id=0, key=self.grace):
b'{"osd_heartbeat_grace":"19"}',
self.set_cmd.format(osd_id=0, key=self.grace, value='21'):
b"""{"success":"osd_heartbeat_grace = '21'"}"""}
_check_output.side_effect = lambda x: output[' '.join(x)]
self.assertTrue(
utils.apply_osd_settings({'osd heartbeat grace': '21'}))
check_output_calls = [
call(['ceph', 'daemon', 'osd.0', 'config', '--format=json', 'get',
'osd_heartbeat_grace']),
call(['ceph', 'daemon', 'osd.0', 'config', '--format=json', 'set',
'osd_heartbeat_grace', '21'])]
_check_output.assert_has_calls(check_output_calls)
self.assertTrue(_check_output.call_count == len(check_output_calls))
@patch.object(utils, 'get_local_osd_ids')
@patch.object(utils.subprocess, 'check_output')
def test_apply_osd_settings_noop_on_one_osd(self, _check_output,
_get_local_osd_ids):
_get_local_osd_ids.return_value = ['0', '1']
output = {
self.get_cmd.format(osd_id=0, key=self.grace):
b'{"osd_heartbeat_grace":"21"}',
self.get_cmd.format(osd_id=1, key=self.grace):
b'{"osd_heartbeat_grace":"20"}',
self.set_cmd.format(osd_id=1, key=self.grace, value='21'):
b"""{"success":"osd_heartbeat_interval = '2'"}"""}
_check_output.side_effect = lambda x: output[' '.join(x)]
self.assertTrue(
utils.apply_osd_settings({'osd heartbeat grace': '21'}))
check_output_calls = [
call(['ceph', 'daemon', 'osd.0', 'config', '--format=json', 'get',
'osd_heartbeat_grace']),
call(['ceph', 'daemon', 'osd.1', 'config', '--format=json', 'get',
'osd_heartbeat_grace']),
call(['ceph', 'daemon', 'osd.1', 'config', '--format=json', 'set',
'osd_heartbeat_grace', '21'])]
_check_output.assert_has_calls(check_output_calls)
self.assertTrue(_check_output.call_count == len(check_output_calls))
@patch.object(utils, 'get_local_osd_ids')
@patch.object(utils.subprocess, 'check_output')
def _test_apply_osd_settings_error(self, _check_output,
_get_local_osd_ids):
_get_local_osd_ids.return_value = ['0']
output = {
self.get_cmd.format(osd_id=0, key=self.grace):
b"""{"error":"error setting 'osd_heartbeat_grace'"}"""}
_check_output.side_effect = lambda x: output[' '.join(x)]
self.assertFalse(
utils.apply_osd_settings({'osd heartbeat grace': '21'}))
check_output_calls = [
call(['ceph', 'daemon', 'osd.0', 'config', '--format=json', 'get',
'osd_heartbeat_grace'])]
_check_output.assert_has_calls(check_output_calls)
self.assertTrue(_check_output.call_count == len(check_output_calls))
@patch.object(utils, 'get_local_osd_ids')
@patch.object(utils.subprocess, 'check_output')
def test_apply_osd_settings_error(self, _check_output,
_get_local_osd_ids):
_get_local_osd_ids.return_value = ['0', '1']
output = {
self.get_cmd.format(osd_id=0, key=self.grace):
b'{"osd_heartbeat_grace":"19"}',
self.get_cmd.format(osd_id=0, key=self.interval):
b'{"osd_heartbeat_interval":"3"}',
self.set_cmd.format(osd_id=0, key=self.interval, value='2'):
b"""{"success":"osd_heartbeat_interval = '2'"}""",
self.set_cmd.format(osd_id=0, key=self.grace, value='21'):
b"""{"error":"error setting 'osd_heartbeat_grace'"}"""}
_check_output.side_effect = lambda x: output[' '.join(x)]
check_output_calls = [
call(['ceph', 'daemon', 'osd.0', 'config', '--format=json', 'get',
'osd_heartbeat_grace']),
call(['ceph', 'daemon', 'osd.0', 'config', '--format=json', 'get',
'osd_heartbeat_interval']),
call(['ceph', 'daemon', 'osd.0', 'config', '--format=json', 'set',
'osd_heartbeat_grace', '21'])]
with self.assertRaises(utils.OSDConfigSetError):
utils.apply_osd_settings({
'osd heartbeat grace': '21',
'osd heartbeat interval': '2'})
_check_output.assert_has_calls(check_output_calls)
self.assertTrue(
_check_output.call_count == len(check_output_calls))
class CephVolumeSizeCalculatorTestCase(unittest.TestCase):
@patch.object(utils, 'get_conf')