diff --git a/lib/backend.py b/lib/backend.py index 179e9f5773fe7932ae2061b49354df9bb80bbfb2..ccb6a17843d287c5a51d26a45373ca4f601e2a6e 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -64,6 +64,7 @@ from ganeti import mcpu from ganeti import compat from ganeti import pathutils from ganeti import vcluster +from ganeti import ht _BOOT_ID_PATH = "/proc/sys/kernel/random/boot_id" @@ -3768,6 +3769,25 @@ def RunRestrictedCmd(cmd, lock = None +def SetWatcherPause(until, _filename=pathutils.WATCHER_PAUSEFILE): + """Creates or removes the watcher pause file. + + @type until: None or number + @param until: Unix timestamp saying until when the watcher shouldn't run + + """ + if until is None: + logging.info("Received request to no longer pause watcher") + utils.RemoveFile(_filename) + else: + logging.info("Received request to pause watcher until %s", until) + + if not ht.TNumber(until): + _Fail("Duration must be numeric") + + utils.WriteFile(_filename, data="%d\n" % (until, ), mode=0644) + + class HooksRunner(object): """Hook runner. diff --git a/lib/rpc_defs.py b/lib/rpc_defs.py index ecea05437685a9b53f8d1529d0a7df6540adb06b..449b50ad853044361bb62173315ae9531b4cb2f4 100644 --- a/lib/rpc_defs.py +++ b/lib/rpc_defs.py @@ -505,6 +505,9 @@ _MISC_CALLS = [ ("hvname", None, "Hypervisor name"), ("hvfull", None, "Parameters to be validated"), ], None, None, "Validate hypervisor params"), + ("set_watcher_pause", MULTI, None, constants.RPC_TMO_URGENT, [ + ("until", None, None), + ], None, None, "Set watcher pause end"), ] CALLS = { diff --git a/lib/server/noded.py b/lib/server/noded.py index 91ce4cebcbc452ad8e8660b36d1324735c5dca8f..0a1d0d6d51109ffdcc794c0a44a6036310b86d35 100644 --- a/lib/server/noded.py +++ b/lib/server/noded.py @@ -827,6 +827,14 @@ class NodeRequestHandler(http.server.HttpServerHandler): (values,) = params return ssconf.WriteSsconfFiles(values) + @staticmethod + def perspective_set_watcher_pause(params): + """Set watcher pause. + + """ + (until, ) = params + return backend.SetWatcherPause(until) + # os ----------------------- @staticmethod diff --git a/test/ganeti.backend_unittest.py b/test/ganeti.backend_unittest.py index f551ac2543f897e298d0d405521409e87ace8742..4a852425b0d052277a53baa998dbc0bcd8feed9d 100755 --- a/test/ganeti.backend_unittest.py +++ b/test/ganeti.backend_unittest.py @@ -482,5 +482,40 @@ class TestRunRestrictedCmd(unittest.TestCase): self.fail("Did not raise exception") +class TestSetWatcherPause(unittest.TestCase): + def setUp(self): + self.tmpdir = tempfile.mkdtemp() + self.filename = utils.PathJoin(self.tmpdir, "pause") + + def tearDown(self): + shutil.rmtree(self.tmpdir) + + def testUnsetNonExisting(self): + self.assertFalse(os.path.exists(self.filename)) + backend.SetWatcherPause(None, _filename=self.filename) + self.assertFalse(os.path.exists(self.filename)) + + def testSetNonNumeric(self): + for i in ["", [], {}, "Hello World", "0", "1.0"]: + self.assertFalse(os.path.exists(self.filename)) + + try: + backend.SetWatcherPause(i, _filename=self.filename) + except backend.RPCFail, err: + self.assertEqual(str(err), "Duration must be numeric") + else: + self.fail("Did not raise exception") + + self.assertFalse(os.path.exists(self.filename)) + + def testSet(self): + self.assertFalse(os.path.exists(self.filename)) + + for i in range(10): + backend.SetWatcherPause(i, _filename=self.filename) + self.assertEqual(utils.ReadFile(self.filename), "%s\n" % i) + self.assertEqual(os.stat(self.filename).st_mode & 0777, 0644) + + if __name__ == "__main__": testutils.GanetiTestProgram()