#
#
-# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Google Inc.
+# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 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
# W0614: Unused import %s from wildcard import (since we need cli)
# C0103: Invalid name gnt-instance
+import copy
import itertools
import simplejson
import logging
from ganeti import netutils
from ganeti import ssh
from ganeti import objects
+from ganeti import ht
_EXPAND_CLUSTER = "cluster"
]
+_MISSING = object()
_ENV_OVERRIDE = frozenset(["list"])
"""
instance_name = args[0]
op = opcodes.OpInstanceActivateDisks(instance_name=instance_name,
- ignore_size=opts.ignore_size)
+ ignore_size=opts.ignore_size,
+ wait_for_sync=opts.wait_for_sync)
disks_info = SubmitOrSend(op, opts)
for host, iname, nname in disks_info:
ToStdout("%s:%s:%s", host, iname, nname)
"""
instance_name = args[0]
+
+ disks = []
+
if opts.disks:
- try:
- opts.disks = [int(v) for v in opts.disks.split(",")]
- except (ValueError, TypeError), err:
- ToStderr("Invalid disks value: %s" % str(err))
- return 1
- else:
- opts.disks = []
+ for didx, ddict in opts.disks:
+ didx = int(didx)
+
+ if not ht.TDict(ddict):
+ msg = "Invalid disk/%d value: expected dict, got %s" % (didx, ddict)
+ raise errors.OpPrereqError(msg)
+
+ if constants.IDISK_SIZE in ddict:
+ try:
+ ddict[constants.IDISK_SIZE] = \
+ utils.ParseUnit(ddict[constants.IDISK_SIZE])
+ except ValueError, err:
+ raise errors.OpPrereqError("Invalid disk size for disk %d: %s" %
+ (didx, err))
+
+ disks.append((didx, ddict))
+
+ # TODO: Verify modifyable parameters (already done in
+ # LUInstanceRecreateDisks, but it'd be nice to have in the client)
if opts.node:
+ if opts.iallocator:
+ msg = "At most one of either --nodes or --iallocator can be passed"
+ raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
pnode, snode = SplitNodeOption(opts.node)
nodes = [pnode]
if snode is not None:
nodes = []
op = opcodes.OpInstanceRecreateDisks(instance_name=instance_name,
- disks=opts.disks,
- nodes=nodes)
+ disks=disks, nodes=nodes,
+ iallocator=opts.iallocator)
SubmitOrSend(op, opts)
+
return 0
except (TypeError, ValueError), err:
raise errors.OpPrereqError("Invalid disk index: %s" % str(err),
errors.ECODE_INVAL)
- amount = utils.ParseUnit(args[2])
+ try:
+ amount = utils.ParseUnit(args[2])
+ except errors.UnitParseError:
+ raise errors.OpPrereqError("Can't parse the given amount '%s'" % args[2],
+ errors.ECODE_INVAL)
op = opcodes.OpInstanceGrowDisk(instance_name=instance,
disk=disk, amount=amount,
- wait_for_sync=opts.wait_for_sync)
+ wait_for_sync=opts.wait_for_sync,
+ absolute=opts.absolute)
SubmitOrSend(op, opts)
return 0
cleanup=opts.cleanup, iallocator=iallocator,
target_node=target_node,
allow_failover=opts.allow_failover,
+ allow_runtime_changes=opts.allow_runtime_chgs,
ignore_ipolicy=opts.ignore_ipolicy)
- SubmitOpCode(op, cl=cl, opts=opts)
+ SubmitOrSend(op, cl=cl, opts=opts)
return 0
## instance["auto_balance"])
buf.write(" Nodes:\n")
buf.write(" - primary: %s\n" % instance["pnode"])
- buf.write(" - secondaries: %s\n" % utils.CommaJoin(instance["snodes"]))
+ 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)
FormatParameterDict(buf, instance["hv_instance"], instance["hv_actual"],
level=2)
buf.write(" Hardware:\n")
- buf.write(" - VCPUs: %s\n" %
- compat.TryToRoman(instance["be_actual"][constants.BE_VCPUS],
- convert=opts.roman_integers))
- buf.write(" - maxmem: %sMiB\n" %
- compat.TryToRoman(instance["be_actual"][constants.BE_MAXMEM],
- convert=opts.roman_integers))
- buf.write(" - minmem: %sMiB\n" %
- compat.TryToRoman(instance["be_actual"][constants.BE_MINMEM],
- convert=opts.roman_integers))
# deprecated "memory" value, kept for one version for compatibility
# TODO(ganeti 2.7) remove.
- buf.write(" - memory: %sMiB\n" %
- compat.TryToRoman(instance["be_actual"][constants.BE_MAXMEM],
- convert=opts.roman_integers))
- buf.write(" - %s: %s\n" %
- (constants.BE_ALWAYS_FAILOVER,
- instance["be_actual"][constants.BE_ALWAYS_FAILOVER]))
+ 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) in enumerate(instance["nics"]):
buf.write(" - nic/%d: MAC: %s, IP: %s, mode: %s, link: %s\n" %
return retcode
+def _ConvertNicDiskModifications(mods):
+ """Converts NIC/disk modifications from CLI to opcode.
+
+ When L{opcodes.OpInstanceSetParams} was changed to support adding/removing
+ disks at arbitrary indices, its parameter format changed. This function
+ converts legacy requests (e.g. "--net add" or "--disk add:size=4G") to the
+ newer format and adds support for new-style requests (e.g. "--new 4:add").
+
+ @type mods: list of tuples
+ @param mods: Modifications as given by command line parser
+ @rtype: list of tuples
+ @return: Modifications as understood by L{opcodes.OpInstanceSetParams}
+
+ """
+ result = []
+
+ for (idx, params) in mods:
+ if idx == constants.DDM_ADD:
+ # Add item as last item (legacy interface)
+ action = constants.DDM_ADD
+ idxno = -1
+ elif idx == constants.DDM_REMOVE:
+ # Remove last item (legacy interface)
+ action = constants.DDM_REMOVE
+ idxno = -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)
+
+ if modify is _MISSING:
+ if not (add is _MISSING or remove is _MISSING):
+ raise errors.OpPrereqError("Cannot add and remove at the same time",
+ errors.ECODE_INVAL)
+ elif add is not _MISSING:
+ action = constants.DDM_ADD
+ elif remove is not _MISSING:
+ action = constants.DDM_REMOVE
+ else:
+ action = constants.DDM_MODIFY
+
+ else:
+ if add is _MISSING and remove is _MISSING:
+ action = constants.DDM_MODIFY
+ else:
+ raise errors.OpPrereqError("Cannot modify and add/remove at the"
+ " same time", errors.ECODE_INVAL)
+
+ assert not (constants.DDMS_VALUES_WITH_MODIFY & set(params.keys()))
+
+ if action == constants.DDM_REMOVE and params:
+ raise errors.OpPrereqError("Not accepting parameters on removal",
+ errors.ECODE_INVAL)
+
+ result.append((action, idxno, params))
+
+ return result
+
+
+def _ParseDiskSizes(mods):
+ """Parses disk sizes in parameters.
+
+ """
+ for (action, _, params) in mods:
+ if params and constants.IDISK_SIZE in params:
+ params[constants.IDISK_SIZE] = \
+ utils.ParseUnit(params[constants.IDISK_SIZE])
+ elif action == constants.DDM_ADD:
+ raise errors.OpPrereqError("Missing required parameter 'size'",
+ errors.ECODE_INVAL)
+
+ return mods
+
+
def SetInstanceParams(opts, args):
"""Modifies an instance.
"""
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):
+ opts.offline_inst or opts.online_inst or opts.runtime_mem):
ToStderr("Please give at least one of the parameters.")
return 1
utils.ForceDictType(opts.hvparams, constants.HVS_PARAMETER_TYPES,
allowed_values=[constants.VALUE_DEFAULT])
- for idx, (nic_op, nic_dict) in enumerate(opts.nics):
- try:
- nic_op = int(nic_op)
- opts.nics[idx] = (nic_op, nic_dict)
- 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 (TypeError, ValueError):
- pass
- if disk_op == constants.DDM_ADD:
- if "size" not in disk_dict:
- raise errors.OpPrereqError("Missing required parameter 'size'",
- errors.ECODE_INVAL)
- disk_dict["size"] = utils.ParseUnit(disk_dict["size"])
+ nics = _ConvertNicDiskModifications(opts.nics)
+ disks = _ParseDiskSizes(_ConvertNicDiskModifications(opts.disks))
if (opts.disk_template and
opts.disk_template in constants.DTS_INT_MIRROR and
" specifying a secondary node")
return 1
+ if opts.offline_inst:
+ offline = True
+ elif opts.online_inst:
+ offline = False
+ else:
+ offline = None
+
op = opcodes.OpInstanceSetParams(instance_name=args[0],
- nics=opts.nics,
- disks=opts.disks,
+ nics=nics,
+ disks=disks,
disk_template=opts.disk_template,
remote_node=opts.node,
hvparams=opts.hvparams,
beparams=opts.beparams,
+ runtime_mem=opts.runtime_mem,
os_name=opts.os,
osparams=opts.osparams,
force_variant=opts.force_variant,
force=opts.force,
wait_for_sync=opts.wait_for_sync,
- offline_inst=opts.offline_inst,
- online_inst=opts.online_inst,
+ offline=offline,
ignore_ipolicy=opts.ignore_ipolicy)
# even if here we process the result, we allow submit only
iallocator=opts.iallocator,
target_groups=opts.to,
early_release=opts.early_release)
- result = SubmitOpCode(op, cl=cl, opts=opts)
+ result = SubmitOrSend(op, opts, cl=cl)
# Keep track of submitted jobs
jex = JobExecutor(cl=cl, opts=opts)
MigrateInstance, ARGS_ONE_INSTANCE,
[FORCE_OPT, NONLIVE_OPT, MIGRATION_MODE_OPT, CLEANUP_OPT, DRY_RUN_OPT,
PRIORITY_OPT, DST_NODE_OPT, IALLOCATOR_OPT, ALLOW_FAILOVER_OPT,
- IGNORE_IPOLICY_OPT],
+ IGNORE_IPOLICY_OPT, NORUNTIME_CHGS_OPT, SUBMIT_OPT],
"[-f] <instance>", "Migrate instance to its secondary node"
" (only for mirrored instances)"),
"move": (
[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],
+ ONLINE_INST_OPT, IGNORE_IPOLICY_OPT, RUNTIME_MEM_OPT],
"<instance>", "Alters the parameters of an instance"),
"shutdown": (
GenericManyOps("shutdown", _ShutdownInstance), [ArgInstance()],
"<instance>", "Reboots an instance"),
"activate-disks": (
ActivateDisks, ARGS_ONE_INSTANCE,
- [SUBMIT_OPT, IGNORE_SIZE_OPT, PRIORITY_OPT],
+ [SUBMIT_OPT, IGNORE_SIZE_OPT, PRIORITY_OPT, WFSYNC_OPT],
"<instance>", "Activate an instance's disks"),
"deactivate-disks": (
DeactivateDisks, ARGS_ONE_INSTANCE,
"[-f] <instance>", "Deactivate an instance's disks"),
"recreate-disks": (
RecreateDisks, ARGS_ONE_INSTANCE,
- [SUBMIT_OPT, DISKIDX_OPT, NODE_PLACEMENT_OPT, DRY_RUN_OPT, PRIORITY_OPT],
+ [SUBMIT_OPT, DISK_OPT, NODE_PLACEMENT_OPT, DRY_RUN_OPT, PRIORITY_OPT,
+ IALLOCATOR_OPT],
"<instance>", "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, DRY_RUN_OPT, PRIORITY_OPT],
+ [SUBMIT_OPT, NWSYNC_OPT, DRY_RUN_OPT, PRIORITY_OPT, ABSOLUTE_OPT],
"<instance> <disk> <size>", "Grow an instance's disk"),
"change-group": (
ChangeGroup, ARGS_ONE_INSTANCE,
- [TO_GROUP_OPT, IALLOCATOR_OPT, EARLY_RELEASE_OPT],
+ [TO_GROUP_OPT, IALLOCATOR_OPT, EARLY_RELEASE_OPT, PRIORITY_OPT, SUBMIT_OPT],
"[-I <iallocator>] [--to <group>]", "Change group of instance"),
"list-tags": (
- ListTags, ARGS_ONE_INSTANCE, [PRIORITY_OPT],
+ ListTags, ARGS_ONE_INSTANCE, [],
"<instance_name>", "List the tags of the given instance"),
"add-tags": (
AddTags, [ArgInstance(min=1, max=1), ArgUnknown()],
- [TAG_SRC_OPT, PRIORITY_OPT],
+ [TAG_SRC_OPT, PRIORITY_OPT, SUBMIT_OPT],
"<instance_name> tag...", "Add tags to the given instance"),
"remove-tags": (
RemoveTags, [ArgInstance(min=1, max=1), ArgUnknown()],
- [TAG_SRC_OPT, PRIORITY_OPT],
+ [TAG_SRC_OPT, PRIORITY_OPT, SUBMIT_OPT],
"<instance_name> tag...", "Remove tags from given instance"),
}
aliases = {
"start": "startup",
"stop": "shutdown",
+ "show": "info",
}