diff --git a/daemons/ganeti-noded b/daemons/ganeti-noded index 66e98dd856ee4ad05c425757e24dfd348f60e394..f7d76fabf64a5c417c197e4848ff39741319510a 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 c56053488c5141b5b0532aad97a699d13b5c718d..fcddf0e7dd78c79bd7fd15d6eab2790bb6bdc2a5 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 72e11b5f558cf50ad62bf75a6180284c3824305f..05484cf8ac2eb43ec2a41773b2f9f6ad22fcae0f 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 91fcf81aaf155c56ea12eed958c947b555bdb130..896fc4803e817f7965c6976657fba21dd72a86f6 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 57f0a8ef5263c986fd434432b04bd28974831550..0efb6a7bcc83824663938f9f74b70a6bcc2d40ee 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.