X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/41044e04c1fea3630a5129de68a1938b3e3d53d3..def6577f00a482c310f4e20bb315fa90290ad5b7:/lib/cli.py?ds=sidebyside diff --git a/lib/cli.py b/lib/cli.py index b924e89..2e436b6 100644 --- a/lib/cli.py +++ b/lib/cli.py @@ -165,6 +165,7 @@ __all__ = [ "PREALLOC_WIPE_DISKS_OPT", "PRIMARY_IP_VERSION_OPT", "PRIMARY_ONLY_OPT", + "PRINT_JOBID_OPT", "PRIORITY_OPT", "RAPI_CERT_OPT", "READD_OPT", @@ -198,6 +199,7 @@ __all__ = [ "SRC_DIR_OPT", "SRC_NODE_OPT", "SUBMIT_OPT", + "SUBMIT_OPTS", "STARTUP_PAUSED_OPT", "STATIC_OPT", "SYNC_OPT", @@ -680,14 +682,17 @@ def _SplitListKeyVal(opt, value): return retval -def check_list_ident_key_val(_, opt, value): - """Custom parser for "ident:key=val,key=val/ident:key=val" options. +def check_multilist_ident_key_val(_, opt, value): + """Custom parser for "ident:key=val,key=val/ident:key=val//ident:.." options. @rtype: list of dictionary - @return: {ident: {key: val, key: val}, ident: {key: val}} + @return: [{ident: {key: val, key: val}, ident: {key: val}}, {ident:..}] """ - return _SplitListKeyVal(opt, value) + retval = [] + for line in value.split("//"): + retval.append(_SplitListKeyVal(opt, line)) + return retval def check_bool(option, opt, value): # pylint: disable=W0613 @@ -762,7 +767,7 @@ class CliOption(Option): "completion_suggest", ] TYPES = Option.TYPES + ( - "listidentkeyval", + "multilistidentkeyval", "identkeyval", "keyval", "unit", @@ -771,7 +776,7 @@ class CliOption(Option): "maybefloat", ) TYPE_CHECKER = Option.TYPE_CHECKER.copy() - TYPE_CHECKER["listidentkeyval"] = check_list_ident_key_val + TYPE_CHECKER["multilistidentkeyval"] = check_multilist_ident_key_val TYPE_CHECKER["identkeyval"] = check_ident_key_val TYPE_CHECKER["keyval"] = check_key_val TYPE_CHECKER["unit"] = check_unit @@ -829,6 +834,11 @@ SUBMIT_OPT = cli_option("--submit", dest="submit_only", help=("Submit the job and return the job ID, but" " don't wait for the job to finish")) +PRINT_JOBID_OPT = cli_option("--print-jobid", dest="print_jobid", + default=False, action="store_true", + help=("Additionally print the job as first line" + " on stdout (for scripting).")) + SYNC_OPT = cli_option("--sync", dest="do_locking", default=False, action="store_true", help=("Grab locks while doing the queries" @@ -964,7 +974,7 @@ SPECS_NIC_COUNT_OPT = cli_option("--specs-nic-count", dest="ispecs_nic_count", IPOLICY_BOUNDS_SPECS_STR = "--ipolicy-bounds-specs" IPOLICY_BOUNDS_SPECS_OPT = cli_option(IPOLICY_BOUNDS_SPECS_STR, dest="ipolicy_bounds_specs", - type="listidentkeyval", default=None, + type="multilistidentkeyval", default=None, help="Complete instance specs limits") IPOLICY_STD_SPECS_STR = "--ipolicy-std-specs" @@ -1631,6 +1641,13 @@ INCLUDEDEFAULTS_OPT = cli_option("--include-defaults", dest="include_defaults", #: Options provided by all commands COMMON_OPTS = [DEBUG_OPT, REASON_OPT] +# options related to asynchronous job handling + +SUBMIT_OPTS = [ + SUBMIT_OPT, + PRINT_JOBID_OPT, + ] + # common options for creating instances. add and import then add their own # specific ones. COMMON_CREATE_OPTS = [ @@ -1651,6 +1668,7 @@ COMMON_CREATE_OPTS = [ OSPARAMS_OPT, OS_SIZE_OPT, SUBMIT_OPT, + PRINT_JOBID_OPT, TAG_ADD_OPT, DRY_RUN_OPT, PRIORITY_OPT, @@ -2245,6 +2263,8 @@ def SubmitOpCode(op, cl=None, feedback_fn=None, opts=None, reporter=None): SetGenericOpcodeOpts([op], opts) job_id = SendJob([op], cl=cl) + if hasattr(opts, "print_jobid") and opts.print_jobid: + ToStdout("%d" % job_id) op_results = PollJob(job_id, cl=cl, feedback_fn=feedback_fn, reporter=reporter) @@ -2268,6 +2288,8 @@ def SubmitOrSend(op, opts, cl=None, feedback_fn=None): job = [op] SetGenericOpcodeOpts(job, opts) job_id = SendJob(job, cl=cl) + if opts.print_jobid: + ToStdout("%d" % job_id) raise JobSubmittedException(job_id) else: return SubmitOpCode(op, cl=cl, feedback_fn=feedback_fn, opts=opts) @@ -2644,6 +2666,9 @@ def GenericInstanceCreate(mode, opts, args): raise errors.OpPrereqError("Invalid disk size for disk %d: %s" % (didx, err), errors.ECODE_INVAL) elif constants.IDISK_ADOPT in ddict: + if constants.IDISK_SPINDLES in ddict: + raise errors.OpPrereqError("spindles is not a valid option when" + " adopting a disk", errors.ECODE_INVAL) if mode == constants.INSTANCE_IMPORT: raise errors.OpPrereqError("Disk adoption not allowed for instance" " import", errors.ECODE_INVAL) @@ -3762,7 +3787,7 @@ def FormatPolicyInfo(custom_ipolicy, eff_ipolicy, iscluster): ) ret.append( - ("enabled disk templates", + ("allowed disk templates", _FormatListInfoDefault(custom_ipolicy.get(constants.IPOLICY_DTS), eff_ipolicy[constants.IPOLICY_DTS])) ) @@ -3796,12 +3821,17 @@ def PrintIPolicyCommand(buf, ipolicy, isgroup): if stdspecs: buf.write(" %s " % IPOLICY_STD_SPECS_STR) _PrintSpecsParameters(buf, stdspecs) - minmax = ipolicy.get("minmax") - if minmax: - minspecs = minmax[0].get("min") - maxspecs = minmax[0].get("max") + minmaxes = ipolicy.get("minmax", []) + first = True + for minmax in minmaxes: + minspecs = minmax.get("min") + maxspecs = minmax.get("max") if minspecs and maxspecs: - buf.write(" %s " % IPOLICY_BOUNDS_SPECS_STR) + if first: + buf.write(" %s " % IPOLICY_BOUNDS_SPECS_STR) + first = False + else: + buf.write("//") buf.write("min:") _PrintSpecsParameters(buf, minspecs) buf.write("/max:") @@ -3945,8 +3975,9 @@ def _ParseISpec(spec, keyname, required): def _GetISpecsInAllowedValues(minmax_ispecs, allowed_values): ret = None - if minmax_ispecs and allowed_values and len(minmax_ispecs) == 1: - for (key, spec) in minmax_ispecs.items(): + if (minmax_ispecs and allowed_values and len(minmax_ispecs) == 1 and + len(minmax_ispecs[0]) == 1): + for (key, spec) in minmax_ispecs[0].items(): # This loop is executed exactly once if key in allowed_values and not spec: ret = key @@ -3959,13 +3990,16 @@ def _InitISpecsFromFullOpts(ipolicy_out, minmax_ispecs, std_ispecs, if found_allowed is not None: ipolicy_out[constants.ISPECS_MINMAX] = found_allowed elif minmax_ispecs is not None: - minmax_out = {} - for (key, spec) in minmax_ispecs.items(): - if key not in constants.ISPECS_MINMAX_KEYS: - msg = "Invalid key in bounds instance specifications: %s" % key - raise errors.OpPrereqError(msg, errors.ECODE_INVAL) - minmax_out[key] = _ParseISpec(spec, key, True) - ipolicy_out[constants.ISPECS_MINMAX] = [minmax_out] + minmax_out = [] + for mmpair in minmax_ispecs: + mmpair_out = {} + for (key, spec) in mmpair.items(): + if key not in constants.ISPECS_MINMAX_KEYS: + msg = "Invalid key in bounds instance specifications: %s" % key + raise errors.OpPrereqError(msg, errors.ECODE_INVAL) + mmpair_out[key] = _ParseISpec(spec, key, True) + minmax_out.append(mmpair_out) + ipolicy_out[constants.ISPECS_MINMAX] = minmax_out if std_ispecs is not None: assert not group_ipolicy # This is not an option for gnt-group ipolicy_out[constants.ISPECS_STD] = _ParseISpec(std_ispecs, "std", False)