X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/bf689b7a82613e34b6d377b9a809908c2a65a9f7..76eeabc7132dd64b239cc821dfc7c17d210a699f:/lib/opcodes.py diff --git a/lib/opcodes.py b/lib/opcodes.py index 076d578..73d5cb3 100644 --- a/lib/opcodes.py +++ b/lib/opcodes.py @@ -1,7 +1,7 @@ # # -# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Google Inc. +# Copyright (C) 2006, 2007, 2008, 2009, 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 @@ -35,11 +35,13 @@ opcodes. import logging import re +import ipaddr -from ganeti import compat from ganeti import constants from ganeti import errors from ganeti import ht +from ganeti import objects +from ganeti import outils # Common opcode attributes @@ -50,7 +52,7 @@ _POutputFields = ("output_fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), #: the shutdown timeout _PShutdownTimeout = \ - ("shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TPositiveInt, + ("shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TNonNegativeInt, "How long to wait for instance to shut down") #: the force parameter @@ -72,7 +74,7 @@ _PGroupName = ("group_name", ht.NoDefault, ht.TNonEmptyString, "Group name") #: Migration type (live/non-live) _PMigrationMode = ("mode", None, - ht.TOr(ht.TNone, ht.TElemOf(constants.HT_MIGRATION_MODES)), + ht.TMaybe(ht.TElemOf(constants.HT_MIGRATION_MODES)), "Migration mode") #: Obsolete 'live' migration mode (boolean) @@ -80,10 +82,12 @@ _PMigrationLive = ("live", None, ht.TMaybeBool, "Legacy setting for live migration, do not use") #: Tag type -_PTagKind = ("kind", ht.NoDefault, ht.TElemOf(constants.VALID_TAG_TYPES), None) +_PTagKind = ("kind", ht.NoDefault, ht.TElemOf(constants.VALID_TAG_TYPES), + "Tag kind") #: List of tag strings -_PTags = ("tags", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), None) +_PTags = ("tags", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), + "List of tag names") _PForceVariant = ("force_variant", False, ht.TBool, "Whether to force an unknown OS variant") @@ -91,6 +95,10 @@ _PForceVariant = ("force_variant", False, ht.TBool, _PWaitForSync = ("wait_for_sync", True, ht.TBool, "Whether to wait for the disk to synchronize") +_PWaitForSyncFalse = ("wait_for_sync", False, ht.TBool, + "Whether to wait for the disk to synchronize" + " (defaults to false)") + _PIgnoreConsistency = ("ignore_consistency", False, ht.TBool, "Whether to ignore disk consistency") @@ -103,7 +111,7 @@ _PNameCheck = ("name_check", True, ht.TBool, "Whether to check name") _PNodeGroupAllocPolicy = \ ("alloc_policy", None, - ht.TOr(ht.TNone, ht.TElemOf(constants.VALID_ALLOC_POLICIES)), + ht.TMaybe(ht.TElemOf(constants.VALID_ALLOC_POLICIES)), "Instance allocation policy") _PGroupNodeParams = ("ndparams", None, ht.TMaybeDict, @@ -141,20 +149,58 @@ _PIgnoreErrors = ("ignore_errors", ht.EmptyList, ht.TListOf(ht.TElemOf(constants.CV_ALL_ECODES_STRINGS)), "List of error codes that should be treated as warnings") +# Disk parameters +_PDiskParams = \ + ("diskparams", None, + ht.TMaybe(ht.TDictOf(ht.TElemOf(constants.DISK_TEMPLATES), ht.TDict)), + "Disk templates' parameter defaults") + +# Parameters for node resource model +_PHvState = ("hv_state", None, ht.TMaybeDict, "Set hypervisor states") +_PDiskState = ("disk_state", None, ht.TMaybeDict, "Set disk states") + +#: Opportunistic locking +_POpportunisticLocking = \ + ("opportunistic_locking", False, ht.TBool, + ("Whether to employ opportunistic locking for nodes, meaning nodes" + " already locked by another opcode won't be considered for instance" + " allocation (only when an iallocator is used)")) + +_PIgnoreIpolicy = ("ignore_ipolicy", False, ht.TBool, + "Whether to ignore ipolicy violations") + +# Allow runtime changes while migrating +_PAllowRuntimeChgs = ("allow_runtime_changes", True, ht.TBool, + "Allow runtime changes (eg. memory ballooning)") + +#: IAllocator field builder +_PIAllocFromDesc = lambda desc: ("iallocator", None, ht.TMaybeString, desc) + +#: a required network name +_PNetworkName = ("network_name", ht.NoDefault, ht.TNonEmptyString, + "Set network name") + +_PTargetGroups = \ + ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString), + "Destination group names or UUIDs (defaults to \"all but current group\")") + #: OP_ID conversion regular expression _OPID_RE = re.compile("([a-z])([A-Z])") #: Utility function for L{OpClusterSetParams} -_TestClusterOsList = ht.TOr(ht.TNone, - ht.TListOf(ht.TAnd(ht.TList, ht.TIsLength(2), - ht.TMap(ht.WithDesc("GetFirstItem")(compat.fst), - ht.TElemOf(constants.DDMS_VALUES))))) +_TestClusterOsListItem = \ + ht.TAnd(ht.TIsLength(2), ht.TItems([ + ht.TElemOf(constants.DDMS_VALUES), + ht.TNonEmptyString, + ])) +_TestClusterOsList = ht.TMaybeListOf(_TestClusterOsListItem) # TODO: Generate check from constants.INIC_PARAMS_TYPES #: Utility function for testing NIC definitions -_TestNicDef = ht.TDictOf(ht.TElemOf(constants.INIC_PARAMS), - ht.TOr(ht.TNone, ht.TNonEmptyString)) +_TestNicDef = \ + ht.Comment("NIC parameters")(ht.TDictOf(ht.TElemOf(constants.INIC_PARAMS), + ht.TMaybeString)) _TSetParamsResultItemItems = [ ht.Comment("name of changed parameter")(ht.TNonEmptyString), @@ -165,6 +211,26 @@ _TSetParamsResult = \ ht.TListOf(ht.TAnd(ht.TIsLength(len(_TSetParamsResultItemItems)), ht.TItems(_TSetParamsResultItemItems))) +# In the disks option we can provide arbitrary parameters too, which +# we may not be able to validate at this level, so we just check the +# format of the dict here and the checks concerning IDISK_PARAMS will +# happen at the LU level +_TDiskParams = \ + ht.Comment("Disk parameters")(ht.TDictOf(ht.TNonEmptyString, + ht.TOr(ht.TNonEmptyString, ht.TInt))) + +_TQueryRow = \ + ht.TListOf(ht.TAnd(ht.TIsLength(2), + ht.TItems([ht.TElemOf(constants.RS_ALL), + ht.TAny]))) + +_TQueryResult = ht.TListOf(_TQueryRow) + +_TOldQueryRow = ht.TListOf(ht.TAny) + +_TOldQueryResult = ht.TListOf(_TOldQueryRow) + + _SUMMARY_PREFIX = { "CLUSTER_": "C_", "GROUP_": "G_", @@ -200,6 +266,28 @@ def _NameToId(name): return "_".join(n.upper() for n in elems) +def _GenerateObjectTypeCheck(obj, fields_types): + """Helper to generate type checks for objects. + + @param obj: The object to generate type checks + @param fields_types: The fields and their types as a dict + @return: A ht type check function + + """ + assert set(obj.GetAllSlots()) == set(fields_types.keys()), \ + "%s != %s" % (set(obj.GetAllSlots()), set(fields_types.keys())) + return ht.TStrictDict(True, True, fields_types) + + +_TQueryFieldDef = \ + _GenerateObjectTypeCheck(objects.QueryFieldDefinition, { + "name": ht.TNonEmptyString, + "title": ht.TNonEmptyString, + "kind": ht.TElemOf(constants.QFT_ALL), + "doc": ht.TNonEmptyString, + }) + + def RequireFileStorage(): """Checks that file storage is enabled. @@ -251,7 +339,7 @@ def _BuildDiskTemplateCheck(accept_none): template_check = ht.TElemOf(constants.DISK_TEMPLATES) if accept_none: - template_check = ht.TOr(template_check, ht.TNone) + template_check = ht.TMaybe(template_check) return ht.TAnd(template_check, _CheckFileStorage) @@ -264,6 +352,7 @@ def _CheckStorageType(storage_type): raise errors.OpPrereqError("Unknown storage type: %s" % storage_type, errors.ECODE_INVAL) if storage_type == constants.ST_FILE: + # TODO: What about shared file storage? RequireFileStorage() return True @@ -273,7 +362,62 @@ _PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType, "Storage type") -class _AutoOpParamSlots(type): +@ht.WithDesc("IPv4 network") +def _CheckCIDRNetNotation(value): + """Ensure a given CIDR notation type is valid. + + """ + try: + ipaddr.IPv4Network(value) + except ipaddr.AddressValueError: + return False + return True + + +@ht.WithDesc("IPv4 address") +def _CheckCIDRAddrNotation(value): + """Ensure a given CIDR notation type is valid. + + """ + try: + ipaddr.IPv4Address(value) + except ipaddr.AddressValueError: + return False + return True + + +@ht.WithDesc("IPv6 address") +def _CheckCIDR6AddrNotation(value): + """Ensure a given CIDR notation type is valid. + + """ + try: + ipaddr.IPv6Address(value) + except ipaddr.AddressValueError: + return False + return True + + +@ht.WithDesc("IPv6 network") +def _CheckCIDR6NetNotation(value): + """Ensure a given CIDR notation type is valid. + + """ + try: + ipaddr.IPv6Network(value) + except ipaddr.AddressValueError: + return False + return True + + +_TIpAddress4 = ht.TAnd(ht.TString, _CheckCIDRAddrNotation) +_TIpAddress6 = ht.TAnd(ht.TString, _CheckCIDR6AddrNotation) +_TIpNetwork4 = ht.TAnd(ht.TString, _CheckCIDRNetNotation) +_TIpNetwork6 = ht.TAnd(ht.TString, _CheckCIDR6NetNotation) +_TMaybeAddr4List = ht.TMaybe(ht.TListOf(_TIpAddress4)) + + +class _AutoOpParamSlots(outils.AutoSlots): """Meta class for opcode definitions. """ @@ -287,27 +431,33 @@ class _AutoOpParamSlots(type): @param attrs: Class attributes """ - assert "__slots__" not in attrs, \ - "Class '%s' defines __slots__ when it should use OP_PARAMS" % name assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name + slots = mcs._GetSlots(attrs) + assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \ + "Class '%s' uses unknown field in OP_DSC_FIELD" % name + assert ("OP_DSC_FORMATTER" not in attrs or + callable(attrs["OP_DSC_FORMATTER"])), \ + ("Class '%s' uses non-callable in OP_DSC_FORMATTER (%s)" % + (name, type(attrs["OP_DSC_FORMATTER"]))) + attrs["OP_ID"] = _NameToId(name) + return outils.AutoSlots.__new__(mcs, name, bases, attrs) + + @classmethod + def _GetSlots(mcs, attrs): + """Build the slots out of OP_PARAMS. + + """ # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams params = attrs.setdefault("OP_PARAMS", []) # Use parameter names as slots - slots = [pname for (pname, _, _, _) in params] - - assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \ - "Class '%s' uses unknown field in OP_DSC_FIELD" % name - - attrs["__slots__"] = slots + return [pname for (pname, _, _, _) in params] - return type.__new__(mcs, name, bases, attrs) - -class BaseOpCode(object): +class BaseOpCode(outils.ValidatedSlots): """A simple serializable object. This object serves as a parent class for OpCode without any custom @@ -318,22 +468,6 @@ class BaseOpCode(object): # as OP_ID is dynamically defined __metaclass__ = _AutoOpParamSlots - def __init__(self, **kwargs): - """Constructor for BaseOpCode. - - The constructor takes only keyword arguments and will set - attributes on this object based on the passed arguments. As such, - it means that you should not pass arguments which are not in the - __slots__ attribute for this class. - - """ - slots = self._all_slots() - for key in kwargs: - if key not in slots: - raise TypeError("Object %s doesn't support the parameter '%s'" % - (self.__class__.__name__, key)) - setattr(self, key, kwargs[key]) - def __getstate__(self): """Generic serializer. @@ -345,7 +479,7 @@ class BaseOpCode(object): """ state = {} - for name in self._all_slots(): + for name in self.GetAllSlots(): if hasattr(self, name): state[name] = getattr(self, name) return state @@ -364,7 +498,7 @@ class BaseOpCode(object): raise ValueError("Invalid data to __setstate__: expected dict, got %s" % type(state)) - for name in self._all_slots(): + for name in self.GetAllSlots(): if name not in state and hasattr(self, name): delattr(self, name) @@ -372,16 +506,6 @@ class BaseOpCode(object): setattr(self, name, state[name]) @classmethod - def _all_slots(cls): - """Compute the list of all declared slots for a class. - - """ - slots = [] - for parent in cls.__mro__: - slots.extend(getattr(parent, "__slots__", [])) - return slots - - @classmethod def GetAllParams(cls): """Compute list of all parameters for an opcode. @@ -391,7 +515,7 @@ class BaseOpCode(object): slots.extend(getattr(parent, "OP_PARAMS", [])) return slots - def Validate(self, set_defaults): + def Validate(self, set_defaults): # pylint: disable=W0221 """Validate opcode parameters, optionally setting default values. @type set_defaults: bool @@ -422,8 +546,9 @@ class BaseOpCode(object): if set_defaults or hasattr(self, attr_name): attr_val = getattr(self, attr_name) if not test(attr_val): - logging.error("OpCode %s, parameter %s, has invalid type %s/value %s", - self.OP_ID, attr_name, type(attr_val), attr_val) + logging.error("OpCode %s, parameter %s, has invalid type %s/value" + " '%s' expecting type %s", + self.OP_ID, attr_name, type(attr_val), attr_val, test) raise errors.OpPrereqError("Parameter '%s.%s' fails validation" % (self.OP_ID, attr_name), errors.ECODE_INVAL) @@ -443,11 +568,12 @@ def _BuildJobDepCheck(relative): job_id = ht.TJobId job_dep = \ - ht.TAnd(ht.TIsLength(2), + ht.TAnd(ht.TOr(ht.TList, ht.TTuple), + ht.TIsLength(2), ht.TItems([job_id, ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))])) - return ht.TOr(ht.TNone, ht.TListOf(job_dep)) + return ht.TMaybeListOf(job_dep) TNoRelativeJobDependencies = _BuildJobDepCheck(False) @@ -478,6 +604,9 @@ class OpCode(BaseOpCode): @cvar OP_DSC_FIELD: The name of a field whose value will be included in the string returned by Summary(); see the docstring of that method for details). + @cvar OP_DSC_FORMATTER: A callable that should format the OP_DSC_FIELD; if + not present, then the field will be simply converted + to string @cvar OP_PARAMS: List of opcode attributes, the default values they should get if not already defined, and types they must match. @cvar OP_RESULT: Callable to verify opcode result @@ -493,12 +622,13 @@ class OpCode(BaseOpCode): WITH_LU = True OP_PARAMS = [ ("dry_run", None, ht.TMaybeBool, "Run checks only, don't execute"), - ("debug_level", None, ht.TOr(ht.TNone, ht.TPositiveInt), "Debug level"), + ("debug_level", None, ht.TMaybe(ht.TNonNegativeInt), "Debug level"), ("priority", constants.OP_PRIO_DEFAULT, ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID), "Opcode priority"), (DEPEND_ATTR, None, _BuildJobDepCheck(True), "Job dependencies; if used through ``SubmitManyJobs`` relative (negative)" - " job IDs can be used"), + " job IDs can be used; see :doc:`design document `" + " for details"), (COMMENT_ATTR, None, ht.TMaybeString, "Comment describing the purpose of the opcode"), ] @@ -564,7 +694,10 @@ class OpCode(BaseOpCode): field_name = getattr(self, "OP_DSC_FIELD", None) if field_name: field_value = getattr(self, field_name, None) - if isinstance(field_value, (list, tuple)): + field_formatter = getattr(self, "OP_DSC_FORMATTER", None) + if callable(field_formatter): + field_value = field_formatter(field_value) + elif isinstance(field_value, (list, tuple)): field_value = ",".join(str(i) for i in field_value) txt = "%s(%s)" % (txt, field_value) return txt @@ -593,6 +726,7 @@ class OpClusterPostInit(OpCode): after the cluster has been initialized. """ + OP_RESULT = ht.TBool class OpClusterDestroy(OpCode): @@ -602,10 +736,12 @@ class OpClusterDestroy(OpCode): lost after the execution of this opcode. """ + OP_RESULT = ht.TNonEmptyString class OpClusterQuery(OpCode): """Query cluster information.""" + OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TAny) class OpClusterVerify(OpCode): @@ -618,7 +754,7 @@ class OpClusterVerify(OpCode): _PSkipChecks, _PIgnoreErrors, _PVerbose, - ("group_name", None, ht.TMaybeString, "Group to verify") + ("group_name", None, ht.TMaybeString, "Group to verify"), ] OP_RESULT = TJobIdListOnly @@ -714,13 +850,18 @@ class OpClusterRepairDiskSizes(OpCode): OP_PARAMS = [ ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None), ] + OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3), + ht.TItems([ht.TNonEmptyString, + ht.TNonNegativeInt, + ht.TNonNegativeInt]))) class OpClusterConfigQuery(OpCode): """Query cluster configuration values.""" OP_PARAMS = [ - _POutputFields + _POutputFields, ] + OP_RESULT = ht.TListOf(ht.TAny) class OpClusterRename(OpCode): @@ -736,6 +877,7 @@ class OpClusterRename(OpCode): OP_PARAMS = [ ("name", ht.NoDefault, ht.TNonEmptyString, None), ] + OP_RESULT = ht.TNonEmptyString class OpClusterSetParams(OpCode): @@ -746,23 +888,25 @@ class OpClusterSetParams(OpCode): """ OP_PARAMS = [ - ("vg_name", None, ht.TMaybeString, "Volume group name"), + _PHvState, + _PDiskState, + ("vg_name", None, ht.TMaybe(ht.TString), "Volume group name"), ("enabled_hypervisors", None, - ht.TOr(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)), ht.TTrue), - ht.TNone), + ht.TMaybe(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)), + ht.TTrue)), "List of enabled hypervisors"), - ("hvparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict), - ht.TNone), + ("hvparams", None, + ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)), "Cluster-wide hypervisor parameter defaults, hypervisor-dependent"), - ("beparams", None, ht.TOr(ht.TDict, ht.TNone), + ("beparams", None, ht.TMaybeDict, "Cluster-wide backend parameter defaults"), - ("os_hvp", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict), - ht.TNone), + ("os_hvp", None, ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)), "Cluster-wide per-OS hypervisor parameter defaults"), - ("osparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict), - ht.TNone), + ("osparams", None, + ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)), "Cluster-wide OS parameter defaults"), - ("candidate_pool_size", None, ht.TOr(ht.TStrictPositiveInt, ht.TNone), + _PDiskParams, + ("candidate_pool_size", None, ht.TMaybe(ht.TPositiveInt), "Master candidate pool size"), ("uid_pool", None, ht.NoType, "Set UID pool, must be list of lists describing UID ranges (two items," @@ -779,44 +923,50 @@ class OpClusterSetParams(OpCode): "Whether to wipe disks before allocating them to instances"), ("nicparams", None, ht.TMaybeDict, "Cluster-wide NIC parameter defaults"), ("ndparams", None, ht.TMaybeDict, "Cluster-wide node parameter defaults"), - ("drbd_helper", None, ht.TOr(ht.TString, ht.TNone), "DRBD helper program"), - ("default_iallocator", None, ht.TOr(ht.TString, ht.TNone), + ("ipolicy", None, ht.TMaybeDict, + "Cluster-wide :ref:`instance policy ` specs"), + ("drbd_helper", None, ht.TMaybe(ht.TString), "DRBD helper program"), + ("default_iallocator", None, ht.TMaybe(ht.TString), "Default iallocator for cluster"), - ("master_netdev", None, ht.TOr(ht.TString, ht.TNone), + ("master_netdev", None, ht.TMaybe(ht.TString), "Master network device"), - ("master_netmask", None, ht.TOr(ht.TInt, ht.TNone), + ("master_netmask", None, ht.TMaybe(ht.TNonNegativeInt), "Netmask of the master IP"), - ("reserved_lvs", None, ht.TOr(ht.TListOf(ht.TNonEmptyString), ht.TNone), + ("reserved_lvs", None, ht.TMaybeListOf(ht.TNonEmptyString), "List of reserved LVs"), ("hidden_os", None, _TestClusterOsList, - "Modify list of hidden operating systems. Each modification must have" - " two items, the operation and the OS name. The operation can be" - " ``%s`` or ``%s``." % (constants.DDM_ADD, constants.DDM_REMOVE)), + "Modify list of hidden operating systems: each modification must have" + " two items, the operation and the OS name; the operation can be" + " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)), ("blacklisted_os", None, _TestClusterOsList, - "Modify list of blacklisted operating systems. Each modification must have" - " two items, the operation and the OS name. The operation can be" - " ``%s`` or ``%s``." % (constants.DDM_ADD, constants.DDM_REMOVE)), + "Modify list of blacklisted operating systems: each modification must" + " have two items, the operation and the OS name; the operation can be" + " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)), ("use_external_mip_script", None, ht.TMaybeBool, "Whether to use an external master IP address setup script"), ] + OP_RESULT = ht.TNone class OpClusterRedistConf(OpCode): """Force a full push of the cluster configuration. """ + OP_RESULT = ht.TNone class OpClusterActivateMasterIp(OpCode): """Activate the master IP on the master node. """ + OP_RESULT = ht.TNone class OpClusterDeactivateMasterIp(OpCode): """Deactivate the master IP on the master node. """ + OP_RESULT = ht.TNone class OpQuery(OpCode): @@ -833,9 +983,14 @@ class OpQuery(OpCode): _PUseLocking, ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), "Requested fields"), - ("qfilter", None, ht.TOr(ht.TNone, ht.TListOf), + ("qfilter", None, ht.TMaybe(ht.TList), "Query filter"), ] + OP_RESULT = \ + _GenerateObjectTypeCheck(objects.QueryResponse, { + "fields": ht.TListOf(_TQueryFieldDef), + "data": _TQueryResult, + }) class OpQueryFields(OpCode): @@ -848,9 +1003,13 @@ class OpQueryFields(OpCode): OP_DSC_FIELD = "what" OP_PARAMS = [ _PQueryWhat, - ("fields", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString)), + ("fields", None, ht.TMaybeListOf(ht.TNonEmptyString), "Requested fields; if not given, all are returned"), ] + OP_RESULT = \ + _GenerateObjectTypeCheck(objects.QueryFieldsResponse, { + "fields": ht.TListOf(_TQueryFieldDef), + }) class OpOobCommand(OpCode): @@ -858,15 +1017,39 @@ class OpOobCommand(OpCode): OP_PARAMS = [ ("node_names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "List of nodes to run the OOB command against"), - ("command", None, ht.TElemOf(constants.OOB_COMMANDS), + ("command", ht.NoDefault, ht.TElemOf(constants.OOB_COMMANDS), "OOB command to be run"), ("timeout", constants.OOB_TIMEOUT, ht.TInt, "Timeout before the OOB helper will be terminated"), ("ignore_status", False, ht.TBool, "Ignores the node offline status for power off"), - ("power_delay", constants.OOB_POWER_DELAY, ht.TPositiveFloat, + ("power_delay", constants.OOB_POWER_DELAY, ht.TNonNegativeFloat, "Time in seconds to wait between powering on nodes"), ] + # Fixme: Make it more specific with all the special cases in LUOobCommand + OP_RESULT = _TQueryResult + + +class OpRestrictedCommand(OpCode): + """Runs a restricted command on node(s). + + """ + OP_PARAMS = [ + _PUseLocking, + ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), + "Nodes on which the command should be run (at least one)"), + ("command", ht.NoDefault, ht.TNonEmptyString, + "Command name (no parameters)"), + ] + + _RESULT_ITEMS = [ + ht.Comment("success")(ht.TBool), + ht.Comment("output or error message")(ht.TString), + ] + + OP_RESULT = \ + ht.TListOf(ht.TAnd(ht.TIsLength(len(_RESULT_ITEMS)), + ht.TItems(_RESULT_ITEMS))) # node opcodes @@ -883,6 +1066,7 @@ class OpNodeRemove(OpCode): OP_PARAMS = [ _PNodeName, ] + OP_RESULT = ht.TNone class OpNodeAdd(OpCode): @@ -916,6 +1100,8 @@ class OpNodeAdd(OpCode): OP_DSC_FIELD = "node_name" OP_PARAMS = [ _PNodeName, + _PHvState, + _PDiskState, ("primary_ip", None, ht.NoType, "Primary IP address"), ("secondary_ip", None, ht.TMaybeString, "Secondary IP address"), ("readd", False, ht.TBool, "Whether node is re-added to cluster"), @@ -926,6 +1112,7 @@ class OpNodeAdd(OpCode): "Whether node can host instances"), ("ndparams", None, ht.TMaybeDict, "Node parameters"), ] + OP_RESULT = ht.TNone class OpNodeQuery(OpCode): @@ -936,6 +1123,7 @@ class OpNodeQuery(OpCode): ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Empty list to query all nodes, node names otherwise"), ] + OP_RESULT = _TOldQueryResult class OpNodeQueryvols(OpCode): @@ -945,6 +1133,7 @@ class OpNodeQueryvols(OpCode): ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Empty list to query all nodes, node names otherwise"), ] + OP_RESULT = ht.TListOf(ht.TAny) class OpNodeQueryStorage(OpCode): @@ -955,16 +1144,19 @@ class OpNodeQueryStorage(OpCode): ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "List of nodes"), ("name", None, ht.TMaybeString, "Storage name"), ] + OP_RESULT = _TOldQueryResult class OpNodeModifyStorage(OpCode): """Modifies the properies of a storage unit""" + OP_DSC_FIELD = "node_name" OP_PARAMS = [ _PNodeName, _PStorageType, _PStorageName, ("changes", ht.NoDefault, ht.TDict, "Requested changes"), ] + OP_RESULT = ht.TNone class OpRepairNodeStorage(OpCode): @@ -976,6 +1168,7 @@ class OpRepairNodeStorage(OpCode): _PStorageName, _PIgnoreConsistency, ] + OP_RESULT = ht.TNone class OpNodeSetParams(OpCode): @@ -984,6 +1177,8 @@ class OpNodeSetParams(OpCode): OP_PARAMS = [ _PNodeName, _PForce, + _PHvState, + _PDiskState, ("master_candidate", None, ht.TMaybeBool, "Whether the node should become a master candidate"), ("offline", None, ht.TMaybeBool, @@ -1012,6 +1207,7 @@ class OpNodePowercycle(OpCode): _PNodeName, _PForce, ] + OP_RESULT = ht.TMaybeString class OpNodeMigrate(OpCode): @@ -1022,9 +1218,12 @@ class OpNodeMigrate(OpCode): _PMigrationMode, _PMigrationLive, _PMigrationTargetNode, - ("iallocator", None, ht.TMaybeString, - "Iallocator for deciding the target node for shared-storage instances"), + _PAllowRuntimeChgs, + _PIgnoreIpolicy, + _PIAllocFromDesc("Iallocator for deciding the target node" + " for shared-storage instances"), ] + OP_RESULT = TJobIdListOnly class OpNodeEvacuate(OpCode): @@ -1034,8 +1233,8 @@ class OpNodeEvacuate(OpCode): _PEarlyRelease, _PNodeName, ("remote_node", None, ht.TMaybeString, "New secondary node"), - ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"), - ("mode", ht.NoDefault, ht.TElemOf(constants.IALLOCATOR_NEVAC_MODES), + _PIAllocFromDesc("Iallocator for computing solution"), + ("mode", ht.NoDefault, ht.TElemOf(constants.NODE_EVAC_MODES), "Node evacuation mode"), ] OP_RESULT = TJobIdListOnly @@ -1061,11 +1260,10 @@ class OpInstanceCreate(OpCode): _PForceVariant, _PWaitForSync, _PNameCheck, + _PIgnoreIpolicy, + _POpportunisticLocking, ("beparams", ht.EmptyDict, ht.TDict, "Backend parameters for instance"), - ("disks", ht.NoDefault, - # TODO: Generate check from constants.IDISK_PARAMS_TYPES - ht.TListOf(ht.TDictOf(ht.TElemOf(constants.IDISK_PARAMS), - ht.TOr(ht.TNonEmptyString, ht.TInt))), + ("disks", ht.NoDefault, ht.TListOf(_TDiskParams), "Disk descriptions, for example ``[{\"%s\": 100}, {\"%s\": 5}]``;" " each disk definition must contain a ``%s`` value and" " can contain an optional ``%s`` value denoting the disk access mode" @@ -1075,18 +1273,18 @@ class OpInstanceCreate(OpCode): " or ".join("``%s``" % i for i in sorted(constants.DISK_ACCESS_SET)))), ("disk_template", ht.NoDefault, _BuildDiskTemplateCheck(True), "Disk template"), - ("file_driver", None, ht.TOr(ht.TNone, ht.TElemOf(constants.FILE_DRIVER)), + ("file_driver", None, ht.TMaybe(ht.TElemOf(constants.FILE_DRIVER)), "Driver for file-backed disks"), ("file_storage_dir", None, ht.TMaybeString, "Directory for storing file-backed disks"), ("hvparams", ht.EmptyDict, ht.TDict, "Hypervisor parameters for instance, hypervisor-dependent"), ("hypervisor", None, ht.TMaybeString, "Hypervisor"), - ("iallocator", None, ht.TMaybeString, - "Iallocator for deciding which node(s) to use"), + _PIAllocFromDesc("Iallocator for deciding which node(s) to use"), ("identify_defaults", False, ht.TBool, "Reset instance parameters to default if equal"), ("ip_check", True, ht.TBool, _PIpCheckDoc), + ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"), ("mode", ht.NoDefault, ht.TElemOf(constants.INSTANCE_CREATE_MODES), "Instance creation mode"), ("nics", ht.NoDefault, ht.TListOf(_TestNicDef), @@ -1101,12 +1299,12 @@ class OpInstanceCreate(OpCode): ("os_type", None, ht.TMaybeString, "Operating system"), ("pnode", None, ht.TMaybeString, "Primary node"), ("snode", None, ht.TMaybeString, "Secondary node"), - ("source_handshake", None, ht.TOr(ht.TList, ht.TNone), + ("source_handshake", None, ht.TMaybe(ht.TList), "Signed handshake from source (remote import only)"), ("source_instance_name", None, ht.TMaybeString, "Source instance name (remote import only)"), ("source_shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, - ht.TPositiveInt, + ht.TNonNegativeInt, "How long source instance was given to shut down (remote import only)"), ("source_x509_ca", None, ht.TMaybeString, "Source X509 CA in PEM format (remote import only)"), @@ -1118,6 +1316,66 @@ class OpInstanceCreate(OpCode): OP_RESULT = ht.Comment("instance nodes")(ht.TListOf(ht.TNonEmptyString)) +class OpInstanceMultiAlloc(OpCode): + """Allocates multiple instances. + + """ + OP_PARAMS = [ + _POpportunisticLocking, + _PIAllocFromDesc("Iallocator used to allocate all the instances"), + ("instances", ht.EmptyList, ht.TListOf(ht.TInstanceOf(OpInstanceCreate)), + "List of instance create opcodes describing the instances to allocate"), + ] + _JOB_LIST = ht.Comment("List of submitted jobs")(TJobIdList) + ALLOCATABLE_KEY = "allocatable" + FAILED_KEY = "allocatable" + OP_RESULT = ht.TStrictDict(True, True, { + constants.JOB_IDS_KEY: _JOB_LIST, + ALLOCATABLE_KEY: ht.TListOf(ht.TNonEmptyString), + FAILED_KEY: ht.TListOf(ht.TNonEmptyString), + }) + + def __getstate__(self): + """Generic serializer. + + """ + state = OpCode.__getstate__(self) + if hasattr(self, "instances"): + # pylint: disable=E1101 + state["instances"] = [inst.__getstate__() for inst in self.instances] + return state + + def __setstate__(self, state): + """Generic unserializer. + + This method just restores from the serialized state the attributes + of the current instance. + + @param state: the serialized opcode data + @type state: C{dict} + + """ + if not isinstance(state, dict): + raise ValueError("Invalid data to __setstate__: expected dict, got %s" % + type(state)) + + if "instances" in state: + state["instances"] = map(OpCode.LoadOpCode, state["instances"]) + + return OpCode.__setstate__(self, state) + + def Validate(self, set_defaults): + """Validates this opcode. + + We do this recursively. + + """ + OpCode.Validate(self, set_defaults) + + for inst in self.instances: # pylint: disable=E1101 + inst.Validate(set_defaults) + + class OpInstanceReinstall(OpCode): """Reinstall an instance's OS.""" OP_DSC_FIELD = "instance_name" @@ -1127,6 +1385,7 @@ class OpInstanceReinstall(OpCode): ("os_type", None, ht.TMaybeString, "Instance operating system"), ("osparams", None, ht.TMaybeDict, "Temporary OS parameters"), ] + OP_RESULT = ht.TNone class OpInstanceRemove(OpCode): @@ -1138,6 +1397,7 @@ class OpInstanceRemove(OpCode): ("ignore_failures", False, ht.TBool, "Whether to ignore failures during removal"), ] + OP_RESULT = ht.TNone class OpInstanceRename(OpCode): @@ -1164,6 +1424,7 @@ class OpInstanceStartup(OpCode): _PNoRemember, _PStartupPaused, ] + OP_RESULT = ht.TNone class OpInstanceShutdown(OpCode): @@ -1171,11 +1432,13 @@ class OpInstanceShutdown(OpCode): OP_DSC_FIELD = "instance_name" OP_PARAMS = [ _PInstanceName, + _PForce, _PIgnoreOfflineNodes, - ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TPositiveInt, + ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TNonNegativeInt, "How long to wait for instance to shut down"), _PNoRemember, ] + OP_RESULT = ht.TNone class OpInstanceReboot(OpCode): @@ -1189,6 +1452,7 @@ class OpInstanceReboot(OpCode): ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES), "How to reboot instance"), ] + OP_RESULT = ht.TNone class OpInstanceReplaceDisks(OpCode): @@ -1197,14 +1461,15 @@ class OpInstanceReplaceDisks(OpCode): OP_PARAMS = [ _PInstanceName, _PEarlyRelease, + _PIgnoreIpolicy, ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES), "Replacement mode"), - ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt), + ("disks", ht.EmptyList, ht.TListOf(ht.TNonNegativeInt), "Disk indexes"), ("remote_node", None, ht.TMaybeString, "New secondary node"), - ("iallocator", None, ht.TMaybeString, - "Iallocator for deciding new secondary node"), + _PIAllocFromDesc("Iallocator for deciding new secondary node"), ] + OP_RESULT = ht.TNone class OpInstanceFailover(OpCode): @@ -1215,9 +1480,11 @@ class OpInstanceFailover(OpCode): _PShutdownTimeout, _PIgnoreConsistency, _PMigrationTargetNode, - ("iallocator", None, ht.TMaybeString, - "Iallocator for deciding the target node for shared-storage instances"), + _PIgnoreIpolicy, + _PIAllocFromDesc("Iallocator for deciding the target node for" + " shared-storage instances"), ] + OP_RESULT = ht.TNone class OpInstanceMigrate(OpCode): @@ -1236,13 +1503,16 @@ class OpInstanceMigrate(OpCode): _PMigrationMode, _PMigrationLive, _PMigrationTargetNode, + _PAllowRuntimeChgs, + _PIgnoreIpolicy, ("cleanup", False, ht.TBool, "Whether a previously failed migration should be cleaned up"), - ("iallocator", None, ht.TMaybeString, - "Iallocator for deciding the target node for shared-storage instances"), + _PIAllocFromDesc("Iallocator for deciding the target node for" + " shared-storage instances"), ("allow_failover", False, ht.TBool, "Whether we can fallback to failover if migration is not possible"), ] + OP_RESULT = ht.TNone class OpInstanceMove(OpCode): @@ -1259,17 +1529,20 @@ class OpInstanceMove(OpCode): OP_PARAMS = [ _PInstanceName, _PShutdownTimeout, + _PIgnoreIpolicy, ("target_node", ht.NoDefault, ht.TNonEmptyString, "Target node"), _PIgnoreConsistency, ] + OP_RESULT = ht.TNone class OpInstanceConsole(OpCode): """Connect to an instance's console.""" OP_DSC_FIELD = "instance_name" OP_PARAMS = [ - _PInstanceName + _PInstanceName, ] + OP_RESULT = ht.TDict class OpInstanceActivateDisks(OpCode): @@ -1278,7 +1551,12 @@ class OpInstanceActivateDisks(OpCode): OP_PARAMS = [ _PInstanceName, ("ignore_size", False, ht.TBool, "Whether to ignore recorded size"), + _PWaitForSyncFalse, ] + OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3), + ht.TItems([ht.TNonEmptyString, + ht.TNonEmptyString, + ht.TNonEmptyString]))) class OpInstanceDeactivateDisks(OpCode): @@ -1288,18 +1566,28 @@ class OpInstanceDeactivateDisks(OpCode): _PInstanceName, _PForce, ] + OP_RESULT = ht.TNone class OpInstanceRecreateDisks(OpCode): """Recreate an instance's disks.""" + _TDiskChanges = \ + ht.TAnd(ht.TIsLength(2), + ht.TItems([ht.Comment("Disk index")(ht.TNonNegativeInt), + ht.Comment("Parameters")(_TDiskParams)])) + OP_DSC_FIELD = "instance_name" OP_PARAMS = [ _PInstanceName, - ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt), - "List of disk indexes"), + ("disks", ht.EmptyList, + ht.TOr(ht.TListOf(ht.TNonNegativeInt), ht.TListOf(_TDiskChanges)), + "List of disk indexes (deprecated) or a list of tuples containing a disk" + " index and a possibly empty dictionary with disk parameter changes"), ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "New instance nodes, if relocation is desired"), + _PIAllocFromDesc("Iallocator for deciding new nodes"), ] + OP_RESULT = ht.TNone class OpInstanceQuery(OpCode): @@ -1310,6 +1598,7 @@ class OpInstanceQuery(OpCode): ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Empty list to query all instances, instance names otherwise"), ] + OP_RESULT = _TOldQueryResult class OpInstanceQueryData(OpCode): @@ -1322,35 +1611,73 @@ class OpInstanceQueryData(OpCode): "Whether to only return configuration data without querying" " nodes"), ] + OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TDict) + + +def _TestInstSetParamsModList(fn): + """Generates a check for modification lists. + + """ + # Old format + # TODO: Remove in version 2.8 including support in LUInstanceSetParams + old_mod_item_fn = \ + ht.TAnd(ht.TIsLength(2), ht.TItems([ + ht.TOr(ht.TElemOf(constants.DDMS_VALUES), ht.TNonNegativeInt), + fn, + ])) + + # New format, supporting adding/removing disks/NICs at arbitrary indices + mod_item_fn = \ + ht.TAnd(ht.TIsLength(3), ht.TItems([ + ht.TElemOf(constants.DDMS_VALUES_WITH_MODIFY), + ht.Comment("Disk index, can be negative, e.g. -1 for last disk")(ht.TInt), + fn, + ])) + + return ht.TOr(ht.Comment("Recommended")(ht.TListOf(mod_item_fn)), + ht.Comment("Deprecated")(ht.TListOf(old_mod_item_fn))) class OpInstanceSetParams(OpCode): - """Change the parameters of an instance.""" + """Change the parameters of an instance. + + """ + TestNicModifications = _TestInstSetParamsModList(_TestNicDef) + TestDiskModifications = _TestInstSetParamsModList(_TDiskParams) + OP_DSC_FIELD = "instance_name" OP_PARAMS = [ _PInstanceName, _PForce, _PForceVariant, - # TODO: Use _TestNicDef - ("nics", ht.EmptyList, ht.TList, - "List of NIC changes. Each item is of the form ``(op, settings)``." - " ``op`` can be ``%s`` to add a new NIC with the specified settings," - " ``%s`` to remove the last NIC or a number to modify the settings" - " of the NIC with that index." % - (constants.DDM_ADD, constants.DDM_REMOVE)), - ("disks", ht.EmptyList, ht.TList, "List of disk changes. See ``nics``."), + _PIgnoreIpolicy, + ("nics", ht.EmptyList, TestNicModifications, + "List of NIC changes: each item is of the form ``(op, index, settings)``," + " ``op`` is one of ``%s``, ``%s`` or ``%s``, ``index`` can be either -1" + " to refer to the last position, or a zero-based index number; a" + " deprecated version of this parameter used the form ``(op, settings)``," + " where ``op`` can be ``%s`` to add a new NIC with the specified" + " settings, ``%s`` to remove the last NIC or a number to modify the" + " settings of the NIC with that index" % + (constants.DDM_ADD, constants.DDM_MODIFY, constants.DDM_REMOVE, + constants.DDM_ADD, constants.DDM_REMOVE)), + ("disks", ht.EmptyList, TestDiskModifications, + "List of disk changes; see ``nics``"), ("beparams", ht.EmptyDict, ht.TDict, "Per-instance backend parameters"), + ("runtime_mem", None, ht.TMaybePositiveInt, "New runtime memory"), ("hvparams", ht.EmptyDict, ht.TDict, "Per-instance hypervisor parameters, hypervisor-dependent"), - ("disk_template", None, ht.TOr(ht.TNone, _BuildDiskTemplateCheck(False)), + ("disk_template", None, ht.TMaybe(_BuildDiskTemplateCheck(False)), "Disk template for instance"), ("remote_node", None, ht.TMaybeString, "Secondary node (used when changing disk template)"), ("os_name", None, ht.TMaybeString, - "Change instance's OS name. Does not reinstall the instance."), + "Change the instance's OS without reinstalling the instance"), ("osparams", None, ht.TMaybeDict, "Per-instance OS parameters"), ("wait_for_sync", True, ht.TBool, "Whether to wait for the disk to synchronize, when changing template"), + ("offline", None, ht.TMaybeBool, "Whether to mark instance as offline"), + ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"), ] OP_RESULT = _TSetParamsResult @@ -1362,9 +1689,12 @@ class OpInstanceGrowDisk(OpCode): _PInstanceName, _PWaitForSync, ("disk", ht.NoDefault, ht.TInt, "Disk index"), - ("amount", ht.NoDefault, ht.TInt, + ("amount", ht.NoDefault, ht.TNonNegativeInt, "Amount of disk space to add (megabytes)"), + ("absolute", False, ht.TBool, + "Whether the amount parameter is an absolute target or a relative one"), ] + OP_RESULT = ht.TNone class OpInstanceChangeGroup(OpCode): @@ -1373,9 +1703,8 @@ class OpInstanceChangeGroup(OpCode): OP_PARAMS = [ _PInstanceName, _PEarlyRelease, - ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"), - ("target_groups", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString)), - "Destination group names or UUIDs (defaults to \"all but current group\""), + _PIAllocFromDesc("Iallocator for computing solution"), + _PTargetGroups, ] OP_RESULT = TJobIdListOnly @@ -1389,7 +1718,13 @@ class OpGroupAdd(OpCode): _PGroupName, _PNodeGroupAllocPolicy, _PGroupNodeParams, + _PDiskParams, + _PHvState, + _PDiskState, + ("ipolicy", None, ht.TMaybeDict, + "Group-wide :ref:`instance policy ` specs"), ] + OP_RESULT = ht.TNone class OpGroupAssignNodes(OpCode): @@ -1401,6 +1736,7 @@ class OpGroupAssignNodes(OpCode): ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), "List of nodes to assign"), ] + OP_RESULT = ht.TNone class OpGroupQuery(OpCode): @@ -1410,6 +1746,7 @@ class OpGroupQuery(OpCode): ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Empty list to query all groups, group names otherwise"), ] + OP_RESULT = _TOldQueryResult class OpGroupSetParams(OpCode): @@ -1419,6 +1756,10 @@ class OpGroupSetParams(OpCode): _PGroupName, _PNodeGroupAllocPolicy, _PGroupNodeParams, + _PDiskParams, + _PHvState, + _PDiskState, + ("ipolicy", None, ht.TMaybeDict, "Group-wide instance policy specs"), ] OP_RESULT = _TSetParamsResult @@ -1429,6 +1770,7 @@ class OpGroupRemove(OpCode): OP_PARAMS = [ _PGroupName, ] + OP_RESULT = ht.TNone class OpGroupRename(OpCode): @@ -1446,9 +1788,8 @@ class OpGroupEvacuate(OpCode): OP_PARAMS = [ _PGroupName, _PEarlyRelease, - ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"), - ("target_groups", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString)), - "Destination group names or UUIDs"), + _PIAllocFromDesc("Iallocator for computing solution"), + _PTargetGroups, ] OP_RESULT = TJobIdListOnly @@ -1461,6 +1802,18 @@ class OpOsDiagnose(OpCode): ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Which operating systems to diagnose"), ] + OP_RESULT = _TOldQueryResult + + +# ExtStorage opcodes +class OpExtStorageDiagnose(OpCode): + """Compute the list of external storage providers.""" + OP_PARAMS = [ + _POutputFields, + ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), + "Which ExtStorage Provider to diagnose"), + ] + OP_RESULT = _TOldQueryResult # Exports opcodes @@ -1471,6 +1824,9 @@ class OpBackupQuery(OpCode): ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Empty list to query all nodes, node names otherwise"), ] + OP_RESULT = ht.TDictOf(ht.TNonEmptyString, + ht.TOr(ht.Comment("False on error")(ht.TBool), + ht.TListOf(ht.TNonEmptyString))) class OpBackupPrepare(OpCode): @@ -1486,16 +1842,18 @@ class OpBackupPrepare(OpCode): ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES), "Export mode"), ] + OP_RESULT = ht.TMaybeDict class OpBackupExport(OpCode): """Export an instance. - For local exports, the export destination is the node name. For remote - exports, the export destination is a list of tuples, each consisting of - hostname/IP address, port, HMAC and HMAC salt. The HMAC is calculated using - the cluster domain secret over the value "${index}:${hostname}:${port}". The - destination X509 CA must be a signed certificate. + For local exports, the export destination is the node name. For + remote exports, the export destination is a list of tuples, each + consisting of hostname/IP address, port, magic, HMAC and HMAC + salt. The HMAC is calculated using the cluster domain secret over + the value "${index}:${hostname}:${port}". The destination X509 CA + must be a signed certificate. @ivar mode: Export mode (one of L{constants.EXPORT_MODES}) @ivar target_node: Export destination @@ -1519,11 +1877,16 @@ class OpBackupExport(OpCode): "Whether to ignore failures while removing instances"), ("mode", constants.EXPORT_MODE_LOCAL, ht.TElemOf(constants.EXPORT_MODES), "Export mode"), - ("x509_key_name", None, ht.TOr(ht.TList, ht.TNone), + ("x509_key_name", None, ht.TMaybe(ht.TList), "Name of X509 key (remote export only)"), ("destination_x509_ca", None, ht.TMaybeString, "Destination X509 CA (remote export only)"), ] + OP_RESULT = \ + ht.TAnd(ht.TIsLength(2), ht.TItems([ + ht.Comment("Finalizing status")(ht.TBool), + ht.Comment("Status for every exported disk")(ht.TListOf(ht.TBool)), + ])) class OpBackupRemove(OpCode): @@ -1532,6 +1895,7 @@ class OpBackupRemove(OpCode): OP_PARAMS = [ _PInstanceName, ] + OP_RESULT = ht.TNone # Tags opcodes @@ -1540,17 +1904,26 @@ class OpTagsGet(OpCode): OP_DSC_FIELD = "name" OP_PARAMS = [ _PTagKind, + # Not using _PUseLocking as the default is different for historical reasons + ("use_locking", True, ht.TBool, "Whether to use synchronization"), # Name is only meaningful for nodes and instances - ("name", ht.NoDefault, ht.TMaybeString, None), + ("name", ht.NoDefault, ht.TMaybeString, + "Name of object to retrieve tags from"), ] + OP_RESULT = ht.TListOf(ht.TNonEmptyString) class OpTagsSearch(OpCode): """Searches the tags in the cluster for a given pattern.""" OP_DSC_FIELD = "pattern" OP_PARAMS = [ - ("pattern", ht.NoDefault, ht.TNonEmptyString, None), + ("pattern", ht.NoDefault, ht.TNonEmptyString, + "Search pattern (regular expression)"), ] + OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(2), ht.TItems([ + ht.TNonEmptyString, + ht.TNonEmptyString, + ]))) class OpTagsSet(OpCode): @@ -1558,9 +1931,11 @@ class OpTagsSet(OpCode): OP_PARAMS = [ _PTagKind, _PTags, - # Name is only meaningful for nodes and instances - ("name", ht.NoDefault, ht.TMaybeString, None), + # Name is only meaningful for groups, nodes and instances + ("name", ht.NoDefault, ht.TMaybeString, + "Name of object where tag(s) should be added"), ] + OP_RESULT = ht.TNone class OpTagsDel(OpCode): @@ -1568,9 +1943,11 @@ class OpTagsDel(OpCode): OP_PARAMS = [ _PTagKind, _PTags, - # Name is only meaningful for nodes and instances - ("name", ht.NoDefault, ht.TMaybeString, None), + # Name is only meaningful for groups, nodes and instances + ("name", ht.NoDefault, ht.TMaybeString, + "Name of object where tag(s) should be deleted"), ] + OP_RESULT = ht.TNone # Test opcodes @@ -1600,9 +1977,19 @@ class OpTestDelay(OpCode): ("duration", ht.NoDefault, ht.TNumber, None), ("on_master", True, ht.TBool, None), ("on_nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None), - ("repeat", 0, ht.TPositiveInt, None), + ("repeat", 0, ht.TNonNegativeInt, None), ] + def OP_DSC_FORMATTER(self, value): # pylint: disable=C0103,R0201 + """Custom formatter for duration. + + """ + try: + v = float(value) + except TypeError: + v = value + return str(v) + class OpTestAllocator(OpCode): """Allocator framework testing. @@ -1615,29 +2002,32 @@ class OpTestAllocator(OpCode): return the allocator output (direction 'out') """ - OP_DSC_FIELD = "allocator" + OP_DSC_FIELD = "iallocator" OP_PARAMS = [ ("direction", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS), None), ("mode", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_MODES), None), ("name", ht.NoDefault, ht.TNonEmptyString, None), - ("nics", ht.NoDefault, ht.TOr(ht.TNone, ht.TListOf( - ht.TDictOf(ht.TElemOf([constants.INIC_MAC, constants.INIC_IP, "bridge"]), - ht.TOr(ht.TNone, ht.TNonEmptyString)))), None), - ("disks", ht.NoDefault, ht.TOr(ht.TNone, ht.TList), None), + ("nics", ht.NoDefault, + ht.TMaybeListOf(ht.TDictOf(ht.TElemOf([constants.INIC_MAC, + constants.INIC_IP, + "bridge"]), + ht.TMaybeString)), + None), + ("disks", ht.NoDefault, ht.TMaybe(ht.TList), None), ("hypervisor", None, ht.TMaybeString, None), - ("allocator", None, ht.TMaybeString, None), + _PIAllocFromDesc(None), ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None), - ("memory", None, ht.TOr(ht.TNone, ht.TPositiveInt), None), - ("vcpus", None, ht.TOr(ht.TNone, ht.TPositiveInt), None), + ("memory", None, ht.TMaybe(ht.TNonNegativeInt), None), + ("vcpus", None, ht.TMaybe(ht.TNonNegativeInt), None), ("os", None, ht.TMaybeString, None), ("disk_template", None, ht.TMaybeString, None), - ("instances", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString)), - None), + ("instances", None, ht.TMaybeListOf(ht.TNonEmptyString), None), ("evac_mode", None, - ht.TOr(ht.TNone, ht.TElemOf(constants.IALLOCATOR_NEVAC_MODES)), None), - ("target_groups", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString)), - None), + ht.TMaybe(ht.TElemOf(constants.IALLOCATOR_NEVAC_MODES)), None), + ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString), None), + ("spindle_use", 1, ht.TNonNegativeInt, None), + ("count", 1, ht.TNonNegativeInt, None), ] @@ -1666,6 +2056,103 @@ class OpTestDummy(OpCode): WITH_LU = False +# Network opcodes +# Add a new network in the cluster +class OpNetworkAdd(OpCode): + """Add an IP network to the cluster.""" + OP_DSC_FIELD = "network_name" + OP_PARAMS = [ + _PNetworkName, + ("network", ht.NoDefault, _TIpNetwork4, "IPv4 subnet"), + ("gateway", None, ht.TMaybe(_TIpAddress4), "IPv4 gateway"), + ("network6", None, ht.TMaybe(_TIpNetwork6), "IPv6 subnet"), + ("gateway6", None, ht.TMaybe(_TIpAddress6), "IPv6 gateway"), + ("mac_prefix", None, ht.TMaybeString, + "MAC address prefix that overrides cluster one"), + ("add_reserved_ips", None, _TMaybeAddr4List, + "Which IP addresses to reserve"), + ("conflicts_check", True, ht.TBool, + "Whether to check for conflicting IP addresses"), + ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Network tags"), + ] + OP_RESULT = ht.TNone + + +class OpNetworkRemove(OpCode): + """Remove an existing network from the cluster. + Must not be connected to any nodegroup. + + """ + OP_DSC_FIELD = "network_name" + OP_PARAMS = [ + _PNetworkName, + _PForce, + ] + OP_RESULT = ht.TNone + + +class OpNetworkSetParams(OpCode): + """Modify Network's parameters except for IPv4 subnet""" + OP_DSC_FIELD = "network_name" + OP_PARAMS = [ + _PNetworkName, + ("gateway", None, ht.TMaybeValueNone(_TIpAddress4), "IPv4 gateway"), + ("network6", None, ht.TMaybeValueNone(_TIpNetwork6), "IPv6 subnet"), + ("gateway6", None, ht.TMaybeValueNone(_TIpAddress6), "IPv6 gateway"), + ("mac_prefix", None, ht.TMaybeValueNone(ht.TString), + "MAC address prefix that overrides cluster one"), + ("add_reserved_ips", None, _TMaybeAddr4List, + "Which external IP addresses to reserve"), + ("remove_reserved_ips", None, _TMaybeAddr4List, + "Which external IP addresses to release"), + ] + OP_RESULT = ht.TNone + + +class OpNetworkConnect(OpCode): + """Connect a Network to a specific Nodegroup with the defined netparams + (mode, link). Nics in this Network will inherit those params. + Produce errors if a NIC (that its not already assigned to a network) + has an IP that is contained in the Network this will produce error unless + --no-conflicts-check is passed. + + """ + OP_DSC_FIELD = "network_name" + OP_PARAMS = [ + _PGroupName, + _PNetworkName, + ("network_mode", ht.NoDefault, ht.TElemOf(constants.NIC_VALID_MODES), + "Connectivity mode"), + ("network_link", ht.NoDefault, ht.TString, "Connectivity link"), + ("conflicts_check", True, ht.TBool, "Whether to check for conflicting IPs"), + ] + OP_RESULT = ht.TNone + + +class OpNetworkDisconnect(OpCode): + """Disconnect a Network from a Nodegroup. Produce errors if NICs are + present in the Network unless --no-conficts-check option is passed. + + """ + OP_DSC_FIELD = "network_name" + OP_PARAMS = [ + _PGroupName, + _PNetworkName, + ] + OP_RESULT = ht.TNone + + +class OpNetworkQuery(OpCode): + """Compute the list of networks.""" + OP_PARAMS = [ + _POutputFields, + _PUseLocking, + ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), + "Empty list to query all groups, group names otherwise"), + ] + OP_RESULT = _TOldQueryResult + + def _GetOpList(): """Returns list of all defined opcodes.