4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 This module implements the data structures which define the cluster
25 operations - the so-called opcodes.
27 Every operation which modifies the cluster state is expressed via
32 # this are practically structures, so disable the message about too
34 # pylint: disable=R0903
40 from ganeti import constants
41 from ganeti import errors
43 from ganeti import objects
44 from ganeti import outils
47 # Common opcode attributes
49 #: output fields for a query operation
50 _POutputFields = ("output_fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
51 "Selected output fields")
53 #: the shutdown timeout
55 ("shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TNonNegativeInt,
56 "How long to wait for instance to shut down")
58 #: the force parameter
59 _PForce = ("force", False, ht.TBool, "Whether to force the operation")
61 #: a required instance name (for single-instance LUs)
62 _PInstanceName = ("instance_name", ht.NoDefault, ht.TNonEmptyString,
65 #: a instance UUID (for single-instance LUs)
66 _PInstanceUuid = ("instance_uuid", None, ht.TMaybeString,
69 #: Whether to ignore offline nodes
70 _PIgnoreOfflineNodes = ("ignore_offline_nodes", False, ht.TBool,
71 "Whether to ignore offline nodes")
73 #: a required node name (for single-node LUs)
74 _PNodeName = ("node_name", ht.NoDefault, ht.TNonEmptyString, "Node name")
76 #: a node UUID (for use with _PNodeName)
77 _PNodeUuid = ("node_uuid", None, ht.TMaybeString, "Node UUID")
79 #: a required node group name (for single-group LUs)
80 _PGroupName = ("group_name", ht.NoDefault, ht.TNonEmptyString, "Group name")
82 #: Migration type (live/non-live)
83 _PMigrationMode = ("mode", None,
84 ht.TMaybe(ht.TElemOf(constants.HT_MIGRATION_MODES)),
87 #: Obsolete 'live' migration mode (boolean)
88 _PMigrationLive = ("live", None, ht.TMaybeBool,
89 "Legacy setting for live migration, do not use")
92 _PTagKind = ("kind", ht.NoDefault, ht.TElemOf(constants.VALID_TAG_TYPES),
95 #: List of tag strings
96 _PTags = ("tags", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
99 _PForceVariant = ("force_variant", False, ht.TBool,
100 "Whether to force an unknown OS variant")
102 _PWaitForSync = ("wait_for_sync", True, ht.TBool,
103 "Whether to wait for the disk to synchronize")
105 _PWaitForSyncFalse = ("wait_for_sync", False, ht.TBool,
106 "Whether to wait for the disk to synchronize"
107 " (defaults to false)")
109 _PIgnoreConsistency = ("ignore_consistency", False, ht.TBool,
110 "Whether to ignore disk consistency")
112 _PStorageName = ("name", ht.NoDefault, ht.TMaybeString, "Storage name")
114 _PUseLocking = ("use_locking", False, ht.TBool,
115 "Whether to use synchronization")
117 _PNameCheck = ("name_check", True, ht.TBool, "Whether to check name")
119 _PNodeGroupAllocPolicy = \
120 ("alloc_policy", None,
121 ht.TMaybe(ht.TElemOf(constants.VALID_ALLOC_POLICIES)),
122 "Instance allocation policy")
124 _PGroupNodeParams = ("ndparams", None, ht.TMaybeDict,
125 "Default node parameters for group")
127 _PQueryWhat = ("what", ht.NoDefault, ht.TElemOf(constants.QR_VIA_OP),
128 "Resource(s) to query for")
130 _PEarlyRelease = ("early_release", False, ht.TBool,
131 "Whether to release locks as soon as possible")
133 _PIpCheckDoc = "Whether to ensure instance's IP address is inactive"
135 #: Do not remember instance state changes
136 _PNoRemember = ("no_remember", False, ht.TBool,
137 "Do not remember the state change")
139 #: Target node for instance migration/failover
140 _PMigrationTargetNode = ("target_node", None, ht.TMaybeString,
141 "Target node for shared-storage instances")
143 _PMigrationTargetNodeUuid = ("target_node_uuid", None, ht.TMaybeString,
144 "Target node UUID for shared-storage instances")
146 _PStartupPaused = ("startup_paused", False, ht.TBool,
147 "Pause instance at startup")
149 _PVerbose = ("verbose", False, ht.TBool, "Verbose mode")
151 # Parameters for cluster verification
152 _PDebugSimulateErrors = ("debug_simulate_errors", False, ht.TBool,
153 "Whether to simulate errors (useful for debugging)")
154 _PErrorCodes = ("error_codes", False, ht.TBool, "Error codes")
155 _PSkipChecks = ("skip_checks", ht.EmptyList,
156 ht.TListOf(ht.TElemOf(constants.VERIFY_OPTIONAL_CHECKS)),
157 "Which checks to skip")
158 _PIgnoreErrors = ("ignore_errors", ht.EmptyList,
159 ht.TListOf(ht.TElemOf(constants.CV_ALL_ECODES_STRINGS)),
160 "List of error codes that should be treated as warnings")
165 ht.TMaybe(ht.TDictOf(ht.TElemOf(constants.DISK_TEMPLATES), ht.TDict)),
166 "Disk templates' parameter defaults")
168 # Parameters for node resource model
169 _PHvState = ("hv_state", None, ht.TMaybeDict, "Set hypervisor states")
170 _PDiskState = ("disk_state", None, ht.TMaybeDict, "Set disk states")
172 #: Opportunistic locking
173 _POpportunisticLocking = \
174 ("opportunistic_locking", False, ht.TBool,
175 ("Whether to employ opportunistic locking for nodes, meaning nodes"
176 " already locked by another opcode won't be considered for instance"
177 " allocation (only when an iallocator is used)"))
179 _PIgnoreIpolicy = ("ignore_ipolicy", False, ht.TBool,
180 "Whether to ignore ipolicy violations")
182 # Allow runtime changes while migrating
183 _PAllowRuntimeChgs = ("allow_runtime_changes", True, ht.TBool,
184 "Allow runtime changes (eg. memory ballooning)")
186 #: IAllocator field builder
187 _PIAllocFromDesc = lambda desc: ("iallocator", None, ht.TMaybeString, desc)
189 #: a required network name
190 _PNetworkName = ("network_name", ht.NoDefault, ht.TNonEmptyString,
194 ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString),
195 "Destination group names or UUIDs (defaults to \"all but current group\")")
197 #: OP_ID conversion regular expression
198 _OPID_RE = re.compile("([a-z])([A-Z])")
200 #: Utility function for L{OpClusterSetParams}
201 _TestClusterOsListItem = \
202 ht.TAnd(ht.TIsLength(2), ht.TItems([
203 ht.TElemOf(constants.DDMS_VALUES),
207 _TestClusterOsList = ht.TMaybeListOf(_TestClusterOsListItem)
209 # TODO: Generate check from constants.INIC_PARAMS_TYPES
210 #: Utility function for testing NIC definitions
212 ht.Comment("NIC parameters")(ht.TDictOf(ht.TElemOf(constants.INIC_PARAMS),
215 _TSetParamsResultItemItems = [
216 ht.Comment("name of changed parameter")(ht.TNonEmptyString),
217 ht.Comment("new value")(ht.TAny),
220 _TSetParamsResult = \
221 ht.TListOf(ht.TAnd(ht.TIsLength(len(_TSetParamsResultItemItems)),
222 ht.TItems(_TSetParamsResultItemItems)))
224 # In the disks option we can provide arbitrary parameters too, which
225 # we may not be able to validate at this level, so we just check the
226 # format of the dict here and the checks concerning IDISK_PARAMS will
227 # happen at the LU level
229 ht.Comment("Disk parameters")(ht.TDictOf(ht.TNonEmptyString,
230 ht.TOr(ht.TNonEmptyString, ht.TInt)))
233 ht.TListOf(ht.TAnd(ht.TIsLength(2),
234 ht.TItems([ht.TElemOf(constants.RS_ALL),
237 _TQueryResult = ht.TListOf(_TQueryRow)
239 _TOldQueryRow = ht.TListOf(ht.TAny)
241 _TOldQueryResult = ht.TListOf(_TOldQueryRow)
251 #: Attribute name for dependencies
252 DEPEND_ATTR = "depends"
254 #: Attribute name for comment
255 COMMENT_ATTR = "comment"
258 def _NameComponents(name):
259 """Split an opcode class name into its components
262 @param name: the class name, as OpXxxYyy
263 @rtype: array of strings
264 @return: the components of the name
267 assert name.startswith("Op")
268 # Note: (?<=[a-z])(?=[A-Z]) would be ideal, since it wouldn't
269 # consume any input, and hence we would just have all the elements
270 # in the list, one by one; but it seems that split doesn't work on
271 # non-consuming input, hence we have to process the input string a
273 name = _OPID_RE.sub(r"\1,\2", name)
274 elems = name.split(",")
279 """Convert an opcode class name to an OP_ID.
282 @param name: the class name, as OpXxxYyy
284 @return: the name in the OP_XXXX_YYYY format
287 if not name.startswith("Op"):
289 return "_".join(n.upper() for n in _NameComponents(name))
292 def NameToReasonSrc(name):
293 """Convert an opcode class name to a source string for the reason trail
296 @param name: the class name, as OpXxxYyy
298 @return: the name in the OP_XXXX_YYYY format
301 if not name.startswith("Op"):
303 return "%s:%s" % (constants.OPCODE_REASON_SRC_OPCODE,
304 "_".join(n.lower() for n in _NameComponents(name)))
307 def _GenerateObjectTypeCheck(obj, fields_types):
308 """Helper to generate type checks for objects.
310 @param obj: The object to generate type checks
311 @param fields_types: The fields and their types as a dict
312 @return: A ht type check function
315 assert set(obj.GetAllSlots()) == set(fields_types.keys()), \
316 "%s != %s" % (set(obj.GetAllSlots()), set(fields_types.keys()))
317 return ht.TStrictDict(True, True, fields_types)
321 _GenerateObjectTypeCheck(objects.QueryFieldDefinition, {
322 "name": ht.TNonEmptyString,
323 "title": ht.TNonEmptyString,
324 "kind": ht.TElemOf(constants.QFT_ALL),
325 "doc": ht.TNonEmptyString,
329 def _BuildDiskTemplateCheck(accept_none):
330 """Builds check for disk template.
332 @type accept_none: bool
333 @param accept_none: whether to accept None as a correct value
337 template_check = ht.TElemOf(constants.DISK_TEMPLATES)
340 template_check = ht.TMaybe(template_check)
342 return template_check
345 def _CheckStorageType(storage_type):
346 """Ensure a given storage type is valid.
349 if storage_type not in constants.STORAGE_TYPES:
350 raise errors.OpPrereqError("Unknown storage type: %s" % storage_type,
355 #: Storage type parameter
356 _PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType,
360 @ht.WithDesc("IPv4 network")
361 def _CheckCIDRNetNotation(value):
362 """Ensure a given CIDR notation type is valid.
366 ipaddr.IPv4Network(value)
367 except ipaddr.AddressValueError:
372 @ht.WithDesc("IPv4 address")
373 def _CheckCIDRAddrNotation(value):
374 """Ensure a given CIDR notation type is valid.
378 ipaddr.IPv4Address(value)
379 except ipaddr.AddressValueError:
384 @ht.WithDesc("IPv6 address")
385 def _CheckCIDR6AddrNotation(value):
386 """Ensure a given CIDR notation type is valid.
390 ipaddr.IPv6Address(value)
391 except ipaddr.AddressValueError:
396 @ht.WithDesc("IPv6 network")
397 def _CheckCIDR6NetNotation(value):
398 """Ensure a given CIDR notation type is valid.
402 ipaddr.IPv6Network(value)
403 except ipaddr.AddressValueError:
408 _TIpAddress4 = ht.TAnd(ht.TString, _CheckCIDRAddrNotation)
409 _TIpAddress6 = ht.TAnd(ht.TString, _CheckCIDR6AddrNotation)
410 _TIpNetwork4 = ht.TAnd(ht.TString, _CheckCIDRNetNotation)
411 _TIpNetwork6 = ht.TAnd(ht.TString, _CheckCIDR6NetNotation)
412 _TMaybeAddr4List = ht.TMaybe(ht.TListOf(_TIpAddress4))
415 class _AutoOpParamSlots(outils.AutoSlots):
416 """Meta class for opcode definitions.
419 def __new__(mcs, name, bases, attrs):
420 """Called when a class should be created.
422 @param mcs: The meta class
423 @param name: Name of created class
424 @param bases: Base classes
426 @param attrs: Class attributes
429 assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
431 slots = mcs._GetSlots(attrs)
432 assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
433 "Class '%s' uses unknown field in OP_DSC_FIELD" % name
434 assert ("OP_DSC_FORMATTER" not in attrs or
435 callable(attrs["OP_DSC_FORMATTER"])), \
436 ("Class '%s' uses non-callable in OP_DSC_FORMATTER (%s)" %
437 (name, type(attrs["OP_DSC_FORMATTER"])))
439 attrs["OP_ID"] = _NameToId(name)
441 return outils.AutoSlots.__new__(mcs, name, bases, attrs)
444 def _GetSlots(mcs, attrs):
445 """Build the slots out of OP_PARAMS.
448 # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams
449 params = attrs.setdefault("OP_PARAMS", [])
451 # Use parameter names as slots
452 return [pname for (pname, _, _, _) in params]
455 class BaseOpCode(outils.ValidatedSlots):
456 """A simple serializable object.
458 This object serves as a parent class for OpCode without any custom
462 # pylint: disable=E1101
463 # as OP_ID is dynamically defined
464 __metaclass__ = _AutoOpParamSlots
466 def __getstate__(self):
467 """Generic serializer.
469 This method just returns the contents of the instance as a
473 @return: the instance attributes and their values
477 for name in self.GetAllSlots():
478 if hasattr(self, name):
479 state[name] = getattr(self, name)
482 def __setstate__(self, state):
483 """Generic unserializer.
485 This method just restores from the serialized state the attributes
486 of the current instance.
488 @param state: the serialized opcode data
492 if not isinstance(state, dict):
493 raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
496 for name in self.GetAllSlots():
497 if name not in state and hasattr(self, name):
501 setattr(self, name, state[name])
504 def GetAllParams(cls):
505 """Compute list of all parameters for an opcode.
509 for parent in cls.__mro__:
510 slots.extend(getattr(parent, "OP_PARAMS", []))
513 def Validate(self, set_defaults): # pylint: disable=W0221
514 """Validate opcode parameters, optionally setting default values.
516 @type set_defaults: bool
517 @param set_defaults: Whether to set default values
518 @raise errors.OpPrereqError: When a parameter value doesn't match
522 for (attr_name, default, test, _) in self.GetAllParams():
523 assert test == ht.NoType or callable(test)
525 if not hasattr(self, attr_name):
526 if default == ht.NoDefault:
527 raise errors.OpPrereqError("Required parameter '%s.%s' missing" %
528 (self.OP_ID, attr_name),
531 if callable(default):
535 setattr(self, attr_name, dval)
537 if test == ht.NoType:
541 if set_defaults or hasattr(self, attr_name):
542 attr_val = getattr(self, attr_name)
543 if not test(attr_val):
544 logging.error("OpCode %s, parameter %s, has invalid type %s/value"
545 " '%s' expecting type %s",
546 self.OP_ID, attr_name, type(attr_val), attr_val, test)
547 raise errors.OpPrereqError("Parameter '%s.%s' fails validation" %
548 (self.OP_ID, attr_name),
552 def _BuildJobDepCheck(relative):
553 """Builds check for job dependencies (L{DEPEND_ATTR}).
556 @param relative: Whether to accept relative job IDs (negative)
561 job_id = ht.TOr(ht.TJobId, ht.TRelativeJobId)
566 ht.TAnd(ht.TOr(ht.TList, ht.TTuple),
569 ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))]))
571 return ht.TMaybeListOf(job_dep)
574 TNoRelativeJobDependencies = _BuildJobDepCheck(False)
576 #: List of submission status and job ID as returned by C{SubmitManyJobs}
578 ht.TAnd(ht.TIsLength(2),
579 ht.TItems([ht.Comment("success")(ht.TBool),
580 ht.Comment("Job ID if successful, error message"
581 " otherwise")(ht.TOr(ht.TString,
583 TJobIdList = ht.TListOf(_TJobIdListItem)
585 #: Result containing only list of submitted jobs
586 TJobIdListOnly = ht.TStrictDict(True, True, {
587 constants.JOB_IDS_KEY: ht.Comment("List of submitted jobs")(TJobIdList),
591 class OpCode(BaseOpCode):
594 This is the root of the actual OpCode hierarchy. All clases derived
595 from this class should override OP_ID.
597 @cvar OP_ID: The ID of this opcode. This should be unique amongst all
598 children of this class.
599 @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
600 string returned by Summary(); see the docstring of that
602 @cvar OP_DSC_FORMATTER: A callable that should format the OP_DSC_FIELD; if
603 not present, then the field will be simply converted
605 @cvar OP_PARAMS: List of opcode attributes, the default values they should
606 get if not already defined, and types they must match.
607 @cvar OP_RESULT: Callable to verify opcode result
608 @cvar WITH_LU: Boolean that specifies whether this should be included in
609 mcpu's dispatch table
610 @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
612 @ivar priority: Opcode priority for queue
615 # pylint: disable=E1101
616 # as OP_ID is dynamically defined
619 ("dry_run", None, ht.TMaybeBool, "Run checks only, don't execute"),
620 ("debug_level", None, ht.TMaybe(ht.TNonNegativeInt), "Debug level"),
621 ("priority", constants.OP_PRIO_DEFAULT,
622 ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID), "Opcode priority"),
623 (DEPEND_ATTR, None, _BuildJobDepCheck(True),
624 "Job dependencies; if used through ``SubmitManyJobs`` relative (negative)"
625 " job IDs can be used; see :doc:`design document <design-chained-jobs>`"
627 (COMMENT_ATTR, None, ht.TMaybeString,
628 "Comment describing the purpose of the opcode"),
629 (constants.OPCODE_REASON, ht.EmptyList, ht.TMaybeList,
630 "The reason trail, describing why the OpCode is executed"),
634 def __getstate__(self):
635 """Specialized getstate for opcodes.
637 This method adds to the state dictionary the OP_ID of the class,
638 so that on unload we can identify the correct class for
639 instantiating the opcode.
642 @return: the state as a dictionary
645 data = BaseOpCode.__getstate__(self)
646 data["OP_ID"] = self.OP_ID
650 def LoadOpCode(cls, data):
651 """Generic load opcode method.
653 The method identifies the correct opcode class from the dict-form
654 by looking for a OP_ID key, if this is not found, or its value is
655 not available in this module as a child of this class, we fail.
658 @param data: the serialized opcode
661 if not isinstance(data, dict):
662 raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
663 if "OP_ID" not in data:
664 raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
665 op_id = data["OP_ID"]
667 if op_id in OP_MAPPING:
668 op_class = OP_MAPPING[op_id]
670 raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
673 new_data = data.copy()
674 del new_data["OP_ID"]
675 op.__setstate__(new_data)
679 """Generates a summary description of this opcode.
681 The summary is the value of the OP_ID attribute (without the "OP_"
682 prefix), plus the value of the OP_DSC_FIELD attribute, if one was
683 defined; this field should allow to easily identify the operation
684 (for an instance creation job, e.g., it would be the instance
688 assert self.OP_ID is not None and len(self.OP_ID) > 3
689 # all OP_ID start with OP_, we remove that
691 field_name = getattr(self, "OP_DSC_FIELD", None)
693 field_value = getattr(self, field_name, None)
694 field_formatter = getattr(self, "OP_DSC_FORMATTER", None)
695 if callable(field_formatter):
696 field_value = field_formatter(field_value)
697 elif isinstance(field_value, (list, tuple)):
698 field_value = ",".join(str(i) for i in field_value)
699 txt = "%s(%s)" % (txt, field_value)
702 def TinySummary(self):
703 """Generates a compact summary description of the opcode.
706 assert self.OP_ID.startswith("OP_")
708 text = self.OP_ID[3:]
710 for (prefix, supplement) in _SUMMARY_PREFIX.items():
711 if text.startswith(prefix):
712 return supplement + text[len(prefix):]
719 class OpClusterPostInit(OpCode):
720 """Post cluster initialization.
722 This opcode does not touch the cluster at all. Its purpose is to run hooks
723 after the cluster has been initialized.
729 class OpClusterDestroy(OpCode):
730 """Destroy the cluster.
732 This opcode has no other parameters. All the state is irreversibly
733 lost after the execution of this opcode.
736 OP_RESULT = ht.TNonEmptyString
739 class OpClusterQuery(OpCode):
740 """Query cluster information."""
741 OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TAny)
744 class OpClusterVerify(OpCode):
745 """Submits all jobs necessary to verify the cluster.
749 _PDebugSimulateErrors,
754 ("group_name", None, ht.TMaybeString, "Group to verify"),
756 OP_RESULT = TJobIdListOnly
759 class OpClusterVerifyConfig(OpCode):
760 """Verify the cluster config.
764 _PDebugSimulateErrors,
772 class OpClusterVerifyGroup(OpCode):
773 """Run verify on a node group from the cluster.
775 @type skip_checks: C{list}
776 @ivar skip_checks: steps to be skipped from the verify process; this
777 needs to be a subset of
778 L{constants.VERIFY_OPTIONAL_CHECKS}; currently
779 only L{constants.VERIFY_NPLUSONE_MEM} can be passed
782 OP_DSC_FIELD = "group_name"
785 _PDebugSimulateErrors,
794 class OpClusterVerifyDisks(OpCode):
795 """Verify the cluster disks.
798 OP_RESULT = TJobIdListOnly
801 class OpGroupVerifyDisks(OpCode):
802 """Verifies the status of all disks in a node group.
804 Result: a tuple of three elements:
805 - dict of node names with issues (values: error msg)
806 - list of instances with degraded disks (that should be activated)
807 - dict of instances with missing logical volumes (values: (node, vol)
808 pairs with details about the missing volumes)
810 In normal operation, all lists should be empty. A non-empty instance
811 list (3rd element of the result) is still ok (errors were fixed) but
812 non-empty node list means some node is down, and probably there are
813 unfixable drbd errors.
815 Note that only instances that are drbd-based are taken into
816 consideration. This might need to be revisited in the future.
819 OP_DSC_FIELD = "group_name"
824 ht.TAnd(ht.TIsLength(3),
825 ht.TItems([ht.TDictOf(ht.TString, ht.TString),
826 ht.TListOf(ht.TString),
827 ht.TDictOf(ht.TString,
828 ht.TListOf(ht.TListOf(ht.TString)))]))
831 class OpClusterRepairDiskSizes(OpCode):
832 """Verify the disk sizes of the instances and fixes configuration
835 Parameters: optional instances list, in case we want to restrict the
836 checks to only a subset of the instances.
838 Result: a list of tuples, (instance, disk, parameter, new-size) for changed
841 In normal operation, the list should be empty.
843 @type instances: list
844 @ivar instances: the list of instances to check, or empty for all instances
848 ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
850 OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(4),
851 ht.TItems([ht.TNonEmptyString,
854 ht.TNonNegativeInt])))
857 class OpClusterConfigQuery(OpCode):
858 """Query cluster configuration values."""
862 OP_RESULT = ht.TListOf(ht.TAny)
865 class OpClusterRename(OpCode):
866 """Rename the cluster.
869 @ivar name: The new name of the cluster. The name and/or the master IP
870 address will be changed to match the new name and its IP
874 OP_DSC_FIELD = "name"
876 ("name", ht.NoDefault, ht.TNonEmptyString, None),
878 OP_RESULT = ht.TNonEmptyString
881 class OpClusterSetParams(OpCode):
882 """Change the parameters of the cluster.
884 @type vg_name: C{str} or C{None}
885 @ivar vg_name: The new volume group name or None to disable LVM usage.
892 ("vg_name", None, ht.TMaybe(ht.TString), "Volume group name"),
893 ("enabled_hypervisors", None,
894 ht.TMaybe(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)),
896 "List of enabled hypervisors"),
898 ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)),
899 "Cluster-wide hypervisor parameter defaults, hypervisor-dependent"),
900 ("beparams", None, ht.TMaybeDict,
901 "Cluster-wide backend parameter defaults"),
902 ("os_hvp", None, ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)),
903 "Cluster-wide per-OS hypervisor parameter defaults"),
905 ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)),
906 "Cluster-wide OS parameter defaults"),
908 ("candidate_pool_size", None, ht.TMaybe(ht.TPositiveInt),
909 "Master candidate pool size"),
910 ("uid_pool", None, ht.NoType,
911 "Set UID pool, must be list of lists describing UID ranges (two items,"
912 " start and end inclusive)"),
913 ("add_uids", None, ht.NoType,
914 "Extend UID pool, must be list of lists describing UID ranges (two"
915 " items, start and end inclusive) to be added"),
916 ("remove_uids", None, ht.NoType,
917 "Shrink UID pool, must be list of lists describing UID ranges (two"
918 " items, start and end inclusive) to be removed"),
919 ("maintain_node_health", None, ht.TMaybeBool,
920 "Whether to automatically maintain node health"),
921 ("prealloc_wipe_disks", None, ht.TMaybeBool,
922 "Whether to wipe disks before allocating them to instances"),
923 ("nicparams", None, ht.TMaybeDict, "Cluster-wide NIC parameter defaults"),
924 ("ndparams", None, ht.TMaybeDict, "Cluster-wide node parameter defaults"),
925 ("ipolicy", None, ht.TMaybeDict,
926 "Cluster-wide :ref:`instance policy <rapi-ipolicy>` specs"),
927 ("drbd_helper", None, ht.TMaybe(ht.TString), "DRBD helper program"),
928 ("default_iallocator", None, ht.TMaybe(ht.TString),
929 "Default iallocator for cluster"),
930 ("master_netdev", None, ht.TMaybe(ht.TString),
931 "Master network device"),
932 ("master_netmask", None, ht.TMaybe(ht.TNonNegativeInt),
933 "Netmask of the master IP"),
934 ("reserved_lvs", None, ht.TMaybeListOf(ht.TNonEmptyString),
935 "List of reserved LVs"),
936 ("hidden_os", None, _TestClusterOsList,
937 "Modify list of hidden operating systems: each modification must have"
938 " two items, the operation and the OS name; the operation can be"
939 " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)),
940 ("blacklisted_os", None, _TestClusterOsList,
941 "Modify list of blacklisted operating systems: each modification must"
942 " have two items, the operation and the OS name; the operation can be"
943 " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)),
944 ("use_external_mip_script", None, ht.TMaybeBool,
945 "Whether to use an external master IP address setup script"),
946 ("enabled_disk_templates", None,
947 ht.TMaybe(ht.TAnd(ht.TListOf(ht.TElemOf(constants.DISK_TEMPLATES)),
949 "List of enabled disk templates"),
950 ("modify_etc_hosts", None, ht.TMaybeBool,
951 "Whether the cluster can modify and keep in sync the /etc/hosts files"),
952 ("file_storage_dir", None, ht.TMaybe(ht.TString),
953 "Default directory for storing file-backed disks"),
954 ("shared_file_storage_dir", None, ht.TMaybe(ht.TString),
955 "Default directory for storing shared-file-backed disks"),
960 class OpClusterRedistConf(OpCode):
961 """Force a full push of the cluster configuration.
967 class OpClusterActivateMasterIp(OpCode):
968 """Activate the master IP on the master node.
974 class OpClusterDeactivateMasterIp(OpCode):
975 """Deactivate the master IP on the master node.
981 class OpQuery(OpCode):
982 """Query for resources/items.
984 @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
985 @ivar fields: List of fields to retrieve
986 @ivar qfilter: Query filter
989 OP_DSC_FIELD = "what"
993 ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
995 ("qfilter", None, ht.TMaybe(ht.TList),
999 _GenerateObjectTypeCheck(objects.QueryResponse, {
1000 "fields": ht.TListOf(_TQueryFieldDef),
1001 "data": _TQueryResult,
1005 class OpQueryFields(OpCode):
1006 """Query for available resource/item fields.
1008 @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
1009 @ivar fields: List of fields to retrieve
1012 OP_DSC_FIELD = "what"
1015 ("fields", None, ht.TMaybeListOf(ht.TNonEmptyString),
1016 "Requested fields; if not given, all are returned"),
1019 _GenerateObjectTypeCheck(objects.QueryFieldsResponse, {
1020 "fields": ht.TListOf(_TQueryFieldDef),
1024 class OpOobCommand(OpCode):
1025 """Interact with OOB."""
1027 ("node_names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1028 "List of node names to run the OOB command against"),
1029 ("node_uuids", None, ht.TMaybeListOf(ht.TNonEmptyString),
1030 "List of node UUIDs to run the OOB command against"),
1031 ("command", ht.NoDefault, ht.TElemOf(constants.OOB_COMMANDS),
1032 "OOB command to be run"),
1033 ("timeout", constants.OOB_TIMEOUT, ht.TInt,
1034 "Timeout before the OOB helper will be terminated"),
1035 ("ignore_status", False, ht.TBool,
1036 "Ignores the node offline status for power off"),
1037 ("power_delay", constants.OOB_POWER_DELAY, ht.TNonNegativeFloat,
1038 "Time in seconds to wait between powering on nodes"),
1040 # Fixme: Make it more specific with all the special cases in LUOobCommand
1041 OP_RESULT = _TQueryResult
1044 class OpRestrictedCommand(OpCode):
1045 """Runs a restricted command on node(s).
1050 ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
1051 "Nodes on which the command should be run (at least one)"),
1052 ("node_uuids", None, ht.TMaybeListOf(ht.TNonEmptyString),
1053 "Node UUIDs on which the command should be run (at least one)"),
1054 ("command", ht.NoDefault, ht.TNonEmptyString,
1055 "Command name (no parameters)"),
1059 ht.Comment("success")(ht.TBool),
1060 ht.Comment("output or error message")(ht.TString),
1064 ht.TListOf(ht.TAnd(ht.TIsLength(len(_RESULT_ITEMS)),
1065 ht.TItems(_RESULT_ITEMS)))
1070 class OpNodeRemove(OpCode):
1073 @type node_name: C{str}
1074 @ivar node_name: The name of the node to remove. If the node still has
1075 instances on it, the operation will fail.
1078 OP_DSC_FIELD = "node_name"
1083 OP_RESULT = ht.TNone
1086 class OpNodeAdd(OpCode):
1087 """Add a node to the cluster.
1089 @type node_name: C{str}
1090 @ivar node_name: The name of the node to add. This can be a short name,
1091 but it will be expanded to the FQDN.
1092 @type primary_ip: IP address
1093 @ivar primary_ip: The primary IP of the node. This will be ignored when the
1094 opcode is submitted, but will be filled during the node
1095 add (so it will be visible in the job query).
1096 @type secondary_ip: IP address
1097 @ivar secondary_ip: The secondary IP of the node. This needs to be passed
1098 if the cluster has been initialized in 'dual-network'
1099 mode, otherwise it must not be given.
1100 @type readd: C{bool}
1101 @ivar readd: Whether to re-add an existing node to the cluster. If
1102 this is not passed, then the operation will abort if the node
1103 name is already in the cluster; use this parameter to 'repair'
1104 a node that had its configuration broken, or was reinstalled
1105 without removal from the cluster.
1107 @ivar group: The node group to which this node will belong.
1108 @type vm_capable: C{bool}
1109 @ivar vm_capable: The vm_capable node attribute
1110 @type master_capable: C{bool}
1111 @ivar master_capable: The master_capable node attribute
1114 OP_DSC_FIELD = "node_name"
1119 ("primary_ip", None, ht.NoType, "Primary IP address"),
1120 ("secondary_ip", None, ht.TMaybeString, "Secondary IP address"),
1121 ("readd", False, ht.TBool, "Whether node is re-added to cluster"),
1122 ("group", None, ht.TMaybeString, "Initial node group"),
1123 ("master_capable", None, ht.TMaybeBool,
1124 "Whether node can become master or master candidate"),
1125 ("vm_capable", None, ht.TMaybeBool,
1126 "Whether node can host instances"),
1127 ("ndparams", None, ht.TMaybeDict, "Node parameters"),
1129 OP_RESULT = ht.TNone
1132 class OpNodeQuery(OpCode):
1133 """Compute the list of nodes."""
1137 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1138 "Empty list to query all nodes, node names otherwise"),
1140 OP_RESULT = _TOldQueryResult
1143 class OpNodeQueryvols(OpCode):
1144 """Get list of volumes on node."""
1147 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1148 "Empty list to query all nodes, node names otherwise"),
1150 OP_RESULT = ht.TListOf(ht.TAny)
1153 class OpNodeQueryStorage(OpCode):
1154 """Get information on storage for node(s)."""
1158 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "List of nodes"),
1159 ("name", None, ht.TMaybeString, "Storage name"),
1161 OP_RESULT = _TOldQueryResult
1164 class OpNodeModifyStorage(OpCode):
1165 """Modifies the properies of a storage unit"""
1166 OP_DSC_FIELD = "node_name"
1172 ("changes", ht.NoDefault, ht.TDict, "Requested changes"),
1174 OP_RESULT = ht.TNone
1177 class OpRepairNodeStorage(OpCode):
1178 """Repairs the volume group on a node."""
1179 OP_DSC_FIELD = "node_name"
1185 _PIgnoreConsistency,
1187 OP_RESULT = ht.TNone
1190 class OpNodeSetParams(OpCode):
1191 """Change the parameters of a node."""
1192 OP_DSC_FIELD = "node_name"
1199 ("master_candidate", None, ht.TMaybeBool,
1200 "Whether the node should become a master candidate"),
1201 ("offline", None, ht.TMaybeBool,
1202 "Whether the node should be marked as offline"),
1203 ("drained", None, ht.TMaybeBool,
1204 "Whether the node should be marked as drained"),
1205 ("auto_promote", False, ht.TBool,
1206 "Whether node(s) should be promoted to master candidate if necessary"),
1207 ("master_capable", None, ht.TMaybeBool,
1208 "Denote whether node can become master or master candidate"),
1209 ("vm_capable", None, ht.TMaybeBool,
1210 "Denote whether node can host instances"),
1211 ("secondary_ip", None, ht.TMaybeString,
1212 "Change node's secondary IP address"),
1213 ("ndparams", None, ht.TMaybeDict, "Set node parameters"),
1214 ("powered", None, ht.TMaybeBool,
1215 "Whether the node should be marked as powered"),
1217 OP_RESULT = _TSetParamsResult
1220 class OpNodePowercycle(OpCode):
1221 """Tries to powercycle a node."""
1222 OP_DSC_FIELD = "node_name"
1228 OP_RESULT = ht.TMaybeString
1231 class OpNodeMigrate(OpCode):
1232 """Migrate all instances from a node."""
1233 OP_DSC_FIELD = "node_name"
1239 _PMigrationTargetNode,
1240 _PMigrationTargetNodeUuid,
1243 _PIAllocFromDesc("Iallocator for deciding the target node"
1244 " for shared-storage instances"),
1246 OP_RESULT = TJobIdListOnly
1249 class OpNodeEvacuate(OpCode):
1250 """Evacuate instances off a number of nodes."""
1251 OP_DSC_FIELD = "node_name"
1256 ("remote_node", None, ht.TMaybeString, "New secondary node"),
1257 ("remote_node_uuid", None, ht.TMaybeString, "New secondary node UUID"),
1258 _PIAllocFromDesc("Iallocator for computing solution"),
1259 ("mode", ht.NoDefault, ht.TElemOf(constants.NODE_EVAC_MODES),
1260 "Node evacuation mode"),
1262 OP_RESULT = TJobIdListOnly
1267 class OpInstanceCreate(OpCode):
1268 """Create an instance.
1270 @ivar instance_name: Instance name
1271 @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES})
1272 @ivar source_handshake: Signed handshake from source (remote import only)
1273 @ivar source_x509_ca: Source X509 CA in PEM format (remote import only)
1274 @ivar source_instance_name: Previous name of instance (remote import only)
1275 @ivar source_shutdown_timeout: Shutdown timeout used for source instance
1276 (remote import only)
1279 OP_DSC_FIELD = "instance_name"
1286 _POpportunisticLocking,
1287 ("beparams", ht.EmptyDict, ht.TDict, "Backend parameters for instance"),
1288 ("disks", ht.NoDefault, ht.TListOf(_TDiskParams),
1289 "Disk descriptions, for example ``[{\"%s\": 100}, {\"%s\": 5}]``;"
1290 " each disk definition must contain a ``%s`` value and"
1291 " can contain an optional ``%s`` value denoting the disk access mode"
1293 (constants.IDISK_SIZE, constants.IDISK_SIZE, constants.IDISK_SIZE,
1294 constants.IDISK_MODE,
1295 " or ".join("``%s``" % i for i in sorted(constants.DISK_ACCESS_SET)))),
1296 ("disk_template", ht.NoDefault, _BuildDiskTemplateCheck(True),
1298 ("file_driver", None, ht.TMaybe(ht.TElemOf(constants.FILE_DRIVER)),
1299 "Driver for file-backed disks"),
1300 ("file_storage_dir", None, ht.TMaybeString,
1301 "Directory for storing file-backed disks"),
1302 ("hvparams", ht.EmptyDict, ht.TDict,
1303 "Hypervisor parameters for instance, hypervisor-dependent"),
1304 ("hypervisor", None, ht.TMaybeString, "Hypervisor"),
1305 _PIAllocFromDesc("Iallocator for deciding which node(s) to use"),
1306 ("identify_defaults", False, ht.TBool,
1307 "Reset instance parameters to default if equal"),
1308 ("ip_check", True, ht.TBool, _PIpCheckDoc),
1309 ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"),
1310 ("mode", ht.NoDefault, ht.TElemOf(constants.INSTANCE_CREATE_MODES),
1311 "Instance creation mode"),
1312 ("nics", ht.NoDefault, ht.TListOf(_TestNicDef),
1313 "List of NIC (network interface) definitions, for example"
1314 " ``[{}, {}, {\"%s\": \"198.51.100.4\"}]``; each NIC definition can"
1315 " contain the optional values %s" %
1317 ", ".join("``%s``" % i for i in sorted(constants.INIC_PARAMS)))),
1318 ("no_install", None, ht.TMaybeBool,
1319 "Do not install the OS (will disable automatic start)"),
1320 ("osparams", ht.EmptyDict, ht.TDict, "OS parameters for instance"),
1321 ("os_type", None, ht.TMaybeString, "Operating system"),
1322 ("pnode", None, ht.TMaybeString, "Primary node"),
1323 ("pnode_uuid", None, ht.TMaybeString, "Primary node UUID"),
1324 ("snode", None, ht.TMaybeString, "Secondary node"),
1325 ("snode_uuid", None, ht.TMaybeString, "Secondary node UUID"),
1326 ("source_handshake", None, ht.TMaybe(ht.TList),
1327 "Signed handshake from source (remote import only)"),
1328 ("source_instance_name", None, ht.TMaybeString,
1329 "Source instance name (remote import only)"),
1330 ("source_shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT,
1332 "How long source instance was given to shut down (remote import only)"),
1333 ("source_x509_ca", None, ht.TMaybeString,
1334 "Source X509 CA in PEM format (remote import only)"),
1335 ("src_node", None, ht.TMaybeString, "Source node for import"),
1336 ("src_node_uuid", None, ht.TMaybeString, "Source node UUID for import"),
1337 ("src_path", None, ht.TMaybeString, "Source directory for import"),
1338 ("start", True, ht.TBool, "Whether to start instance after creation"),
1339 ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Instance tags"),
1341 OP_RESULT = ht.Comment("instance nodes")(ht.TListOf(ht.TNonEmptyString))
1344 class OpInstanceMultiAlloc(OpCode):
1345 """Allocates multiple instances.
1349 _POpportunisticLocking,
1350 _PIAllocFromDesc("Iallocator used to allocate all the instances"),
1351 ("instances", ht.EmptyList, ht.TListOf(ht.TInstanceOf(OpInstanceCreate)),
1352 "List of instance create opcodes describing the instances to allocate"),
1354 _JOB_LIST = ht.Comment("List of submitted jobs")(TJobIdList)
1355 ALLOCATABLE_KEY = "allocatable"
1356 FAILED_KEY = "allocatable"
1357 OP_RESULT = ht.TStrictDict(True, True, {
1358 constants.JOB_IDS_KEY: _JOB_LIST,
1359 ALLOCATABLE_KEY: ht.TListOf(ht.TNonEmptyString),
1360 FAILED_KEY: ht.TListOf(ht.TNonEmptyString),
1363 def __getstate__(self):
1364 """Generic serializer.
1367 state = OpCode.__getstate__(self)
1368 if hasattr(self, "instances"):
1369 # pylint: disable=E1101
1370 state["instances"] = [inst.__getstate__() for inst in self.instances]
1373 def __setstate__(self, state):
1374 """Generic unserializer.
1376 This method just restores from the serialized state the attributes
1377 of the current instance.
1379 @param state: the serialized opcode data
1380 @type state: C{dict}
1383 if not isinstance(state, dict):
1384 raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
1387 if "instances" in state:
1388 state["instances"] = map(OpCode.LoadOpCode, state["instances"])
1390 return OpCode.__setstate__(self, state)
1392 def Validate(self, set_defaults):
1393 """Validates this opcode.
1395 We do this recursively.
1398 OpCode.Validate(self, set_defaults)
1400 for inst in self.instances: # pylint: disable=E1101
1401 inst.Validate(set_defaults)
1404 class OpInstanceReinstall(OpCode):
1405 """Reinstall an instance's OS."""
1406 OP_DSC_FIELD = "instance_name"
1411 ("os_type", None, ht.TMaybeString, "Instance operating system"),
1412 ("osparams", None, ht.TMaybeDict, "Temporary OS parameters"),
1414 OP_RESULT = ht.TNone
1417 class OpInstanceRemove(OpCode):
1418 """Remove an instance."""
1419 OP_DSC_FIELD = "instance_name"
1424 ("ignore_failures", False, ht.TBool,
1425 "Whether to ignore failures during removal"),
1427 OP_RESULT = ht.TNone
1430 class OpInstanceRename(OpCode):
1431 """Rename an instance."""
1436 ("new_name", ht.NoDefault, ht.TNonEmptyString, "New instance name"),
1437 ("ip_check", False, ht.TBool, _PIpCheckDoc),
1439 OP_RESULT = ht.Comment("New instance name")(ht.TNonEmptyString)
1442 class OpInstanceStartup(OpCode):
1443 """Startup an instance."""
1444 OP_DSC_FIELD = "instance_name"
1449 _PIgnoreOfflineNodes,
1450 ("hvparams", ht.EmptyDict, ht.TDict,
1451 "Temporary hypervisor parameters, hypervisor-dependent"),
1452 ("beparams", ht.EmptyDict, ht.TDict, "Temporary backend parameters"),
1456 OP_RESULT = ht.TNone
1459 class OpInstanceShutdown(OpCode):
1460 """Shutdown an instance."""
1461 OP_DSC_FIELD = "instance_name"
1466 _PIgnoreOfflineNodes,
1467 ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TNonNegativeInt,
1468 "How long to wait for instance to shut down"),
1471 OP_RESULT = ht.TNone
1474 class OpInstanceReboot(OpCode):
1475 """Reboot an instance."""
1476 OP_DSC_FIELD = "instance_name"
1481 ("ignore_secondaries", False, ht.TBool,
1482 "Whether to start the instance even if secondary disks are failing"),
1483 ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES),
1484 "How to reboot instance"),
1486 OP_RESULT = ht.TNone
1489 class OpInstanceReplaceDisks(OpCode):
1490 """Replace the disks of an instance."""
1491 OP_DSC_FIELD = "instance_name"
1497 ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES),
1498 "Replacement mode"),
1499 ("disks", ht.EmptyList, ht.TListOf(ht.TNonNegativeInt),
1501 ("remote_node", None, ht.TMaybeString, "New secondary node"),
1502 ("remote_node_uuid", None, ht.TMaybeString, "New secondary node UUID"),
1503 _PIAllocFromDesc("Iallocator for deciding new secondary node"),
1505 OP_RESULT = ht.TNone
1508 class OpInstanceFailover(OpCode):
1509 """Failover an instance."""
1510 OP_DSC_FIELD = "instance_name"
1515 _PIgnoreConsistency,
1516 _PMigrationTargetNode,
1517 _PMigrationTargetNodeUuid,
1519 _PIAllocFromDesc("Iallocator for deciding the target node for"
1520 " shared-storage instances"),
1521 ("cleanup", False, ht.TBool,
1522 "Whether a previously failed failover should be cleaned up"),
1524 OP_RESULT = ht.TNone
1527 class OpInstanceMigrate(OpCode):
1528 """Migrate an instance.
1530 This migrates (without shutting down an instance) to its secondary
1533 @ivar instance_name: the name of the instance
1534 @ivar mode: the migration mode (live, non-live or None for auto)
1537 OP_DSC_FIELD = "instance_name"
1543 _PMigrationTargetNode,
1544 _PMigrationTargetNodeUuid,
1547 ("cleanup", False, ht.TBool,
1548 "Whether a previously failed migration should be cleaned up"),
1549 _PIAllocFromDesc("Iallocator for deciding the target node for"
1550 " shared-storage instances"),
1551 ("allow_failover", False, ht.TBool,
1552 "Whether we can fallback to failover if migration is not possible"),
1554 OP_RESULT = ht.TNone
1557 class OpInstanceMove(OpCode):
1558 """Move an instance.
1560 This move (with shutting down an instance and data copying) to an
1563 @ivar instance_name: the name of the instance
1564 @ivar target_node: the destination node
1567 OP_DSC_FIELD = "instance_name"
1573 ("target_node", ht.NoDefault, ht.TNonEmptyString, "Target node"),
1574 ("target_node_uuid", None, ht.TMaybeString, "Target node UUID"),
1575 _PIgnoreConsistency,
1577 OP_RESULT = ht.TNone
1580 class OpInstanceConsole(OpCode):
1581 """Connect to an instance's console."""
1582 OP_DSC_FIELD = "instance_name"
1587 OP_RESULT = ht.TDict
1590 class OpInstanceActivateDisks(OpCode):
1591 """Activate an instance's disks."""
1592 OP_DSC_FIELD = "instance_name"
1596 ("ignore_size", False, ht.TBool, "Whether to ignore recorded size"),
1599 OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3),
1600 ht.TItems([ht.TNonEmptyString,
1602 ht.TNonEmptyString])))
1605 class OpInstanceDeactivateDisks(OpCode):
1606 """Deactivate an instance's disks."""
1607 OP_DSC_FIELD = "instance_name"
1613 OP_RESULT = ht.TNone
1616 class OpInstanceRecreateDisks(OpCode):
1617 """Recreate an instance's disks."""
1619 ht.TAnd(ht.TIsLength(2),
1620 ht.TItems([ht.Comment("Disk index")(ht.TNonNegativeInt),
1621 ht.Comment("Parameters")(_TDiskParams)]))
1623 OP_DSC_FIELD = "instance_name"
1627 ("disks", ht.EmptyList,
1628 ht.TOr(ht.TListOf(ht.TNonNegativeInt), ht.TListOf(_TDiskChanges)),
1629 "List of disk indexes (deprecated) or a list of tuples containing a disk"
1630 " index and a possibly empty dictionary with disk parameter changes"),
1631 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1632 "New instance nodes, if relocation is desired"),
1633 ("node_uuids", None, ht.TMaybeListOf(ht.TNonEmptyString),
1634 "New instance node UUIDs, if relocation is desired"),
1635 _PIAllocFromDesc("Iallocator for deciding new nodes"),
1637 OP_RESULT = ht.TNone
1640 class OpInstanceQuery(OpCode):
1641 """Compute the list of instances."""
1645 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1646 "Empty list to query all instances, instance names otherwise"),
1648 OP_RESULT = _TOldQueryResult
1651 class OpInstanceQueryData(OpCode):
1652 """Compute the run-time status of instances."""
1655 ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1657 ("static", False, ht.TBool,
1658 "Whether to only return configuration data without querying"
1661 OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TDict)
1664 def _TestInstSetParamsModList(fn):
1665 """Generates a check for modification lists.
1669 # TODO: Remove in version 2.8 including support in LUInstanceSetParams
1671 ht.TAnd(ht.TIsLength(2), ht.TItems([
1672 ht.TOr(ht.TElemOf(constants.DDMS_VALUES), ht.TNonNegativeInt),
1676 # New format, supporting adding/removing disks/NICs at arbitrary indices
1678 ht.TAnd(ht.TIsLength(3), ht.TItems([
1679 ht.TElemOf(constants.DDMS_VALUES_WITH_MODIFY),
1680 ht.Comment("Device index, can be negative, e.g. -1 for last disk")
1681 (ht.TOr(ht.TInt, ht.TString)),
1685 return ht.TOr(ht.Comment("Recommended")(ht.TListOf(mod_item_fn)),
1686 ht.Comment("Deprecated")(ht.TListOf(old_mod_item_fn)))
1689 class OpInstanceSetParams(OpCode):
1690 """Change the parameters of an instance.
1693 TestNicModifications = _TestInstSetParamsModList(_TestNicDef)
1694 TestDiskModifications = _TestInstSetParamsModList(_TDiskParams)
1696 OP_DSC_FIELD = "instance_name"
1703 ("nics", ht.EmptyList, TestNicModifications,
1704 "List of NIC changes: each item is of the form"
1705 " ``(op, identifier, settings)``, ``op`` is one of ``%s``, ``%s`` or"
1706 " ``%s``, ``identifier`` can be a zero-based index number (or -1 to refer"
1707 " to the last position), the NIC's UUID of the NIC's name; a"
1708 " deprecated version of this parameter used the form ``(op, settings)``,"
1709 " where ``op`` can be ``%s`` to add a new NIC with the specified"
1710 " settings, ``%s`` to remove the last NIC or a number to modify the"
1711 " settings of the NIC with that index" %
1712 (constants.DDM_ADD, constants.DDM_MODIFY, constants.DDM_REMOVE,
1713 constants.DDM_ADD, constants.DDM_REMOVE)),
1714 ("disks", ht.EmptyList, TestDiskModifications,
1715 "List of disk changes; see ``nics``"),
1716 ("beparams", ht.EmptyDict, ht.TDict, "Per-instance backend parameters"),
1717 ("runtime_mem", None, ht.TMaybePositiveInt, "New runtime memory"),
1718 ("hvparams", ht.EmptyDict, ht.TDict,
1719 "Per-instance hypervisor parameters, hypervisor-dependent"),
1720 ("disk_template", None, ht.TMaybe(_BuildDiskTemplateCheck(False)),
1721 "Disk template for instance"),
1722 ("pnode", None, ht.TMaybeString, "New primary node"),
1723 ("pnode_uuid", None, ht.TMaybeString, "New primary node UUID"),
1724 ("remote_node", None, ht.TMaybeString,
1725 "Secondary node (used when changing disk template)"),
1726 ("remote_node_uuid", None, ht.TMaybeString,
1727 "Secondary node UUID (used when changing disk template)"),
1728 ("os_name", None, ht.TMaybeString,
1729 "Change the instance's OS without reinstalling the instance"),
1730 ("osparams", None, ht.TMaybeDict, "Per-instance OS parameters"),
1731 ("wait_for_sync", True, ht.TBool,
1732 "Whether to wait for the disk to synchronize, when changing template"),
1733 ("offline", None, ht.TMaybeBool, "Whether to mark instance as offline"),
1734 ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"),
1736 OP_RESULT = _TSetParamsResult
1739 class OpInstanceGrowDisk(OpCode):
1740 """Grow a disk of an instance."""
1741 OP_DSC_FIELD = "instance_name"
1746 ("disk", ht.NoDefault, ht.TInt, "Disk index"),
1747 ("amount", ht.NoDefault, ht.TNonNegativeInt,
1748 "Amount of disk space to add (megabytes)"),
1749 ("absolute", False, ht.TBool,
1750 "Whether the amount parameter is an absolute target or a relative one"),
1752 OP_RESULT = ht.TNone
1755 class OpInstanceChangeGroup(OpCode):
1756 """Moves an instance to another node group."""
1757 OP_DSC_FIELD = "instance_name"
1762 _PIAllocFromDesc("Iallocator for computing solution"),
1765 OP_RESULT = TJobIdListOnly
1768 # Node group opcodes
1770 class OpGroupAdd(OpCode):
1771 """Add a node group to the cluster."""
1772 OP_DSC_FIELD = "group_name"
1775 _PNodeGroupAllocPolicy,
1780 ("ipolicy", None, ht.TMaybeDict,
1781 "Group-wide :ref:`instance policy <rapi-ipolicy>` specs"),
1783 OP_RESULT = ht.TNone
1786 class OpGroupAssignNodes(OpCode):
1787 """Assign nodes to a node group."""
1788 OP_DSC_FIELD = "group_name"
1792 ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
1793 "List of nodes to assign"),
1794 ("node_uuids", None, ht.TMaybeListOf(ht.TNonEmptyString),
1795 "List of node UUIDs to assign"),
1797 OP_RESULT = ht.TNone
1800 class OpGroupQuery(OpCode):
1801 """Compute the list of node groups."""
1804 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1805 "Empty list to query all groups, group names otherwise"),
1807 OP_RESULT = _TOldQueryResult
1810 class OpGroupSetParams(OpCode):
1811 """Change the parameters of a node group."""
1812 OP_DSC_FIELD = "group_name"
1815 _PNodeGroupAllocPolicy,
1820 ("ipolicy", None, ht.TMaybeDict, "Group-wide instance policy specs"),
1822 OP_RESULT = _TSetParamsResult
1825 class OpGroupRemove(OpCode):
1826 """Remove a node group from the cluster."""
1827 OP_DSC_FIELD = "group_name"
1831 OP_RESULT = ht.TNone
1834 class OpGroupRename(OpCode):
1835 """Rename a node group in the cluster."""
1838 ("new_name", ht.NoDefault, ht.TNonEmptyString, "New group name"),
1840 OP_RESULT = ht.Comment("New group name")(ht.TNonEmptyString)
1843 class OpGroupEvacuate(OpCode):
1844 """Evacuate a node group in the cluster."""
1845 OP_DSC_FIELD = "group_name"
1849 _PIAllocFromDesc("Iallocator for computing solution"),
1852 OP_RESULT = TJobIdListOnly
1856 class OpOsDiagnose(OpCode):
1857 """Compute the list of guest operating systems."""
1860 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1861 "Which operating systems to diagnose"),
1863 OP_RESULT = _TOldQueryResult
1866 # ExtStorage opcodes
1867 class OpExtStorageDiagnose(OpCode):
1868 """Compute the list of external storage providers."""
1871 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1872 "Which ExtStorage Provider to diagnose"),
1874 OP_RESULT = _TOldQueryResult
1878 class OpBackupQuery(OpCode):
1879 """Compute the list of exported images."""
1882 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1883 "Empty list to query all nodes, node names otherwise"),
1885 OP_RESULT = ht.TDictOf(ht.TNonEmptyString,
1886 ht.TOr(ht.Comment("False on error")(ht.TBool),
1887 ht.TListOf(ht.TNonEmptyString)))
1890 class OpBackupPrepare(OpCode):
1891 """Prepares an instance export.
1893 @ivar instance_name: Instance name
1894 @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1897 OP_DSC_FIELD = "instance_name"
1901 ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES),
1904 OP_RESULT = ht.TMaybeDict
1907 class OpBackupExport(OpCode):
1908 """Export an instance.
1910 For local exports, the export destination is the node name. For
1911 remote exports, the export destination is a list of tuples, each
1912 consisting of hostname/IP address, port, magic, HMAC and HMAC
1913 salt. The HMAC is calculated using the cluster domain secret over
1914 the value "${index}:${hostname}:${port}". The destination X509 CA
1915 must be a signed certificate.
1917 @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1918 @ivar target_node: Export destination
1919 @ivar x509_key_name: X509 key to use (remote export only)
1920 @ivar destination_x509_ca: Destination X509 CA in PEM format (remote export
1924 OP_DSC_FIELD = "instance_name"
1929 # TODO: Rename target_node as it changes meaning for different export modes
1930 # (e.g. "destination")
1931 ("target_node", ht.NoDefault, ht.TOr(ht.TNonEmptyString, ht.TList),
1932 "Destination information, depends on export mode"),
1933 ("target_node_uuid", None, ht.TMaybeString,
1934 "Target node UUID (if local export)"),
1935 ("shutdown", True, ht.TBool, "Whether to shutdown instance before export"),
1936 ("remove_instance", False, ht.TBool,
1937 "Whether to remove instance after export"),
1938 ("ignore_remove_failures", False, ht.TBool,
1939 "Whether to ignore failures while removing instances"),
1940 ("mode", constants.EXPORT_MODE_LOCAL, ht.TElemOf(constants.EXPORT_MODES),
1942 ("x509_key_name", None, ht.TMaybe(ht.TList),
1943 "Name of X509 key (remote export only)"),
1944 ("destination_x509_ca", None, ht.TMaybeString,
1945 "Destination X509 CA (remote export only)"),
1948 ht.TAnd(ht.TIsLength(2), ht.TItems([
1949 ht.Comment("Finalizing status")(ht.TBool),
1950 ht.Comment("Status for every exported disk")(ht.TListOf(ht.TBool)),
1954 class OpBackupRemove(OpCode):
1955 """Remove an instance's export."""
1956 OP_DSC_FIELD = "instance_name"
1961 OP_RESULT = ht.TNone
1965 class OpTagsGet(OpCode):
1966 """Returns the tags of the given object."""
1967 OP_DSC_FIELD = "name"
1970 # Not using _PUseLocking as the default is different for historical reasons
1971 ("use_locking", True, ht.TBool, "Whether to use synchronization"),
1972 # Name is only meaningful for nodes and instances
1973 ("name", ht.NoDefault, ht.TMaybeString,
1974 "Name of object to retrieve tags from"),
1976 OP_RESULT = ht.TListOf(ht.TNonEmptyString)
1979 class OpTagsSearch(OpCode):
1980 """Searches the tags in the cluster for a given pattern."""
1981 OP_DSC_FIELD = "pattern"
1983 ("pattern", ht.NoDefault, ht.TNonEmptyString,
1984 "Search pattern (regular expression)"),
1986 OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(2), ht.TItems([
1992 class OpTagsSet(OpCode):
1993 """Add a list of tags on a given object."""
1997 # Name is only meaningful for groups, nodes and instances
1998 ("name", ht.NoDefault, ht.TMaybeString,
1999 "Name of object where tag(s) should be added"),
2001 OP_RESULT = ht.TNone
2004 class OpTagsDel(OpCode):
2005 """Remove a list of tags from a given object."""
2009 # Name is only meaningful for groups, nodes and instances
2010 ("name", ht.NoDefault, ht.TMaybeString,
2011 "Name of object where tag(s) should be deleted"),
2013 OP_RESULT = ht.TNone
2017 class OpTestDelay(OpCode):
2018 """Sleeps for a configured amount of time.
2020 This is used just for debugging and testing.
2023 - duration: the time to sleep, in seconds
2024 - on_master: if true, sleep on the master
2025 - on_nodes: list of nodes in which to sleep
2027 If the on_master parameter is true, it will execute a sleep on the
2028 master (before any node sleep).
2030 If the on_nodes list is not empty, it will sleep on those nodes
2031 (after the sleep on the master, if that is enabled).
2033 As an additional feature, the case of duration < 0 will be reported
2034 as an execution error, so this opcode can be used as a failure
2035 generator. The case of duration == 0 will not be treated specially.
2038 OP_DSC_FIELD = "duration"
2040 ("duration", ht.NoDefault, ht.TNumber, None),
2041 ("on_master", True, ht.TBool, None),
2042 ("on_nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
2043 ("on_node_uuids", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
2044 ("repeat", 0, ht.TNonNegativeInt, None),
2047 def OP_DSC_FORMATTER(self, value): # pylint: disable=C0103,R0201
2048 """Custom formatter for duration.
2058 class OpTestAllocator(OpCode):
2059 """Allocator framework testing.
2061 This opcode has two modes:
2062 - gather and return allocator input for a given mode (allocate new
2063 or replace secondary) and a given instance definition (direction
2065 - run a selected allocator for a given operation (as above) and
2066 return the allocator output (direction 'out')
2069 OP_DSC_FIELD = "iallocator"
2071 ("direction", ht.NoDefault,
2072 ht.TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS), None),
2073 ("mode", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_MODES), None),
2074 ("name", ht.NoDefault, ht.TNonEmptyString, None),
2075 ("nics", ht.NoDefault,
2076 ht.TMaybeListOf(ht.TDictOf(ht.TElemOf([constants.INIC_MAC,
2081 ("disks", ht.NoDefault, ht.TMaybe(ht.TList), None),
2082 ("hypervisor", None, ht.TMaybeString, None),
2083 _PIAllocFromDesc(None),
2084 ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
2085 ("memory", None, ht.TMaybe(ht.TNonNegativeInt), None),
2086 ("vcpus", None, ht.TMaybe(ht.TNonNegativeInt), None),
2087 ("os", None, ht.TMaybeString, None),
2088 ("disk_template", None, ht.TMaybeString, None),
2089 ("instances", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
2091 ht.TMaybe(ht.TElemOf(constants.IALLOCATOR_NEVAC_MODES)), None),
2092 ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
2093 ("spindle_use", 1, ht.TNonNegativeInt, None),
2094 ("count", 1, ht.TNonNegativeInt, None),
2098 class OpTestJqueue(OpCode):
2099 """Utility opcode to test some aspects of the job queue.
2103 ("notify_waitlock", False, ht.TBool, None),
2104 ("notify_exec", False, ht.TBool, None),
2105 ("log_messages", ht.EmptyList, ht.TListOf(ht.TString), None),
2106 ("fail", False, ht.TBool, None),
2110 class OpTestDummy(OpCode):
2111 """Utility opcode used by unittests.
2115 ("result", ht.NoDefault, ht.NoType, None),
2116 ("messages", ht.NoDefault, ht.NoType, None),
2117 ("fail", ht.NoDefault, ht.NoType, None),
2118 ("submit_jobs", None, ht.NoType, None),
2124 # Add a new network in the cluster
2125 class OpNetworkAdd(OpCode):
2126 """Add an IP network to the cluster."""
2127 OP_DSC_FIELD = "network_name"
2130 ("network", ht.NoDefault, _TIpNetwork4, "IPv4 subnet"),
2131 ("gateway", None, ht.TMaybe(_TIpAddress4), "IPv4 gateway"),
2132 ("network6", None, ht.TMaybe(_TIpNetwork6), "IPv6 subnet"),
2133 ("gateway6", None, ht.TMaybe(_TIpAddress6), "IPv6 gateway"),
2134 ("mac_prefix", None, ht.TMaybeString,
2135 "MAC address prefix that overrides cluster one"),
2136 ("add_reserved_ips", None, _TMaybeAddr4List,
2137 "Which IP addresses to reserve"),
2138 ("conflicts_check", True, ht.TBool,
2139 "Whether to check for conflicting IP addresses"),
2140 ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Network tags"),
2142 OP_RESULT = ht.TNone
2145 class OpNetworkRemove(OpCode):
2146 """Remove an existing network from the cluster.
2147 Must not be connected to any nodegroup.
2150 OP_DSC_FIELD = "network_name"
2155 OP_RESULT = ht.TNone
2158 class OpNetworkSetParams(OpCode):
2159 """Modify Network's parameters except for IPv4 subnet"""
2160 OP_DSC_FIELD = "network_name"
2163 ("gateway", None, ht.TMaybeValueNone(_TIpAddress4), "IPv4 gateway"),
2164 ("network6", None, ht.TMaybeValueNone(_TIpNetwork6), "IPv6 subnet"),
2165 ("gateway6", None, ht.TMaybeValueNone(_TIpAddress6), "IPv6 gateway"),
2166 ("mac_prefix", None, ht.TMaybeValueNone(ht.TString),
2167 "MAC address prefix that overrides cluster one"),
2168 ("add_reserved_ips", None, _TMaybeAddr4List,
2169 "Which external IP addresses to reserve"),
2170 ("remove_reserved_ips", None, _TMaybeAddr4List,
2171 "Which external IP addresses to release"),
2173 OP_RESULT = ht.TNone
2176 class OpNetworkConnect(OpCode):
2177 """Connect a Network to a specific Nodegroup with the defined netparams
2178 (mode, link). Nics in this Network will inherit those params.
2179 Produce errors if a NIC (that its not already assigned to a network)
2180 has an IP that is contained in the Network this will produce error unless
2181 --no-conflicts-check is passed.
2184 OP_DSC_FIELD = "network_name"
2188 ("network_mode", ht.NoDefault, ht.TElemOf(constants.NIC_VALID_MODES),
2189 "Connectivity mode"),
2190 ("network_link", ht.NoDefault, ht.TString, "Connectivity link"),
2191 ("conflicts_check", True, ht.TBool, "Whether to check for conflicting IPs"),
2193 OP_RESULT = ht.TNone
2196 class OpNetworkDisconnect(OpCode):
2197 """Disconnect a Network from a Nodegroup. Produce errors if NICs are
2198 present in the Network unless --no-conficts-check option is passed.
2201 OP_DSC_FIELD = "network_name"
2206 OP_RESULT = ht.TNone
2209 class OpNetworkQuery(OpCode):
2210 """Compute the list of networks."""
2214 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
2215 "Empty list to query all groups, group names otherwise"),
2217 OP_RESULT = _TOldQueryResult
2221 """Returns list of all defined opcodes.
2223 Does not eliminate duplicates by C{OP_ID}.
2226 return [v for v in globals().values()
2227 if (isinstance(v, type) and issubclass(v, OpCode) and
2228 hasattr(v, "OP_ID") and v is not OpCode)]
2231 OP_MAPPING = dict((v.OP_ID, v) for v in _GetOpList())