X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/0e82dcf92b6b991bd9d90e7b0aeae36dfdfe2a98..2a6f6ef7ce23bdf1b55627785320d8086dcb77aa:/lib/client/gnt_cluster.py diff --git a/lib/client/gnt_cluster.py b/lib/client/gnt_cluster.py index 3de05c2..408fde7 100644 --- a/lib/client/gnt_cluster.py +++ b/lib/client/gnt_cluster.py @@ -1,7 +1,7 @@ # # -# Copyright (C) 2006, 2007, 2010, 2011 Google Inc. +# Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Google Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -26,6 +26,7 @@ # W0614: Unused import %s from wildcard import (since we need cli) # C0103: Invalid name gnt-cluster +from cStringIO import StringIO import os.path import time import OpenSSL @@ -42,6 +43,7 @@ from ganeti import objects from ganeti import uidpool from ganeti import compat from ganeti import netutils +from ganeti import pathutils ON_OPT = cli_option("--on", default=False, @@ -49,14 +51,31 @@ ON_OPT = cli_option("--on", default=False, help="Recover from an EPO") GROUPS_OPT = cli_option("--groups", default=False, - action="store_true", dest="groups", - help="Arguments are node groups instead of nodes") + action="store_true", dest="groups", + help="Arguments are node groups instead of nodes") + +FORCE_FAILOVER = cli_option("--yes-do-it", dest="yes_do_it", + help="Override interactive check for --no-voting", + default=False, action="store_true") _EPO_PING_INTERVAL = 30 # 30 seconds between pings _EPO_PING_TIMEOUT = 1 # 1 second _EPO_REACHABLE_TIMEOUT = 15 * 60 # 15 minutes +def _CheckNoLvmStorageOptDeprecated(opts): + """Checks if the legacy option '--no-lvm-storage' is used. + + """ + if not opts.lvm_storage: + ToStderr("The option --no-lvm-storage is no longer supported. If you want" + " to disable lvm-based storage cluster-wide, use the option" + " --enabled-disk-templates to disable all of these lvm-base disk " + " templates: %s" % + utils.CommaJoin(utils.GetLvmDiskTemplates())) + return 1 + + @UsesRPC def InitCluster(opts, args): """Initialize the cluster. @@ -69,13 +88,28 @@ def InitCluster(opts, args): @return: the desired exit code """ - if not opts.lvm_storage and opts.vg_name: - ToStderr("Options --no-lvm-storage and --vg-name conflict.") + if _CheckNoLvmStorageOptDeprecated(opts): return 1 - - vg_name = opts.vg_name - if opts.lvm_storage and not opts.vg_name: - vg_name = constants.DEFAULT_VG + enabled_disk_templates = opts.enabled_disk_templates + if enabled_disk_templates: + enabled_disk_templates = enabled_disk_templates.split(",") + else: + enabled_disk_templates = constants.DEFAULT_ENABLED_DISK_TEMPLATES + + vg_name = None + if opts.vg_name is not None: + vg_name = opts.vg_name + if vg_name: + if not utils.IsLvmEnabled(enabled_disk_templates): + ToStdout("You specified a volume group with --vg-name, but you did not" + " enable any disk template that uses lvm.") + else: + if utils.IsLvmEnabled(enabled_disk_templates): + ToStderr("LVM disk templates are enabled, but vg name not set.") + return 1 + else: + if utils.IsLvmEnabled(enabled_disk_templates): + vg_name = constants.DEFAULT_VG if not opts.drbd_storage and opts.drbd_helper: ToStderr("Options --no-drbd-storage and --drbd-usermode-helper conflict.") @@ -98,9 +132,19 @@ def InitCluster(opts, args): beparams = opts.beparams nicparams = opts.nicparams + diskparams = dict(opts.diskparams) + + # check the disk template types here, as we cannot rely on the type check done + # by the opcode parameter types + diskparams_keys = set(diskparams.keys()) + if not (diskparams_keys <= constants.DISK_TEMPLATES): + unknown = utils.NiceSort(diskparams_keys - constants.DISK_TEMPLATES) + ToStderr("Disk templates unknown: %s" % utils.CommaJoin(unknown)) + return 1 + # prepare beparams dict beparams = objects.FillDict(constants.BEC_DEFAULTS, beparams) - utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES) + utils.ForceDictType(beparams, constants.BES_PARAMETER_COMPAT) # prepare nicparams dict nicparams = objects.FillDict(constants.NICC_DEFAULTS, nicparams) @@ -120,6 +164,28 @@ def InitCluster(opts, args): hvparams[hv] = objects.FillDict(constants.HVC_DEFAULTS[hv], hvparams[hv]) utils.ForceDictType(hvparams[hv], constants.HVS_PARAMETER_TYPES) + # prepare diskparams dict + for templ in constants.DISK_TEMPLATES: + if templ not in diskparams: + diskparams[templ] = {} + diskparams[templ] = objects.FillDict(constants.DISK_DT_DEFAULTS[templ], + diskparams[templ]) + utils.ForceDictType(diskparams[templ], constants.DISK_DT_TYPES) + + # prepare ipolicy dict + ipolicy = CreateIPolicyFromOpts( + ispecs_mem_size=opts.ispecs_mem_size, + ispecs_cpu_count=opts.ispecs_cpu_count, + ispecs_disk_count=opts.ispecs_disk_count, + ispecs_disk_size=opts.ispecs_disk_size, + ispecs_nic_count=opts.ispecs_nic_count, + minmax_ispecs=opts.ipolicy_bounds_specs, + std_ispecs=opts.ipolicy_std_specs, + ipolicy_disk_templates=opts.ipolicy_disk_templates, + ipolicy_vcpu_ratio=opts.ipolicy_vcpu_ratio, + ipolicy_spindle_ratio=opts.ipolicy_spindle_ratio, + fill_all=True) + if opts.candidate_pool_size is None: opts.candidate_pool_size = constants.MASTER_POOL_SIZE_DEFAULT @@ -151,6 +217,13 @@ def InitCluster(opts, args): ToStderr("Invalid master netmask value: %s" % str(err)) return 1 + if opts.disk_state: + disk_state = utils.FlatToDict(opts.disk_state) + else: + disk_state = {} + + hv_state = dict(opts.hv_state) + bootstrap.InitCluster(cluster_name=args[0], secondary_ip=opts.secondary_ip, vg_name=vg_name, @@ -164,6 +237,8 @@ def InitCluster(opts, args): beparams=beparams, nicparams=nicparams, ndparams=ndparams, + diskparams=diskparams, + ipolicy=ipolicy, candidate_pool_size=opts.candidate_pool_size, modify_etc_hosts=opts.modify_etc_hosts, modify_ssh_setup=opts.modify_ssh_setup, @@ -174,6 +249,9 @@ def InitCluster(opts, args): primary_ip_version=primary_ip_version, prealloc_wipe_disks=opts.prealloc_wipe_disks, use_external_mip_script=external_ip_setup_script, + hv_state=hv_state, + disk_state=disk_state, + enabled_disk_templates=enabled_disk_templates, ) op = opcodes.OpClusterPostInit() SubmitOpCode(op, opts=opts) @@ -197,10 +275,10 @@ def DestroyCluster(opts, args): return 1 op = opcodes.OpClusterDestroy() - master = SubmitOpCode(op, opts=opts) + master_uuid = SubmitOpCode(op, opts=opts) # if we reached this, the opcode didn't fail; we can proceed to # shutdown all the daemons - bootstrap.FinalizeClusterDestroy(master) + bootstrap.FinalizeClusterDestroy(master_uuid) return 0 @@ -288,13 +366,14 @@ def ShowClusterVersion(opts, args): @return: the desired exit code """ - cl = GetClient() + cl = GetClient(query=True) result = cl.QueryClusterInfo() ToStdout("Software version: %s", result["software_version"]) ToStdout("Internode protocol: %s", result["protocol_version"]) ToStdout("Configuration format: %s", result["config_version"]) ToStdout("OS api version: %s", result["os_api_version"]) ToStdout("Export interface: %s", result["export_version"]) + ToStdout("VCS version: %s", result["vcs_version"]) return 0 @@ -313,24 +392,24 @@ def ShowClusterMaster(opts, args): return 0 -def _PrintGroupedParams(paramsdict, level=1, roman=False): - """Print Grouped parameters (be, nic, disk) by group. +def _FormatGroupedParams(paramsdict, roman=False): + """Format Grouped parameters (be, nic, disk) by group. @type paramsdict: dict of dicts @param paramsdict: {group: {param: value, ...}, ...} - @type level: int - @param level: Level of indention + @rtype: dict of dicts + @return: copy of the input dictionaries with strings as values """ - indent = " " * level - for item, val in sorted(paramsdict.items()): + ret = {} + for (item, val) in paramsdict.items(): if isinstance(val, dict): - ToStdout("%s- %s:", indent, item) - _PrintGroupedParams(val, level=level + 1, roman=roman) + ret[item] = _FormatGroupedParams(val, roman=roman) elif roman and isinstance(val, int): - ToStdout("%s %s: %s", indent, item, compat.TryToRoman(val)) + ret[item] = compat.TryToRoman(val) else: - ToStdout("%s %s: %s", indent, item, val) + ret[item] = str(val) + return ret def ShowClusterConfig(opts, args): @@ -343,80 +422,91 @@ def ShowClusterConfig(opts, args): @return: the desired exit code """ - cl = GetClient() + cl = GetClient(query=True) result = cl.QueryClusterInfo() - ToStdout("Cluster name: %s", result["name"]) - ToStdout("Cluster UUID: %s", result["uuid"]) - - ToStdout("Creation time: %s", utils.FormatTime(result["ctime"])) - ToStdout("Modification time: %s", utils.FormatTime(result["mtime"])) - - ToStdout("Master node: %s", result["master"]) - - ToStdout("Architecture (this node): %s (%s)", - result["architecture"][0], result["architecture"][1]) - if result["tags"]: tags = utils.CommaJoin(utils.NiceSort(result["tags"])) else: tags = "(none)" + if result["reserved_lvs"]: + reserved_lvs = utils.CommaJoin(result["reserved_lvs"]) + else: + reserved_lvs = "(none)" - ToStdout("Tags: %s", tags) + enabled_hv = result["enabled_hypervisors"] + hvparams = dict((k, v) for k, v in result["hvparams"].iteritems() + if k in enabled_hv) - ToStdout("Default hypervisor: %s", result["default_hypervisor"]) - ToStdout("Enabled hypervisors: %s", - utils.CommaJoin(result["enabled_hypervisors"])) + info = [ + ("Cluster name", result["name"]), + ("Cluster UUID", result["uuid"]), - ToStdout("Hypervisor parameters:") - _PrintGroupedParams(result["hvparams"]) + ("Creation time", utils.FormatTime(result["ctime"])), + ("Modification time", utils.FormatTime(result["mtime"])), - ToStdout("OS-specific hypervisor parameters:") - _PrintGroupedParams(result["os_hvp"]) + ("Master node", result["master"]), - ToStdout("OS parameters:") - _PrintGroupedParams(result["osparams"]) + ("Architecture (this node)", + "%s (%s)" % (result["architecture"][0], result["architecture"][1])), - ToStdout("Hidden OSes: %s", utils.CommaJoin(result["hidden_os"])) - ToStdout("Blacklisted OSes: %s", utils.CommaJoin(result["blacklisted_os"])) + ("Tags", tags), - ToStdout("Cluster parameters:") - ToStdout(" - candidate pool size: %s", - compat.TryToRoman(result["candidate_pool_size"], - convert=opts.roman_integers)) - ToStdout(" - master netdev: %s", result["master_netdev"]) - ToStdout(" - master netmask: %s", result["master_netmask"]) - ToStdout(" - use external master IP address setup script: %s", - result["use_external_mip_script"]) - ToStdout(" - lvm volume group: %s", result["volume_group_name"]) - if result["reserved_lvs"]: - reserved_lvs = utils.CommaJoin(result["reserved_lvs"]) - else: - reserved_lvs = "(none)" - ToStdout(" - lvm reserved volumes: %s", reserved_lvs) - ToStdout(" - drbd usermode helper: %s", result["drbd_usermode_helper"]) - ToStdout(" - file storage path: %s", result["file_storage_dir"]) - ToStdout(" - shared file storage path: %s", - result["shared_file_storage_dir"]) - ToStdout(" - maintenance of node health: %s", - result["maintain_node_health"]) - ToStdout(" - uid pool: %s", - uidpool.FormatUidPool(result["uid_pool"], - roman=opts.roman_integers)) - ToStdout(" - default instance allocator: %s", result["default_iallocator"]) - ToStdout(" - primary ip version: %d", result["primary_ip_version"]) - ToStdout(" - preallocation wipe disks: %s", result["prealloc_wipe_disks"]) - ToStdout(" - OS search path: %s", utils.CommaJoin(constants.OS_SEARCH_PATH)) - - ToStdout("Default node parameters:") - _PrintGroupedParams(result["ndparams"], roman=opts.roman_integers) - - ToStdout("Default instance parameters:") - _PrintGroupedParams(result["beparams"], roman=opts.roman_integers) - - ToStdout("Default nic parameters:") - _PrintGroupedParams(result["nicparams"], roman=opts.roman_integers) + ("Default hypervisor", result["default_hypervisor"]), + ("Enabled hypervisors", utils.CommaJoin(enabled_hv)), + + ("Hypervisor parameters", _FormatGroupedParams(hvparams)), + + ("OS-specific hypervisor parameters", + _FormatGroupedParams(result["os_hvp"])), + ("OS parameters", _FormatGroupedParams(result["osparams"])), + + ("Hidden OSes", utils.CommaJoin(result["hidden_os"])), + ("Blacklisted OSes", utils.CommaJoin(result["blacklisted_os"])), + + ("Cluster parameters", [ + ("candidate pool size", + compat.TryToRoman(result["candidate_pool_size"], + convert=opts.roman_integers)), + ("master netdev", result["master_netdev"]), + ("master netmask", result["master_netmask"]), + ("use external master IP address setup script", + result["use_external_mip_script"]), + ("lvm volume group", result["volume_group_name"]), + ("lvm reserved volumes", reserved_lvs), + ("drbd usermode helper", result["drbd_usermode_helper"]), + ("file storage path", result["file_storage_dir"]), + ("shared file storage path", result["shared_file_storage_dir"]), + ("maintenance of node health", result["maintain_node_health"]), + ("uid pool", uidpool.FormatUidPool(result["uid_pool"])), + ("default instance allocator", result["default_iallocator"]), + ("primary ip version", result["primary_ip_version"]), + ("preallocation wipe disks", result["prealloc_wipe_disks"]), + ("OS search path", utils.CommaJoin(pathutils.OS_SEARCH_PATH)), + ("ExtStorage Providers search path", + utils.CommaJoin(pathutils.ES_SEARCH_PATH)), + ("enabled disk templates", + utils.CommaJoin(result["enabled_disk_templates"])), + ]), + + ("Default node parameters", + _FormatGroupedParams(result["ndparams"], roman=opts.roman_integers)), + + ("Default instance parameters", + _FormatGroupedParams(result["beparams"], roman=opts.roman_integers)), + + ("Default nic parameters", + _FormatGroupedParams(result["nicparams"], roman=opts.roman_integers)), + + ("Default disk parameters", + _FormatGroupedParams(result["diskparams"], roman=opts.roman_integers)), + + ("Instance policy - limits for instances", + FormatPolicyInfo(result["ipolicy"], None, True)), + ] + + PrintGenericInfo(info) return 0 @@ -432,6 +522,8 @@ def ClusterCopyFile(opts, args): """ filename = args[0] + filename = os.path.abspath(filename) + if not os.path.exists(filename): raise errors.OpPrereqError("No such filename '%s'" % filename, errors.ECODE_INVAL) @@ -444,7 +536,7 @@ def ClusterCopyFile(opts, args): secondary_ips=opts.use_replication_network, nodegroup=opts.nodegroup) - srun = ssh.SshRunner(cluster_name=cluster_name) + srun = ssh.SshRunner(cluster_name) for node in results: if not srun.CopyFileToNode(node, filename): ToStderr("Copy of file %s to node %s failed", filename, node) @@ -479,10 +571,19 @@ def RunClusterCommand(opts, args): nodes.append(master_node) for name in nodes: - result = srun.Run(name, "root", command) + result = srun.Run(name, constants.SSH_LOGIN_USER, command) + + if opts.failure_only and result.exit_code == constants.EXIT_SUCCESS: + # Do not output anything for successful commands + continue + ToStdout("------------------------------------------------") - ToStdout("node: %s", name) - ToStdout("%s", result.output) + if opts.show_machine_names: + for line in result.output.splitlines(): + ToStdout("%s: %s", name, line) + else: + ToStdout("node: %s", name) + ToStdout("%s", result.output) ToStdout("return code = %s", result.exit_code) return 0 @@ -608,6 +709,8 @@ def VerifyDisks(opts, args): ToStdout("You need to replace or recreate disks for all the above" " instances if this message persists after fixing broken nodes.") retcode = constants.EXIT_FAILURE + elif not instances: + ToStdout("No disks need to be activated.") return retcode @@ -641,7 +744,7 @@ def MasterFailover(opts, args): @return: the desired exit code """ - if opts.no_voting: + if opts.no_voting and not opts.yes_do_it: usertext = ("This will perform the failover even if most other nodes" " are down, or if this node is outdated. This is dangerous" " as it can lead to a non-consistent cluster. Check the" @@ -725,7 +828,7 @@ def _ReadAndVerifyCert(cert_filename, verify_private_key=False): return pem -def _RenewCrypto(new_cluster_cert, new_rapi_cert, #pylint: disable=R0911 +def _RenewCrypto(new_cluster_cert, new_rapi_cert, # pylint: disable=R0911 rapi_cert_filename, new_spice_cert, spice_cert_filename, spice_cacert_filename, new_confd_hmac_key, new_cds, cds_filename, force): @@ -817,20 +920,20 @@ def _RenewCrypto(new_cluster_cert, new_rapi_cert, #pylint: disable=R0911 files_to_copy = [] if new_cluster_cert: - files_to_copy.append(constants.NODED_CERT_FILE) + files_to_copy.append(pathutils.NODED_CERT_FILE) if new_rapi_cert or rapi_cert_pem: - files_to_copy.append(constants.RAPI_CERT_FILE) + files_to_copy.append(pathutils.RAPI_CERT_FILE) if new_spice_cert or spice_cert_pem: - files_to_copy.append(constants.SPICE_CERT_FILE) - files_to_copy.append(constants.SPICE_CACERT_FILE) + files_to_copy.append(pathutils.SPICE_CERT_FILE) + files_to_copy.append(pathutils.SPICE_CACERT_FILE) if new_confd_hmac_key: - files_to_copy.append(constants.CONFD_HMAC_KEY) + files_to_copy.append(pathutils.CONFD_HMAC_KEY) if new_cds or cds: - files_to_copy.append(constants.CLUSTER_DOMAIN_SECRET_FILE) + files_to_copy.append(pathutils.CLUSTER_DOMAIN_SECRET_FILE) if files_to_copy: for node_name in ctx.nonmaster_nodes: @@ -873,10 +976,10 @@ def SetClusterParams(opts, args): @return: the desired exit code """ - if not (not opts.lvm_storage or opts.vg_name or - not opts.drbd_storage or opts.drbd_helper or + if not (opts.vg_name is not None or opts.drbd_helper or opts.enabled_hypervisors or opts.hvparams or - opts.beparams or opts.nicparams or opts.ndparams or + opts.beparams or opts.nicparams or + opts.ndparams or opts.diskparams or opts.candidate_pool_size is not None or opts.uid_pool is not None or opts.maintain_node_health is not None or @@ -887,17 +990,36 @@ def SetClusterParams(opts, args): opts.master_netdev is not None or opts.master_netmask is not None or opts.use_external_mip_script is not None or - opts.prealloc_wipe_disks is not None): + opts.prealloc_wipe_disks is not None or + opts.hv_state or + opts.enabled_disk_templates or + opts.disk_state or + opts.ipolicy_bounds_specs is not None or + opts.ipolicy_std_specs is not None or + opts.ipolicy_disk_templates is not None or + opts.ipolicy_vcpu_ratio is not None or + opts.ipolicy_spindle_ratio is not None or + opts.modify_etc_hosts is not None or + opts.file_storage_dir is not None): ToStderr("Please give at least one of the parameters.") return 1 - vg_name = opts.vg_name - if not opts.lvm_storage and opts.vg_name: - ToStderr("Options --no-lvm-storage and --vg-name conflict.") + if _CheckNoLvmStorageOptDeprecated(opts): return 1 - if not opts.lvm_storage: - vg_name = "" + enabled_disk_templates = None + if opts.enabled_disk_templates: + enabled_disk_templates = opts.enabled_disk_templates.split(",") + + # consistency between vg name and enabled disk templates + vg_name = None + if opts.vg_name is not None: + vg_name = opts.vg_name + if enabled_disk_templates: + if vg_name and not utils.IsLvmEnabled(enabled_disk_templates): + ToStdout("You specified a volume group with --vg-name, but you did not" + " enable any of the following lvm-based disk templates: %s" % + utils.CommaJoin(utils.GetLvmDiskTemplates())) drbd_helper = opts.drbd_helper if not opts.drbd_storage and opts.drbd_helper: @@ -916,8 +1038,13 @@ def SetClusterParams(opts, args): for hv_params in hvparams.values(): utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES) + diskparams = dict(opts.diskparams) + + for dt_params in diskparams.values(): + utils.ForceDictType(dt_params, constants.DISK_DT_TYPES) + beparams = opts.beparams - utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES) + utils.ForceDictType(beparams, constants.BES_PARAMETER_COMPAT) nicparams = opts.nicparams utils.ForceDictType(nicparams, constants.NICS_PARAMETER_TYPES) @@ -926,6 +1053,14 @@ def SetClusterParams(opts, args): if ndparams is not None: utils.ForceDictType(ndparams, constants.NDS_PARAMETER_TYPES) + ipolicy = CreateIPolicyFromOpts( + minmax_ispecs=opts.ipolicy_bounds_specs, + std_ispecs=opts.ipolicy_std_specs, + ipolicy_disk_templates=opts.ipolicy_disk_templates, + ipolicy_vcpu_ratio=opts.ipolicy_vcpu_ratio, + ipolicy_spindle_ratio=opts.ipolicy_spindle_ratio, + ) + mnh = opts.maintain_node_health uid_pool = opts.uid_pool @@ -955,27 +1090,43 @@ def SetClusterParams(opts, args): ext_ip_script = opts.use_external_mip_script - op = opcodes.OpClusterSetParams(vg_name=vg_name, - drbd_helper=drbd_helper, - enabled_hypervisors=hvlist, - hvparams=hvparams, - os_hvp=None, - beparams=beparams, - nicparams=nicparams, - ndparams=ndparams, - candidate_pool_size=opts.candidate_pool_size, - maintain_node_health=mnh, - uid_pool=uid_pool, - add_uids=add_uids, - remove_uids=remove_uids, - default_iallocator=opts.default_iallocator, - prealloc_wipe_disks=opts.prealloc_wipe_disks, - master_netdev=opts.master_netdev, - master_netmask=opts.master_netmask, - reserved_lvs=opts.reserved_lvs, - use_external_mip_script=ext_ip_script, - ) - SubmitOpCode(op, opts=opts) + if opts.disk_state: + disk_state = utils.FlatToDict(opts.disk_state) + else: + disk_state = {} + + hv_state = dict(opts.hv_state) + + op = opcodes.OpClusterSetParams( + vg_name=vg_name, + drbd_helper=drbd_helper, + enabled_hypervisors=hvlist, + hvparams=hvparams, + os_hvp=None, + beparams=beparams, + nicparams=nicparams, + ndparams=ndparams, + diskparams=diskparams, + ipolicy=ipolicy, + candidate_pool_size=opts.candidate_pool_size, + maintain_node_health=mnh, + modify_etc_hosts=opts.modify_etc_hosts, + uid_pool=uid_pool, + add_uids=add_uids, + remove_uids=remove_uids, + default_iallocator=opts.default_iallocator, + prealloc_wipe_disks=opts.prealloc_wipe_disks, + master_netdev=opts.master_netdev, + master_netmask=opts.master_netmask, + reserved_lvs=opts.reserved_lvs, + use_external_mip_script=ext_ip_script, + hv_state=hv_state, + disk_state=disk_state, + enabled_disk_templates=enabled_disk_templates, + force=opts.force, + file_storage_dir=opts.file_storage_dir, + ) + SubmitOrSend(op, opts) return 0 @@ -1087,12 +1238,13 @@ def _OobPower(opts, node_list, power): return True -def _InstanceStart(opts, inst_list, start): +def _InstanceStart(opts, inst_list, start, no_remember=False): """Puts the instances in the list to desired state. @param opts: The command line options selected by the user @param inst_list: The list of instances to operate on @param start: True if they should be started, False for shutdown + @param no_remember: If the instance state should be remembered @return: The success of the operation (none failed) """ @@ -1101,7 +1253,8 @@ def _InstanceStart(opts, inst_list, start): text_submit, text_success, text_failed = ("startup", "started", "starting") else: opcls = compat.partial(opcodes.OpInstanceShutdown, - timeout=opts.shutdown_timeout) + timeout=opts.shutdown_timeout, + no_remember=no_remember) text_submit, text_success, text_failed = ("shutdown", "stopped", "stopping") jex = JobExecutor(opts=opts) @@ -1281,7 +1434,7 @@ def _EpoOff(opts, node_list, inst_map): @return: The desired exit status """ - if not _InstanceStart(opts, inst_map.keys(), False): + if not _InstanceStart(opts, inst_map.keys(), False, no_remember=True): ToStderr("Please investigate and stop instances manually before continuing") return constants.EXIT_FAILURE @@ -1294,7 +1447,9 @@ def _EpoOff(opts, node_list, inst_map): return constants.EXIT_FAILURE -def Epo(opts, args): +def Epo(opts, args, cl=None, _on_fn=_EpoOn, _off_fn=_EpoOff, + _confirm_fn=ConfirmOperation, + _stdout_fn=ToStdout, _stderr_fn=ToStderr): """EPO operations. @param opts: the command line options selected by the user @@ -1305,32 +1460,29 @@ def Epo(opts, args): """ if opts.groups and opts.show_all: - ToStderr("Only one of --groups or --all are allowed") + _stderr_fn("Only one of --groups or --all are allowed") return constants.EXIT_FAILURE elif args and opts.show_all: - ToStderr("Arguments in combination with --all are not allowed") + _stderr_fn("Arguments in combination with --all are not allowed") return constants.EXIT_FAILURE - client = GetClient() + if cl is None: + cl = GetClient() if opts.groups: - node_query_list = itertools.chain(*client.QueryGroups(names=args, - fields=["node_list"], - use_locking=False)) + node_query_list = \ + itertools.chain(*cl.QueryGroups(args, ["node_list"], False)) else: node_query_list = args - result = client.QueryNodes(names=node_query_list, - fields=["name", "master", "pinst_list", - "sinst_list", "powered", "offline"], - use_locking=False) + result = cl.QueryNodes(node_query_list, ["name", "master", "pinst_list", + "sinst_list", "powered", "offline"], + False) + + all_nodes = map(compat.fst, result) node_list = [] inst_map = {} - for (idx, (node, master, pinsts, sinsts, powered, - offline)) in enumerate(result): - # Normalize the node_query_list as well - if not opts.show_all: - node_query_list[idx] = node + for (node, master, pinsts, sinsts, powered, offline) in result: if not offline: for inst in (pinsts + sinsts): if inst in inst_map: @@ -1346,25 +1498,45 @@ def Epo(opts, args): # already operating on the master at this point :) continue elif master and not opts.show_all: - ToStderr("%s is the master node, please do a master-failover to another" - " node not affected by the EPO or use --all if you intend to" - " shutdown the whole cluster", node) + _stderr_fn("%s is the master node, please do a master-failover to another" + " node not affected by the EPO or use --all if you intend to" + " shutdown the whole cluster", node) return constants.EXIT_FAILURE elif powered is None: - ToStdout("Node %s does not support out-of-band handling, it can not be" - " handled in a fully automated manner", node) + _stdout_fn("Node %s does not support out-of-band handling, it can not be" + " handled in a fully automated manner", node) elif powered == opts.on: - ToStdout("Node %s is already in desired power state, skipping", node) + _stdout_fn("Node %s is already in desired power state, skipping", node) elif not offline or (offline and powered): node_list.append(node) - if not opts.force and not ConfirmOperation(node_query_list, "nodes", "epo"): + if not (opts.force or _confirm_fn(all_nodes, "nodes", "epo")): return constants.EXIT_FAILURE if opts.on: - return _EpoOn(opts, node_query_list, node_list, inst_map) + return _on_fn(opts, all_nodes, node_list, inst_map) else: - return _EpoOff(opts, node_list, inst_map) + return _off_fn(opts, node_list, inst_map) + + +def _GetCreateCommand(info): + buf = StringIO() + buf.write("gnt-cluster init") + PrintIPolicyCommand(buf, info["ipolicy"], False) + buf.write(" ") + buf.write(info["name"]) + return buf.getvalue() + + +def ShowCreateCommand(opts, args): + """Shows the command that can be used to re-create the cluster. + + Currently it works only for ipolicy specs. + + """ + cl = GetClient(query=True) + result = cl.QueryClusterInfo() + ToStdout(_GetCreateCommand(result)) commands = { @@ -1376,7 +1548,9 @@ commands = { NOMODIFY_SSH_SETUP_OPT, SECONDARY_IP_OPT, VG_NAME_OPT, MAINTAIN_NODE_HEALTH_OPT, UIDPOOL_OPT, DRBD_HELPER_OPT, NODRBD_STORAGE_OPT, DEFAULT_IALLOCATOR_OPT, PRIMARY_IP_VERSION_OPT, PREALLOC_WIPE_DISKS_OPT, - NODE_PARAMS_OPT, GLOBAL_SHARED_FILEDIR_OPT, USE_EXTERNAL_MIP_SCRIPT], + NODE_PARAMS_OPT, GLOBAL_SHARED_FILEDIR_OPT, USE_EXTERNAL_MIP_SCRIPT, + DISK_PARAMS_OPT, HV_STATE_OPT, DISK_STATE_OPT, ENABLED_DISK_TEMPLATES_OPT, + IPOLICY_STD_SPECS_OPT] + INSTANCE_POLICY_OPTS + SPLIT_ISPECS_OPTS, "[opts...] ", "Initialises a new cluster configuration"), "destroy": ( DestroyCluster, ARGS_NONE, [YES_DOIT_OPT], @@ -1387,7 +1561,7 @@ commands = { "", "Renames the cluster"), "redist-conf": ( - RedistributeConfig, ARGS_NONE, [SUBMIT_OPT, DRY_RUN_OPT, PRIORITY_OPT], + RedistributeConfig, ARGS_NONE, SUBMIT_OPTS + [DRY_RUN_OPT, PRIORITY_OPT], "", "Forces a push of the configuration file and ssconf files" " to the nodes in the cluster"), "verify": ( @@ -1402,7 +1576,7 @@ commands = { RepairDiskSizes, ARGS_MANY_INSTANCES, [DRY_RUN_OPT, PRIORITY_OPT], "[instance...]", "Updates mismatches in recorded disk sizes"), "master-failover": ( - MasterFailover, ARGS_NONE, [NOVOTING_OPT], + MasterFailover, ARGS_NONE, [NOVOTING_OPT, FORCE_FAILOVER], "", "Makes the current node the master"), "master-ping": ( MasterPing, ARGS_NONE, [], @@ -1419,7 +1593,7 @@ commands = { "[-n node...] ", "Copies a file to all (or only some) nodes"), "command": ( RunClusterCommand, [ArgCommand(min=1)], - [NODE_LIST_OPT, NODEGROUP_OPT], + [NODE_LIST_OPT, NODEGROUP_OPT, SHOW_MACHINE_OPT, FAILURE_ONLY_OPT], "[-n node...] ", "Runs a command on all (or only some) nodes"), "info": ( ShowClusterConfig, ARGS_NONE, [ROMAN_OPT], @@ -1427,10 +1601,10 @@ commands = { "list-tags": ( ListTags, ARGS_NONE, [], "", "List the tags of the cluster"), "add-tags": ( - AddTags, [ArgUnknown()], [TAG_SRC_OPT, PRIORITY_OPT], + AddTags, [ArgUnknown()], [TAG_SRC_OPT, PRIORITY_OPT] + SUBMIT_OPTS, "tag...", "Add tags to the cluster"), "remove-tags": ( - RemoveTags, [ArgUnknown()], [TAG_SRC_OPT, PRIORITY_OPT], + RemoveTags, [ArgUnknown()], [TAG_SRC_OPT, PRIORITY_OPT] + SUBMIT_OPTS, "tag...", "Remove tags from the cluster"), "search-tags": ( SearchTags, [ArgUnknown(min=1, max=1)], [PRIORITY_OPT], "", @@ -1448,12 +1622,16 @@ commands = { "{pause |continue|info}", "Change watcher properties"), "modify": ( SetClusterParams, ARGS_NONE, - [BACKEND_OPT, CP_SIZE_OPT, ENABLED_HV_OPT, HVLIST_OPT, MASTER_NETDEV_OPT, + [FORCE_OPT, + BACKEND_OPT, CP_SIZE_OPT, ENABLED_HV_OPT, HVLIST_OPT, MASTER_NETDEV_OPT, MASTER_NETMASK_OPT, NIC_PARAMS_OPT, NOLVM_STORAGE_OPT, VG_NAME_OPT, MAINTAIN_NODE_HEALTH_OPT, UIDPOOL_OPT, ADD_UIDS_OPT, REMOVE_UIDS_OPT, DRBD_HELPER_OPT, NODRBD_STORAGE_OPT, DEFAULT_IALLOCATOR_OPT, RESERVED_LVS_OPT, DRY_RUN_OPT, PRIORITY_OPT, PREALLOC_WIPE_DISKS_OPT, - NODE_PARAMS_OPT, USE_EXTERNAL_MIP_SCRIPT], + NODE_PARAMS_OPT, USE_EXTERNAL_MIP_SCRIPT, DISK_PARAMS_OPT, HV_STATE_OPT, + DISK_STATE_OPT] + SUBMIT_OPTS + + [ENABLED_DISK_TEMPLATES_OPT, IPOLICY_STD_SPECS_OPT, MODIFY_ETCHOSTS_OPT] + + INSTANCE_POLICY_OPTS + [GLOBAL_FILEDIR_OPT], "[opts...]", "Alters the parameters of the cluster"), "renew-crypto": ( @@ -1475,12 +1653,16 @@ commands = { "deactivate-master-ip": ( DeactivateMasterIp, ARGS_NONE, [CONFIRM_OPT], "", "Deactivates the master IP"), + "show-ispecs-cmd": ( + ShowCreateCommand, ARGS_NONE, [], "", + "Show the command line to re-create the cluster"), } #: dictionary with aliases for commands aliases = { "masterfailover": "master-failover", + "show": "info", }