4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 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 objectutils
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 #: Whether to ignore offline nodes
66 _PIgnoreOfflineNodes = ("ignore_offline_nodes", False, ht.TBool,
67 "Whether to ignore offline nodes")
69 #: a required node name (for single-node LUs)
70 _PNodeName = ("node_name", ht.NoDefault, ht.TNonEmptyString, "Node name")
72 #: a required node group name (for single-group LUs)
73 _PGroupName = ("group_name", ht.NoDefault, ht.TNonEmptyString, "Group name")
75 #: Migration type (live/non-live)
76 _PMigrationMode = ("mode", None,
77 ht.TMaybe(ht.TElemOf(constants.HT_MIGRATION_MODES)),
80 #: Obsolete 'live' migration mode (boolean)
81 _PMigrationLive = ("live", None, ht.TMaybeBool,
82 "Legacy setting for live migration, do not use")
85 _PTagKind = ("kind", ht.NoDefault, ht.TElemOf(constants.VALID_TAG_TYPES),
88 #: List of tag strings
89 _PTags = ("tags", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
92 _PForceVariant = ("force_variant", False, ht.TBool,
93 "Whether to force an unknown OS variant")
95 _PWaitForSync = ("wait_for_sync", True, ht.TBool,
96 "Whether to wait for the disk to synchronize")
98 _PWaitForSyncFalse = ("wait_for_sync", False, ht.TBool,
99 "Whether to wait for the disk to synchronize"
100 " (defaults to false)")
102 _PIgnoreConsistency = ("ignore_consistency", False, ht.TBool,
103 "Whether to ignore disk consistency")
105 _PStorageName = ("name", ht.NoDefault, ht.TMaybeString, "Storage name")
107 _PUseLocking = ("use_locking", False, ht.TBool,
108 "Whether to use synchronization")
110 _PNameCheck = ("name_check", True, ht.TBool, "Whether to check name")
112 _PNodeGroupAllocPolicy = \
113 ("alloc_policy", None,
114 ht.TMaybe(ht.TElemOf(constants.VALID_ALLOC_POLICIES)),
115 "Instance allocation policy")
117 _PGroupNodeParams = ("ndparams", None, ht.TMaybeDict,
118 "Default node parameters for group")
120 _PQueryWhat = ("what", ht.NoDefault, ht.TElemOf(constants.QR_VIA_OP),
121 "Resource(s) to query for")
123 _PEarlyRelease = ("early_release", False, ht.TBool,
124 "Whether to release locks as soon as possible")
126 _PIpCheckDoc = "Whether to ensure instance's IP address is inactive"
128 #: Do not remember instance state changes
129 _PNoRemember = ("no_remember", False, ht.TBool,
130 "Do not remember the state change")
132 #: Target node for instance migration/failover
133 _PMigrationTargetNode = ("target_node", None, ht.TMaybeString,
134 "Target node for shared-storage instances")
136 _PStartupPaused = ("startup_paused", False, ht.TBool,
137 "Pause instance at startup")
139 _PVerbose = ("verbose", False, ht.TBool, "Verbose mode")
141 # Parameters for cluster verification
142 _PDebugSimulateErrors = ("debug_simulate_errors", False, ht.TBool,
143 "Whether to simulate errors (useful for debugging)")
144 _PErrorCodes = ("error_codes", False, ht.TBool, "Error codes")
145 _PSkipChecks = ("skip_checks", ht.EmptyList,
146 ht.TListOf(ht.TElemOf(constants.VERIFY_OPTIONAL_CHECKS)),
147 "Which checks to skip")
148 _PIgnoreErrors = ("ignore_errors", ht.EmptyList,
149 ht.TListOf(ht.TElemOf(constants.CV_ALL_ECODES_STRINGS)),
150 "List of error codes that should be treated as warnings")
155 ht.TMaybe(ht.TDictOf(ht.TElemOf(constants.DISK_TEMPLATES), ht.TDict)),
156 "Disk templates' parameter defaults")
158 # Parameters for node resource model
159 _PHvState = ("hv_state", None, ht.TMaybeDict, "Set hypervisor states")
160 _PDiskState = ("disk_state", None, ht.TMaybeDict, "Set disk states")
162 #: Opportunistic locking
163 _POpportunisticLocking = \
164 ("opportunistic_locking", False, ht.TBool,
165 ("Whether to employ opportunistic locking for nodes, meaning nodes"
166 " already locked by another opcode won't be considered for instance"
167 " allocation (only when an iallocator is used)"))
169 _PIgnoreIpolicy = ("ignore_ipolicy", False, ht.TBool,
170 "Whether to ignore ipolicy violations")
172 # Allow runtime changes while migrating
173 _PAllowRuntimeChgs = ("allow_runtime_changes", True, ht.TBool,
174 "Allow runtime changes (eg. memory ballooning)")
176 #: IAllocator field builder
177 _PIAllocFromDesc = lambda desc: ("iallocator", None, ht.TMaybeString, desc)
179 #: a required network name
180 _PNetworkName = ("network_name", ht.NoDefault, ht.TNonEmptyString,
184 ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString),
185 "Destination group names or UUIDs (defaults to \"all but current group\")")
187 #: OP_ID conversion regular expression
188 _OPID_RE = re.compile("([a-z])([A-Z])")
190 #: Utility function for L{OpClusterSetParams}
191 _TestClusterOsListItem = \
192 ht.TAnd(ht.TIsLength(2), ht.TItems([
193 ht.TElemOf(constants.DDMS_VALUES),
197 _TestClusterOsList = ht.TMaybeListOf(_TestClusterOsListItem)
199 # TODO: Generate check from constants.INIC_PARAMS_TYPES
200 #: Utility function for testing NIC definitions
202 ht.Comment("NIC parameters")(ht.TDictOf(ht.TElemOf(constants.INIC_PARAMS),
205 _TSetParamsResultItemItems = [
206 ht.Comment("name of changed parameter")(ht.TNonEmptyString),
207 ht.Comment("new value")(ht.TAny),
210 _TSetParamsResult = \
211 ht.TListOf(ht.TAnd(ht.TIsLength(len(_TSetParamsResultItemItems)),
212 ht.TItems(_TSetParamsResultItemItems)))
214 # In the disks option we can provide arbitrary parameters too, which
215 # we may not be able to validate at this level, so we just check the
216 # format of the dict here and the checks concerning IDISK_PARAMS will
217 # happen at the LU level
219 ht.Comment("Disk parameters")(ht.TDictOf(ht.TNonEmptyString,
220 ht.TOr(ht.TNonEmptyString, ht.TInt)))
223 ht.TListOf(ht.TAnd(ht.TIsLength(2),
224 ht.TItems([ht.TElemOf(constants.RS_ALL),
227 _TQueryResult = ht.TListOf(_TQueryRow)
229 _TOldQueryRow = ht.TListOf(ht.TAny)
231 _TOldQueryResult = ht.TListOf(_TOldQueryRow)
241 #: Attribute name for dependencies
242 DEPEND_ATTR = "depends"
244 #: Attribute name for comment
245 COMMENT_ATTR = "comment"
249 """Convert an opcode class name to an OP_ID.
252 @param name: the class name, as OpXxxYyy
254 @return: the name in the OP_XXXX_YYYY format
257 if not name.startswith("Op"):
259 # Note: (?<=[a-z])(?=[A-Z]) would be ideal, since it wouldn't
260 # consume any input, and hence we would just have all the elements
261 # in the list, one by one; but it seems that split doesn't work on
262 # non-consuming input, hence we have to process the input string a
264 name = _OPID_RE.sub(r"\1,\2", name)
265 elems = name.split(",")
266 return "_".join(n.upper() for n in elems)
269 def _GenerateObjectTypeCheck(obj, fields_types):
270 """Helper to generate type checks for objects.
272 @param obj: The object to generate type checks
273 @param fields_types: The fields and their types as a dict
274 @return: A ht type check function
277 assert set(obj.GetAllSlots()) == set(fields_types.keys()), \
278 "%s != %s" % (set(obj.GetAllSlots()), set(fields_types.keys()))
279 return ht.TStrictDict(True, True, fields_types)
283 _GenerateObjectTypeCheck(objects.QueryFieldDefinition, {
284 "name": ht.TNonEmptyString,
285 "title": ht.TNonEmptyString,
286 "kind": ht.TElemOf(constants.QFT_ALL),
287 "doc": ht.TNonEmptyString,
291 def RequireFileStorage():
292 """Checks that file storage is enabled.
294 While it doesn't really fit into this module, L{utils} was deemed too large
295 of a dependency to be imported for just one or two functions.
297 @raise errors.OpPrereqError: when file storage is disabled
300 if not constants.ENABLE_FILE_STORAGE:
301 raise errors.OpPrereqError("File storage disabled at configure time",
305 def RequireSharedFileStorage():
306 """Checks that shared file storage is enabled.
308 While it doesn't really fit into this module, L{utils} was deemed too large
309 of a dependency to be imported for just one or two functions.
311 @raise errors.OpPrereqError: when shared file storage is disabled
314 if not constants.ENABLE_SHARED_FILE_STORAGE:
315 raise errors.OpPrereqError("Shared file storage disabled at"
316 " configure time", errors.ECODE_INVAL)
319 @ht.WithDesc("CheckFileStorage")
320 def _CheckFileStorage(value):
321 """Ensures file storage is enabled if used.
324 if value == constants.DT_FILE:
326 elif value == constants.DT_SHARED_FILE:
327 RequireSharedFileStorage()
331 def _BuildDiskTemplateCheck(accept_none):
332 """Builds check for disk template.
334 @type accept_none: bool
335 @param accept_none: whether to accept None as a correct value
339 template_check = ht.TElemOf(constants.DISK_TEMPLATES)
342 template_check = ht.TMaybe(template_check)
344 return ht.TAnd(template_check, _CheckFileStorage)
347 def _CheckStorageType(storage_type):
348 """Ensure a given storage type is valid.
351 if storage_type not in constants.VALID_STORAGE_TYPES:
352 raise errors.OpPrereqError("Unknown storage type: %s" % storage_type,
354 if storage_type == constants.ST_FILE:
355 # TODO: What about shared file storage?
360 #: Storage type parameter
361 _PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType,
364 _CheckNetworkType = ht.TElemOf(constants.NETWORK_VALID_TYPES)
367 @ht.WithDesc("IPv4 network")
368 def _CheckCIDRNetNotation(value):
369 """Ensure a given CIDR notation type is valid.
373 ipaddr.IPv4Network(value)
374 except ipaddr.AddressValueError:
379 @ht.WithDesc("IPv4 address")
380 def _CheckCIDRAddrNotation(value):
381 """Ensure a given CIDR notation type is valid.
385 ipaddr.IPv4Address(value)
386 except ipaddr.AddressValueError:
391 @ht.WithDesc("IPv6 address")
392 def _CheckCIDR6AddrNotation(value):
393 """Ensure a given CIDR notation type is valid.
397 ipaddr.IPv6Address(value)
398 except ipaddr.AddressValueError:
403 @ht.WithDesc("IPv6 network")
404 def _CheckCIDR6NetNotation(value):
405 """Ensure a given CIDR notation type is valid.
409 ipaddr.IPv6Network(value)
410 except ipaddr.AddressValueError:
415 _TIpAddress4 = ht.TAnd(ht.TString, _CheckCIDRAddrNotation)
416 _TIpAddress6 = ht.TAnd(ht.TString, _CheckCIDR6AddrNotation)
417 _TIpNetwork4 = ht.TAnd(ht.TString, _CheckCIDRNetNotation)
418 _TIpNetwork6 = ht.TAnd(ht.TString, _CheckCIDR6NetNotation)
419 _TMaybeAddr4List = ht.TMaybe(ht.TListOf(_TIpAddress4))
422 class _AutoOpParamSlots(objectutils.AutoSlots):
423 """Meta class for opcode definitions.
426 def __new__(mcs, name, bases, attrs):
427 """Called when a class should be created.
429 @param mcs: The meta class
430 @param name: Name of created class
431 @param bases: Base classes
433 @param attrs: Class attributes
436 assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
438 slots = mcs._GetSlots(attrs)
439 assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
440 "Class '%s' uses unknown field in OP_DSC_FIELD" % name
441 assert ("OP_DSC_FORMATTER" not in attrs or
442 callable(attrs["OP_DSC_FORMATTER"])), \
443 ("Class '%s' uses non-callable in OP_DSC_FORMATTER (%s)" %
444 (name, type(attrs["OP_DSC_FORMATTER"])))
446 attrs["OP_ID"] = _NameToId(name)
448 return objectutils.AutoSlots.__new__(mcs, name, bases, attrs)
451 def _GetSlots(mcs, attrs):
452 """Build the slots out of OP_PARAMS.
455 # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams
456 params = attrs.setdefault("OP_PARAMS", [])
458 # Use parameter names as slots
459 return [pname for (pname, _, _, _) in params]
462 class BaseOpCode(objectutils.ValidatedSlots):
463 """A simple serializable object.
465 This object serves as a parent class for OpCode without any custom
469 # pylint: disable=E1101
470 # as OP_ID is dynamically defined
471 __metaclass__ = _AutoOpParamSlots
473 def __getstate__(self):
474 """Generic serializer.
476 This method just returns the contents of the instance as a
480 @return: the instance attributes and their values
484 for name in self.GetAllSlots():
485 if hasattr(self, name):
486 state[name] = getattr(self, name)
489 def __setstate__(self, state):
490 """Generic unserializer.
492 This method just restores from the serialized state the attributes
493 of the current instance.
495 @param state: the serialized opcode data
499 if not isinstance(state, dict):
500 raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
503 for name in self.GetAllSlots():
504 if name not in state and hasattr(self, name):
508 setattr(self, name, state[name])
511 def GetAllParams(cls):
512 """Compute list of all parameters for an opcode.
516 for parent in cls.__mro__:
517 slots.extend(getattr(parent, "OP_PARAMS", []))
520 def Validate(self, set_defaults): # pylint: disable=W0221
521 """Validate opcode parameters, optionally setting default values.
523 @type set_defaults: bool
524 @param set_defaults: Whether to set default values
525 @raise errors.OpPrereqError: When a parameter value doesn't match
529 for (attr_name, default, test, _) in self.GetAllParams():
530 assert test == ht.NoType or callable(test)
532 if not hasattr(self, attr_name):
533 if default == ht.NoDefault:
534 raise errors.OpPrereqError("Required parameter '%s.%s' missing" %
535 (self.OP_ID, attr_name),
538 if callable(default):
542 setattr(self, attr_name, dval)
544 if test == ht.NoType:
548 if set_defaults or hasattr(self, attr_name):
549 attr_val = getattr(self, attr_name)
550 if not test(attr_val):
551 logging.error("OpCode %s, parameter %s, has invalid type %s/value"
552 " '%s' expecting type %s",
553 self.OP_ID, attr_name, type(attr_val), attr_val, test)
554 raise errors.OpPrereqError("Parameter '%s.%s' fails validation" %
555 (self.OP_ID, attr_name),
559 def _BuildJobDepCheck(relative):
560 """Builds check for job dependencies (L{DEPEND_ATTR}).
563 @param relative: Whether to accept relative job IDs (negative)
568 job_id = ht.TOr(ht.TJobId, ht.TRelativeJobId)
573 ht.TAnd(ht.TOr(ht.TList, ht.TTuple),
576 ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))]))
578 return ht.TMaybeListOf(job_dep)
581 TNoRelativeJobDependencies = _BuildJobDepCheck(False)
583 #: List of submission status and job ID as returned by C{SubmitManyJobs}
585 ht.TAnd(ht.TIsLength(2),
586 ht.TItems([ht.Comment("success")(ht.TBool),
587 ht.Comment("Job ID if successful, error message"
588 " otherwise")(ht.TOr(ht.TString,
590 TJobIdList = ht.TListOf(_TJobIdListItem)
592 #: Result containing only list of submitted jobs
593 TJobIdListOnly = ht.TStrictDict(True, True, {
594 constants.JOB_IDS_KEY: ht.Comment("List of submitted jobs")(TJobIdList),
598 class OpCode(BaseOpCode):
601 This is the root of the actual OpCode hierarchy. All clases derived
602 from this class should override OP_ID.
604 @cvar OP_ID: The ID of this opcode. This should be unique amongst all
605 children of this class.
606 @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
607 string returned by Summary(); see the docstring of that
609 @cvar OP_DSC_FORMATTER: A callable that should format the OP_DSC_FIELD; if
610 not present, then the field will be simply converted
612 @cvar OP_PARAMS: List of opcode attributes, the default values they should
613 get if not already defined, and types they must match.
614 @cvar OP_RESULT: Callable to verify opcode result
615 @cvar WITH_LU: Boolean that specifies whether this should be included in
616 mcpu's dispatch table
617 @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
619 @ivar priority: Opcode priority for queue
622 # pylint: disable=E1101
623 # as OP_ID is dynamically defined
626 ("dry_run", None, ht.TMaybeBool, "Run checks only, don't execute"),
627 ("debug_level", None, ht.TMaybe(ht.TNonNegativeInt), "Debug level"),
628 ("priority", constants.OP_PRIO_DEFAULT,
629 ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID), "Opcode priority"),
630 (DEPEND_ATTR, None, _BuildJobDepCheck(True),
631 "Job dependencies; if used through ``SubmitManyJobs`` relative (negative)"
632 " job IDs can be used; see :doc:`design document <design-chained-jobs>`"
634 (COMMENT_ATTR, None, ht.TMaybeString,
635 "Comment describing the purpose of the opcode"),
639 def __getstate__(self):
640 """Specialized getstate for opcodes.
642 This method adds to the state dictionary the OP_ID of the class,
643 so that on unload we can identify the correct class for
644 instantiating the opcode.
647 @return: the state as a dictionary
650 data = BaseOpCode.__getstate__(self)
651 data["OP_ID"] = self.OP_ID
655 def LoadOpCode(cls, data):
656 """Generic load opcode method.
658 The method identifies the correct opcode class from the dict-form
659 by looking for a OP_ID key, if this is not found, or its value is
660 not available in this module as a child of this class, we fail.
663 @param data: the serialized opcode
666 if not isinstance(data, dict):
667 raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
668 if "OP_ID" not in data:
669 raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
670 op_id = data["OP_ID"]
672 if op_id in OP_MAPPING:
673 op_class = OP_MAPPING[op_id]
675 raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
678 new_data = data.copy()
679 del new_data["OP_ID"]
680 op.__setstate__(new_data)
684 """Generates a summary description of this opcode.
686 The summary is the value of the OP_ID attribute (without the "OP_"
687 prefix), plus the value of the OP_DSC_FIELD attribute, if one was
688 defined; this field should allow to easily identify the operation
689 (for an instance creation job, e.g., it would be the instance
693 assert self.OP_ID is not None and len(self.OP_ID) > 3
694 # all OP_ID start with OP_, we remove that
696 field_name = getattr(self, "OP_DSC_FIELD", None)
698 field_value = getattr(self, field_name, None)
699 field_formatter = getattr(self, "OP_DSC_FORMATTER", None)
700 if callable(field_formatter):
701 field_value = field_formatter(field_value)
702 elif isinstance(field_value, (list, tuple)):
703 field_value = ",".join(str(i) for i in field_value)
704 txt = "%s(%s)" % (txt, field_value)
707 def TinySummary(self):
708 """Generates a compact summary description of the opcode.
711 assert self.OP_ID.startswith("OP_")
713 text = self.OP_ID[3:]
715 for (prefix, supplement) in _SUMMARY_PREFIX.items():
716 if text.startswith(prefix):
717 return supplement + text[len(prefix):]
724 class OpClusterPostInit(OpCode):
725 """Post cluster initialization.
727 This opcode does not touch the cluster at all. Its purpose is to run hooks
728 after the cluster has been initialized.
734 class OpClusterDestroy(OpCode):
735 """Destroy the cluster.
737 This opcode has no other parameters. All the state is irreversibly
738 lost after the execution of this opcode.
741 OP_RESULT = ht.TNonEmptyString
744 class OpClusterQuery(OpCode):
745 """Query cluster information."""
746 OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TAny)
749 class OpClusterVerify(OpCode):
750 """Submits all jobs necessary to verify the cluster.
754 _PDebugSimulateErrors,
759 ("group_name", None, ht.TMaybeString, "Group to verify"),
761 OP_RESULT = TJobIdListOnly
764 class OpClusterVerifyConfig(OpCode):
765 """Verify the cluster config.
769 _PDebugSimulateErrors,
777 class OpClusterVerifyGroup(OpCode):
778 """Run verify on a node group from the cluster.
780 @type skip_checks: C{list}
781 @ivar skip_checks: steps to be skipped from the verify process; this
782 needs to be a subset of
783 L{constants.VERIFY_OPTIONAL_CHECKS}; currently
784 only L{constants.VERIFY_NPLUSONE_MEM} can be passed
787 OP_DSC_FIELD = "group_name"
790 _PDebugSimulateErrors,
799 class OpClusterVerifyDisks(OpCode):
800 """Verify the cluster disks.
803 OP_RESULT = TJobIdListOnly
806 class OpGroupVerifyDisks(OpCode):
807 """Verifies the status of all disks in a node group.
809 Result: a tuple of three elements:
810 - dict of node names with issues (values: error msg)
811 - list of instances with degraded disks (that should be activated)
812 - dict of instances with missing logical volumes (values: (node, vol)
813 pairs with details about the missing volumes)
815 In normal operation, all lists should be empty. A non-empty instance
816 list (3rd element of the result) is still ok (errors were fixed) but
817 non-empty node list means some node is down, and probably there are
818 unfixable drbd errors.
820 Note that only instances that are drbd-based are taken into
821 consideration. This might need to be revisited in the future.
824 OP_DSC_FIELD = "group_name"
829 ht.TAnd(ht.TIsLength(3),
830 ht.TItems([ht.TDictOf(ht.TString, ht.TString),
831 ht.TListOf(ht.TString),
832 ht.TDictOf(ht.TString,
833 ht.TListOf(ht.TListOf(ht.TString)))]))
836 class OpClusterRepairDiskSizes(OpCode):
837 """Verify the disk sizes of the instances and fixes configuration
840 Parameters: optional instances list, in case we want to restrict the
841 checks to only a subset of the instances.
843 Result: a list of tuples, (instance, disk, new-size) for changed
846 In normal operation, the list should be empty.
848 @type instances: list
849 @ivar instances: the list of instances to check, or empty for all instances
853 ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
855 OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3),
856 ht.TItems([ht.TNonEmptyString,
858 ht.TNonNegativeInt])))
861 class OpClusterConfigQuery(OpCode):
862 """Query cluster configuration values."""
866 OP_RESULT = ht.TListOf(ht.TAny)
869 class OpClusterRename(OpCode):
870 """Rename the cluster.
873 @ivar name: The new name of the cluster. The name and/or the master IP
874 address will be changed to match the new name and its IP
878 OP_DSC_FIELD = "name"
880 ("name", ht.NoDefault, ht.TNonEmptyString, None),
882 OP_RESULT = ht.TNonEmptyString
885 class OpClusterSetParams(OpCode):
886 """Change the parameters of the cluster.
888 @type vg_name: C{str} or C{None}
889 @ivar vg_name: The new volume group name or None to disable LVM usage.
895 ("vg_name", None, ht.TMaybe(ht.TString), "Volume group name"),
896 ("enabled_hypervisors", None,
897 ht.TMaybe(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)),
899 "List of enabled hypervisors"),
901 ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)),
902 "Cluster-wide hypervisor parameter defaults, hypervisor-dependent"),
903 ("beparams", None, ht.TMaybeDict,
904 "Cluster-wide backend parameter defaults"),
905 ("os_hvp", None, ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)),
906 "Cluster-wide per-OS hypervisor parameter defaults"),
908 ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)),
909 "Cluster-wide OS parameter defaults"),
911 ("candidate_pool_size", None, ht.TMaybe(ht.TPositiveInt),
912 "Master candidate pool size"),
913 ("uid_pool", None, ht.NoType,
914 "Set UID pool, must be list of lists describing UID ranges (two items,"
915 " start and end inclusive)"),
916 ("add_uids", None, ht.NoType,
917 "Extend UID pool, must be list of lists describing UID ranges (two"
918 " items, start and end inclusive) to be added"),
919 ("remove_uids", None, ht.NoType,
920 "Shrink UID pool, must be list of lists describing UID ranges (two"
921 " items, start and end inclusive) to be removed"),
922 ("maintain_node_health", None, ht.TMaybeBool,
923 "Whether to automatically maintain node health"),
924 ("prealloc_wipe_disks", None, ht.TMaybeBool,
925 "Whether to wipe disks before allocating them to instances"),
926 ("nicparams", None, ht.TMaybeDict, "Cluster-wide NIC parameter defaults"),
927 ("ndparams", None, ht.TMaybeDict, "Cluster-wide node parameter defaults"),
928 ("ipolicy", None, ht.TMaybeDict,
929 "Cluster-wide :ref:`instance policy <rapi-ipolicy>` specs"),
930 ("drbd_helper", None, ht.TMaybe(ht.TString), "DRBD helper program"),
931 ("default_iallocator", None, ht.TMaybe(ht.TString),
932 "Default iallocator for cluster"),
933 ("master_netdev", None, ht.TMaybe(ht.TString),
934 "Master network device"),
935 ("master_netmask", None, ht.TMaybe(ht.TNonNegativeInt),
936 "Netmask of the master IP"),
937 ("reserved_lvs", None, ht.TMaybeListOf(ht.TNonEmptyString),
938 "List of reserved LVs"),
939 ("hidden_os", None, _TestClusterOsList,
940 "Modify list of hidden operating systems: each modification must have"
941 " two items, the operation and the OS name; the operation can be"
942 " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)),
943 ("blacklisted_os", None, _TestClusterOsList,
944 "Modify list of blacklisted operating systems: each modification must"
945 " have two items, the operation and the OS name; the operation can be"
946 " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)),
947 ("use_external_mip_script", None, ht.TMaybeBool,
948 "Whether to use an external master IP address setup script"),
953 class OpClusterRedistConf(OpCode):
954 """Force a full push of the cluster configuration.
960 class OpClusterActivateMasterIp(OpCode):
961 """Activate the master IP on the master node.
967 class OpClusterDeactivateMasterIp(OpCode):
968 """Deactivate the master IP on the master node.
974 class OpQuery(OpCode):
975 """Query for resources/items.
977 @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
978 @ivar fields: List of fields to retrieve
979 @ivar qfilter: Query filter
982 OP_DSC_FIELD = "what"
986 ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
988 ("qfilter", None, ht.TMaybe(ht.TList),
992 _GenerateObjectTypeCheck(objects.QueryResponse, {
993 "fields": ht.TListOf(_TQueryFieldDef),
994 "data": _TQueryResult,
998 class OpQueryFields(OpCode):
999 """Query for available resource/item fields.
1001 @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
1002 @ivar fields: List of fields to retrieve
1005 OP_DSC_FIELD = "what"
1008 ("fields", None, ht.TMaybeListOf(ht.TNonEmptyString),
1009 "Requested fields; if not given, all are returned"),
1012 _GenerateObjectTypeCheck(objects.QueryFieldsResponse, {
1013 "fields": ht.TListOf(_TQueryFieldDef),
1017 class OpOobCommand(OpCode):
1018 """Interact with OOB."""
1020 ("node_names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1021 "List of nodes to run the OOB command against"),
1022 ("command", ht.NoDefault, ht.TElemOf(constants.OOB_COMMANDS),
1023 "OOB command to be run"),
1024 ("timeout", constants.OOB_TIMEOUT, ht.TInt,
1025 "Timeout before the OOB helper will be terminated"),
1026 ("ignore_status", False, ht.TBool,
1027 "Ignores the node offline status for power off"),
1028 ("power_delay", constants.OOB_POWER_DELAY, ht.TNonNegativeFloat,
1029 "Time in seconds to wait between powering on nodes"),
1031 # Fixme: Make it more specific with all the special cases in LUOobCommand
1032 OP_RESULT = _TQueryResult
1035 class OpRestrictedCommand(OpCode):
1036 """Runs a restricted command on node(s).
1041 ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
1042 "Nodes on which the command should be run (at least one)"),
1043 ("command", ht.NoDefault, ht.TNonEmptyString,
1044 "Command name (no parameters)"),
1048 ht.Comment("success")(ht.TBool),
1049 ht.Comment("output or error message")(ht.TString),
1053 ht.TListOf(ht.TAnd(ht.TIsLength(len(_RESULT_ITEMS)),
1054 ht.TItems(_RESULT_ITEMS)))
1059 class OpNodeRemove(OpCode):
1062 @type node_name: C{str}
1063 @ivar node_name: The name of the node to remove. If the node still has
1064 instances on it, the operation will fail.
1067 OP_DSC_FIELD = "node_name"
1071 OP_RESULT = ht.TNone
1074 class OpNodeAdd(OpCode):
1075 """Add a node to the cluster.
1077 @type node_name: C{str}
1078 @ivar node_name: The name of the node to add. This can be a short name,
1079 but it will be expanded to the FQDN.
1080 @type primary_ip: IP address
1081 @ivar primary_ip: The primary IP of the node. This will be ignored when the
1082 opcode is submitted, but will be filled during the node
1083 add (so it will be visible in the job query).
1084 @type secondary_ip: IP address
1085 @ivar secondary_ip: The secondary IP of the node. This needs to be passed
1086 if the cluster has been initialized in 'dual-network'
1087 mode, otherwise it must not be given.
1088 @type readd: C{bool}
1089 @ivar readd: Whether to re-add an existing node to the cluster. If
1090 this is not passed, then the operation will abort if the node
1091 name is already in the cluster; use this parameter to 'repair'
1092 a node that had its configuration broken, or was reinstalled
1093 without removal from the cluster.
1095 @ivar group: The node group to which this node will belong.
1096 @type vm_capable: C{bool}
1097 @ivar vm_capable: The vm_capable node attribute
1098 @type master_capable: C{bool}
1099 @ivar master_capable: The master_capable node attribute
1102 OP_DSC_FIELD = "node_name"
1107 ("primary_ip", None, ht.NoType, "Primary IP address"),
1108 ("secondary_ip", None, ht.TMaybeString, "Secondary IP address"),
1109 ("readd", False, ht.TBool, "Whether node is re-added to cluster"),
1110 ("group", None, ht.TMaybeString, "Initial node group"),
1111 ("master_capable", None, ht.TMaybeBool,
1112 "Whether node can become master or master candidate"),
1113 ("vm_capable", None, ht.TMaybeBool,
1114 "Whether node can host instances"),
1115 ("ndparams", None, ht.TMaybeDict, "Node parameters"),
1117 OP_RESULT = ht.TNone
1120 class OpNodeQuery(OpCode):
1121 """Compute the list of nodes."""
1125 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1126 "Empty list to query all nodes, node names otherwise"),
1128 OP_RESULT = _TOldQueryResult
1131 class OpNodeQueryvols(OpCode):
1132 """Get list of volumes on node."""
1135 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1136 "Empty list to query all nodes, node names otherwise"),
1138 OP_RESULT = ht.TListOf(ht.TAny)
1141 class OpNodeQueryStorage(OpCode):
1142 """Get information on storage for node(s)."""
1146 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "List of nodes"),
1147 ("name", None, ht.TMaybeString, "Storage name"),
1149 OP_RESULT = _TOldQueryResult
1152 class OpNodeModifyStorage(OpCode):
1153 """Modifies the properies of a storage unit"""
1154 OP_DSC_FIELD = "node_name"
1159 ("changes", ht.NoDefault, ht.TDict, "Requested changes"),
1161 OP_RESULT = ht.TNone
1164 class OpRepairNodeStorage(OpCode):
1165 """Repairs the volume group on a node."""
1166 OP_DSC_FIELD = "node_name"
1171 _PIgnoreConsistency,
1173 OP_RESULT = ht.TNone
1176 class OpNodeSetParams(OpCode):
1177 """Change the parameters of a node."""
1178 OP_DSC_FIELD = "node_name"
1184 ("master_candidate", None, ht.TMaybeBool,
1185 "Whether the node should become a master candidate"),
1186 ("offline", None, ht.TMaybeBool,
1187 "Whether the node should be marked as offline"),
1188 ("drained", None, ht.TMaybeBool,
1189 "Whether the node should be marked as drained"),
1190 ("auto_promote", False, ht.TBool,
1191 "Whether node(s) should be promoted to master candidate if necessary"),
1192 ("master_capable", None, ht.TMaybeBool,
1193 "Denote whether node can become master or master candidate"),
1194 ("vm_capable", None, ht.TMaybeBool,
1195 "Denote whether node can host instances"),
1196 ("secondary_ip", None, ht.TMaybeString,
1197 "Change node's secondary IP address"),
1198 ("ndparams", None, ht.TMaybeDict, "Set node parameters"),
1199 ("powered", None, ht.TMaybeBool,
1200 "Whether the node should be marked as powered"),
1202 OP_RESULT = _TSetParamsResult
1205 class OpNodePowercycle(OpCode):
1206 """Tries to powercycle a node."""
1207 OP_DSC_FIELD = "node_name"
1212 OP_RESULT = ht.TMaybeString
1215 class OpNodeMigrate(OpCode):
1216 """Migrate all instances from a node."""
1217 OP_DSC_FIELD = "node_name"
1222 _PMigrationTargetNode,
1225 _PIAllocFromDesc("Iallocator for deciding the target node"
1226 " for shared-storage instances"),
1228 OP_RESULT = TJobIdListOnly
1231 class OpNodeEvacuate(OpCode):
1232 """Evacuate instances off a number of nodes."""
1233 OP_DSC_FIELD = "node_name"
1237 ("remote_node", None, ht.TMaybeString, "New secondary node"),
1238 _PIAllocFromDesc("Iallocator for computing solution"),
1239 ("mode", ht.NoDefault, ht.TElemOf(constants.NODE_EVAC_MODES),
1240 "Node evacuation mode"),
1242 OP_RESULT = TJobIdListOnly
1247 class OpInstanceCreate(OpCode):
1248 """Create an instance.
1250 @ivar instance_name: Instance name
1251 @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES})
1252 @ivar source_handshake: Signed handshake from source (remote import only)
1253 @ivar source_x509_ca: Source X509 CA in PEM format (remote import only)
1254 @ivar source_instance_name: Previous name of instance (remote import only)
1255 @ivar source_shutdown_timeout: Shutdown timeout used for source instance
1256 (remote import only)
1259 OP_DSC_FIELD = "instance_name"
1266 _POpportunisticLocking,
1267 ("beparams", ht.EmptyDict, ht.TDict, "Backend parameters for instance"),
1268 ("disks", ht.NoDefault, ht.TListOf(_TDiskParams),
1269 "Disk descriptions, for example ``[{\"%s\": 100}, {\"%s\": 5}]``;"
1270 " each disk definition must contain a ``%s`` value and"
1271 " can contain an optional ``%s`` value denoting the disk access mode"
1273 (constants.IDISK_SIZE, constants.IDISK_SIZE, constants.IDISK_SIZE,
1274 constants.IDISK_MODE,
1275 " or ".join("``%s``" % i for i in sorted(constants.DISK_ACCESS_SET)))),
1276 ("disk_template", ht.NoDefault, _BuildDiskTemplateCheck(True),
1278 ("file_driver", None, ht.TMaybe(ht.TElemOf(constants.FILE_DRIVER)),
1279 "Driver for file-backed disks"),
1280 ("file_storage_dir", None, ht.TMaybeString,
1281 "Directory for storing file-backed disks"),
1282 ("hvparams", ht.EmptyDict, ht.TDict,
1283 "Hypervisor parameters for instance, hypervisor-dependent"),
1284 ("hypervisor", None, ht.TMaybeString, "Hypervisor"),
1285 _PIAllocFromDesc("Iallocator for deciding which node(s) to use"),
1286 ("identify_defaults", False, ht.TBool,
1287 "Reset instance parameters to default if equal"),
1288 ("ip_check", True, ht.TBool, _PIpCheckDoc),
1289 ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"),
1290 ("mode", ht.NoDefault, ht.TElemOf(constants.INSTANCE_CREATE_MODES),
1291 "Instance creation mode"),
1292 ("nics", ht.NoDefault, ht.TListOf(_TestNicDef),
1293 "List of NIC (network interface) definitions, for example"
1294 " ``[{}, {}, {\"%s\": \"198.51.100.4\"}]``; each NIC definition can"
1295 " contain the optional values %s" %
1297 ", ".join("``%s``" % i for i in sorted(constants.INIC_PARAMS)))),
1298 ("no_install", None, ht.TMaybeBool,
1299 "Do not install the OS (will disable automatic start)"),
1300 ("osparams", ht.EmptyDict, ht.TDict, "OS parameters for instance"),
1301 ("os_type", None, ht.TMaybeString, "Operating system"),
1302 ("pnode", None, ht.TMaybeString, "Primary node"),
1303 ("snode", None, ht.TMaybeString, "Secondary node"),
1304 ("source_handshake", None, ht.TMaybe(ht.TList),
1305 "Signed handshake from source (remote import only)"),
1306 ("source_instance_name", None, ht.TMaybeString,
1307 "Source instance name (remote import only)"),
1308 ("source_shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT,
1310 "How long source instance was given to shut down (remote import only)"),
1311 ("source_x509_ca", None, ht.TMaybeString,
1312 "Source X509 CA in PEM format (remote import only)"),
1313 ("src_node", None, ht.TMaybeString, "Source node for import"),
1314 ("src_path", None, ht.TMaybeString, "Source directory for import"),
1315 ("start", True, ht.TBool, "Whether to start instance after creation"),
1316 ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Instance tags"),
1318 OP_RESULT = ht.Comment("instance nodes")(ht.TListOf(ht.TNonEmptyString))
1321 class OpInstanceMultiAlloc(OpCode):
1322 """Allocates multiple instances.
1326 _POpportunisticLocking,
1327 _PIAllocFromDesc("Iallocator used to allocate all the instances"),
1328 ("instances", ht.EmptyList, ht.TListOf(ht.TInstanceOf(OpInstanceCreate)),
1329 "List of instance create opcodes describing the instances to allocate"),
1331 _JOB_LIST = ht.Comment("List of submitted jobs")(TJobIdList)
1332 ALLOCATABLE_KEY = "allocatable"
1333 FAILED_KEY = "allocatable"
1334 OP_RESULT = ht.TStrictDict(True, True, {
1335 constants.JOB_IDS_KEY: _JOB_LIST,
1336 ALLOCATABLE_KEY: ht.TListOf(ht.TNonEmptyString),
1337 FAILED_KEY: ht.TListOf(ht.TNonEmptyString),
1340 def __getstate__(self):
1341 """Generic serializer.
1344 state = OpCode.__getstate__(self)
1345 if hasattr(self, "instances"):
1346 # pylint: disable=E1101
1347 state["instances"] = [inst.__getstate__() for inst in self.instances]
1350 def __setstate__(self, state):
1351 """Generic unserializer.
1353 This method just restores from the serialized state the attributes
1354 of the current instance.
1356 @param state: the serialized opcode data
1357 @type state: C{dict}
1360 if not isinstance(state, dict):
1361 raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
1364 if "instances" in state:
1365 state["instances"] = map(OpCode.LoadOpCode, state["instances"])
1367 return OpCode.__setstate__(self, state)
1369 def Validate(self, set_defaults):
1370 """Validates this opcode.
1372 We do this recursively.
1375 OpCode.Validate(self, set_defaults)
1377 for inst in self.instances: # pylint: disable=E1101
1378 inst.Validate(set_defaults)
1381 class OpInstanceReinstall(OpCode):
1382 """Reinstall an instance's OS."""
1383 OP_DSC_FIELD = "instance_name"
1387 ("os_type", None, ht.TMaybeString, "Instance operating system"),
1388 ("osparams", None, ht.TMaybeDict, "Temporary OS parameters"),
1390 OP_RESULT = ht.TNone
1393 class OpInstanceRemove(OpCode):
1394 """Remove an instance."""
1395 OP_DSC_FIELD = "instance_name"
1399 ("ignore_failures", False, ht.TBool,
1400 "Whether to ignore failures during removal"),
1402 OP_RESULT = ht.TNone
1405 class OpInstanceRename(OpCode):
1406 """Rename an instance."""
1410 ("new_name", ht.NoDefault, ht.TNonEmptyString, "New instance name"),
1411 ("ip_check", False, ht.TBool, _PIpCheckDoc),
1413 OP_RESULT = ht.Comment("New instance name")(ht.TNonEmptyString)
1416 class OpInstanceStartup(OpCode):
1417 """Startup an instance."""
1418 OP_DSC_FIELD = "instance_name"
1422 _PIgnoreOfflineNodes,
1423 ("hvparams", ht.EmptyDict, ht.TDict,
1424 "Temporary hypervisor parameters, hypervisor-dependent"),
1425 ("beparams", ht.EmptyDict, ht.TDict, "Temporary backend parameters"),
1429 OP_RESULT = ht.TNone
1432 class OpInstanceShutdown(OpCode):
1433 """Shutdown an instance."""
1434 OP_DSC_FIELD = "instance_name"
1438 _PIgnoreOfflineNodes,
1439 ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TNonNegativeInt,
1440 "How long to wait for instance to shut down"),
1443 OP_RESULT = ht.TNone
1446 class OpInstanceReboot(OpCode):
1447 """Reboot an instance."""
1448 OP_DSC_FIELD = "instance_name"
1452 ("ignore_secondaries", False, ht.TBool,
1453 "Whether to start the instance even if secondary disks are failing"),
1454 ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES),
1455 "How to reboot instance"),
1457 OP_RESULT = ht.TNone
1460 class OpInstanceReplaceDisks(OpCode):
1461 """Replace the disks of an instance."""
1462 OP_DSC_FIELD = "instance_name"
1467 ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES),
1468 "Replacement mode"),
1469 ("disks", ht.EmptyList, ht.TListOf(ht.TNonNegativeInt),
1471 ("remote_node", None, ht.TMaybeString, "New secondary node"),
1472 _PIAllocFromDesc("Iallocator for deciding new secondary node"),
1474 OP_RESULT = ht.TNone
1477 class OpInstanceFailover(OpCode):
1478 """Failover an instance."""
1479 OP_DSC_FIELD = "instance_name"
1483 _PIgnoreConsistency,
1484 _PMigrationTargetNode,
1486 _PIAllocFromDesc("Iallocator for deciding the target node for"
1487 " shared-storage instances"),
1489 OP_RESULT = ht.TNone
1492 class OpInstanceMigrate(OpCode):
1493 """Migrate an instance.
1495 This migrates (without shutting down an instance) to its secondary
1498 @ivar instance_name: the name of the instance
1499 @ivar mode: the migration mode (live, non-live or None for auto)
1502 OP_DSC_FIELD = "instance_name"
1507 _PMigrationTargetNode,
1510 ("cleanup", False, ht.TBool,
1511 "Whether a previously failed migration should be cleaned up"),
1512 _PIAllocFromDesc("Iallocator for deciding the target node for"
1513 " shared-storage instances"),
1514 ("allow_failover", False, ht.TBool,
1515 "Whether we can fallback to failover if migration is not possible"),
1517 OP_RESULT = ht.TNone
1520 class OpInstanceMove(OpCode):
1521 """Move an instance.
1523 This move (with shutting down an instance and data copying) to an
1526 @ivar instance_name: the name of the instance
1527 @ivar target_node: the destination node
1530 OP_DSC_FIELD = "instance_name"
1535 ("target_node", ht.NoDefault, ht.TNonEmptyString, "Target node"),
1536 _PIgnoreConsistency,
1538 OP_RESULT = ht.TNone
1541 class OpInstanceConsole(OpCode):
1542 """Connect to an instance's console."""
1543 OP_DSC_FIELD = "instance_name"
1547 OP_RESULT = ht.TDict
1550 class OpInstanceActivateDisks(OpCode):
1551 """Activate an instance's disks."""
1552 OP_DSC_FIELD = "instance_name"
1555 ("ignore_size", False, ht.TBool, "Whether to ignore recorded size"),
1558 OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3),
1559 ht.TItems([ht.TNonEmptyString,
1561 ht.TNonEmptyString])))
1564 class OpInstanceDeactivateDisks(OpCode):
1565 """Deactivate an instance's disks."""
1566 OP_DSC_FIELD = "instance_name"
1571 OP_RESULT = ht.TNone
1574 class OpInstanceRecreateDisks(OpCode):
1575 """Recreate an instance's disks."""
1577 ht.TAnd(ht.TIsLength(2),
1578 ht.TItems([ht.Comment("Disk index")(ht.TNonNegativeInt),
1579 ht.Comment("Parameters")(_TDiskParams)]))
1581 OP_DSC_FIELD = "instance_name"
1584 ("disks", ht.EmptyList,
1585 ht.TOr(ht.TListOf(ht.TNonNegativeInt), ht.TListOf(_TDiskChanges)),
1586 "List of disk indexes (deprecated) or a list of tuples containing a disk"
1587 " index and a possibly empty dictionary with disk parameter changes"),
1588 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1589 "New instance nodes, if relocation is desired"),
1590 _PIAllocFromDesc("Iallocator for deciding new nodes"),
1592 OP_RESULT = ht.TNone
1595 class OpInstanceQuery(OpCode):
1596 """Compute the list of instances."""
1600 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1601 "Empty list to query all instances, instance names otherwise"),
1603 OP_RESULT = _TOldQueryResult
1606 class OpInstanceQueryData(OpCode):
1607 """Compute the run-time status of instances."""
1610 ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1612 ("static", False, ht.TBool,
1613 "Whether to only return configuration data without querying"
1616 OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TDict)
1619 def _TestInstSetParamsModList(fn):
1620 """Generates a check for modification lists.
1624 # TODO: Remove in version 2.8 including support in LUInstanceSetParams
1626 ht.TAnd(ht.TIsLength(2), ht.TItems([
1627 ht.TOr(ht.TElemOf(constants.DDMS_VALUES), ht.TNonNegativeInt),
1631 # New format, supporting adding/removing disks/NICs at arbitrary indices
1633 ht.TAnd(ht.TIsLength(3), ht.TItems([
1634 ht.TElemOf(constants.DDMS_VALUES_WITH_MODIFY),
1635 ht.Comment("Disk index, can be negative, e.g. -1 for last disk")(ht.TInt),
1639 return ht.TOr(ht.Comment("Recommended")(ht.TListOf(mod_item_fn)),
1640 ht.Comment("Deprecated")(ht.TListOf(old_mod_item_fn)))
1643 class OpInstanceSetParams(OpCode):
1644 """Change the parameters of an instance.
1647 TestNicModifications = _TestInstSetParamsModList(_TestNicDef)
1648 TestDiskModifications = _TestInstSetParamsModList(_TDiskParams)
1650 OP_DSC_FIELD = "instance_name"
1656 ("nics", ht.EmptyList, TestNicModifications,
1657 "List of NIC changes: each item is of the form ``(op, index, settings)``,"
1658 " ``op`` is one of ``%s``, ``%s`` or ``%s``, ``index`` can be either -1"
1659 " to refer to the last position, or a zero-based index number; a"
1660 " deprecated version of this parameter used the form ``(op, settings)``,"
1661 " where ``op`` can be ``%s`` to add a new NIC with the specified"
1662 " settings, ``%s`` to remove the last NIC or a number to modify the"
1663 " settings of the NIC with that index" %
1664 (constants.DDM_ADD, constants.DDM_MODIFY, constants.DDM_REMOVE,
1665 constants.DDM_ADD, constants.DDM_REMOVE)),
1666 ("disks", ht.EmptyList, TestDiskModifications,
1667 "List of disk changes; see ``nics``"),
1668 ("beparams", ht.EmptyDict, ht.TDict, "Per-instance backend parameters"),
1669 ("runtime_mem", None, ht.TMaybePositiveInt, "New runtime memory"),
1670 ("hvparams", ht.EmptyDict, ht.TDict,
1671 "Per-instance hypervisor parameters, hypervisor-dependent"),
1672 ("disk_template", None, ht.TMaybe(_BuildDiskTemplateCheck(False)),
1673 "Disk template for instance"),
1674 ("remote_node", None, ht.TMaybeString,
1675 "Secondary node (used when changing disk template)"),
1676 ("os_name", None, ht.TMaybeString,
1677 "Change the instance's OS without reinstalling the instance"),
1678 ("osparams", None, ht.TMaybeDict, "Per-instance OS parameters"),
1679 ("wait_for_sync", True, ht.TBool,
1680 "Whether to wait for the disk to synchronize, when changing template"),
1681 ("offline", None, ht.TMaybeBool, "Whether to mark instance as offline"),
1682 ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"),
1684 OP_RESULT = _TSetParamsResult
1687 class OpInstanceGrowDisk(OpCode):
1688 """Grow a disk of an instance."""
1689 OP_DSC_FIELD = "instance_name"
1693 ("disk", ht.NoDefault, ht.TInt, "Disk index"),
1694 ("amount", ht.NoDefault, ht.TNonNegativeInt,
1695 "Amount of disk space to add (megabytes)"),
1696 ("absolute", False, ht.TBool,
1697 "Whether the amount parameter is an absolute target or a relative one"),
1699 OP_RESULT = ht.TNone
1702 class OpInstanceChangeGroup(OpCode):
1703 """Moves an instance to another node group."""
1704 OP_DSC_FIELD = "instance_name"
1708 _PIAllocFromDesc("Iallocator for computing solution"),
1711 OP_RESULT = TJobIdListOnly
1714 # Node group opcodes
1716 class OpGroupAdd(OpCode):
1717 """Add a node group to the cluster."""
1718 OP_DSC_FIELD = "group_name"
1721 _PNodeGroupAllocPolicy,
1726 ("ipolicy", None, ht.TMaybeDict,
1727 "Group-wide :ref:`instance policy <rapi-ipolicy>` specs"),
1729 OP_RESULT = ht.TNone
1732 class OpGroupAssignNodes(OpCode):
1733 """Assign nodes to a node group."""
1734 OP_DSC_FIELD = "group_name"
1738 ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
1739 "List of nodes to assign"),
1741 OP_RESULT = ht.TNone
1744 class OpGroupQuery(OpCode):
1745 """Compute the list of node groups."""
1748 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1749 "Empty list to query all groups, group names otherwise"),
1751 OP_RESULT = _TOldQueryResult
1754 class OpGroupSetParams(OpCode):
1755 """Change the parameters of a node group."""
1756 OP_DSC_FIELD = "group_name"
1759 _PNodeGroupAllocPolicy,
1764 ("ipolicy", None, ht.TMaybeDict, "Group-wide instance policy specs"),
1766 OP_RESULT = _TSetParamsResult
1769 class OpGroupRemove(OpCode):
1770 """Remove a node group from the cluster."""
1771 OP_DSC_FIELD = "group_name"
1775 OP_RESULT = ht.TNone
1778 class OpGroupRename(OpCode):
1779 """Rename a node group in the cluster."""
1782 ("new_name", ht.NoDefault, ht.TNonEmptyString, "New group name"),
1784 OP_RESULT = ht.Comment("New group name")(ht.TNonEmptyString)
1787 class OpGroupEvacuate(OpCode):
1788 """Evacuate a node group in the cluster."""
1789 OP_DSC_FIELD = "group_name"
1793 _PIAllocFromDesc("Iallocator for computing solution"),
1796 OP_RESULT = TJobIdListOnly
1800 class OpOsDiagnose(OpCode):
1801 """Compute the list of guest operating systems."""
1804 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1805 "Which operating systems to diagnose"),
1807 OP_RESULT = _TOldQueryResult
1810 # ExtStorage opcodes
1811 class OpExtStorageDiagnose(OpCode):
1812 """Compute the list of external storage providers."""
1815 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1816 "Which ExtStorage Provider to diagnose"),
1818 OP_RESULT = _TOldQueryResult
1822 class OpBackupQuery(OpCode):
1823 """Compute the list of exported images."""
1826 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1827 "Empty list to query all nodes, node names otherwise"),
1829 OP_RESULT = ht.TDictOf(ht.TNonEmptyString,
1830 ht.TOr(ht.Comment("False on error")(ht.TBool),
1831 ht.TListOf(ht.TNonEmptyString)))
1834 class OpBackupPrepare(OpCode):
1835 """Prepares an instance export.
1837 @ivar instance_name: Instance name
1838 @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1841 OP_DSC_FIELD = "instance_name"
1844 ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES),
1847 OP_RESULT = ht.TMaybeDict
1850 class OpBackupExport(OpCode):
1851 """Export an instance.
1853 For local exports, the export destination is the node name. For
1854 remote exports, the export destination is a list of tuples, each
1855 consisting of hostname/IP address, port, magic, HMAC and HMAC
1856 salt. The HMAC is calculated using the cluster domain secret over
1857 the value "${index}:${hostname}:${port}". The destination X509 CA
1858 must be a signed certificate.
1860 @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1861 @ivar target_node: Export destination
1862 @ivar x509_key_name: X509 key to use (remote export only)
1863 @ivar destination_x509_ca: Destination X509 CA in PEM format (remote export
1867 OP_DSC_FIELD = "instance_name"
1871 # TODO: Rename target_node as it changes meaning for different export modes
1872 # (e.g. "destination")
1873 ("target_node", ht.NoDefault, ht.TOr(ht.TNonEmptyString, ht.TList),
1874 "Destination information, depends on export mode"),
1875 ("shutdown", True, ht.TBool, "Whether to shutdown instance before export"),
1876 ("remove_instance", False, ht.TBool,
1877 "Whether to remove instance after export"),
1878 ("ignore_remove_failures", False, ht.TBool,
1879 "Whether to ignore failures while removing instances"),
1880 ("mode", constants.EXPORT_MODE_LOCAL, ht.TElemOf(constants.EXPORT_MODES),
1882 ("x509_key_name", None, ht.TMaybe(ht.TList),
1883 "Name of X509 key (remote export only)"),
1884 ("destination_x509_ca", None, ht.TMaybeString,
1885 "Destination X509 CA (remote export only)"),
1888 ht.TAnd(ht.TIsLength(2), ht.TItems([
1889 ht.Comment("Finalizing status")(ht.TBool),
1890 ht.Comment("Status for every exported disk")(ht.TListOf(ht.TBool)),
1894 class OpBackupRemove(OpCode):
1895 """Remove an instance's export."""
1896 OP_DSC_FIELD = "instance_name"
1900 OP_RESULT = ht.TNone
1904 class OpTagsGet(OpCode):
1905 """Returns the tags of the given object."""
1906 OP_DSC_FIELD = "name"
1909 # Not using _PUseLocking as the default is different for historical reasons
1910 ("use_locking", True, ht.TBool, "Whether to use synchronization"),
1911 # Name is only meaningful for nodes and instances
1912 ("name", ht.NoDefault, ht.TMaybeString,
1913 "Name of object to retrieve tags from"),
1915 OP_RESULT = ht.TListOf(ht.TNonEmptyString)
1918 class OpTagsSearch(OpCode):
1919 """Searches the tags in the cluster for a given pattern."""
1920 OP_DSC_FIELD = "pattern"
1922 ("pattern", ht.NoDefault, ht.TNonEmptyString,
1923 "Search pattern (regular expression)"),
1925 OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(2), ht.TItems([
1931 class OpTagsSet(OpCode):
1932 """Add a list of tags on a given object."""
1936 # Name is only meaningful for groups, nodes and instances
1937 ("name", ht.NoDefault, ht.TMaybeString,
1938 "Name of object where tag(s) should be added"),
1940 OP_RESULT = ht.TNone
1943 class OpTagsDel(OpCode):
1944 """Remove a list of tags from a given object."""
1948 # Name is only meaningful for groups, nodes and instances
1949 ("name", ht.NoDefault, ht.TMaybeString,
1950 "Name of object where tag(s) should be deleted"),
1952 OP_RESULT = ht.TNone
1956 class OpTestDelay(OpCode):
1957 """Sleeps for a configured amount of time.
1959 This is used just for debugging and testing.
1962 - duration: the time to sleep
1963 - on_master: if true, sleep on the master
1964 - on_nodes: list of nodes in which to sleep
1966 If the on_master parameter is true, it will execute a sleep on the
1967 master (before any node sleep).
1969 If the on_nodes list is not empty, it will sleep on those nodes
1970 (after the sleep on the master, if that is enabled).
1972 As an additional feature, the case of duration < 0 will be reported
1973 as an execution error, so this opcode can be used as a failure
1974 generator. The case of duration == 0 will not be treated specially.
1977 OP_DSC_FIELD = "duration"
1979 ("duration", ht.NoDefault, ht.TNumber, None),
1980 ("on_master", True, ht.TBool, None),
1981 ("on_nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
1982 ("repeat", 0, ht.TNonNegativeInt, None),
1985 def OP_DSC_FORMATTER(self, value): # pylint: disable=C0103,R0201
1986 """Custom formatter for duration.
1996 class OpTestAllocator(OpCode):
1997 """Allocator framework testing.
1999 This opcode has two modes:
2000 - gather and return allocator input for a given mode (allocate new
2001 or replace secondary) and a given instance definition (direction
2003 - run a selected allocator for a given operation (as above) and
2004 return the allocator output (direction 'out')
2007 OP_DSC_FIELD = "iallocator"
2009 ("direction", ht.NoDefault,
2010 ht.TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS), None),
2011 ("mode", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_MODES), None),
2012 ("name", ht.NoDefault, ht.TNonEmptyString, None),
2013 ("nics", ht.NoDefault,
2014 ht.TMaybeListOf(ht.TDictOf(ht.TElemOf([constants.INIC_MAC,
2019 ("disks", ht.NoDefault, ht.TMaybe(ht.TList), None),
2020 ("hypervisor", None, ht.TMaybeString, None),
2021 _PIAllocFromDesc(None),
2022 ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
2023 ("memory", None, ht.TMaybe(ht.TNonNegativeInt), None),
2024 ("vcpus", None, ht.TMaybe(ht.TNonNegativeInt), None),
2025 ("os", None, ht.TMaybeString, None),
2026 ("disk_template", None, ht.TMaybeString, None),
2027 ("instances", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
2029 ht.TMaybe(ht.TElemOf(constants.IALLOCATOR_NEVAC_MODES)), None),
2030 ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
2031 ("spindle_use", 1, ht.TNonNegativeInt, None),
2032 ("count", 1, ht.TNonNegativeInt, None),
2036 class OpTestJqueue(OpCode):
2037 """Utility opcode to test some aspects of the job queue.
2041 ("notify_waitlock", False, ht.TBool, None),
2042 ("notify_exec", False, ht.TBool, None),
2043 ("log_messages", ht.EmptyList, ht.TListOf(ht.TString), None),
2044 ("fail", False, ht.TBool, None),
2048 class OpTestDummy(OpCode):
2049 """Utility opcode used by unittests.
2053 ("result", ht.NoDefault, ht.NoType, None),
2054 ("messages", ht.NoDefault, ht.NoType, None),
2055 ("fail", ht.NoDefault, ht.NoType, None),
2056 ("submit_jobs", None, ht.NoType, None),
2062 # Add a new network in the cluster
2063 class OpNetworkAdd(OpCode):
2064 """Add an IP network to the cluster."""
2065 OP_DSC_FIELD = "network_name"
2068 ("network_type", None, ht.TMaybe(_CheckNetworkType), "Network type"),
2069 ("network", ht.NoDefault, _TIpNetwork4, "IPv4 subnet"),
2070 ("gateway", None, ht.TMaybe(_TIpAddress4), "IPv4 gateway"),
2071 ("network6", None, ht.TMaybe(_TIpNetwork6), "IPv6 subnet"),
2072 ("gateway6", None, ht.TMaybe(_TIpAddress6), "IPv6 gateway"),
2073 ("mac_prefix", None, ht.TMaybeString,
2074 "MAC address prefix that overrides cluster one"),
2075 ("add_reserved_ips", None, _TMaybeAddr4List,
2076 "Which IP addresses to reserve"),
2077 ("conflicts_check", True, ht.TBool,
2078 "Whether to check for conflicting IP addresses"),
2079 ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Network tags"),
2081 OP_RESULT = ht.TNone
2084 class OpNetworkRemove(OpCode):
2085 """Remove an existing network from the cluster.
2086 Must not be connected to any nodegroup.
2089 OP_DSC_FIELD = "network_name"
2094 OP_RESULT = ht.TNone
2097 class OpNetworkSetParams(OpCode):
2098 """Modify Network's parameters except for IPv4 subnet"""
2099 OP_DSC_FIELD = "network_name"
2102 ("network_type", None, ht.TMaybeValueNone(_CheckNetworkType),
2104 ("gateway", None, ht.TMaybeValueNone(_TIpAddress4), "IPv4 gateway"),
2105 ("network6", None, ht.TMaybeValueNone(_TIpNetwork6), "IPv6 subnet"),
2106 ("gateway6", None, ht.TMaybeValueNone(_TIpAddress6), "IPv6 gateway"),
2107 ("mac_prefix", None, ht.TMaybeValueNone(ht.TString),
2108 "MAC address prefix that overrides cluster one"),
2109 ("add_reserved_ips", None, _TMaybeAddr4List,
2110 "Which external IP addresses to reserve"),
2111 ("remove_reserved_ips", None, _TMaybeAddr4List,
2112 "Which external IP addresses to release"),
2114 OP_RESULT = ht.TNone
2117 class OpNetworkConnect(OpCode):
2118 """Connect a Network to a specific Nodegroup with the defined netparams
2119 (mode, link). Nics in this Network will inherit those params.
2120 Produce errors if a NIC (that its not already assigned to a network)
2121 has an IP that is contained in the Network this will produce error unless
2122 --no-conflicts-check is passed.
2125 OP_DSC_FIELD = "network_name"
2129 ("network_mode", ht.NoDefault, ht.TElemOf(constants.NIC_VALID_MODES),
2130 "Connectivity mode"),
2131 ("network_link", ht.NoDefault, ht.TString, "Connectivity link"),
2132 ("conflicts_check", True, ht.TBool, "Whether to check for conflicting IPs"),
2134 OP_RESULT = ht.TNone
2137 class OpNetworkDisconnect(OpCode):
2138 """Disconnect a Network from a Nodegroup. Produce errors if NICs are
2139 present in the Network unless --no-conficts-check option is passed.
2142 OP_DSC_FIELD = "network_name"
2146 ("conflicts_check", True, ht.TBool, "Whether to check for conflicting IPs"),
2148 OP_RESULT = ht.TNone
2151 class OpNetworkQuery(OpCode):
2152 """Compute the list of networks."""
2156 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
2157 "Empty list to query all groups, group names otherwise"),
2159 OP_RESULT = _TOldQueryResult
2163 """Returns list of all defined opcodes.
2165 Does not eliminate duplicates by C{OP_ID}.
2168 return [v for v in globals().values()
2169 if (isinstance(v, type) and issubclass(v, OpCode) and
2170 hasattr(v, "OP_ID") and v is not OpCode)]
2173 OP_MAPPING = dict((v.OP_ID, v) for v in _GetOpList())