"DRAINED_OPT",
"DRY_RUN_OPT",
"DRBD_HELPER_OPT",
+ "DST_NODE_OPT",
"EARLY_RELEASE_OPT",
"ENABLED_HV_OPT",
"ERROR_CODES_OPT",
"FORCE_VARIANT_OPT",
"GLOBAL_FILEDIR_OPT",
"HID_OS_OPT",
+ "GLOBAL_SHARED_FILEDIR_OPT",
"HVLIST_OPT",
"HVOPTS_OPT",
"HYPERVISOR_OPT",
"OSPARAMS_OPT",
"OS_OPT",
"OS_SIZE_OPT",
+ "OOB_TIMEOUT_OPT",
+ "POWER_DELAY_OPT",
"PREALLOC_WIPE_DISKS_OPT",
"PRIMARY_IP_VERSION_OPT",
"PRIORITY_OPT",
"VG_NAME_OPT",
"YES_DOIT_OPT",
# Generic functions for CLI programs
+ "ConfirmOperation",
"GenericMain",
"GenericInstanceCreate",
"GenericList",
action="store_true", default=False,
help="Remove the instance from the cluster")
+DST_NODE_OPT = cli_option("-n", "--target-node", dest="dst_node",
+ help="Specifies the new node for the instance",
+ metavar="NODE", default=None,
+ completion_suggest=OPT_COMPL_ONE_NODE)
+
NEW_SECONDARY_OPT = cli_option("-n", "--new-secondary", dest="dst_node",
help="Specifies the new secondary node",
metavar="NODE", default=None,
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)
+
NOMODIFY_ETCHOSTS_OPT = cli_option("--no-etc-hosts", dest="modify_etc_hosts",
help="Don't modify /etc/hosts",
action="store_false", default=True)
dest="node_powered",
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")
+
+POWER_DELAY_OPT = cli_option("--power-delay", dest="power_delay", type="float",
+ default=constants.OOB_POWER_DELAY,
+ help="Time in seconds to wait between power-ons")
+
#: Options provided by all commands
COMMON_OPTS = [DEBUG_OPT]
"""Callable class for formatting fields of a query.
"""
- def __init__(self, fn, status_fn):
+ def __init__(self, fn, status_fn, verbose):
"""Initializes this class.
@type fn: callable
@param fn: Formatting function
@type status_fn: callable
@param status_fn: Function to report fields' status
+ @type verbose: boolean
+ @param verbose: whether to use verbose field descriptions or not
"""
self._fn = fn
self._status_fn = status_fn
+ self._verbose = verbose
def __call__(self, data):
"""Returns a field's string representation.
assert value is None, \
"Found value %r for abnormal status %s" % (value, status)
- if status == constants.RS_UNKNOWN:
- return "(unknown)"
+ return FormatResultError(status, self._verbose)
- if status == constants.RS_NODATA:
- return "(nodata)"
- if status == constants.RS_UNAVAIL:
- return "(unavail)"
+def FormatResultError(status, verbose):
+ """Formats result status other than L{constants.RS_NORMAL}.
- if status == constants.RS_OFFLINE:
- return "(offline)"
+ @param status: The result status
+ @type verbose: boolean
+ @param verbose: Whether to return the verbose text
+ @return: Text of result status
+ """
+ assert status != constants.RS_NORMAL, \
+ "FormatResultError called with status equal to constants.RS_NORMAL"
+ try:
+ (verbose_text, normal_text) = constants.RSS_DESCRIPTION[status]
+ except KeyError:
raise NotImplementedError("Unknown status %s" % status)
+ else:
+ if verbose:
+ return verbose_text
+ return normal_text
def FormatQueryResult(result, unit=None, format_override=None, separator=None,
- header=False):
+ header=False, verbose=False):
"""Formats data in L{objects.QueryResponse}.
@type result: L{objects.QueryResponse}
@param separator: String used to separate fields
@type header: bool
@param header: Whether to output header row
+ @type verbose: boolean
+ @param verbose: whether to use verbose field descriptions or not
"""
if unit is None:
assert fdef.title and fdef.name
(fn, align_right) = _GetColumnFormatter(fdef, format_override, unit)
columns.append(TableColumn(fdef.title,
- _QueryColumnFormatter(fn, _RecordStatus),
+ _QueryColumnFormatter(fn, _RecordStatus,
+ verbose),
align_right))
table = FormatTable(result.data, columns, header, separator)
def GenericList(resource, fields, names, unit, separator, header, cl=None,
- format_override=None):
+ format_override=None, verbose=False):
"""Generic implementation for listing all items of a resource.
- @param resource: One of L{constants.QR_OP_LUXI}
+ @param resource: One of L{constants.QR_VIA_LUXI}
@type fields: list of strings
@param fields: List of fields to query for
@type names: list of strings
@type format_override: dict
@param format_override: Dictionary for overriding field formatting functions,
indexed by field name, contents like L{_DEFAULT_FORMAT_QUERY}
+ @type verbose: boolean
+ @param verbose: whether to use verbose field descriptions or not
"""
if cl is None:
(status, data) = FormatQueryResult(response, unit=unit, separator=separator,
header=header,
- format_override=format_override)
+ format_override=format_override,
+ verbose=verbose)
for line in data:
ToStdout(line)
def GenericListFields(resource, fields, separator, header, cl=None):
"""Generic implementation for listing fields for a resource.
- @param resource: One of L{constants.QR_OP_LUXI}
+ @param resource: One of L{constants.QR_VIA_LUXI}
@type fields: list of strings
@param fields: List of fields to query for
@type separator: string or None
columns = [
TableColumn("Name", str, False),
TableColumn("Title", str, False),
- # TODO: Add field description to master daemon
+ TableColumn("Description", str, False),
]
- rows = [[fdef.name, fdef.title] for fdef in response.fields]
+ rows = [[fdef.name, fdef.title, fdef.doc] for fdef in response.fields]
for line in FormatTable(rows, columns, header, separator):
ToStdout(line)
for key in sorted(actual):
val = param_dict.get(key, "default (%s)" % actual[key])
buf.write("%s- %s: %s\n" % (indent, key, val))
+
+
+def ConfirmOperation(names, list_type, text, extra=""):
+ """Ask the user to confirm an operation on a list of list_type.
+
+ This function is used to request confirmation for doing an operation
+ on a given list of list_type.
+
+ @type names: list
+ @param names: the list of names that we display when
+ we ask for confirmation
+ @type list_type: str
+ @param list_type: Human readable name for elements in the list (e.g. nodes)
+ @type text: str
+ @param text: the operation that the user should confirm
+ @rtype: boolean
+ @return: True or False depending on user's confirmation.
+
+ """
+ count = len(names)
+ msg = ("The %s will operate on %d %s.\n%s"
+ "Do you want to continue?" % (text, count, list_type, extra))
+ affected = (("\nAffected %s:\n" % list_type) +
+ "\n".join([" %s" % name for name in names]))
+
+ choices = [("y", True, "Yes, execute the %s" % text),
+ ("n", False, "No, abort the %s" % text)]
+
+ if count > 20:
+ choices.insert(1, ("v", "v", "View the list of affected %s" % list_type))
+ question = msg
+ else:
+ question = msg + affected
+
+ choice = AskUser(question, choices)
+ if choice == "v":
+ choices.pop(1)
+ choice = AskUser(msg + affected, choices)
+ return choice