X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/7e5eaaa80706899ea7534bd718de54bc363f381c..433c63aab0342a567f993374c86d73391e67d85d:/lib/cli.py diff --git a/lib/cli.py b/lib/cli.py index f797381..d9c2dbb 100644 --- a/lib/cli.py +++ b/lib/cli.py @@ -82,8 +82,10 @@ __all__ = [ "NODE_PLACEMENT_OPT", "NOHDR_OPT", "NOIPCHECK_OPT", + "NONAMECHECK_OPT", "NOLVM_STORAGE_OPT", "NOMODIFY_ETCHOSTS_OPT", + "NOMODIFY_SSH_SETUP_OPT", "NONICS_OPT", "NONLIVE_OPT", "NONPLUS1_OPT", @@ -168,7 +170,7 @@ UN_PREFIX = "-" class _Argument: - def __init__(self, min=0, max=None): + def __init__(self, min=0, max=None): # pylint: disable-msg=W0622 self.min = min self.max = max @@ -183,6 +185,7 @@ class ArgSuggest(_Argument): Value can be any of the ones passed to the constructor. """ + # pylint: disable-msg=W0622 def __init__(self, min=0, max=None, choices=None): _Argument.__init__(self, min=min, max=max) self.choices = choices @@ -249,7 +252,6 @@ ARGS_ONE_INSTANCE = [ArgInstance(min=1, max=1)] ARGS_ONE_NODE = [ArgNode(min=1, max=1)] - def _ExtractTagsObject(opts, args): """Extract the tag type object. @@ -310,8 +312,8 @@ def ListTags(opts, args): """ kind, name = _ExtractTagsObject(opts, args) - op = opcodes.OpGetTags(kind=kind, name=name) - result = SubmitOpCode(op) + cl = GetClient() + result = cl.QueryTags(kind, name) result = list(result) result.sort() for tag in result: @@ -352,7 +354,7 @@ def RemoveTags(opts, args): SubmitOpCode(op) -def check_unit(option, opt, value): +def check_unit(option, opt, value): # pylint: disable-msg=W0613 """OptParsers custom converter for units. """ @@ -382,7 +384,7 @@ def _SplitKeyVal(opt, data): """ kv_dict = {} if data: - for elem in data.split(","): + for elem in utils.UnescapeAndSplit(data, sep=","): if "=" in elem: key, val = elem.split("=", 1) else: @@ -399,7 +401,7 @@ def _SplitKeyVal(opt, data): return kv_dict -def check_ident_key_val(option, opt, value): +def check_ident_key_val(option, opt, value): # pylint: disable-msg=W0613 """Custom parser for ident:key=val,key=val options. This will store the parsed values as a tuple (ident, {key: val}). As such, @@ -427,7 +429,7 @@ def check_ident_key_val(option, opt, value): return retval -def check_key_val(option, opt, value): +def check_key_val(option, opt, value): # pylint: disable-msg=W0613 """Custom parser class for key=val,key=val options. This will store the parsed values as a dict {key: val}. @@ -596,6 +598,11 @@ NOIPCHECK_OPT = cli_option("--no-ip-check", dest="ip_check", default=True, help="Don't check that the instance's IP" " is alive") +NONAMECHECK_OPT = cli_option("--no-name-check", dest="name_check", + default=True, action="store_false", + help="Don't check that the instance's name" + " is resolvable") + NET_OPT = cli_option("--net", help="NIC parameters", default=[], dest="nics", action="append", type="identkeyval") @@ -794,6 +801,10 @@ NOMODIFY_ETCHOSTS_OPT = cli_option("--no-etc-hosts", dest="modify_etc_hosts", help="Don't modify /etc/hosts", action="store_false", default=True) +NOMODIFY_SSH_SETUP_OPT = cli_option("--no-ssh-init", dest="modify_ssh_setup", + help="Don't initialize SSH keys", + action="store_false", default=True) + ERROR_CODES_OPT = cli_option("--error-codes", dest="error_codes", help="Enable parseable error messages", action="store_true", default=False) @@ -821,8 +832,8 @@ TIMEOUT_OPT = cli_option("--timeout", dest="timeout", type="int", default=constants.DEFAULT_SHUTDOWN_TIMEOUT, help="Maximum time to wait") -SHUTDOWN_TIMEOUT_OPT = cli_option("--shutdown-timeout", dest="timeout", - type="int", +SHUTDOWN_TIMEOUT_OPT = cli_option("--shutdown-timeout", + dest="shutdown_timeout", type="int", default=constants.DEFAULT_SHUTDOWN_TIMEOUT, help="Maximum time to wait for instance shutdown") @@ -1214,13 +1225,21 @@ def GetClient(): try: client = luxi.Client() except luxi.NoMasterError: - master, myself = ssconf.GetMasterAndMyself() + ss = ssconf.SimpleStore() + + # Try to read ssconf file + try: + ss.GetMasterNode() + except errors.ConfigurationError: + raise errors.OpPrereqError("Cluster not initialized or this machine is" + " not part of a cluster") + + master, myself = ssconf.GetMasterAndMyself(ss=ss) if master != myself: raise errors.OpPrereqError("This is not the master node, please connect" " to node '%s' and rerun the command" % master) - else: - raise + raise return client @@ -1261,8 +1280,13 @@ def FormatError(err): msg = "Failure: can't resolve hostname '%s'" obuf.write(msg % err.args[0]) elif isinstance(err, errors.OpPrereqError): - obuf.write("Failure: prerequisites not met for this" - " operation:\n%s" % msg) + if len(err.args) == 2: + obuf.write("Failure: prerequisites not met for this" + " operation:\nerror type: %s, error details:\n%s" % + (err.args[1], err.args[0])) + else: + obuf.write("Failure: prerequisites not met for this" + " operation:\n%s" % msg) elif isinstance(err, errors.OpExecError): obuf.write("Failure: command execution error:\n%s" % msg) elif isinstance(err, errors.TagError): @@ -1378,7 +1402,7 @@ def GenericInstanceCreate(mode, opts, args): if opts.nics: try: - nic_max = max(int(nidx[0])+1 for nidx in opts.nics) + nic_max = max(int(nidx[0]) + 1 for nidx in opts.nics) except ValueError, err: raise errors.OpPrereqError("Invalid NIC index passed: %s" % str(err)) nics = [{}] * nic_max @@ -1409,7 +1433,7 @@ def GenericInstanceCreate(mode, opts, args): if opts.sd_size is not None: opts.disks = [(0, {"size": opts.sd_size})] try: - disk_max = max(int(didx[0])+1 for didx in opts.disks) + disk_max = max(int(didx[0]) + 1 for didx in opts.disks) except ValueError, err: raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err)) disks = [{}] * disk_max @@ -1449,6 +1473,7 @@ def GenericInstanceCreate(mode, opts, args): nics=nics, pnode=pnode, snode=snode, ip_check=opts.ip_check, + name_check=opts.name_check, wait_for_sync=opts.wait_for_sync, file_storage_dir=opts.file_storage_dir, file_driver=opts.file_driver, @@ -1506,8 +1531,8 @@ def GenerateTable(headers, fields, separator, data, if unitfields is None: unitfields = [] - numfields = utils.FieldSet(*numfields) - unitfields = utils.FieldSet(*unitfields) + numfields = utils.FieldSet(*numfields) # pylint: disable-msg=W0142 + unitfields = utils.FieldSet(*unitfields) # pylint: disable-msg=W0142 format_fields = [] for field in fields: @@ -1536,7 +1561,7 @@ def GenerateTable(headers, fields, separator, data, if unitfields.Matches(fields[idx]): try: val = int(val) - except ValueError: + except (TypeError, ValueError): pass else: val = row[idx] = utils.FormatUnit(val, units) @@ -1555,6 +1580,12 @@ def GenerateTable(headers, fields, separator, data, args.append(hdr) result.append(format % tuple(args)) + if separator is None: + assert len(mlens) == len(fields) + + if fields and not numfields.Matches(fields[-1]): + mlens[-1] = 0 + for line in data: args = [] if line is None: @@ -1611,7 +1642,7 @@ def ParseTimespec(value): if value[-1] not in suffix_map: try: value = int(value) - except ValueError: + except (TypeError, ValueError): raise errors.OpPrereqError("Invalid time specification '%s'" % value) else: multiplier = suffix_map[value[-1]] @@ -1621,7 +1652,7 @@ def ParseTimespec(value): " suffix passed)") try: value = int(value) * multiplier - except ValueError: + except (TypeError, ValueError): raise errors.OpPrereqError("Invalid time specification '%s'" % value) return value @@ -1648,7 +1679,7 @@ def GetOnlineNodes(nodes, cl=None, nowarn=False): use_locking=False) offline = [row[0] for row in result if row[1]] if offline and not nowarn: - ToStderr("Note: skipping offline node(s): %s" % ", ".join(offline)) + ToStderr("Note: skipping offline node(s): %s" % utils.CommaJoin(offline)) return [row[0] for row in result if not row[1]] @@ -1740,7 +1771,7 @@ class JobExecutor(object): if self.verbose: ok_jobs = [row[1] for row in self.jobs if row[0]] if ok_jobs: - ToStdout("Submitted jobs %s", ", ".join(ok_jobs)) + ToStdout("Submitted jobs %s", utils.CommaJoin(ok_jobs)) for submit_status, jid, name in self.jobs: if not submit_status: ToStderr("Failed to submit job for %s: %s", name, jid)