From 538475caba3a3d9e15ba21a3da7c12336d578621 Mon Sep 17 00:00:00 2001 From: Iustin Pop <iustin@google.com> Date: Wed, 16 Apr 2008 13:57:20 +0000 Subject: [PATCH] IAllocator part 3: LUCreateInstance changes This (final) patch allows the instance's nodes to be selected automatically based on the passed allocator algorithm. The patch changes the pnode opcode parameter from required to optional, now either the pnode or the iallocator must be passed. A possible improvement could be to organize all the _IAllocator functions into a separate class, but that can come later and the current version is functionally ok. Reviewed-by: ultrotter --- doc/examples/dumb-allocator | 9 ++-- lib/cmdlib.py | 88 +++++++++++++++++++++++++++++++++++-- lib/opcodes.py | 1 + scripts/gnt-backup | 6 ++- scripts/gnt-instance | 10 +++-- 5 files changed, 103 insertions(+), 11 deletions(-) diff --git a/doc/examples/dumb-allocator b/doc/examples/dumb-allocator index f6d6560fd..641c41835 100755 --- a/doc/examples/dumb-allocator +++ b/doc/examples/dumb-allocator @@ -47,16 +47,17 @@ def SelectNode(nodes, request, to_skip): return selected -def OutputError(text): +def OutputError(text, exit_code=1): """Builds an error response with a given info message. """ error = { "success": False, "info": text, + "nodes": [], } print simplejson.dumps(error, indent=2) - return 1 + return exit_code def main(): @@ -78,14 +79,14 @@ def main(): npri = SelectNode(nodes, request, []) if npri is None: - return OutputError("Can't find a suitable primary node") + return OutputError("Can't find a suitable primary node", exit_code=0) result_nodes = [npri] if request["disk_template"] == "drbd": nsec = SelectNode(nodes, request, result_nodes) if nsec is None: return OutputError("Can't find a suitable secondary node (%s selected" - " as primary)" % npri) + " as primary)" % npri, exit_code=0) result_nodes.append(nsec) result = { diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 9c3e56082..5d18c2f7b 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -3126,10 +3126,59 @@ class LUCreateInstance(LogicalUnit): """ HPATH = "instance-add" HTYPE = constants.HTYPE_INSTANCE - _OP_REQP = ["instance_name", "mem_size", "disk_size", "pnode", + _OP_REQP = ["instance_name", "mem_size", "disk_size", "disk_template", "swap_size", "mode", "start", "vcpus", "wait_for_sync", "ip_check", "mac"] + def _RunAllocator(self): + """Run the allocator based on input opcode. + + """ + al_data = _IAllocatorGetClusterData(self.cfg, self.sstore) + disks = [{"size": self.op.disk_size, "mode": "w"}, + {"size": self.op.swap_size, "mode": "w"}] + nics = [{"mac": self.op.mac, "ip": getattr(self.op, "ip", None), + "bridge": self.op.bridge}] + op = opcodes.OpTestAllocator(name=self.op.instance_name, + disk_template=self.op.disk_template, + tags=[], + os=self.op.os_type, + vcpus=self.op.vcpus, + mem_size=self.op.mem_size, + disks=disks, + nics=nics) + + _IAllocatorAddNewInstance(al_data, op) + + if _JSON_INDENT is None: + text = simplejson.dumps(al_data) + else: + text = simplejson.dumps(al_data, indent=_JSON_INDENT) + + result = _IAllocatorRun(self.op.iallocator, text) + + result = _IAllocatorValidateResult(result) + + if not result["success"]: + raise errors.OpPrereqError("Can't compute nodes using" + " iallocator '%s': %s" % (self.op.iallocator, + result["info"])) + req_nodes = 1 + if self.op.disk_template in constants.DTS_NET_MIRROR: + req_nodes += 1 + + if len(result["nodes"]) != req_nodes: + raise errors.OpPrereqError("iallocator '%s' returned invalid number" + " of nodes (%s), required %s" % + (len(result["nodes"]), req_nodes)) + self.op.pnode = result["nodes"][0] + logger.ToStdout("Selected nodes for the instance: %s" % + (", ".join(result["nodes"]),)) + logger.Info("Selected nodes for instance %s via iallocator %s: %s" % + (self.op.instance_name, self.op.iallocator, result["nodes"])) + if req_nodes == 2: + self.op.snode = result["nodes"][1] + def BuildHooksEnv(self): """Build hooks env. @@ -3166,7 +3215,9 @@ class LUCreateInstance(LogicalUnit): """Check prerequisites. """ - for attr in ["kernel_path", "initrd_path", "hvm_boot_order"]: + # set optional parameters to none if they don't exist + for attr in ["kernel_path", "initrd_path", "hvm_boot_order", "pnode", + "iallocator"]: if not hasattr(self.op, attr): setattr(self.op, attr, None) @@ -3284,6 +3335,14 @@ class LUCreateInstance(LogicalUnit): if self.op.file_storage_dir and os.path.isabs(self.op.file_storage_dir): raise errors.OpPrereqError("File storage directory not a relative" " path") + #### allocator run + + if [self.op.iallocator, self.op.pnode].count(None) != 1: + raise errors.OpPrereqError("One and only one of iallocator and primary" + " node must be given") + + if self.op.iallocator is not None: + self._RunAllocator() #### node related checks @@ -4784,7 +4843,7 @@ def _IAllocatorRun(name, data): alloc_script = utils.FindFile(name, constants.IALLOCATOR_SEARCH_PATH, os.path.isfile) if alloc_script is None: - raise errors.OpExecError("Can't find allocator") + raise errors.OpExecError("Can't find allocator '%s'" % name) fd, fin_name = tempfile.mkstemp(prefix="ganeti-iallocator.") try: @@ -4800,6 +4859,29 @@ def _IAllocatorRun(name, data): return result.stdout +def _IAllocatorValidateResult(data): + """Process the allocator results. + + """ + try: + rdict = simplejson.loads(data) + except Exception, err: + raise errors.OpExecError("Can't parse iallocator results: %s" % str(err)) + + if not isinstance(rdict, dict): + raise errors.OpExecError("Can't parse iallocator results: not a dict") + + for key in "success", "info", "nodes": + if key not in rdict: + raise errors.OpExecError("Can't parse iallocator results:" + " missing key '%s'" % key) + + if not isinstance(rdict["nodes"], list): + raise errors.OpExecError("Can't parse iallocator results: 'nodes' key" + " is not a list") + return rdict + + class LUTestAllocator(NoHooksLU): """Run allocator tests. diff --git a/lib/opcodes.py b/lib/opcodes.py index a80bb166c..117757f9f 100644 --- a/lib/opcodes.py +++ b/lib/opcodes.py @@ -284,6 +284,7 @@ class OpCreateInstance(OpCode): "wait_for_sync", "ip_check", "mac", "kernel_path", "initrd_path", "hvm_boot_order", "file_storage_dir", "file_driver", + "iallocator", ] diff --git a/scripts/gnt-backup b/scripts/gnt-backup index 2876b25d6..c250e250d 100755 --- a/scripts/gnt-backup +++ b/scripts/gnt-backup @@ -99,7 +99,8 @@ def ImportInstance(opts, args): vcpus=opts.vcpus, ip_check=opts.ip_check, ip=opts.ip, bridge=opts.bridge, start=False, src_node=opts.src_node, src_path=opts.src_dir, - wait_for_sync=opts.wait_for_sync, mac="auto") + wait_for_sync=opts.wait_for_sync, mac="auto", + iallocator=opts.iallocator) SubmitOpCode(op) return 0 @@ -137,6 +138,9 @@ import_opts = [ make_option("--no-ip-check", dest="ip_check", default=True, action="store_false", help="Don't check that the instance's IP" " is alive"), + make_option("--iallocator", metavar="<NAME>", + help="Select nodes for the instance automatically using the" + " <NAME> iallocator plugin", default=None, type="string"), ] commands = { diff --git a/scripts/gnt-instance b/scripts/gnt-instance index 19b10940c..eb9752103 100755 --- a/scripts/gnt-instance +++ b/scripts/gnt-instance @@ -261,7 +261,8 @@ def AddInstance(opts, args): initrd_path=initrd_path, hvm_boot_order=opts.hvm_boot_order, file_storage_dir = opts.file_storage_dir, - file_driver = opts.file_driver) + file_driver = opts.file_driver, + iallocator=opts.iallocator) SubmitOpCode(op) return 0 @@ -760,14 +761,17 @@ add_opts = [ default=None, type="string", metavar="<FILENAME>"), make_option("--hvm-boot-order", dest="hvm_boot_order", - help="boot device order for HVM (one or more of [acdn])", + help="Boot device order for HVM (one or more of [acdn])", default=None, type="string", metavar="<BOOTORDER>"), make_option("--file-storage-dir", dest="file_storage_dir", help="Relative path under default cluster-wide file storage dir" " to store file-based disks", default=None, metavar="<DIR>"), make_option("--file-driver", dest="file_driver", help="Driver to use" - " for image files", default="loop", metavar="<DRIVER>") + " for image files", default="loop", metavar="<DRIVER>"), + make_option("--iallocator", metavar="<NAME>", + help="Select nodes for the instance automatically using the" + " <NAME> iallocator plugin", default=None, type="string"), ] commands = { -- GitLab