From 8d528b7cce3db73301cf3d82962cacbce492ad48 Mon Sep 17 00:00:00 2001 From: Iustin Pop <iustin@google.com> Date: Mon, 28 Apr 2008 09:47:23 +0000 Subject: [PATCH] Move iallocator script execution to ganeti-noded Currently the iallocator execution takes place in the master, which is a violation of the current architecture, and will create problems with a threaded master daemon. This patch moves the execution to the backend, similar to the hooks runner, by: - introducing a new class that handles the execution in the backend (and could be used also for listing the allocators, etc.) - introducing a new rpc call - replacing the actual execution in IAllocator.Run() with a rpc call This passes burnin with the dumb allocator Reviewed-by: imsnah --- daemons/ganeti-noded | 11 +++++++++++ lib/backend.py | 36 ++++++++++++++++++++++++++++++++++++ lib/cmdlib.py | 27 ++++++++++++--------------- lib/constants.py | 3 +++ lib/rpc.py | 18 ++++++++++++++++++ 5 files changed, 80 insertions(+), 15 deletions(-) diff --git a/daemons/ganeti-noded b/daemons/ganeti-noded index 66e98dd85..f7d76fabf 100755 --- a/daemons/ganeti-noded +++ b/daemons/ganeti-noded @@ -484,6 +484,17 @@ class ServerObject(BaseHTTPServer.BaseHTTPRequestHandler): hr = backend.HooksRunner() return hr.RunHooks(hpath, phase, env) + # iallocator ----------------- + + @staticmethod + def perspective_iallocator_runner(params): + """Run an iallocator script. + + """ + name, idata = params + iar = backend.IAllocatorRunner() + return iar.Run(name, idata) + # test ----------------------- @staticmethod diff --git a/lib/backend.py b/lib/backend.py index c56053488..fcddf0e7d 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -1633,6 +1633,42 @@ class HooksRunner(object): return rr +class IAllocatorRunner(object): + """IAllocator runner. + + This class is instantiated on the node side (ganeti-noded) and not on + the master side. + + """ + def Run(self, name, idata): + """Run an iallocator script. + + Return value: tuple of: + - run status (one of the IARUN_ constants) + - stdout + - stderr + - fail reason (as from utils.RunResult) + + """ + alloc_script = utils.FindFile(name, constants.IALLOCATOR_SEARCH_PATH, + os.path.isfile) + if alloc_script is None: + return (constants.IARUN_NOTFOUND, None, None, None) + + fd, fin_name = tempfile.mkstemp(prefix="ganeti-iallocator.") + try: + os.write(fd, idata) + os.close(fd) + result = utils.RunCmd([alloc_script, fin_name]) + if result.failed: + return (constants.IARUN_FAILURE, result.stdout, result.stderr, + result.fail_reason) + finally: + os.unlink(fin_name) + + return (constants.IARUN_SUCCESS, result.stdout, result.stderr, None) + + class DevCacheManager(object): """Simple class for managing a cache of block device information. diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 72e11b5f5..05484cf8a 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -5005,29 +5005,26 @@ class IAllocator(object): self.in_text = serializer.Dump(self.in_data) - def Run(self, name, validate=True): + def Run(self, name, validate=True, call_fn=rpc.call_iallocator_runner): """Run an instance allocator and return the results. """ data = self.in_text - alloc_script = utils.FindFile(name, constants.IALLOCATOR_SEARCH_PATH, - os.path.isfile) - if alloc_script is None: - raise errors.OpExecError("Can't find allocator '%s'" % name) + result = call_fn(self.sstore.GetMasterNode(), name, self.in_text) - fd, fin_name = tempfile.mkstemp(prefix="ganeti-iallocator.") - try: - os.write(fd, data) - os.close(fd) - result = utils.RunCmd([alloc_script, fin_name]) - if result.failed: + if not isinstance(result, tuple) or len(result) != 4: + raise errors.OpExecError("Invalid result from master iallocator runner") + + rcode, stdout, stderr, fail = result + + if rcode == constants.IARUN_NOTFOUND: + raise errors.OpExecError("Can't find allocator '%s'" % name) + elif rcode == constants.IARUN_FAILURE: raise errors.OpExecError("Instance allocator call failed: %s," " output: %s" % - (result.fail_reason, result.output)) - finally: - os.unlink(fin_name) - self.out_text = result.stdout + (fail, stdout+stderr)) + self.out_text = stdout if validate: self._ValidateResult() diff --git a/lib/constants.py b/lib/constants.py index 91fcf81aa..896fc4803 100644 --- a/lib/constants.py +++ b/lib/constants.py @@ -190,3 +190,6 @@ IALLOCATOR_DIR_OUT = "out" IALLOCATOR_MODE_ALLOC = "allocate" IALLOCATOR_MODE_RELOC = "relocate" IALLOCATOR_SEARCH_PATH = _autoconf.IALLOCATOR_SEARCH_PATH +IARUN_NOTFOUND = 1 +IARUN_FAILURE = 2 +IARUN_SUCCESS = 3 diff --git a/lib/rpc.py b/lib/rpc.py index 57f0a8ef5..0efb6a7bc 100644 --- a/lib/rpc.py +++ b/lib/rpc.py @@ -577,6 +577,24 @@ def call_hooks_runner(node_list, hpath, phase, env): return result +def call_iallocator_runner(node, name, idata): + """Call an iallocator on a remote node + + Args: + - name: the iallocator name + - input: the json-encoded input string + + This is a single-node call. + + """ + params = [name, idata] + c = Client("iallocator_runner", params) + c.connect(node) + c.run() + result = c.getresult().get(node, False) + return result + + def call_blockdev_snapshot(node, cf_bdev): """Request a snapshot of the given block device. -- GitLab