diff --git a/qa/tasks/mgr/mgr_test_case.py b/qa/tasks/mgr/mgr_test_case.py index ea9d6a3b15d..b73a6a296e1 100644 --- a/qa/tasks/mgr/mgr_test_case.py +++ b/qa/tasks/mgr/mgr_test_case.py @@ -48,13 +48,13 @@ class MgrCluster(CephCluster): return [s['name'] for s in self.get_mgr_map()["standbys"]] def set_module_conf(self, module, key, val): - self.mon_manager.raw_cluster_cmd("config-key", "set", + self.mon_manager.raw_cluster_cmd("config", "set", "mgr", "mgr/{0}/{1}".format( module, key ), val) def set_module_localized_conf(self, module, mgr_id, key, val): - self.mon_manager.raw_cluster_cmd("config-key", "set", + self.mon_manager.raw_cluster_cmd("config", "set", "mgr", "mgr/{0}/{1}/{2}".format( module, mgr_id, key ), val) diff --git a/qa/tasks/mgr/test_module_selftest.py b/qa/tasks/mgr/test_module_selftest.py index c984b99589b..63531fd5eff 100644 --- a/qa/tasks/mgr/test_module_selftest.py +++ b/qa/tasks/mgr/test_module_selftest.py @@ -2,10 +2,13 @@ import time import requests import errno +import logging from teuthology.exceptions import CommandFailedError from tasks.mgr.mgr_test_case import MgrTestCase +log = logging.getLogger(__name__) + class TestModuleSelftest(MgrTestCase): """ @@ -49,6 +52,111 @@ class TestModuleSelftest(MgrTestCase): self._load_module("selftest") self.mgr_cluster.mon_manager.raw_cluster_cmd("mgr", "self-test", "run") + def test_selftest_config_update(self): + """ + That configuration updates are seen by running mgr modules + """ + self._load_module("selftest") + + def get_value(): + return self.mgr_cluster.mon_manager.raw_cluster_cmd( + "mgr", "self-test", "config", "get", "testkey").strip() + + self.assertEqual(get_value(), "None") + + self.mgr_cluster.mon_manager.raw_cluster_cmd("config", "set", + "mgr", "mgr/selftest/testkey", "testvalue") + + self.wait_until_equal(get_value, "testvalue",timeout=10) + + active_id = self.mgr_cluster.get_active_id() + + def get_localized_value(): + return self.mgr_cluster.mon_manager.raw_cluster_cmd( + "mgr", "self-test", "config", "get_localized", "testlkey").strip() + + self.mgr_cluster.mon_manager.raw_cluster_cmd("config", "set", + "mgr", "mgr/selftest/{0}/testlkey".format(active_id), + "test localized value") + + self.wait_until_equal(get_localized_value, "test localized value", + timeout=10) + + def test_selftest_config_upgrade(self): + """ + That pre-mimic config-key config settings are migrated into + mimic-style config settings and visible from mgr modules. + """ + self._load_module("selftest") + + def get_value(): + return self.mgr_cluster.mon_manager.raw_cluster_cmd( + "mgr", "self-test", "config", "get", "testkey").strip() + + def get_config(): + lines = self.mgr_cluster.mon_manager.raw_cluster_cmd( + "config", "dump")\ + .strip().split("\n") + result = [] + for line in lines[1:]: + tokens = line.strip().split() + log.info("tokens: {0}".format(tokens)) + subsys, key, value = tokens[0], tokens[2], tokens[3] + result.append((subsys, key, value)) + + return result + + # Stop ceph-mgr while we synthetically create a pre-mimic + # configuration scenario + for mgr_id in self.mgr_cluster.mgr_daemons.keys(): + self.mgr_cluster.mgr_stop(mgr_id) + self.mgr_cluster.mgr_fail(mgr_id) + + # Blow away any modern-style mgr module config options + # (the ceph-mgr implementation may only do the upgrade if + # it doesn't see new style options) + stash = [] + for subsys, key, value in get_config(): + if subsys == "mgr" and key.startswith("mgr/"): + log.info("Removing config key {0} ahead of upgrade".format( + key)) + self.mgr_cluster.mon_manager.raw_cluster_cmd( + "config", "rm", subsys, key) + stash.append((subsys, key, value)) + + # Inject an old-style configuration setting in config-key + self.mgr_cluster.mon_manager.raw_cluster_cmd( + "config-key", "set", "mgr/selftest/testkey", "testvalue") + + # Inject configuration settings that looks data-ish and should + # not be migrated to a config key + self.mgr_cluster.mon_manager.raw_cluster_cmd( + "config-key", "set", "mgr/selftest/testnewline", "foo\nbar") + + # Bring mgr daemons back online, the one that goes active + # should be doing the upgrade. + for mgr_id in self.mgr_cluster.mgr_daemons.keys(): + self.mgr_cluster.mgr_restart(mgr_id) + + # Wait for a new active + self.wait_until_true( + lambda: self.mgr_cluster.get_active_id() != "", timeout=30) + + # Check that the selftest module sees the upgraded value + self.assertEqual(get_value(), "testvalue") + + # Check that the upgraded value is visible in the configuration + seen_keys = [k for s,k,v in get_config()] + self.assertIn("mgr/selftest/testkey", seen_keys) + + # And that the non-config-looking one isn't + self.assertNotIn("mgr/selftest/testnewline", seen_keys) + + # Restore previous configuration + for subsys, key, value in stash: + self.mgr_cluster.mon_manager.raw_cluster_cmd( + "config", "set", subsys, key, value) + def test_selftest_command_spam(self): # Use the selftest module to stress the mgr daemon self._load_module("selftest") diff --git a/src/pybind/mgr/selftest/module.py b/src/pybind/mgr/selftest/module.py index b9530c43b14..ae8f50c47d5 100644 --- a/src/pybind/mgr/selftest/module.py +++ b/src/pybind/mgr/selftest/module.py @@ -29,23 +29,31 @@ class Module(MgrModule): { "cmd": "mgr self-test run", "desc": "Run mgr python interface tests", - "perm": "r" + "perm": "rw" }, { "cmd": "mgr self-test background start name=workload,type=CephString", "desc": "Activate a background workload (one of {0})".format( ", ".join(WORKLOADS)), - "perm": "r" + "perm": "rw" }, { "cmd": "mgr self-test background stop", "desc": "Stop background workload if any is running", - "perm": "r" + "perm": "rw" + }, + { + "cmd": "mgr self-test config get name=key,type=CephString", + "desc": "Peek at a configuration value", + "perm": "rw" + }, + { + "cmd": "mgr self-test config get_localized name=key,type=CephString", + "desc": "Peek at a configuration value (localized variant)", + "perm": "rw" }, ] - - def __init__(self, *args, **kwargs): super(Module, self).__init__(*args, **kwargs) self._event = threading.Event() @@ -73,7 +81,10 @@ class Module(MgrModule): was_running) else: return 0, '', 'No background workload was running' - + elif command['prefix'] == 'mgr self-test config get': + return 0, str(self.get_config(command['key'])), '' + elif command['prefix'] == 'mgr self-test config get_localized': + return 0, str(self.get_localized_config(command['key'])), '' else: return (-errno.EINVAL, '', "Command not found '{0}'".format(command['prefix'])) @@ -139,16 +150,18 @@ class Module(MgrModule): self.set_localized_config("testkey", "testvalue") assert self.get_localized_config("testkey") == "testvalue" - assert sorted(self.get_config_prefix("test").keys()) == sorted( - ["testkey"]) - def _self_test_store(self): + existing_keys = set(self.get_store_prefix("test").keys()) self.set_store("testkey", "testvalue") assert self.get_store("testkey") == "testvalue" self.set_store_json("testjsonkey", {"testblob": 2}) assert self.get_store_json("testjsonkey") == {"testblob": 2} + assert sorted(self.get_store_prefix("test").keys()) == sorted( + list({"testkey", "testjsonkey"} | existing_keys)) + + def _self_test_perf_counters(self): self.get_perf_schema("osd", "0") self.get_counter("osd", "0", "osd.op")