diff --git a/doc/examples/dumb-allocator b/doc/examples/dumb-allocator index f6d6560fd440b66fa3a981b79e513efacb95f1c8..641c418354f605b09ad773611bc84f84757bf3a7 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 9c3e5608212b926bd3e428a4bc4fb7da0ca3a846..5d18c2f7b908c66e0d2c796ee88af8c0e68b7f8f 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 a80bb166c254f56926aad8dc7e45759c6d679119..117757f9f2896889d858a41c7c092db3f4003f6b 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 2876b25d6801866581586e06571ba6df5f98a450..c250e250d9626abfeed5273b2c1352215f03de90 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 19b10940c1118dc3a9c0d282b56cde3e8744f5e3..eb9752103af95924c74b4e78ff6acf6a185b5f88 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 = {