X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/cd415612a48153a7c6061c278156dad6f0b52678..b09cce6429a36523e37b902023d037f9258b7296:/lib/cli.py diff --git a/lib/cli.py b/lib/cli.py index 2af4738..845c8a7 100644 --- a/lib/cli.py +++ b/lib/cli.py @@ -195,6 +195,7 @@ __all__ = [ "USE_REPL_NET_OPT", "VERBOSE_OPT", "VG_NAME_OPT", + "WFSYNC_OPT", "YES_DOIT_OPT", "DISK_STATE_OPT", "HV_STATE_OPT", @@ -415,7 +416,8 @@ def _ExtractTagsObject(opts, args): constants.TAG_NODE, constants.TAG_INSTANCE): if not args: - raise errors.OpPrereqError("no arguments passed to the command") + raise errors.OpPrereqError("no arguments passed to the command", + errors.ECODE_INVAL) name = args.pop(0) retval = kind, name else: @@ -462,7 +464,7 @@ def ListTags(opts, args): """ kind, name = _ExtractTagsObject(opts, args) - cl = GetClient() + cl = GetClient(query=True) result = cl.QueryTags(kind, name) result = list(result) result.sort() @@ -482,7 +484,7 @@ def AddTags(opts, args): kind, name = _ExtractTagsObject(opts, args) _ExtendTags(opts, args) if not args: - raise errors.OpPrereqError("No tags to be added") + raise errors.OpPrereqError("No tags to be added", errors.ECODE_INVAL) op = opcodes.OpTagsSet(kind=kind, name=name, tags=args) SubmitOrSend(op, opts) @@ -499,7 +501,7 @@ def RemoveTags(opts, args): kind, name = _ExtractTagsObject(opts, args) _ExtendTags(opts, args) if not args: - raise errors.OpPrereqError("No tags to be removed") + raise errors.OpPrereqError("No tags to be removed", errors.ECODE_INVAL) op = opcodes.OpTagsDel(kind=kind, name=name, tags=args) SubmitOrSend(op, opts) @@ -747,6 +749,10 @@ NWSYNC_OPT = cli_option("--no-wait-for-sync", dest="wait_for_sync", default=True, action="store_false", help="Don't wait for sync (DANGEROUS!)") +WFSYNC_OPT = cli_option("--wait-for-sync", dest="wait_for_sync", + default=False, action="store_true", + help="Wait for disks to sync") + ONLINE_INST_OPT = cli_option("--online", dest="online_inst", action="store_true", default=False, help="Enable offline instance") @@ -782,18 +788,19 @@ IALLOCATOR_OPT = cli_option("-I", "--iallocator", metavar="", completion_suggest=OPT_COMPL_ONE_IALLOCATOR) DEFAULT_IALLOCATOR_OPT = cli_option("-I", "--default-iallocator", - metavar="", - help="Set the default instance allocator plugin", - default=None, type="string", - completion_suggest=OPT_COMPL_ONE_IALLOCATOR) + metavar="", + help="Set the default instance" + " allocator plugin", + default=None, type="string", + completion_suggest=OPT_COMPL_ONE_IALLOCATOR) OS_OPT = cli_option("-o", "--os-type", dest="os", help="What OS to run", metavar="", completion_suggest=OPT_COMPL_ONE_OS) OSPARAMS_OPT = cli_option("-O", "--os-parameters", dest="osparams", - type="keyval", default={}, - help="OS parameters") + type="keyval", default={}, + help="OS parameters") FORCE_VARIANT_OPT = cli_option("--force-variant", dest="force_variant", action="store_true", default=False, @@ -842,7 +849,7 @@ SPECS_DISK_COUNT_OPT = cli_option("--specs-disk-count", SPECS_DISK_SIZE_OPT = cli_option("--specs-disk-size", dest="ispecs_disk_size", type="keyval", default={}, help="Disk size specs: list of key=value," - " where key is one of min, max, std" + " where key is one of min, max, std" " (in MB or using a unit)") SPECS_NIC_COUNT_OPT = cli_option("--specs-nic-count", dest="ispecs_nic_count", @@ -851,10 +858,10 @@ SPECS_NIC_COUNT_OPT = cli_option("--specs-nic-count", dest="ispecs_nic_count", " where key is one of min, max, std") IPOLICY_DISK_TEMPLATES = cli_option("--ipolicy-disk-templates", - dest="ipolicy_disk_templates", - type="list", default=None, - help="Comma-separated list of" - " enabled disk templates") + dest="ipolicy_disk_templates", + type="list", default=None, + help="Comma-separated list of" + " enabled disk templates") IPOLICY_VCPU_RATIO = cli_option("--ipolicy-vcpu-ratio", dest="ipolicy_vcpu_ratio", @@ -1081,12 +1088,12 @@ DRAINED_OPT = cli_option("-D", "--drained", dest="drained", metavar=_YORNO, " (excluded from allocation operations)")) CAPAB_MASTER_OPT = cli_option("--master-capable", dest="master_capable", - type="bool", default=None, metavar=_YORNO, - help="Set the master_capable flag on the node") + type="bool", default=None, metavar=_YORNO, + help="Set the master_capable flag on the node") CAPAB_VM_OPT = cli_option("--vm-capable", dest="vm_capable", - type="bool", default=None, metavar=_YORNO, - help="Set the vm_capable flag on the node") + type="bool", default=None, metavar=_YORNO, + help="Set the vm_capable flag on the node") ALLOCATABLE_OPT = cli_option("--allocatable", dest="allocatable", type="bool", default=None, metavar=_YORNO, @@ -1143,11 +1150,12 @@ MASTER_NETMASK_OPT = cli_option("--master-netmask", dest="master_netmask", default=None) USE_EXTERNAL_MIP_SCRIPT = cli_option("--use-external-mip-script", - dest="use_external_mip_script", - help="Specify whether to run a user-provided" - " script for the master IP address turnup and" - " turndown operations", - type="bool", metavar=_YORNO, default=None) + dest="use_external_mip_script", + help="Specify whether to run a" + " user-provided script for the master" + " IP address turnup and" + " turndown operations", + type="bool", metavar=_YORNO, default=None) GLOBAL_FILEDIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir", help="Specify the default directory (cluster-" @@ -1156,14 +1164,13 @@ GLOBAL_FILEDIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir", metavar="DIR", default=constants.DEFAULT_FILE_STORAGE_DIR) -GLOBAL_SHARED_FILEDIR_OPT = cli_option("--shared-file-storage-dir", - dest="shared_file_storage_dir", - help="Specify the default directory (cluster-" - "wide) for storing the shared file-based" - " disks [%s]" % - constants.DEFAULT_SHARED_FILE_STORAGE_DIR, - metavar="SHAREDDIR", - default=constants.DEFAULT_SHARED_FILE_STORAGE_DIR) +GLOBAL_SHARED_FILEDIR_OPT = cli_option( + "--shared-file-storage-dir", + dest="shared_file_storage_dir", + help="Specify the default directory (cluster-wide) for storing the" + " shared file-based disks [%s]" % + constants.DEFAULT_SHARED_FILE_STORAGE_DIR, + metavar="SHAREDDIR", default=constants.DEFAULT_SHARED_FILE_STORAGE_DIR) NOMODIFY_ETCHOSTS_OPT = cli_option("--no-etc-hosts", dest="modify_etc_hosts", help="Don't modify /etc/hosts", @@ -1201,9 +1208,10 @@ TIMEOUT_OPT = cli_option("--timeout", dest="timeout", type="int", help="Maximum time to wait") 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") + dest="shutdown_timeout", type="int", + default=constants.DEFAULT_SHUTDOWN_TIMEOUT, + help="Maximum time to wait for instance" + " shutdown") INTERVAL_OPT = cli_option("--interval", dest="interval", type="int", default=None, @@ -1231,19 +1239,19 @@ NEW_RAPI_CERT_OPT = cli_option("--new-rapi-certificate", dest="new_rapi_cert", " certificate")) SPICE_CERT_OPT = cli_option("--spice-certificate", dest="spice_cert", - default=None, - help="File containing new SPICE certificate") + default=None, + help="File containing new SPICE certificate") SPICE_CACERT_OPT = cli_option("--spice-ca-certificate", dest="spice_cacert", - default=None, - help="File containing the certificate of the CA" - " which signed the SPICE certificate") + default=None, + help="File containing the certificate of the CA" + " which signed the SPICE certificate") NEW_SPICE_CERT_OPT = cli_option("--new-spice-certificate", - dest="new_spice_cert", default=None, - action="store_true", - help=("Generate a new self-signed SPICE" - " certificate")) + dest="new_spice_cert", default=None, + action="store_true", + help=("Generate a new self-signed SPICE" + " certificate")) NEW_CONFD_HMAC_KEY_OPT = cli_option("--new-confd-hmac-key", dest="new_confd_hmac_key", @@ -1301,10 +1309,10 @@ REMOVE_UIDS_OPT = cli_option("--remove-uids", default=None, " removed from the user-id pool")) RESERVED_LVS_OPT = cli_option("--reserved-lvs", default=None, - action="store", dest="reserved_lvs", - help=("A comma-separated list of reserved" - " logical volumes names, that will be" - " ignored by cluster verify")) + action="store", dest="reserved_lvs", + help=("A comma-separated list of reserved" + " logical volumes names, that will be" + " ignored by cluster verify")) ROMAN_OPT = cli_option("--roman", dest="roman_integers", default=False, @@ -1359,8 +1367,8 @@ NODE_POWERED_OPT = cli_option("--node-powered", default=None, help="Specify if the SoR for node is powered") OOB_TIMEOUT_OPT = cli_option("--oob-timeout", dest="oob_timeout", type="int", - default=constants.OOB_TIMEOUT, - help="Maximum time to wait for out-of-band helper") + default=constants.OOB_TIMEOUT, + help="Maximum time to wait for out-of-band helper") POWER_DELAY_OPT = cli_option("--power-delay", dest="power_delay", type="float", default=constants.OOB_POWER_DELAY, @@ -1404,14 +1412,17 @@ IGNORE_ERRORS_OPT = cli_option("-I", "--ignore-errors", default=[], DISK_STATE_OPT = cli_option("--disk-state", default=[], dest="disk_state", action="append", - help=("Specify disk state information in the format" - " storage_type/identifier:option=value,..."), + help=("Specify disk state information in the" + " format" + " storage_type/identifier:option=value,...;" + " note this is unused for now"), type="identkeyval") HV_STATE_OPT = cli_option("--hypervisor-state", default=[], dest="hv_state", action="append", help=("Specify hypervisor state information in the" - " format hypervisor:option=value,..."), + " format hypervisor:option=value,...;" + " note this is unused for now"), type="identkeyval") IGNORE_IPOLICY_OPT = cli_option("--ignore-ipolicy", dest="ignore_ipolicy", @@ -2071,10 +2082,23 @@ def SetGenericOpcodeOpts(opcode_list, options): op.priority = _PRIONAME_TO_VALUE[options.priority] -def GetClient(): +def GetClient(query=False): + """Connects to the a luxi socket and returns a client. + + @type query: boolean + @param query: this signifies that the client will only be + used for queries; if the build-time parameter + enable-split-queries is enabled, then the client will be + connected to the query socket instead of the masterd socket + + """ + if query and constants.ENABLE_SPLIT_QUERY: + address = constants.QUERY_SOCKET + else: + address = None # TODO: Cache object? try: - client = luxi.Client() + client = luxi.Client(address=address) except luxi.NoMasterError: ss = ssconf.SimpleStore() @@ -2083,13 +2107,14 @@ def GetClient(): ss.GetMasterNode() except errors.ConfigurationError: raise errors.OpPrereqError("Cluster not initialized or this machine is" - " not part of a cluster") + " not part of a cluster", + errors.ECODE_INVAL) 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) + master, errors.ECODE_INVAL) raise return client @@ -2133,7 +2158,7 @@ def FormatError(err): elif isinstance(err, errors.OpPrereqError): if len(err.args) == 2: obuf.write("Failure: prerequisites not met for this" - " operation:\nerror type: %s, error details:\n%s" % + " operation:\nerror type: %s, error details:\n%s" % (err.args[1], err.args[0])) else: obuf.write("Failure: prerequisites not met for this" @@ -2263,7 +2288,8 @@ def ParseNicOption(optvalue): try: nic_max = max(int(nidx[0]) + 1 for nidx in optvalue) except (TypeError, ValueError), err: - raise errors.OpPrereqError("Invalid NIC index passed: %s" % str(err)) + raise errors.OpPrereqError("Invalid NIC index passed: %s" % str(err), + errors.ECODE_INVAL) nics = [{}] * nic_max for nidx, ndict in optvalue: @@ -2271,7 +2297,7 @@ def ParseNicOption(optvalue): if not isinstance(ndict, dict): raise errors.OpPrereqError("Invalid nic/%d value: expected dict," - " got %s" % (nidx, ndict)) + " got %s" % (nidx, ndict), errors.ECODE_INVAL) utils.ForceDictType(ndict, constants.INIC_PARAMS_TYPES) @@ -2315,15 +2341,16 @@ def GenericInstanceCreate(mode, opts, args): if opts.disk_template == constants.DT_DISKLESS: if opts.disks or opts.sd_size is not None: raise errors.OpPrereqError("Diskless instance but disk" - " information passed") + " information passed", errors.ECODE_INVAL) disks = [] else: if (not opts.disks and not opts.sd_size and mode == constants.INSTANCE_CREATE): - raise errors.OpPrereqError("No disk information specified") + raise errors.OpPrereqError("No disk information specified", + errors.ECODE_INVAL) if opts.disks and opts.sd_size is not None: raise errors.OpPrereqError("Please use either the '--disk' or" - " '-s' option") + " '-s' option", errors.ECODE_INVAL) if opts.sd_size is not None: opts.disks = [(0, {constants.IDISK_SIZE: opts.sd_size})] @@ -2331,7 +2358,8 @@ def GenericInstanceCreate(mode, opts, args): try: 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)) + raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err), + errors.ECODE_INVAL) disks = [{}] * disk_max else: disks = [] @@ -2339,25 +2367,25 @@ def GenericInstanceCreate(mode, opts, args): didx = int(didx) if not isinstance(ddict, dict): msg = "Invalid disk/%d value: expected dict, got %s" % (didx, ddict) - raise errors.OpPrereqError(msg) + raise errors.OpPrereqError(msg, errors.ECODE_INVAL) elif constants.IDISK_SIZE in ddict: if constants.IDISK_ADOPT in ddict: raise errors.OpPrereqError("Only one of 'size' and 'adopt' allowed" - " (disk %d)" % didx) + " (disk %d)" % didx, errors.ECODE_INVAL) try: ddict[constants.IDISK_SIZE] = \ utils.ParseUnit(ddict[constants.IDISK_SIZE]) except ValueError, err: raise errors.OpPrereqError("Invalid disk size for disk %d: %s" % - (didx, err)) + (didx, err), errors.ECODE_INVAL) elif constants.IDISK_ADOPT in ddict: if mode == constants.INSTANCE_IMPORT: raise errors.OpPrereqError("Disk adoption not allowed for instance" - " import") + " import", errors.ECODE_INVAL) ddict[constants.IDISK_SIZE] = 0 else: raise errors.OpPrereqError("Missing size or adoption source for" - " disk %d" % didx) + " disk %d" % didx, errors.ECODE_INVAL) disks[didx] = ddict if opts.tags is not None: @@ -2848,7 +2876,7 @@ def _WarnUnknownFields(fdefs): def GenericList(resource, fields, names, unit, separator, header, cl=None, format_override=None, verbose=False, force_filter=False, - namefield=None, qfilter=None): + namefield=None, qfilter=None, isnumeric=False): """Generic implementation for listing all items of a resource. @param resource: One of L{constants.QR_VIA_LUXI} @@ -2876,12 +2904,17 @@ def GenericList(resource, fields, names, unit, separator, header, cl=None, L{qlang.MakeFilter} for details) @type qfilter: list or None @param qfilter: Query filter (in addition to names) + @param isnumeric: bool + @param isnumeric: Whether the namefield's type is numeric, and therefore + any simple filters built by namefield should use integer values to + reflect that """ if not names: names = None - namefilter = qlang.MakeFilter(names, force_filter, namefield=namefield) + namefilter = qlang.MakeFilter(names, force_filter, namefield=namefield, + isnumeric=isnumeric) if qfilter is None: qfilter = namefilter @@ -3066,7 +3099,8 @@ def ParseTimespec(value): """ value = str(value) if not value: - raise errors.OpPrereqError("Empty time specification passed") + raise errors.OpPrereqError("Empty time specification passed", + errors.ECODE_INVAL) suffix_map = { "s": 1, "m": 60, @@ -3078,17 +3112,19 @@ def ParseTimespec(value): try: value = int(value) except (TypeError, ValueError): - raise errors.OpPrereqError("Invalid time specification '%s'" % value) + raise errors.OpPrereqError("Invalid time specification '%s'" % value, + errors.ECODE_INVAL) else: multiplier = suffix_map[value[-1]] value = value[:-1] if not value: # no data left after stripping the suffix raise errors.OpPrereqError("Invalid time specification (only" - " suffix passed)") + " suffix passed)", errors.ECODE_INVAL) try: value = int(value) * multiplier except (TypeError, ValueError): - raise errors.OpPrereqError("Invalid time specification '%s'" % value) + raise errors.OpPrereqError("Invalid time specification '%s'" % value, + errors.ECODE_INVAL) return value @@ -3441,12 +3477,12 @@ def _MaybeParseUnit(elements): """Parses and returns an array of potential values with units. """ - parsed = [] - for e in elements: - if e == constants.VALUE_DEFAULT: - parsed.append(e) + parsed = {} + for k, v in elements.items(): + if v == constants.VALUE_DEFAULT: + parsed[k] = v else: - parsed.append(utils.ParseUnit(e)) + parsed[k] = utils.ParseUnit(v) return parsed