X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/ff00c1a7ec7e0ca66f11950d2187fc3892016bac..4404ffad6d6cead8dcb46f9aee6f38bbc9aaa7ca:/scripts/gnt-instance diff --git a/scripts/gnt-instance b/scripts/gnt-instance index 9c7dd84..ec19602 100755 --- a/scripts/gnt-instance +++ b/scripts/gnt-instance @@ -1,7 +1,7 @@ #!/usr/bin/python # -# Copyright (C) 2006, 2007 Google Inc. +# Copyright (C) 2006, 2007, 2008, 2009, 2010 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 @@ -18,31 +18,42 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. +"""Instance related commands""" -# pylint: disable-msg=W0401,W0614 +# pylint: disable-msg=W0401,W0614,C0103 # W0401: Wildcard import ganeti.cli # W0614: Unused import %s from wildcard import (since we need cli) +# C0103: Invalid name gnt-instance import sys import os import itertools import simplejson -import time from cStringIO import StringIO from ganeti.cli import * -from ganeti import cli from ganeti import opcodes from ganeti import constants +from ganeti import compat from ganeti import utils from ganeti import errors +from ganeti import netutils _SHUTDOWN_CLUSTER = "cluster" _SHUTDOWN_NODES_BOTH = "nodes" _SHUTDOWN_NODES_PRI = "nodes-pri" _SHUTDOWN_NODES_SEC = "nodes-sec" +_SHUTDOWN_NODES_BOTH_BY_TAGS = "nodes-by-tags" +_SHUTDOWN_NODES_PRI_BY_TAGS = "nodes-pri-by-tags" +_SHUTDOWN_NODES_SEC_BY_TAGS = "nodes-sec-by-tags" _SHUTDOWN_INSTANCES = "instances" +_SHUTDOWN_INSTANCES_BY_TAGS = "instances-by-tags" + +_SHUTDOWN_NODES_TAGS_MODES = ( + _SHUTDOWN_NODES_BOTH_BY_TAGS, + _SHUTDOWN_NODES_PRI_BY_TAGS, + _SHUTDOWN_NODES_SEC_BY_TAGS) _VALUE_TRUE = "true" @@ -76,42 +87,58 @@ def _ExpandMultiNames(mode, names, client=None): @raise errors.OpPrereqError: for invalid input parameters """ + # pylint: disable-msg=W0142 + if client is None: client = GetClient() if mode == _SHUTDOWN_CLUSTER: if names: - raise errors.OpPrereqError("Cluster filter mode takes no arguments") + raise errors.OpPrereqError("Cluster filter mode takes no arguments", + errors.ECODE_INVAL) idata = client.QueryInstances([], ["name"], False) inames = [row[0] for row in idata] elif mode in (_SHUTDOWN_NODES_BOTH, _SHUTDOWN_NODES_PRI, - _SHUTDOWN_NODES_SEC): - if not names: - raise errors.OpPrereqError("No node names passed") - ndata = client.QueryNodes(names, ["name", "pinst_list", "sinst_list"], + _SHUTDOWN_NODES_SEC) + _SHUTDOWN_NODES_TAGS_MODES: + if mode in _SHUTDOWN_NODES_TAGS_MODES: + if not names: + raise errors.OpPrereqError("No node tags passed", errors.ECODE_INVAL) + ndata = client.QueryNodes([], ["name", "pinst_list", + "sinst_list", "tags"], False) + ndata = [row for row in ndata if set(row[3]).intersection(names)] + else: + if not names: + raise errors.OpPrereqError("No node names passed", errors.ECODE_INVAL) + ndata = client.QueryNodes(names, ["name", "pinst_list", "sinst_list"], False) + ipri = [row[1] for row in ndata] pri_names = list(itertools.chain(*ipri)) isec = [row[2] for row in ndata] sec_names = list(itertools.chain(*isec)) - if mode == _SHUTDOWN_NODES_BOTH: + if mode in (_SHUTDOWN_NODES_BOTH, _SHUTDOWN_NODES_BOTH_BY_TAGS): inames = pri_names + sec_names - elif mode == _SHUTDOWN_NODES_PRI: + elif mode in (_SHUTDOWN_NODES_PRI, _SHUTDOWN_NODES_PRI_BY_TAGS): inames = pri_names - elif mode == _SHUTDOWN_NODES_SEC: + elif mode in (_SHUTDOWN_NODES_SEC, _SHUTDOWN_NODES_SEC_BY_TAGS): inames = sec_names else: raise errors.ProgrammerError("Unhandled shutdown type") - elif mode == _SHUTDOWN_INSTANCES: if not names: - raise errors.OpPrereqError("No instance names passed") + raise errors.OpPrereqError("No instance names passed", + errors.ECODE_INVAL) idata = client.QueryInstances(names, ["name"], False) inames = [row[0] for row in idata] - + elif mode == _SHUTDOWN_INSTANCES_BY_TAGS: + if not names: + raise errors.OpPrereqError("No instance tags passed", + errors.ECODE_INVAL) + idata = client.QueryInstances([], ["name", "tags"], False) + inames = [row[0] for row in idata if set(row[1]).intersection(names)] else: - raise errors.OpPrereqError("Unknown mode '%s'" % mode) + raise errors.OpPrereqError("Unknown mode '%s'" % mode, errors.ECODE_INVAL) return inames @@ -172,7 +199,39 @@ def _EnsureInstancesExist(client, names): result = client.QueryInstances(names, ["name"], False) for orig_name, row in zip(names, result): if row[0] is None: - raise errors.OpPrereqError("Instance '%s' does not exist" % orig_name) + raise errors.OpPrereqError("Instance '%s' does not exist" % orig_name, + errors.ECODE_NOENT) + + +def GenericManyOps(operation, fn): + """Generic multi-instance operations. + + The will return a wrapper that processes the options and arguments + given, and uses the passed function to build the opcode needed for + the specific operation. Thus all the generic loop/confirmation code + is abstracted into this function. + + """ + def realfn(opts, args): + if opts.multi_mode is None: + opts.multi_mode = _SHUTDOWN_INSTANCES + cl = GetClient() + inames = _ExpandMultiNames(opts.multi_mode, args, client=cl) + if not inames: + raise errors.OpPrereqError("Selection filter does not match" + " any instances", errors.ECODE_INVAL) + multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1 + if not (opts.force_multi or not multi_on + or _ConfirmOperation(inames, operation)): + return 1 + jex = JobExecutor(verbose=multi_on, cl=cl, opts=opts) + for name in inames: + op = fn(name, opts) + jex.QueueJob(name, op) + results = jex.WaitOrShow(not opts.submit_only) + rcode = compat.all(row[0] for row in results) + return int(not rcode) + return realfn def ListInstances(opts, args): @@ -200,6 +259,7 @@ def ListInstances(opts, args): "snodes": "Secondary_Nodes", "admin_state": "Autostart", "oper_state": "Running", "oper_ram": "Memory", "disk_template": "Disk_template", + "oper_vcpus": "VCPUs", "ip": "IP_address", "mac": "MAC_address", "nic_mode": "NIC_Mode", "nic_link": "NIC_Link", "bridge": "Bridge", @@ -209,12 +269,12 @@ def ListInstances(opts, args): "network_port": "Network_port", "hv/kernel_path": "Kernel_path", "hv/initrd_path": "Initrd_path", - "hv/boot_order": "HVM_boot_order", - "hv/acpi": "HVM_ACPI", - "hv/pae": "HVM_PAE", - "hv/cdrom_image_path": "HVM_CDROM_image_path", - "hv/nic_type": "HVM_NIC_type", - "hv/disk_type": "HVM_Disk_type", + "hv/boot_order": "Boot_order", + "hv/acpi": "ACPI", + "hv/pae": "PAE", + "hv/cdrom_image_path": "CDROM_image_path", + "hv/nic_type": "NIC_type", + "hv/disk_type": "Disk_type", "hv/vnc_bind_address": "VNC_bind_address", "serial_no": "SerialNo", "hypervisor": "Hypervisor", "hvparams": "Hypervisor_parameters", @@ -226,7 +286,7 @@ def ListInstances(opts, args): "nic.count": "NICs", "nic.ips": "NIC_IPs", "nic.modes": "NIC_modes", "nic.links": "NIC_links", "nic.bridges": "NIC_bridges", "nic.macs": "NIC_MACs", - "ctime": "CTime", "mtime": "MTime", + "ctime": "CTime", "mtime": "MTime", "uuid": "UUID", } else: headers = None @@ -258,6 +318,9 @@ def ListInstances(opts, args): elif field == "oper_ram": if val is None: val = "(node down)" + elif field == "oper_vcpus": + if val is None: + val = "(node down)" elif field == "sda_size" or field == "sdb_size": if val is None: val = "N/A" @@ -267,6 +330,8 @@ def ListInstances(opts, args): val = ",".join(str(item) for item in val) elif val is None: val = "-" + if opts.roman_integers and isinstance(val, int): + val = compat.TryToRoman(val) row[idx] = str(val) data = GenerateTable(separator=opts.separator, headers=headers, @@ -282,95 +347,10 @@ def ListInstances(opts, args): def AddInstance(opts, args): """Add an instance to the cluster. - @param opts: the command line options selected by the user - @type args: list - @param args: should contain only one element, the new instance name - @rtype: int - @return: the desired exit code + This is just a wrapper over GenericInstanceCreate. """ - instance = args[0] - - (pnode, snode) = SplitNodeOption(opts.node) - - hypervisor = None - hvparams = {} - if opts.hypervisor: - hypervisor, hvparams = opts.hypervisor - - if opts.nics: - try: - 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 - for nidx, ndict in opts.nics: - nidx = int(nidx) - if not isinstance(ndict, dict): - msg = "Invalid nic/%d value: expected dict, got %s" % (nidx, ndict) - raise errors.OpPrereqError(msg) - nics[nidx] = ndict - elif opts.no_nics: - # no nics - nics = [] - else: - # default of one nic, all auto - nics = [{}] - - 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") - disks = [] - else: - if not opts.disks and not opts.sd_size: - raise errors.OpPrereqError("No disk information specified") - if opts.disks and opts.sd_size is not None: - raise errors.OpPrereqError("Please use either the '--disk' or" - " '-s' option") - 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) - except ValueError, err: - raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err)) - disks = [{}] * disk_max - for didx, ddict in opts.disks: - didx = int(didx) - if not isinstance(ddict, dict): - msg = "Invalid disk/%d value: expected dict, got %s" % (didx, ddict) - raise errors.OpPrereqError(msg) - elif "size" not in ddict: - raise errors.OpPrereqError("Missing size for disk %d" % didx) - try: - ddict["size"] = utils.ParseUnit(ddict["size"]) - except ValueError, err: - raise errors.OpPrereqError("Invalid disk size for disk %d: %s" % - (didx, err)) - disks[didx] = ddict - - utils.ForceDictType(opts.beparams, constants.BES_PARAMETER_TYPES) - utils.ForceDictType(hvparams, constants.HVS_PARAMETER_TYPES) - - op = opcodes.OpCreateInstance(instance_name=instance, - disks=disks, - disk_template=opts.disk_template, - nics=nics, - mode=constants.INSTANCE_CREATE, - os_type=opts.os, pnode=pnode, - snode=snode, - start=opts.start, ip_check=opts.ip_check, - wait_for_sync=opts.wait_for_sync, - hypervisor=hypervisor, - hvparams=hvparams, - beparams=opts.beparams, - iallocator=opts.iallocator, - file_storage_dir=opts.file_storage_dir, - file_driver=opts.file_driver, - ) - - SubmitOrSend(op, opts) - return 0 + return GenericInstanceCreate(constants.INSTANCE_CREATE, opts, args) def BatchCreate(opts, args): @@ -409,9 +389,11 @@ def BatchCreate(opts, args): "nics": None, "start": True, "ip_check": True, + "name_check": True, "hypervisor": None, "hvparams": {}, "file_storage_dir": None, + "force_variant": False, "file_driver": 'loop'} def _PopulateWithDefaults(spec): @@ -426,34 +408,41 @@ def BatchCreate(opts, args): for required_field in ('os', 'template'): if required_field not in spec: raise errors.OpPrereqError('Required field "%s" is missing.' % - required_field) + required_field, errors.ECODE_INVAL) # Validate special fields if spec['primary_node'] is not None: if (spec['template'] in constants.DTS_NET_MIRROR and spec['secondary_node'] is None): raise errors.OpPrereqError('Template requires secondary node, but' - ' there was no secondary provided.') + ' there was no secondary provided.', + errors.ECODE_INVAL) elif spec['iallocator'] is None: raise errors.OpPrereqError('You have to provide at least a primary_node' - ' or an iallocator.') + ' or an iallocator.', + errors.ECODE_INVAL) if (spec['hvparams'] and not isinstance(spec['hvparams'], dict)): - raise errors.OpPrereqError('Hypervisor parameters must be a dict.') + raise errors.OpPrereqError('Hypervisor parameters must be a dict.', + errors.ECODE_INVAL) json_filename = args[0] try: instance_data = simplejson.loads(utils.ReadFile(json_filename)) - except Exception, err: + except Exception, err: # pylint: disable-msg=W0703 ToStderr("Can't parse the instance definition file: %s" % str(err)) return 1 - jex = JobExecutor() + if not isinstance(instance_data, dict): + ToStderr("The instance definition file is not in dict format.") + return 1 + + jex = JobExecutor(opts=opts) # Iterate over the instances and do: # * Populate the specs with default value # * Validate the instance specs - i_names = utils.NiceSort(instance_data.keys()) + i_names = utils.NiceSort(instance_data.keys()) # pylint: disable-msg=E1103 for name in i_names: specs = instance_data[name] specs = _PopulateWithDefaults(specs) @@ -466,10 +455,10 @@ def BatchCreate(opts, args): for elem in specs['disk_size']: try: size = utils.ParseUnit(elem) - except ValueError, err: + except (TypeError, ValueError), err: raise errors.OpPrereqError("Invalid disk size '%s' for" " instance %s: %s" % - (elem, name, err)) + (elem, name, err), errors.ECODE_INVAL) disks.append({"size": size}) utils.ForceDictType(specs['backend'], constants.BES_PARAMETER_TYPES) @@ -484,7 +473,8 @@ def BatchCreate(opts, args): if specs['nics'] is not None and tmp_nics: raise errors.OpPrereqError("'nics' list incompatible with using" - " individual nic fields as well") + " individual nic fields as well", + errors.ECODE_INVAL) elif specs['nics'] is not None: tmp_nics = specs['nics'] elif not tmp_nics: @@ -495,11 +485,13 @@ def BatchCreate(opts, args): disk_template=specs['template'], mode=constants.INSTANCE_CREATE, os_type=specs['os'], + force_variant=specs["force_variant"], pnode=specs['primary_node'], snode=specs['secondary_node'], nics=tmp_nics, start=specs['start'], ip_check=specs['ip_check'], + name_check=specs['name_check'], wait_for_sync=True, iallocator=specs['iallocator'], hypervisor=hypervisor, @@ -532,12 +524,14 @@ def ReinstallInstance(opts, args): inames = _ExpandMultiNames(opts.multi_mode, args) if not inames: - raise errors.OpPrereqError("Selection filter does not match any instances") + raise errors.OpPrereqError("Selection filter does not match any instances", + errors.ECODE_INVAL) # second, if requested, ask for an OS if opts.select_os is True: - op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[]) - result = SubmitOpCode(op) + op = opcodes.OpDiagnoseOS(output_fields=["name", "valid", "variants"], + names=[]) + result = SubmitOpCode(op, opts=opts) if not result: ToStdout("Can't get the OS list") @@ -546,10 +540,12 @@ def ReinstallInstance(opts, args): ToStdout("Available OS templates:") number = 0 choices = [] - for entry in result: - ToStdout("%3s: %s", number, entry[0]) - choices.append(("%s" % number, entry[0], entry[0])) - number = number + 1 + for (name, valid, variants) in result: + if valid: + for entry in CalculateOSNames(name, variants): + ToStdout("%3s: %s", number, entry) + choices.append(("%s" % number, entry, entry)) + number += 1 choices.append(('x', 'exit', 'Exit gnt-instance reinstall')) selected = AskUser("Enter OS template number (or x to abort):", @@ -578,10 +574,11 @@ def ReinstallInstance(opts, args): if not AskUser(usertext): return 1 - jex = JobExecutor(verbose=multi_on) + jex = JobExecutor(verbose=multi_on, opts=opts) for instance_name in inames: op = opcodes.OpReinstallInstance(instance_name=instance_name, - os_type=os_name) + os_type=os_name, + force_variant=opts.force_variant) jex.QueueJob(instance_name, op) jex.WaitOrShow(not opts.submit_only) @@ -613,7 +610,8 @@ def RemoveInstance(opts, args): return 1 op = opcodes.OpRemoveInstance(instance_name=instance_name, - ignore_failures=opts.ignore_failures) + ignore_failures=opts.ignore_failures, + shutdown_timeout=opts.shutdown_timeout) SubmitOrSend(op, opts, cl=cl) return 0 @@ -629,10 +627,19 @@ def RenameInstance(opts, args): @return: the desired exit code """ + if not opts.name_check: + if not AskUser("As you disabled the check of the DNS entry, please verify" + " that '%s' is a FQDN. Continue?" % args[1]): + return 1 + op = opcodes.OpRenameInstance(instance_name=args[0], new_name=args[1], - ignore_ip=opts.ignore_ip) - SubmitOrSend(op, opts) + ip_check=opts.ip_check, + name_check=opts.name_check) + result = SubmitOrSend(op, opts) + + ToStdout("Instance '%s' renamed to '%s'", args[0], result) + return 0 @@ -720,8 +727,9 @@ def GrowDisk(opts, args): disk = args[1] try: disk = int(disk) - except ValueError, err: - raise errors.OpPrereqError("Invalid disk index: %s" % str(err)) + except (TypeError, ValueError), err: + raise errors.OpPrereqError("Invalid disk index: %s" % str(err), + errors.ECODE_INVAL) amount = utils.ParseUnit(args[2]) op = opcodes.OpGrowDisk(instance_name=instance, disk=disk, amount=amount, wait_for_sync=opts.wait_for_sync) @@ -729,109 +737,57 @@ def GrowDisk(opts, args): return 0 -def StartupInstance(opts, args): +def _StartupInstance(name, opts): """Startup instances. - Depending on the options given, this will start one or more - instances. + This returns the opcode to start an instance, and its decorator will + wrap this into a loop starting all desired instances. + @param name: the name of the instance to act on @param opts: the command line options selected by the user - @type args: list - @param args: the instance or node names based on which we - create the final selection (in conjunction with the - opts argument) - @rtype: int - @return: the desired exit code + @return: the opcode needed for the operation """ - cl = GetClient() - if opts.multi_mode is None: - opts.multi_mode = _SHUTDOWN_INSTANCES - inames = _ExpandMultiNames(opts.multi_mode, args, client=cl) - if not inames: - raise errors.OpPrereqError("Selection filter does not match any instances") - multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1 - if not (opts.force_multi or not multi_on - or _ConfirmOperation(inames, "startup")): - return 1 - jex = cli.JobExecutor(verbose=multi_on, cl=cl) - for name in inames: - op = opcodes.OpStartupInstance(instance_name=name, - force=opts.force) - # do not add these parameters to the opcode unless they're defined - if opts.hvparams: - op.hvparams = opts.hvparams - if opts.beparams: - op.beparams = opts.beparams - jex.QueueJob(name, op) - jex.WaitOrShow(not opts.submit_only) - return 0 + op = opcodes.OpStartupInstance(instance_name=name, + force=opts.force) + # do not add these parameters to the opcode unless they're defined + if opts.hvparams: + op.hvparams = opts.hvparams + if opts.beparams: + op.beparams = opts.beparams + return op -def RebootInstance(opts, args): +def _RebootInstance(name, opts): """Reboot instance(s). - Depending on the parameters given, this will reboot one or more - instances. + This returns the opcode to reboot an instance, and its decorator + will wrap this into a loop rebooting all desired instances. + @param name: the name of the instance to act on @param opts: the command line options selected by the user - @type args: list - @param args: the instance or node names based on which we - create the final selection (in conjunction with the - opts argument) - @rtype: int - @return: the desired exit code + @return: the opcode needed for the operation """ - cl = GetClient() - if opts.multi_mode is None: - opts.multi_mode = _SHUTDOWN_INSTANCES - inames = _ExpandMultiNames(opts.multi_mode, args, client=cl) - if not inames: - raise errors.OpPrereqError("Selection filter does not match any instances") - multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1 - if not (opts.force_multi or not multi_on - or _ConfirmOperation(inames, "reboot")): - return 1 - jex = JobExecutor(verbose=multi_on, cl=cl) - for name in inames: - op = opcodes.OpRebootInstance(instance_name=name, + return opcodes.OpRebootInstance(instance_name=name, reboot_type=opts.reboot_type, - ignore_secondaries=opts.ignore_secondaries) - jex.QueueJob(name, op) - jex.WaitOrShow(not opts.submit_only) - return 0 + ignore_secondaries=opts.ignore_secondaries, + shutdown_timeout=opts.shutdown_timeout) -def ShutdownInstance(opts, args): +def _ShutdownInstance(name, opts): """Shutdown an instance. + This returns the opcode to shutdown an instance, and its decorator + will wrap this into a loop shutting down all desired instances. + + @param name: the name of the instance to act on @param opts: the command line options selected by the user - @type args: list - @param args: the instance or node names based on which we - create the final selection (in conjunction with the - opts argument) - @rtype: int - @return: the desired exit code + @return: the opcode needed for the operation """ - cl = GetClient() - if opts.multi_mode is None: - opts.multi_mode = _SHUTDOWN_INSTANCES - inames = _ExpandMultiNames(opts.multi_mode, args, client=cl) - if not inames: - raise errors.OpPrereqError("Selection filter does not match any instances") - multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1 - if not (opts.force_multi or not multi_on - or _ConfirmOperation(inames, "shutdown")): - return 1 - - jex = cli.JobExecutor(verbose=multi_on, cl=cl) - for name in inames: - op = opcodes.OpShutdownInstance(instance_name=name) - jex.QueueJob(name, op) - jex.WaitOrShow(not opts.submit_only) - return 0 + return opcodes.OpShutdownInstance(instance_name=name, + timeout=opts.timeout) def ReplaceDisks(opts, args): @@ -844,21 +800,21 @@ def ReplaceDisks(opts, args): @return: the desired exit code """ - instance_name = args[0] - new_2ndary = opts.new_secondary + new_2ndary = opts.dst_node iallocator = opts.iallocator if opts.disks is None: disks = [] else: try: disks = [int(i) for i in opts.disks.split(",")] - except ValueError, err: - raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err)) + except (TypeError, ValueError), err: + raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err), + errors.ECODE_INVAL) cnt = [opts.on_primary, opts.on_secondary, opts.auto, new_2ndary is not None, iallocator is not None].count(True) if cnt != 1: raise errors.OpPrereqError("One and only one of the -p, -s, -a, -n and -i" - " options must be passed") + " options must be passed", errors.ECODE_INVAL) elif opts.on_primary: mode = constants.REPLACE_DISK_PRI elif opts.on_secondary: @@ -867,14 +823,15 @@ def ReplaceDisks(opts, args): mode = constants.REPLACE_DISK_AUTO if disks: raise errors.OpPrereqError("Cannot specify disks when using automatic" - " mode") + " mode", errors.ECODE_INVAL) elif new_2ndary is not None or iallocator is not None: # replace secondary mode = constants.REPLACE_DISK_CHG op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks, remote_node=new_2ndary, mode=mode, - iallocator=iallocator) + iallocator=iallocator, + early_release=opts.early_release) SubmitOrSend(op, opts) return 0 @@ -906,7 +863,8 @@ def FailoverInstance(opts, args): return 1 op = opcodes.OpFailoverInstance(instance_name=instance_name, - ignore_consistency=opts.ignore_consistency) + ignore_consistency=opts.ignore_consistency, + shutdown_timeout=opts.shutdown_timeout) SubmitOrSend(op, opts, cl=cl) return 0 @@ -937,15 +895,24 @@ def MigrateInstance(opts, args): else: usertext = ("Instance %s will be migrated. Note that migration" % (instance_name,)) - usertext += (" is **experimental** in this version." - " This might impact the instance if anything goes wrong." - " Continue?") + usertext += (" might impact the instance if anything goes wrong" + " (e.g. due to bugs in the hypervisor). Continue?") if not AskUser(usertext): return 1 - op = opcodes.OpMigrateInstance(instance_name=instance_name, live=opts.live, + # this should be removed once --non-live is deprecated + if not opts.live and opts.migration_mode is not None: + raise errors.OpPrereqError("Only one of the --non-live and " + "--migration-mode options can be passed", + errors.ECODE_INVAL) + if not opts.live: # --non-live passed + mode = constants.HT_MIGRATION_NONLIVE + else: + mode = opts.migration_mode + + op = opcodes.OpMigrateInstance(instance_name=instance_name, mode=mode, cleanup=opts.cleanup) - SubmitOpCode(op, cl=cl) + SubmitOpCode(op, cl=cl, opts=opts) return 0 @@ -971,7 +938,8 @@ def MoveInstance(opts, args): return 1 op = opcodes.OpMoveInstance(instance_name=instance_name, - target_node=opts.target_node) + target_node=opts.node, + shutdown_timeout=opts.shutdown_timeout) SubmitOrSend(op, opts, cl=cl) return 0 @@ -989,7 +957,7 @@ def ConnectToInstanceConsole(opts, args): instance_name = args[0] op = opcodes.OpConnectConsole(instance_name=instance_name) - cmd = SubmitOpCode(op) + cmd = SubmitOpCode(op, opts=opts) if opts.show_command: ToStdout("%s", utils.ShellQuoteArgs(cmd)) @@ -999,19 +967,21 @@ def ConnectToInstanceConsole(opts, args): finally: ToStderr("Can't run console command %s with arguments:\n'%s'", cmd[0], " ".join(cmd)) - os._exit(1) + os._exit(1) # pylint: disable-msg=W0212 -def _FormatLogicalID(dev_type, logical_id): +def _FormatLogicalID(dev_type, logical_id, roman): """Formats the logical_id of a disk. """ if dev_type == constants.LD_DRBD8: node_a, node_b, port, minor_a, minor_b, key = logical_id data = [ - ("nodeA", "%s, minor=%s" % (node_a, minor_a)), - ("nodeB", "%s, minor=%s" % (node_b, minor_b)), - ("port", port), + ("nodeA", "%s, minor=%s" % (node_a, compat.TryToRoman(minor_a, + convert=roman))), + ("nodeB", "%s, minor=%s" % (node_b, compat.TryToRoman(minor_b, + convert=roman))), + ("port", compat.TryToRoman(port, convert=roman)), ("auth key", key), ] elif dev_type == constants.LD_LV: @@ -1023,7 +993,7 @@ def _FormatLogicalID(dev_type, logical_id): return data -def _FormatBlockDevInfo(idx, top_level, dev, static): +def _FormatBlockDevInfo(idx, top_level, dev, static, roman): """Show block device information. This is only used by L{ShowInstanceConfig}, but it's too big to be @@ -1038,6 +1008,8 @@ def _FormatBlockDevInfo(idx, top_level, dev, static): @type static: boolean @param static: wheter the device information doesn't contain runtime information but only static data + @type roman: boolean + @param roman: whether to try to use roman integers @return: a list of either strings, tuples or lists (which should be formatted at a higher indent level) @@ -1059,19 +1031,19 @@ def _FormatBlockDevInfo(idx, top_level, dev, static): if major is None: major_string = "N/A" else: - major_string = str(major) + major_string = str(compat.TryToRoman(major, convert=roman)) if minor is None: minor_string = "N/A" else: - minor_string = str(minor) + minor_string = str(compat.TryToRoman(minor, convert=roman)) txt += ("%s (%s:%s)" % (path, major_string, minor_string)) if dtype in (constants.LD_DRBD8, ): if syncp is not None: sync_text = "*RECOVERING* %5.2f%%," % syncp if estt: - sync_text += " ETA %ds" % estt + sync_text += " ETA %ss" % compat.TryToRoman(estt, convert=roman) else: sync_text += " ETA unknown" else: @@ -1100,9 +1072,9 @@ def _FormatBlockDevInfo(idx, top_level, dev, static): if dev["iv_name"] is not None: txt = dev["iv_name"] else: - txt = "disk %d" % idx + txt = "disk %s" % compat.TryToRoman(idx, convert=roman) else: - txt = "child %d" % idx + txt = "child %s" % compat.TryToRoman(idx, convert=roman) if isinstance(dev["size"], int): nice_size = utils.FormatUnit(dev["size"], "h") else: @@ -1113,7 +1085,7 @@ def _FormatBlockDevInfo(idx, top_level, dev, static): data.append(("access mode", dev["mode"])) if dev["logical_id"] is not None: try: - l_id = _FormatLogicalID(dev["dev_type"], dev["logical_id"]) + l_id = _FormatLogicalID(dev["dev_type"], dev["logical_id"], roman) except ValueError: l_id = [str(dev["logical_id"])] if len(l_id) == 1: @@ -1131,7 +1103,7 @@ def _FormatBlockDevInfo(idx, top_level, dev, static): if dev["children"]: data.append("child devices:") for c_idx, child in enumerate(dev["children"]): - data.append(_FormatBlockDevInfo(c_idx, False, child, static)) + data.append(_FormatBlockDevInfo(c_idx, False, child, static, roman)) d1.append(data) return d1 @@ -1165,6 +1137,21 @@ def _FormatList(buf, data, indent_level): _FormatList(buf, elem, indent_level+1) +def _FormatParameterDict(buf, per_inst, actual): + """Formats a parameter dictionary. + + @type buf: L{StringIO} + @param buf: the buffer into which to write + @type per_inst: dict + @param per_inst: the instance's own parameters + @type actual: dict + @param actual: the current parameter set (including defaults) + + """ + for key in sorted(actual): + val = per_inst.get(key, "default (%s)" % actual[key]) + buf.write(" - %s: %s\n" % (key, val)) + def ShowInstanceConfig(opts, args): """Compute instance run-time status. @@ -1187,7 +1174,7 @@ def ShowInstanceConfig(opts, args): retcode = 0 op = opcodes.OpQueryInstanceData(instances=args, static=opts.static) - result = SubmitOpCode(op) + result = SubmitOpCode(op, opts=opts) if not result: ToStdout("No instances.") return 1 @@ -1197,7 +1184,10 @@ def ShowInstanceConfig(opts, args): for instance_name in result: instance = result[instance_name] buf.write("Instance name: %s\n" % instance["name"]) - buf.write("Serial number: %s\n" % instance["serial_no"]) + buf.write("UUID: %s\n" % instance["uuid"]) + buf.write("Serial number: %s\n" % + compat.TryToRoman(instance["serial_no"], + convert=opts.roman_integers)) buf.write("Creation time: %s\n" % utils.FormatTime(instance["ctime"])) buf.write("Modification time: %s\n" % utils.FormatTime(instance["mtime"])) buf.write("State: configured to be %s" % instance["config_state"]) @@ -1208,10 +1198,13 @@ def ShowInstanceConfig(opts, args): ## instance["auto_balance"]) buf.write(" Nodes:\n") buf.write(" - primary: %s\n" % instance["pnode"]) - buf.write(" - secondaries: %s\n" % ", ".join(instance["snodes"])) + buf.write(" - secondaries: %s\n" % utils.CommaJoin(instance["snodes"])) buf.write(" Operating system: %s\n" % instance["os"]) + _FormatParameterDict(buf, instance["os_instance"], instance["os_actual"]) if instance.has_key("network_port"): - buf.write(" Allocated network port: %s\n" % instance["network_port"]) + buf.write(" Allocated network port: %s\n" % + compat.TryToRoman(instance["network_port"], + convert=opts.roman_integers)) buf.write(" Hypervisor: %s\n" % instance["hypervisor"]) # custom VNC console information @@ -1220,11 +1213,11 @@ def ShowInstanceConfig(opts, args): if vnc_bind_address: port = instance["network_port"] display = int(port) - constants.VNC_BASE_PORT - if display > 0 and vnc_bind_address == constants.BIND_ADDRESS_GLOBAL: + if display > 0 and vnc_bind_address == constants.IP4_ADDRESS_ANY: vnc_console_port = "%s:%s (display %s)" % (instance["pnode"], port, display) - elif display > 0 and utils.IsValidIP(vnc_bind_address): + elif display > 0 and netutils.IsValidIP4(vnc_bind_address): vnc_console_port = ("%s:%s (node %s) (display %s)" % (vnc_bind_address, port, instance["pnode"], display)) @@ -1234,25 +1227,23 @@ def ShowInstanceConfig(opts, args): vnc_bind_address) buf.write(" - console connection: vnc to %s\n" % vnc_console_port) - for key in instance["hv_actual"]: - if key in instance["hv_instance"]: - val = instance["hv_instance"][key] - else: - val = "default (%s)" % instance["hv_actual"][key] - buf.write(" - %s: %s\n" % (key, val)) + _FormatParameterDict(buf, instance["hv_instance"], instance["hv_actual"]) buf.write(" Hardware:\n") - buf.write(" - VCPUs: %d\n" % - instance["be_actual"][constants.BE_VCPUS]) - buf.write(" - memory: %dMiB\n" % - instance["be_actual"][constants.BE_MEMORY]) + buf.write(" - VCPUs: %s\n" % + compat.TryToRoman(instance["be_actual"][constants.BE_VCPUS], + convert=opts.roman_integers)) + buf.write(" - memory: %sMiB\n" % + compat.TryToRoman(instance["be_actual"][constants.BE_MEMORY], + convert=opts.roman_integers)) buf.write(" - NICs:\n") - for idx, (mac, ip, mode, link) in enumerate(instance["nics"]): + for idx, (ip, mac, mode, link) in enumerate(instance["nics"]): buf.write(" - nic/%d: MAC: %s, IP: %s, mode: %s, link: %s\n" % (idx, mac, ip, mode, link)) buf.write(" Disks:\n") for idx, device in enumerate(instance["disks"]): - _FormatList(buf, _FormatBlockDevInfo(idx, True, device, opts.static), 2) + _FormatList(buf, _FormatBlockDevInfo(idx, True, device, opts.static, + opts.roman_integers), 2) ToStdout(buf.getvalue().rstrip('\n')) return retcode @@ -1270,8 +1261,8 @@ def SetInstanceParams(opts, args): @return: the desired exit code """ - if not (opts.nics or opts.disks or - opts.hvparams or opts.beparams): + if not (opts.nics or opts.disks or opts.disk_template or + opts.hvparams or opts.beparams or opts.os or opts.osparams): ToStderr("Please give at least one of the parameters.") return 1 @@ -1295,25 +1286,38 @@ def SetInstanceParams(opts, args): try: nic_op = int(nic_op) opts.nics[idx] = (nic_op, nic_dict) - except ValueError: + except (TypeError, ValueError): pass for idx, (disk_op, disk_dict) in enumerate(opts.disks): try: disk_op = int(disk_op) opts.disks[idx] = (disk_op, disk_dict) - except ValueError: + except (TypeError, ValueError): pass if disk_op == constants.DDM_ADD: if 'size' not in disk_dict: - raise errors.OpPrereqError("Missing required parameter 'size'") + raise errors.OpPrereqError("Missing required parameter 'size'", + errors.ECODE_INVAL) disk_dict['size'] = utils.ParseUnit(disk_dict['size']) + if (opts.disk_template and + opts.disk_template in constants.DTS_NET_MIRROR and + not opts.node): + ToStderr("Changing the disk template to a mirrored one requires" + " specifying a secondary node") + return 1 + op = opcodes.OpSetInstanceParams(instance_name=args[0], nics=opts.nics, disks=opts.disks, + disk_template=opts.disk_template, + remote_node=opts.node, hvparams=opts.hvparams, beparams=opts.beparams, + os_name=opts.os, + osparams=opts.osparams, + force_variant=opts.force_variant, force=opts.force) # even if here we process the result, we allow submit only @@ -1323,16 +1327,11 @@ def SetInstanceParams(opts, args): ToStdout("Modified instance %s", args[0]) for param, data in result: ToStdout(" - %-5s -> %s", param, data) - ToStdout("Please don't forget that these parameters take effect" + ToStdout("Please don't forget that most parameters take effect" " only at the next start of the instance.") return 0 -# options used in more than one cmd -node_opt = cli_option("-n", "--node", dest="node", help="Target node", - metavar="", - completion_suggest=OPT_COMPL_ONE_NODE) - # multi-instance selection options m_force_multi = cli_option("--force-multiple", dest="force_multi", help="Do not ask for confirmation when more than" @@ -1359,227 +1358,174 @@ m_inst_opt = cli_option("--instance", dest="multi_mode", help="Filter by instance name [default]", const=_SHUTDOWN_INSTANCES, action="store_const") +m_node_tags_opt = cli_option("--node-tags", dest="multi_mode", + help="Filter by node tag", + const=_SHUTDOWN_NODES_BOTH_BY_TAGS, + action="store_const") + +m_pri_node_tags_opt = cli_option("--pri-node-tags", dest="multi_mode", + help="Filter by primary node tag", + const=_SHUTDOWN_NODES_PRI_BY_TAGS, + action="store_const") + +m_sec_node_tags_opt = cli_option("--sec-node-tags", dest="multi_mode", + help="Filter by secondary node tag", + const=_SHUTDOWN_NODES_SEC_BY_TAGS, + action="store_const") + +m_inst_tags_opt = cli_option("--tags", dest="multi_mode", + help="Filter by instance tag", + const=_SHUTDOWN_INSTANCES_BY_TAGS, + action="store_const") # this is defined separately due to readability only add_opts = [ - DEBUG_OPT, - cli_option("-n", "--node", dest="node", - help="Target node and optional secondary node", - metavar="[:]", - completion_suggest=OPT_COMPL_INST_ADD_NODES), - OS_OPT, BACKEND_OPT, - DISK_TEMPLATE_OPT, - OS_SIZE_OPT, DISK_OPT, - NET_OPT, - NONICS_OPT, - NWSYNC_OPT, - cli_option("--no-start", dest="start", default=True, - action="store_false", help="Don't start the instance after" - " creation"), - NOIPCHECK_OPT, + DISK_TEMPLATE_OPT, FILESTORE_DIR_OPT, FILESTORE_DRIVER_OPT, - IALLOCATOR_OPT, HYPERVISOR_OPT, + IALLOCATOR_OPT, + NET_OPT, + NODE_PLACEMENT_OPT, + NOIPCHECK_OPT, + NONAMECHECK_OPT, + NONICS_OPT, + NOSTART_OPT, + NWSYNC_OPT, + OSPARAMS_OPT, + OS_OPT, + FORCE_VARIANT_OPT, + NO_INSTALL_OPT, + OS_SIZE_OPT, SUBMIT_OPT, ] commands = { - 'add': (AddInstance, [ArgHost(min=1, max=1)], add_opts, - "[...] -t disk-type -n node[:secondary-node] -o os-type ", - "Creates and adds a new instance to the cluster"), - 'batch-create': (BatchCreate, [ArgFile(min=1, max=1)], - [DEBUG_OPT], - "", - "Create a bunch of instances based on specs in the file."), - 'console': (ConnectToInstanceConsole, ARGS_ONE_INSTANCE, - [DEBUG_OPT, - cli_option("--show-cmd", dest="show_command", - action="store_true", default=False, - help=("Show command instead of executing it"))], - "[--show-cmd] ", - "Opens a console on the specified instance"), - 'failover': (FailoverInstance, ARGS_ONE_INSTANCE, - [DEBUG_OPT, FORCE_OPT, - cli_option("--ignore-consistency", dest="ignore_consistency", - action="store_true", default=False, - help="Ignore the consistency of the disks on" - " the secondary"), - SUBMIT_OPT, - ], - "[-f] ", - "Stops the instance and starts it on the backup node, using" - " the remote mirror (only for instances of type drbd)"), - 'migrate': (MigrateInstance, ARGS_ONE_INSTANCE, - [DEBUG_OPT, FORCE_OPT, - cli_option("--non-live", dest="live", - default=True, action="store_false", - help="Do a non-live migration (this usually means" - " freeze the instance, save the state," - " transfer and only then resume running on the" - " secondary node)"), - cli_option("--cleanup", dest="cleanup", - default=False, action="store_true", - help="Instead of performing the migration, try to" - " recover from a failed cleanup. This is safe" - " to run even if the instance is healthy, but it" - " will create extra replication traffic and " - " disrupt briefly the replication (like during the" - " migration"), - ], - "[-f] ", - "Migrate instance to its secondary node" - " (only for instances of type drbd)"), - 'move': (MoveInstance, ARGS_ONE_INSTANCE, - [DEBUG_OPT, FORCE_OPT, SUBMIT_OPT, - cli_option("-n", "--new-node", dest="target_node", - help="Destinattion node", metavar="NODE", - default=None, - completion_suggest=OPT_COMPL_ONE_NODE), - ], - "[-f] ", - "Move instance to an arbitrary node" - " (only for instances of type file and lv)"), - 'info': (ShowInstanceConfig, ARGS_MANY_INSTANCES, - [DEBUG_OPT, - cli_option("-s", "--static", dest="static", - action="store_true", default=False, - help="Only show configuration data, not runtime data"), - cli_option("--all", dest="show_all", - default=False, action="store_true", - help="Show info on all instances on the cluster." - " This can take a long time to run, use wisely."), - ], "[-s] {--all | ...}", - "Show information on the specified instance(s)"), - 'list': (ListInstances, ARGS_MANY_INSTANCES, - [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, SYNC_OPT], - "[...]", - "Lists the instances and their status. The available fields are" - " (see the man page for details): status, oper_state, oper_ram," - " name, os, pnode, snodes, admin_state, admin_ram, disk_template," - " ip, mac, mode, link, sda_size, sdb_size, vcpus, serial_no," - " hypervisor." - " The default field" - " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS), - ), - 'reinstall': (ReinstallInstance, [ArgInstance(min=1)], - [DEBUG_OPT, FORCE_OPT, OS_OPT, - m_force_multi, - m_node_opt, m_pri_node_opt, m_sec_node_opt, - m_clust_opt, m_inst_opt, - cli_option("--select-os", dest="select_os", - action="store_true", default=False, - help="Interactive OS reinstall, lists available" - " OS templates for selection"), - SUBMIT_OPT, - ], - "[-f] ", "Reinstall a stopped instance"), - 'remove': (RemoveInstance, ARGS_ONE_INSTANCE, - [DEBUG_OPT, FORCE_OPT, - cli_option("--ignore-failures", dest="ignore_failures", - action="store_true", default=False, - help=("Remove the instance from the cluster even" - " if there are failures during the removal" - " process (shutdown, disk removal, etc.)")), - SUBMIT_OPT, - ], - "[-f] ", "Shuts down the instance and removes it"), - 'rename': (RenameInstance, - [ArgInstance(min=1, max=1), ArgHost(min=1, max=1)], - [DEBUG_OPT, NOIPCHECK_OPT, SUBMIT_OPT], - " ", "Rename the instance"), - 'replace-disks': (ReplaceDisks, ARGS_ONE_INSTANCE, - [DEBUG_OPT, - cli_option("-n", "--new-secondary", dest="new_secondary", - help=("New secondary node (for secondary" - " node change)"), metavar="NODE", - default=None, - completion_suggest=OPT_COMPL_ONE_NODE), - cli_option("-p", "--on-primary", dest="on_primary", - default=False, action="store_true", - help=("Replace the disk(s) on the primary" - " node (only for the drbd template)")), - cli_option("-s", "--on-secondary", dest="on_secondary", - default=False, action="store_true", - help=("Replace the disk(s) on the secondary" - " node (only for the drbd template)")), - cli_option("-a", "--auto", dest="auto", - default=False, action="store_true", - help=("Automatically replace faulty disks" - " (only for the drbd template)")), - DISKIDX_OPT, - IALLOCATOR_OPT, - SUBMIT_OPT, - ], - "[-s|-p|-n NODE|-I NAME] ", - "Replaces all disks for the instance"), - 'modify': (SetInstanceParams, ARGS_ONE_INSTANCE, - [BACKEND_OPT, DEBUG_OPT, DISK_OPT, FORCE_OPT, HVOPTS_OPT, - NET_OPT, SUBMIT_OPT], - "", "Alters the parameters of an instance"), - 'shutdown': (ShutdownInstance, [ArgInstance(min=1)], - [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt, - m_clust_opt, m_inst_opt, m_force_multi, - SUBMIT_OPT, - ], - "", "Stops an instance"), - 'startup': (StartupInstance, [ArgInstance(min=1)], - [DEBUG_OPT, FORCE_OPT, m_force_multi, - m_node_opt, m_pri_node_opt, m_sec_node_opt, - m_clust_opt, m_inst_opt, - SUBMIT_OPT, - HVOPTS_OPT, - BACKEND_OPT, - ], - "", "Starts an instance"), - 'reboot': (RebootInstance, [ArgInstance(min=1)], - [DEBUG_OPT, m_force_multi, - cli_option("-t", "--type", dest="reboot_type", - help="Type of reboot: soft/hard/full", - default=constants.INSTANCE_REBOOT_HARD, - metavar="", - choices=list(constants.REBOOT_TYPES)), - cli_option("--ignore-secondaries", dest="ignore_secondaries", - default=False, action="store_true", - help="Ignore errors from secondaries"), - m_node_opt, m_pri_node_opt, m_sec_node_opt, - m_clust_opt, m_inst_opt, - SUBMIT_OPT, - ], - "", "Reboots an instance"), - 'activate-disks': (ActivateDisks, ARGS_ONE_INSTANCE, - [DEBUG_OPT, SUBMIT_OPT, - cli_option("--ignore-size", dest="ignore_size", - default=False, action="store_true", - help="Ignore current recorded size" - " (useful for forcing activation when" - " the recorded size is wrong)"), - ], - "", - "Activate an instance's disks"), - 'deactivate-disks': (DeactivateDisks, ARGS_ONE_INSTANCE, - [DEBUG_OPT, SUBMIT_OPT], - "", - "Deactivate an instance's disks"), - 'recreate-disks': (RecreateDisks, ARGS_ONE_INSTANCE, - [DEBUG_OPT, SUBMIT_OPT, DISKIDX_OPT], - "", - "Recreate an instance's disks"), - 'grow-disk': (GrowDisk, - [ArgInstance(min=1, max=1), ArgUnknown(min=1, max=1), - ArgUnknown(min=1, max=1)], - [DEBUG_OPT, SUBMIT_OPT, NWSYNC_OPT], - " ", "Grow an instance's disk"), - 'list-tags': (ListTags, ARGS_ONE_INSTANCE, [DEBUG_OPT], - "", "List the tags of the given instance"), - 'add-tags': (AddTags, [ArgInstance(min=1, max=1), ArgUnknown()], - [DEBUG_OPT, TAG_SRC_OPT], - " tag...", "Add tags to the given instance"), - 'remove-tags': (RemoveTags, [ArgInstance(min=1, max=1), ArgUnknown()], - [DEBUG_OPT, TAG_SRC_OPT], - " tag...", "Remove tags from given instance"), + 'add': ( + AddInstance, [ArgHost(min=1, max=1)], add_opts, + "[...] -t disk-type -n node[:secondary-node] -o os-type ", + "Creates and adds a new instance to the cluster"), + 'batch-create': ( + BatchCreate, [ArgFile(min=1, max=1)], [], + "", + "Create a bunch of instances based on specs in the file."), + 'console': ( + ConnectToInstanceConsole, ARGS_ONE_INSTANCE, + [SHOWCMD_OPT], + "[--show-cmd] ", "Opens a console on the specified instance"), + 'failover': ( + FailoverInstance, ARGS_ONE_INSTANCE, + [FORCE_OPT, IGNORE_CONSIST_OPT, SUBMIT_OPT, SHUTDOWN_TIMEOUT_OPT], + "[-f] ", "Stops the instance and starts it on the backup node," + " using the remote mirror (only for instances of type drbd)"), + 'migrate': ( + MigrateInstance, ARGS_ONE_INSTANCE, + [FORCE_OPT, NONLIVE_OPT, MIGRATION_MODE_OPT, CLEANUP_OPT], + "[-f] ", "Migrate instance to its secondary node" + " (only for instances of type drbd)"), + 'move': ( + MoveInstance, ARGS_ONE_INSTANCE, + [FORCE_OPT, SUBMIT_OPT, SINGLE_NODE_OPT, SHUTDOWN_TIMEOUT_OPT], + "[-f] ", "Move instance to an arbitrary node" + " (only for instances of type file and lv)"), + 'info': ( + ShowInstanceConfig, ARGS_MANY_INSTANCES, + [STATIC_OPT, ALL_OPT, ROMAN_OPT], + "[-s] {--all | ...}", + "Show information on the specified instance(s)"), + 'list': ( + ListInstances, ARGS_MANY_INSTANCES, + [NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, SYNC_OPT, ROMAN_OPT], + "[...]", + "Lists the instances and their status. The available fields are" + " (see the man page for details): status, oper_state, oper_ram," + " oper_vcpus, name, os, pnode, snodes, admin_state, admin_ram," + " disk_template, ip, mac, nic_mode, nic_link, sda_size, sdb_size," + " vcpus, serial_no," + " nic.count, nic.mac/N, nic.ip/N, nic.mode/N, nic.link/N," + " nic.macs, nic.ips, nic.modes, nic.links," + " disk.count, disk.size/N, disk.sizes," + " hv/NAME, be/memory, be/vcpus, be/auto_balance," + " hypervisor." + " The default field" + " list is (in order): %s." % utils.CommaJoin(_LIST_DEF_FIELDS), + ), + 'reinstall': ( + ReinstallInstance, [ArgInstance()], + [FORCE_OPT, OS_OPT, FORCE_VARIANT_OPT, m_force_multi, m_node_opt, + m_pri_node_opt, m_sec_node_opt, m_clust_opt, m_inst_opt, m_node_tags_opt, + m_pri_node_tags_opt, m_sec_node_tags_opt, m_inst_tags_opt, SELECT_OS_OPT, + SUBMIT_OPT], + "[-f] ", "Reinstall a stopped instance"), + 'remove': ( + RemoveInstance, ARGS_ONE_INSTANCE, + [FORCE_OPT, SHUTDOWN_TIMEOUT_OPT, IGNORE_FAILURES_OPT, SUBMIT_OPT], + "[-f] ", "Shuts down the instance and removes it"), + 'rename': ( + RenameInstance, + [ArgInstance(min=1, max=1), ArgHost(min=1, max=1)], + [NOIPCHECK_OPT, NONAMECHECK_OPT, SUBMIT_OPT], + " ", "Rename the instance"), + 'replace-disks': ( + ReplaceDisks, ARGS_ONE_INSTANCE, + [AUTO_REPLACE_OPT, DISKIDX_OPT, IALLOCATOR_OPT, EARLY_RELEASE_OPT, + NEW_SECONDARY_OPT, ON_PRIMARY_OPT, ON_SECONDARY_OPT, SUBMIT_OPT], + "[-s|-p|-n NODE|-I NAME] ", + "Replaces all disks for the instance"), + 'modify': ( + SetInstanceParams, ARGS_ONE_INSTANCE, + [BACKEND_OPT, DISK_OPT, FORCE_OPT, HVOPTS_OPT, NET_OPT, SUBMIT_OPT, + DISK_TEMPLATE_OPT, SINGLE_NODE_OPT, OS_OPT, FORCE_VARIANT_OPT, + OSPARAMS_OPT], + "", "Alters the parameters of an instance"), + 'shutdown': ( + GenericManyOps("shutdown", _ShutdownInstance), [ArgInstance()], + [m_node_opt, m_pri_node_opt, m_sec_node_opt, m_clust_opt, + m_node_tags_opt, m_pri_node_tags_opt, m_sec_node_tags_opt, + m_inst_tags_opt, m_inst_opt, m_force_multi, TIMEOUT_OPT, SUBMIT_OPT], + "", "Stops an instance"), + 'startup': ( + GenericManyOps("startup", _StartupInstance), [ArgInstance()], + [FORCE_OPT, m_force_multi, m_node_opt, m_pri_node_opt, m_sec_node_opt, + m_node_tags_opt, m_pri_node_tags_opt, m_sec_node_tags_opt, + m_inst_tags_opt, m_clust_opt, m_inst_opt, SUBMIT_OPT, HVOPTS_OPT, + BACKEND_OPT], + "", "Starts an instance"), + 'reboot': ( + GenericManyOps("reboot", _RebootInstance), [ArgInstance()], + [m_force_multi, REBOOT_TYPE_OPT, IGNORE_SECONDARIES_OPT, m_node_opt, + m_pri_node_opt, m_sec_node_opt, m_clust_opt, m_inst_opt, SUBMIT_OPT, + m_node_tags_opt, m_pri_node_tags_opt, m_sec_node_tags_opt, + m_inst_tags_opt, SHUTDOWN_TIMEOUT_OPT], + "", "Reboots an instance"), + 'activate-disks': ( + ActivateDisks, ARGS_ONE_INSTANCE, [SUBMIT_OPT, IGNORE_SIZE_OPT], + "", "Activate an instance's disks"), + 'deactivate-disks': ( + DeactivateDisks, ARGS_ONE_INSTANCE, [SUBMIT_OPT], + "", "Deactivate an instance's disks"), + 'recreate-disks': ( + RecreateDisks, ARGS_ONE_INSTANCE, [SUBMIT_OPT, DISKIDX_OPT], + "", "Recreate an instance's disks"), + 'grow-disk': ( + GrowDisk, + [ArgInstance(min=1, max=1), ArgUnknown(min=1, max=1), + ArgUnknown(min=1, max=1)], + [SUBMIT_OPT, NWSYNC_OPT], + " ", "Grow an instance's disk"), + 'list-tags': ( + ListTags, ARGS_ONE_INSTANCE, [], + "", "List the tags of the given instance"), + 'add-tags': ( + AddTags, [ArgInstance(min=1, max=1), ArgUnknown()], + [TAG_SRC_OPT], + " tag...", "Add tags to the given instance"), + 'remove-tags': ( + RemoveTags, [ArgInstance(min=1, max=1), ArgUnknown()], + [TAG_SRC_OPT], + " tag...", "Remove tags from given instance"), } #: dictionary with aliases for commands