X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/d4117a723fab7a634f38b1a29e4cf6f2dee9a3f1..4c6e8e1a6b6321eebd8eb07d123f729a56e8ff65:/lib/client/gnt_instance.py diff --git a/lib/client/gnt_instance.py b/lib/client/gnt_instance.py index 8b6be02..5159fd9 100644 --- a/lib/client/gnt_instance.py +++ b/lib/client/gnt_instance.py @@ -29,7 +29,6 @@ import copy import itertools import simplejson import logging -from cStringIO import StringIO from ganeti.cli import * from ganeti import opcodes @@ -53,21 +52,19 @@ _EXPAND_NODES_SEC_BY_TAGS = "nodes-sec-by-tags" _EXPAND_INSTANCES = "instances" _EXPAND_INSTANCES_BY_TAGS = "instances-by-tags" -_EXPAND_NODES_TAGS_MODES = frozenset([ +_EXPAND_NODES_TAGS_MODES = compat.UniqueFrozenset([ _EXPAND_NODES_BOTH_BY_TAGS, _EXPAND_NODES_PRI_BY_TAGS, _EXPAND_NODES_SEC_BY_TAGS, ]) - #: default list of options for L{ListInstances} _LIST_DEF_FIELDS = [ "name", "hypervisor", "os", "pnode", "status", "oper_ram", ] - _MISSING = object() -_ENV_OVERRIDE = frozenset(["list"]) +_ENV_OVERRIDE = compat.UniqueFrozenset(["list"]) _INST_DATA_VAL = ht.TListOf(ht.TDict) @@ -648,6 +645,7 @@ def _ShutdownInstance(name, opts): """ return opcodes.OpInstanceShutdown(instance_name=name, + force=opts.force, timeout=opts.timeout, ignore_offline_nodes=opts.ignore_offline, no_remember=opts.no_remember) @@ -928,8 +926,8 @@ def _FormatLogicalID(dev_type, logical_id, roman): convert=roman))), ("nodeB", "%s, minor=%s" % (node_b, compat.TryToRoman(minor_b, convert=roman))), - ("port", compat.TryToRoman(port, convert=roman)), - ("auth key", key), + ("port", str(compat.TryToRoman(port, convert=roman))), + ("auth key", str(key)), ] elif dev_type == constants.LD_LV: vg_name, lv_name = logical_id @@ -940,6 +938,10 @@ def _FormatLogicalID(dev_type, logical_id, roman): return data +def _FormatListInfo(data): + return list(str(i) for i in data) + + def _FormatBlockDevInfo(idx, top_level, dev, roman): """Show block device information. @@ -1022,9 +1024,8 @@ def _FormatBlockDevInfo(idx, top_level, dev, roman): if isinstance(dev["size"], int): nice_size = utils.FormatUnit(dev["size"], "h") else: - nice_size = dev["size"] - d1 = ["- %s: %s, size %s" % (txt, dev["dev_type"], nice_size)] - data = [] + nice_size = str(dev["size"]) + data = [(txt, "%s, size %s" % (dev["dev_type"], nice_size))] if top_level: data.append(("access mode", dev["mode"])) if dev["logical_id"] is not None: @@ -1037,8 +1038,7 @@ def _FormatBlockDevInfo(idx, top_level, dev, roman): else: data.extend(l_id) elif dev["physical_id"] is not None: - data.append("physical_id:") - data.append([dev["physical_id"]]) + data.append(("physical_id:", _FormatListInfo(dev["physical_id"]))) if dev["pstatus"]: data.append(("on primary", helper(dev["dev_type"], dev["pstatus"]))) @@ -1046,41 +1046,126 @@ def _FormatBlockDevInfo(idx, top_level, dev, roman): if dev["sstatus"]: data.append(("on secondary", helper(dev["dev_type"], dev["sstatus"]))) - if dev["children"]: - data.append("child devices:") - for c_idx, child in enumerate(dev["children"]): - data.append(_FormatBlockDevInfo(c_idx, False, child, roman)) - d1.append(data) - return d1 + data.append(("name", dev["name"])) + data.append(("UUID", dev["uuid"])) + if dev["children"]: + data.append(("child devices", [ + _FormatBlockDevInfo(c_idx, False, child, roman) + for c_idx, child in enumerate(dev["children"]) + ])) + return data -def _FormatList(buf, data, indent_level): - """Formats a list of data at a given indent level. - - If the element of the list is: - - a string, it is simply formatted as is - - a tuple, it will be split into key, value and the all the - values in a list will be aligned all at the same start column - - a list, will be recursively formatted - - @type buf: StringIO - @param buf: the buffer into which we write the output - @param data: the list to format - @type indent_level: int - @param indent_level: the indent level to format at - """ - max_tlen = max([len(elem[0]) for elem in data - if isinstance(elem, tuple)] or [0]) - for elem in data: - if isinstance(elem, basestring): - buf.write("%*s%s\n" % (2 * indent_level, "", elem)) - elif isinstance(elem, tuple): - key, value = elem - spacer = "%*s" % (max_tlen - len(key), "") - buf.write("%*s%s:%s %s\n" % (2 * indent_level, "", key, spacer, value)) - elif isinstance(elem, list): - _FormatList(buf, elem, indent_level + 1) +def _FormatInstanceNicInfo(idx, nic): + """Helper function for L{_FormatInstanceInfo()}""" + (name, uuid, ip, mac, mode, link, _, netinfo) = nic + network_name = None + if netinfo: + network_name = netinfo["name"] + return [ + ("nic/%d" % idx, ""), + ("MAC", str(mac)), + ("IP", str(ip)), + ("mode", str(mode)), + ("link", str(link)), + ("network", str(network_name)), + ("UUID", str(uuid)), + ("name", str(name)), + ] + + +def _FormatInstanceNodesInfo(instance): + """Helper function for L{_FormatInstanceInfo()}""" + pgroup = ("%s (UUID %s)" % + (instance["pnode_group_name"], instance["pnode_group_uuid"])) + secs = utils.CommaJoin(("%s (group %s, group UUID %s)" % + (name, group_name, group_uuid)) + for (name, group_name, group_uuid) in + zip(instance["snodes"], + instance["snodes_group_names"], + instance["snodes_group_uuids"])) + return [ + [ + ("primary", instance["pnode"]), + ("group", pgroup), + ], + [("secondaries", secs)], + ] + + +def _GetVncConsoleInfo(instance): + """Helper function for L{_FormatInstanceInfo()}""" + vnc_bind_address = instance["hv_actual"].get(constants.HV_VNC_BIND_ADDRESS, + None) + if vnc_bind_address: + port = instance["network_port"] + display = int(port) - constants.VNC_BASE_PORT + 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 netutils.IP4Address.IsValid(vnc_bind_address): + vnc_console_port = ("%s:%s (node %s) (display %s)" % + (vnc_bind_address, port, + instance["pnode"], display)) + else: + # vnc bind address is a file + vnc_console_port = "%s:%s" % (instance["pnode"], + vnc_bind_address) + ret = "vnc to %s" % vnc_console_port + else: + ret = None + return ret + + +def _FormatInstanceInfo(instance, roman_integers): + """Format instance information for L{cli.PrintGenericInfo()}""" + istate = "configured to be %s" % instance["config_state"] + if instance["run_state"]: + istate += ", actual state is %s" % instance["run_state"] + info = [ + ("Instance name", instance["name"]), + ("UUID", instance["uuid"]), + ("Serial number", + str(compat.TryToRoman(instance["serial_no"], convert=roman_integers))), + ("Creation time", utils.FormatTime(instance["ctime"])), + ("Modification time", utils.FormatTime(instance["mtime"])), + ("State", istate), + ("Nodes", _FormatInstanceNodesInfo(instance)), + ("Operating system", instance["os"]), + ("Operating system parameters", + FormatParamsDictInfo(instance["os_instance"], instance["os_actual"])), + ] + + if "network_port" in instance: + info.append(("Allocated network port", + str(compat.TryToRoman(instance["network_port"], + convert=roman_integers)))) + info.append(("Hypervisor", instance["hypervisor"])) + console = _GetVncConsoleInfo(instance) + if console: + info.append(("console connection", console)) + # deprecated "memory" value, kept for one version for compatibility + # TODO(ganeti 2.7) remove. + be_actual = copy.deepcopy(instance["be_actual"]) + be_actual["memory"] = be_actual[constants.BE_MAXMEM] + info.extend([ + ("Hypervisor parameters", + FormatParamsDictInfo(instance["hv_instance"], instance["hv_actual"])), + ("Back-end parameters", + FormatParamsDictInfo(instance["be_instance"], be_actual)), + ("NICs", [ + _FormatInstanceNicInfo(idx, nic) + for (idx, nic) in enumerate(instance["nics"]) + ]), + ("Disk template", instance["disk_template"]), + ("Disks", [ + _FormatBlockDevInfo(idx, True, device, roman_integers) + for (idx, device) in enumerate(instance["disks"]) + ]), + ]) + return info def ShowInstanceConfig(opts, args): @@ -1111,85 +1196,10 @@ def ShowInstanceConfig(opts, args): ToStdout("No instances.") return 1 - buf = StringIO() - retcode = 0 - for instance_name in result: - instance = result[instance_name] - buf.write("Instance name: %s\n" % instance["name"]) - 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"]) - if instance["run_state"]: - buf.write(", actual state is %s" % instance["run_state"]) - buf.write("\n") - ##buf.write("Considered for memory checks in cluster verify: %s\n" % - ## instance["auto_balance"]) - buf.write(" Nodes:\n") - buf.write(" - primary: %s\n" % instance["pnode"]) - buf.write(" group: %s (UUID %s)\n" % - (instance["pnode_group_name"], instance["pnode_group_uuid"])) - buf.write(" - secondaries: %s\n" % - utils.CommaJoin("%s (group %s, group UUID %s)" % - (name, group_name, group_uuid) - for (name, group_name, group_uuid) in - zip(instance["snodes"], - instance["snodes_group_names"], - instance["snodes_group_uuids"]))) - buf.write(" Operating system: %s\n" % instance["os"]) - FormatParameterDict(buf, instance["os_instance"], instance["os_actual"], - level=2) - if "network_port" in instance: - 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 - vnc_bind_address = instance["hv_actual"].get(constants.HV_VNC_BIND_ADDRESS, - None) - if vnc_bind_address: - port = instance["network_port"] - display = int(port) - constants.VNC_BASE_PORT - 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 netutils.IP4Address.IsValid(vnc_bind_address): - vnc_console_port = ("%s:%s (node %s) (display %s)" % - (vnc_bind_address, port, - instance["pnode"], display)) - else: - # vnc bind address is a file - vnc_console_port = "%s:%s" % (instance["pnode"], - vnc_bind_address) - buf.write(" - console connection: vnc to %s\n" % vnc_console_port) - - FormatParameterDict(buf, instance["hv_instance"], instance["hv_actual"], - level=2) - buf.write(" Hardware:\n") - # deprecated "memory" value, kept for one version for compatibility - # TODO(ganeti 2.7) remove. - be_actual = copy.deepcopy(instance["be_actual"]) - be_actual["memory"] = be_actual[constants.BE_MAXMEM] - FormatParameterDict(buf, instance["be_instance"], be_actual, level=2) - # TODO(ganeti 2.7) rework the NICs as well - buf.write(" - NICs:\n") - for idx, (ip, mac, mode, link, network) in enumerate(instance["nics"]): - buf.write(" - nic/%d: MAC: %s, IP: %s," - " mode: %s, link: %s, network: %s\n" % - (idx, mac, ip, mode, link, network)) - buf.write(" Disk template: %s\n" % instance["disk_template"]) - buf.write(" Disks:\n") - - for idx, device in enumerate(instance["disks"]): - _FormatList(buf, _FormatBlockDevInfo(idx, True, device, - opts.roman_integers), 2) - - ToStdout(buf.getvalue().rstrip("\n")) + PrintGenericInfo([ + _FormatInstanceInfo(instance, opts.roman_integers) + for instance in result.values() + ]) return retcode @@ -1209,23 +1219,17 @@ def _ConvertNicDiskModifications(mods): """ result = [] - for (idx, params) in mods: - if idx == constants.DDM_ADD: + for (identifier, params) in mods: + if identifier == constants.DDM_ADD: # Add item as last item (legacy interface) action = constants.DDM_ADD - idxno = -1 - elif idx == constants.DDM_REMOVE: + identifier = -1 + elif identifier == constants.DDM_REMOVE: # Remove last item (legacy interface) action = constants.DDM_REMOVE - idxno = -1 + identifier = -1 else: # Modifications and adding/removing at arbitrary indices - try: - idxno = int(idx) - except (TypeError, ValueError): - raise errors.OpPrereqError("Non-numeric index '%s'" % idx, - errors.ECODE_INVAL) - add = params.pop(constants.DDM_ADD, _MISSING) remove = params.pop(constants.DDM_REMOVE, _MISSING) modify = params.pop(constants.DDM_MODIFY, _MISSING) @@ -1253,7 +1257,7 @@ def _ConvertNicDiskModifications(mods): raise errors.OpPrereqError("Not accepting parameters on removal", errors.ECODE_INVAL) - result.append((action, idxno, params)) + result.append((action, identifier, params)) return result @@ -1287,7 +1291,8 @@ def SetInstanceParams(opts, args): """ if not (opts.nics or opts.disks or opts.disk_template or opts.hvparams or opts.beparams or opts.os or opts.osparams or - opts.offline_inst or opts.online_inst or opts.runtime_mem): + opts.offline_inst or opts.online_inst or opts.runtime_mem or + opts.new_primary_node): ToStderr("Please give at least one of the parameters.") return 1 @@ -1308,6 +1313,14 @@ def SetInstanceParams(opts, args): allowed_values=[constants.VALUE_DEFAULT]) nics = _ConvertNicDiskModifications(opts.nics) + for action, _, __ in nics: + if action == constants.DDM_MODIFY and opts.hotplug and not opts.force: + usertext = ("You are about to hot-modify a NIC. This will be done" + " by removing the exisiting and then adding a new one." + " Network connection might be lost. Continue?") + if not AskUser(usertext): + return 1 + disks = _ParseDiskSizes(_ConvertNicDiskModifications(opts.disks)) if (opts.disk_template and @@ -1327,8 +1340,10 @@ def SetInstanceParams(opts, args): op = opcodes.OpInstanceSetParams(instance_name=args[0], nics=nics, disks=disks, + hotplug=opts.hotplug, disk_template=opts.disk_template, remote_node=opts.node, + pnode=opts.new_primary_node, hvparams=opts.hvparams, beparams=opts.beparams, runtime_mem=opts.runtime_mem, @@ -1338,6 +1353,7 @@ def SetInstanceParams(opts, args): force=opts.force, wait_for_sync=opts.wait_for_sync, offline=offline, + conflicts_check=opts.conflicts_check, ignore_ipolicy=opts.ignore_ipolicy) # even if here we process the result, we allow submit only @@ -1460,7 +1476,7 @@ commands = { FailoverInstance, ARGS_ONE_INSTANCE, [FORCE_OPT, IGNORE_CONSIST_OPT, SUBMIT_OPT, SHUTDOWN_TIMEOUT_OPT, DRY_RUN_OPT, PRIORITY_OPT, DST_NODE_OPT, IALLOCATOR_OPT, - IGNORE_IPOLICY_OPT], + IGNORE_IPOLICY_OPT, CLEANUP_OPT], "[-f] ", "Stops the instance, changes its primary node and" " (if it was originally running) starts it on the new node" " (the secondary for mirrored instances or any node" @@ -1527,11 +1543,12 @@ commands = { [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, DRY_RUN_OPT, PRIORITY_OPT, NWSYNC_OPT, OFFLINE_INST_OPT, - ONLINE_INST_OPT, IGNORE_IPOLICY_OPT, RUNTIME_MEM_OPT], + ONLINE_INST_OPT, IGNORE_IPOLICY_OPT, RUNTIME_MEM_OPT, + NOCONFLICTSCHECK_OPT, NEW_PRIMARY_OPT, HOTPLUG_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, + [FORCE_OPT, 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, DRY_RUN_OPT, PRIORITY_OPT, IGNORE_OFFLINE_OPT, NO_REMEMBER_OPT],