Config modifications are now done using wconfd RPCs. However, offline cluster updates are still allowed.
Added wrappers CommitTemporaryIps, and VerifyConfigAndLog to perform read only config operations with lock. Add a mock for Update function, and use the mock config object in two config tests. Remove config tests on Update Signed-off-by: BSRK Aditya <[email protected]> --- lib/config/__init__.py | 69 ++++++++++++++++++------------------- test/py/ganeti.config_unittest.py | 58 ++----------------------------- test/py/testutils/config_mock.py | 26 ++++++++++++++ 3 files changed, 61 insertions(+), 92 deletions(-) diff --git a/lib/config/__init__.py b/lib/config/__init__.py index cc6efe7..14f2466 100644 --- a/lib/config/__init__.py +++ b/lib/config/__init__.py @@ -668,6 +668,11 @@ class ConfigWriter(object): """ self._wconfd.ReserveMAC(self._GetWConfdContext(), mac) + @ConfigSync(shared=1) + def CommitTemporaryIps(self, _ec_id): + """A simple wrapper around L{_UnlockedCommitTemporaryIps}""" + self._UnlockedCommitTemporaryIps(_ec_id) + def _UnlockedCommitTemporaryIps(self, _ec_id): """Commit all reserved IP address to their respective pools @@ -1092,6 +1097,11 @@ class ConfigWriter(object): return result + @ConfigSync(shared=1) + def VerifyConfigAndLog(self, feedback_fn=None): + """A simple wrapper around L{_UnlockedVerifyConfigAndLog}""" + return self._UnlockedVerifyConfigAndLog(feedback_fn=feedback_fn) + def _UnlockedVerifyConfigAndLog(self, feedback_fn=None): """Verify the configuration and log any errors. @@ -3102,7 +3112,6 @@ class ConfigWriter(object): """ return DetachedConfig(self._ConfigData()) - @ConfigSync() def Update(self, target, feedback_fn, ec_id=None): """Notify function to be called after updates. @@ -3118,60 +3127,48 @@ class ConfigWriter(object): @param feedback_fn: Callable feedback function """ - if self._ConfigData() is None: - raise errors.ProgrammerError("Configuration file not read," - " cannot save.") - - def check_serial(target, current): - if current is None: - raise errors.ConfigurationError("Configuration object unknown") - elif current.serial_no != target.serial_no: - raise errors.ConfigurationError("Configuration object updated since" - " it has been read: %d != %d", - current.serial_no, target.serial_no) - def replace_in(target, tdict): - check_serial(target, tdict.get(target.uuid)) - tdict[target.uuid] = target - - update_serial = False + update_function = None if isinstance(target, objects.Cluster): - check_serial(target, self._ConfigData().cluster) - self._ConfigData().cluster = target + if self._offline: + self.UpdateOfflineCluster(target, feedback_fn) + return + else: + update_function = self._wconfd.UpdateCluster elif isinstance(target, objects.Node): - replace_in(target, self._ConfigData().nodes) - update_serial = True + update_function = self._wconfd.UpdateNode elif isinstance(target, objects.Instance): - replace_in(target, self._ConfigData().instances) + update_function = self._wconfd.UpdateInstance elif isinstance(target, objects.NodeGroup): - replace_in(target, self._ConfigData().nodegroups) + update_function = self._wconfd.UpdateNodeGroup elif isinstance(target, objects.Network): - replace_in(target, self._ConfigData().networks) + update_function = self._wconfd.UpdateNetwork elif isinstance(target, objects.Disk): - replace_in(target, self._ConfigData().disks) + update_function = self._wconfd.UpdateDisk else: raise errors.ProgrammerError("Invalid object type (%s) passed to" " ConfigWriter.Update" % type(target)) - target.serial_no += 1 - target.mtime = now = time.time() - - if update_serial: - # for node updates, we need to increase the cluster serial too - self._ConfigData().cluster.serial_no += 1 - self._ConfigData().cluster.mtime = now - if isinstance(target, objects.Disk): - self._UnlockedReleaseDRBDMinors(target.uuid) + utils.SimpleRetry(True, update_function, 0.1, 30, + args=[target.ToDict()]) + self.OutDate() if ec_id is not None: # Commit all ips reserved by OpInstanceSetParams and OpGroupSetParams # FIXME: After RemoveInstance is moved to WConfd, use its internal # functions from TempRes module. - self._UnlockedCommitTemporaryIps(ec_id) + self.CommitTemporaryIps(ec_id) # Just verify the configuration with our feedback function. # It will get written automatically by the decorator. - self._UnlockedVerifyConfigAndLog(feedback_fn=feedback_fn) + self.VerifyConfigAndLog(feedback_fn=feedback_fn) + + @ConfigSync() + def UpdateOfflineCluster(self, target, feedback_fn): + self._ConfigData().cluster = target + target.serial_no += 1 + target.mtime = time.time() + self.VerifyConfigAndLog(feedback_fn=feedback_fn) def _UnlockedDropECReservations(self, _ec_id): """Drop per-execution-context reservations diff --git a/test/py/ganeti.config_unittest.py b/test/py/ganeti.config_unittest.py index e56c723..2940a0a 100755 --- a/test/py/ganeti.config_unittest.py +++ b/test/py/ganeti.config_unittest.py @@ -303,60 +303,6 @@ class TestConfigRunner(unittest.TestCase): node2.uuid: ["myxenvg/disk0", "myxenvg/meta0"], }) - def testUpdateCluster(self): - """Test updates on the cluster object""" - cfg = self._get_object() - # construct a fake cluster object - fake_cl = objects.Cluster() - # fail if we didn't read the config - self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_cl, None) - - cl = cfg.GetClusterInfo() - # first pass, must not fail - cfg.Update(cl, None) - # second pass, also must not fail (after the config has been written) - cfg.Update(cl, None) - # but the fake_cl update should still fail - self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_cl, None) - - def testUpdateNode(self): - """Test updates on one node object""" - cfg = self._get_object() - # construct a fake node - fake_node = objects.Node() - # fail if we didn't read the config - self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_node, - None) - - node = cfg.GetNodeInfo(cfg.GetNodeList()[0]) - # first pass, must not fail - cfg.Update(node, None) - # second pass, also must not fail (after the config has been written) - cfg.Update(node, None) - # but the fake_node update should still fail - self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_node, - None) - - def testUpdateInstance(self): - """Test updates on one instance object""" - cfg = self._get_object_mock() - # construct a fake instance - inst = self._create_instance(cfg) - fake_instance = objects.Instance() - # fail if we didn't read the config - self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_instance, - None) - - cfg.AddInstance(inst, "my-job") - instance = cfg.GetInstanceInfo(cfg.GetInstanceList()[0]) - # first pass, must not fail - cfg.Update(instance, None) - # second pass, also must not fail (after the config has been written) - cfg.Update(instance, None) - # but the fake_instance update should still fail - self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_instance, - None) - def testUpgradeSave(self): """Test that any modification done during upgrading is saved back""" cfg = self._get_object() @@ -430,7 +376,7 @@ class TestConfigRunner(unittest.TestCase): constants.ND_CPU_SPEED: 1.0, } - cfg = self._get_object() + cfg = self._get_object_mock() node = cfg.GetNodeInfo(cfg.GetNodeList()[0]) node.ndparams = my_ndparams cfg.Update(node, None) @@ -458,7 +404,7 @@ class TestConfigRunner(unittest.TestCase): constants.ND_SSH_PORT: 222, constants.ND_CPU_SPEED: 1.0, } - cfg = self._get_object() + cfg = self._get_object_mock() node = cfg.GetNodeInfo(cfg.GetNodeList()[0]) node.ndparams = node_ndparams cfg.Update(node, None) diff --git a/test/py/testutils/config_mock.py b/test/py/testutils/config_mock.py index e994bda..7c06fbc 100644 --- a/test/py/testutils/config_mock.py +++ b/test/py/testutils/config_mock.py @@ -892,3 +892,29 @@ class ConfigMock(config.ConfigWriter): def AllocatePort(self): return 1 + + def Update(self, target, feedback_fn, ec_id=None): + def replace_in(target, tdict): + tdict[target.uuid] = target + + update_serial = False + if isinstance(target, objects.Cluster): + self._ConfigData().cluster = target + elif isinstance(target, objects.Node): + replace_in(target, self._ConfigData().nodes) + update_serial = True + elif isinstance(target, objects.Instance): + replace_in(target, self._ConfigData().instances) + elif isinstance(target, objects.NodeGroup): + replace_in(target, self._ConfigData().nodegroups) + elif isinstance(target, objects.Network): + replace_in(target, self._ConfigData().networks) + elif isinstance(target, objects.Disk): + replace_in(target, self._ConfigData().disks) + + target.serial_no += 1 + target.mtime = now = time.time() + + if update_serial: + self._ConfigData().cluster.serial_no += 1 # pylint: disable=E1103 + self._ConfigData().cluster.mtime = now -- 1.7.10.4
