4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 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-msg=R0903
39 from ganeti import constants
40 from ganeti import errors
44 # Common opcode attributes
46 #: output fields for a query operation
47 _POutputFields = ("output_fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString))
49 #: the shutdown timeout
50 _PShutdownTimeout = ("shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT,
53 #: the force parameter
54 _PForce = ("force", False, ht.TBool)
56 #: a required instance name (for single-instance LUs)
57 _PInstanceName = ("instance_name", ht.NoDefault, ht.TNonEmptyString)
59 #: Whether to ignore offline nodes
60 _PIgnoreOfflineNodes = ("ignore_offline_nodes", False, ht.TBool)
62 #: a required node name (for single-node LUs)
63 _PNodeName = ("node_name", ht.NoDefault, ht.TNonEmptyString)
65 #: a required node group name (for single-group LUs)
66 _PGroupName = ("group_name", ht.NoDefault, ht.TNonEmptyString)
68 #: Migration type (live/non-live)
69 _PMigrationMode = ("mode", None,
70 ht.TOr(ht.TNone, ht.TElemOf(constants.HT_MIGRATION_MODES)))
72 #: Obsolete 'live' migration mode (boolean)
73 _PMigrationLive = ("live", None, ht.TMaybeBool)
76 _PTagKind = ("kind", ht.NoDefault, ht.TElemOf(constants.VALID_TAG_TYPES))
78 #: List of tag strings
79 _PTags = ("tags", ht.NoDefault, ht.TListOf(ht.TNonEmptyString))
81 #: OP_ID conversion regular expression
82 _OPID_RE = re.compile("([a-z])([A-Z])")
86 """Convert an opcode class name to an OP_ID.
89 @param name: the class name, as OpXxxYyy
91 @return: the name in the OP_XXXX_YYYY format
94 if not name.startswith("Op"):
96 # Note: (?<=[a-z])(?=[A-Z]) would be ideal, since it wouldn't
97 # consume any input, and hence we would just have all the elements
98 # in the list, one by one; but it seems that split doesn't work on
99 # non-consuming input, hence we have to process the input string a
101 name = _OPID_RE.sub(r"\1,\2", name)
102 elems = name.split(",")
103 return "_".join(n.upper() for n in elems)
106 def RequireFileStorage():
107 """Checks that file storage is enabled.
109 While it doesn't really fit into this module, L{utils} was deemed too large
110 of a dependency to be imported for just one or two functions.
112 @raise errors.OpPrereqError: when file storage is disabled
115 if not constants.ENABLE_FILE_STORAGE:
116 raise errors.OpPrereqError("File storage disabled at configure time",
120 def _CheckDiskTemplate(template):
121 """Ensure a given disk template is valid.
124 if template not in constants.DISK_TEMPLATES:
125 # Using str.join directly to avoid importing utils for CommaJoin
126 msg = ("Invalid disk template name '%s', valid templates are: %s" %
127 (template, ", ".join(constants.DISK_TEMPLATES)))
128 raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
129 if template == constants.DT_FILE:
134 def _CheckStorageType(storage_type):
135 """Ensure a given storage type is valid.
138 if storage_type not in constants.VALID_STORAGE_TYPES:
139 raise errors.OpPrereqError("Unknown storage type: %s" % storage_type,
141 if storage_type == constants.ST_FILE:
146 #: Storage type parameter
147 _PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType)
150 class _AutoOpParamSlots(type):
151 """Meta class for opcode definitions.
154 def __new__(mcs, name, bases, attrs):
155 """Called when a class should be created.
157 @param mcs: The meta class
158 @param name: Name of created class
159 @param bases: Base classes
161 @param attrs: Class attributes
164 assert "__slots__" not in attrs, \
165 "Class '%s' defines __slots__ when it should use OP_PARAMS" % name
166 assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
168 attrs["OP_ID"] = _NameToId(name)
170 # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams
171 params = attrs.setdefault("OP_PARAMS", [])
173 # Use parameter names as slots
174 slots = [pname for (pname, _, _) in params]
176 assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
177 "Class '%s' uses unknown field in OP_DSC_FIELD" % name
179 attrs["__slots__"] = slots
181 return type.__new__(mcs, name, bases, attrs)
184 class BaseOpCode(object):
185 """A simple serializable object.
187 This object serves as a parent class for OpCode without any custom
191 # pylint: disable-msg=E1101
192 # as OP_ID is dynamically defined
193 __metaclass__ = _AutoOpParamSlots
195 def __init__(self, **kwargs):
196 """Constructor for BaseOpCode.
198 The constructor takes only keyword arguments and will set
199 attributes on this object based on the passed arguments. As such,
200 it means that you should not pass arguments which are not in the
201 __slots__ attribute for this class.
204 slots = self._all_slots()
207 raise TypeError("Object %s doesn't support the parameter '%s'" %
208 (self.__class__.__name__, key))
209 setattr(self, key, kwargs[key])
211 def __getstate__(self):
212 """Generic serializer.
214 This method just returns the contents of the instance as a
218 @return: the instance attributes and their values
222 for name in self._all_slots():
223 if hasattr(self, name):
224 state[name] = getattr(self, name)
227 def __setstate__(self, state):
228 """Generic unserializer.
230 This method just restores from the serialized state the attributes
231 of the current instance.
233 @param state: the serialized opcode data
237 if not isinstance(state, dict):
238 raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
241 for name in self._all_slots():
242 if name not in state and hasattr(self, name):
246 setattr(self, name, state[name])
250 """Compute the list of all declared slots for a class.
254 for parent in cls.__mro__:
255 slots.extend(getattr(parent, "__slots__", []))
259 def GetAllParams(cls):
260 """Compute list of all parameters for an opcode.
264 for parent in cls.__mro__:
265 slots.extend(getattr(parent, "OP_PARAMS", []))
268 def Validate(self, set_defaults):
269 """Validate opcode parameters, optionally setting default values.
271 @type set_defaults: bool
272 @param set_defaults: Whether to set default values
273 @raise errors.OpPrereqError: When a parameter value doesn't match
277 for (attr_name, default, test) in self.GetAllParams():
278 assert test == ht.NoType or callable(test)
280 if not hasattr(self, attr_name):
281 if default == ht.NoDefault:
282 raise errors.OpPrereqError("Required parameter '%s.%s' missing" %
283 (self.OP_ID, attr_name),
286 if callable(default):
290 setattr(self, attr_name, dval)
292 if test == ht.NoType:
296 if set_defaults or hasattr(self, attr_name):
297 attr_val = getattr(self, attr_name)
298 if not test(attr_val):
299 logging.error("OpCode %s, parameter %s, has invalid type %s/value %s",
300 self.OP_ID, attr_name, type(attr_val), attr_val)
301 raise errors.OpPrereqError("Parameter '%s.%s' fails validation" %
302 (self.OP_ID, attr_name),
306 class OpCode(BaseOpCode):
309 This is the root of the actual OpCode hierarchy. All clases derived
310 from this class should override OP_ID.
312 @cvar OP_ID: The ID of this opcode. This should be unique amongst all
313 children of this class.
314 @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
315 string returned by Summary(); see the docstring of that
317 @cvar OP_PARAMS: List of opcode attributes, the default values they should
318 get if not already defined, and types they must match.
319 @cvar WITH_LU: Boolean that specifies whether this should be included in
320 mcpu's dispatch table
321 @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
323 @ivar priority: Opcode priority for queue
326 # pylint: disable-msg=E1101
327 # as OP_ID is dynamically defined
330 ("dry_run", None, ht.TMaybeBool),
331 ("debug_level", None, ht.TOr(ht.TNone, ht.TPositiveInt)),
332 ("priority", constants.OP_PRIO_DEFAULT,
333 ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID)),
336 def __getstate__(self):
337 """Specialized getstate for opcodes.
339 This method adds to the state dictionary the OP_ID of the class,
340 so that on unload we can identify the correct class for
341 instantiating the opcode.
344 @return: the state as a dictionary
347 data = BaseOpCode.__getstate__(self)
348 data["OP_ID"] = self.OP_ID
352 def LoadOpCode(cls, data):
353 """Generic load opcode method.
355 The method identifies the correct opcode class from the dict-form
356 by looking for a OP_ID key, if this is not found, or its value is
357 not available in this module as a child of this class, we fail.
360 @param data: the serialized opcode
363 if not isinstance(data, dict):
364 raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
365 if "OP_ID" not in data:
366 raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
367 op_id = data["OP_ID"]
369 if op_id in OP_MAPPING:
370 op_class = OP_MAPPING[op_id]
372 raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
375 new_data = data.copy()
376 del new_data["OP_ID"]
377 op.__setstate__(new_data)
381 """Generates a summary description of this opcode.
383 The summary is the value of the OP_ID attribute (without the "OP_"
384 prefix), plus the value of the OP_DSC_FIELD attribute, if one was
385 defined; this field should allow to easily identify the operation
386 (for an instance creation job, e.g., it would be the instance
390 assert self.OP_ID is not None and len(self.OP_ID) > 3
391 # all OP_ID start with OP_, we remove that
393 field_name = getattr(self, "OP_DSC_FIELD", None)
395 field_value = getattr(self, field_name, None)
396 if isinstance(field_value, (list, tuple)):
397 field_value = ",".join(str(i) for i in field_value)
398 txt = "%s(%s)" % (txt, field_value)
404 class OpClusterPostInit(OpCode):
405 """Post cluster initialization.
407 This opcode does not touch the cluster at all. Its purpose is to run hooks
408 after the cluster has been initialized.
413 class OpClusterDestroy(OpCode):
414 """Destroy the cluster.
416 This opcode has no other parameters. All the state is irreversibly
417 lost after the execution of this opcode.
422 class OpClusterQuery(OpCode):
423 """Query cluster information."""
426 class OpClusterVerify(OpCode):
427 """Verify the cluster state.
429 @type skip_checks: C{list}
430 @ivar skip_checks: steps to be skipped from the verify process; this
431 needs to be a subset of
432 L{constants.VERIFY_OPTIONAL_CHECKS}; currently
433 only L{constants.VERIFY_NPLUSONE_MEM} can be passed
437 ("skip_checks", ht.EmptyList,
438 ht.TListOf(ht.TElemOf(constants.VERIFY_OPTIONAL_CHECKS))),
439 ("verbose", False, ht.TBool),
440 ("error_codes", False, ht.TBool),
441 ("debug_simulate_errors", False, ht.TBool),
445 class OpClusterVerifyDisks(OpCode):
446 """Verify the cluster disks.
450 Result: a tuple of four elements:
451 - list of node names with bad data returned (unreachable, etc.)
452 - dict of node names with broken volume groups (values: error msg)
453 - list of instances with degraded disks (that should be activated)
454 - dict of instances with missing logical volumes (values: (node, vol)
455 pairs with details about the missing volumes)
457 In normal operation, all lists should be empty. A non-empty instance
458 list (3rd element of the result) is still ok (errors were fixed) but
459 non-empty node list means some node is down, and probably there are
460 unfixable drbd errors.
462 Note that only instances that are drbd-based are taken into
463 consideration. This might need to be revisited in the future.
468 class OpClusterRepairDiskSizes(OpCode):
469 """Verify the disk sizes of the instances and fixes configuration
472 Parameters: optional instances list, in case we want to restrict the
473 checks to only a subset of the instances.
475 Result: a list of tuples, (instance, disk, new-size) for changed
478 In normal operation, the list should be empty.
480 @type instances: list
481 @ivar instances: the list of instances to check, or empty for all instances
485 ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
489 class OpClusterConfigQuery(OpCode):
490 """Query cluster configuration values."""
496 class OpClusterRename(OpCode):
497 """Rename the cluster.
500 @ivar name: The new name of the cluster. The name and/or the master IP
501 address will be changed to match the new name and its IP
505 OP_DSC_FIELD = "name"
507 ("name", ht.NoDefault, ht.TNonEmptyString),
511 class OpClusterSetParams(OpCode):
512 """Change the parameters of the cluster.
514 @type vg_name: C{str} or C{None}
515 @ivar vg_name: The new volume group name or None to disable LVM usage.
519 ("vg_name", None, ht.TMaybeString),
520 ("enabled_hypervisors", None,
521 ht.TOr(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)), ht.TTrue),
523 ("hvparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
525 ("beparams", None, ht.TOr(ht.TDict, ht.TNone)),
526 ("os_hvp", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
528 ("osparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
530 ("candidate_pool_size", None, ht.TOr(ht.TStrictPositiveInt, ht.TNone)),
531 ("uid_pool", None, ht.NoType),
532 ("add_uids", None, ht.NoType),
533 ("remove_uids", None, ht.NoType),
534 ("maintain_node_health", None, ht.TMaybeBool),
535 ("prealloc_wipe_disks", None, ht.TMaybeBool),
536 ("nicparams", None, ht.TMaybeDict),
537 ("ndparams", None, ht.TMaybeDict),
538 ("drbd_helper", None, ht.TOr(ht.TString, ht.TNone)),
539 ("default_iallocator", None, ht.TOr(ht.TString, ht.TNone)),
540 ("master_netdev", None, ht.TOr(ht.TString, ht.TNone)),
541 ("reserved_lvs", None, ht.TOr(ht.TListOf(ht.TNonEmptyString), ht.TNone)),
542 ("hidden_os", None, ht.TOr(ht.TListOf(
545 ht.TMap(lambda v: v[0], ht.TElemOf(constants.DDMS_VALUES)))),
547 ("blacklisted_os", None, ht.TOr(ht.TListOf(
550 ht.TMap(lambda v: v[0], ht.TElemOf(constants.DDMS_VALUES)))),
555 class OpClusterRedistConf(OpCode):
556 """Force a full push of the cluster configuration.
561 class OpQuery(OpCode):
562 """Query for resources/items.
564 @ivar what: Resources to query for, must be one of L{constants.QR_OP_QUERY}
565 @ivar fields: List of fields to retrieve
566 @ivar filter: Query filter
570 ("what", ht.NoDefault, ht.TElemOf(constants.QR_OP_QUERY)),
571 ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
572 ("filter", None, ht.TOr(ht.TNone,
573 ht.TListOf(ht.TOr(ht.TNonEmptyString, ht.TList)))),
577 class OpQueryFields(OpCode):
578 """Query for available resource/item fields.
580 @ivar what: Resources to query for, must be one of L{constants.QR_OP_QUERY}
581 @ivar fields: List of fields to retrieve
585 ("what", ht.NoDefault, ht.TElemOf(constants.QR_OP_QUERY)),
586 ("fields", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString))),
590 class OpOobCommand(OpCode):
591 """Interact with OOB."""
593 ("node_names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
594 ("command", None, ht.TElemOf(constants.OOB_COMMANDS)),
595 ("timeout", constants.OOB_TIMEOUT, ht.TInt),
601 class OpNodeRemove(OpCode):
604 @type node_name: C{str}
605 @ivar node_name: The name of the node to remove. If the node still has
606 instances on it, the operation will fail.
609 OP_DSC_FIELD = "node_name"
615 class OpNodeAdd(OpCode):
616 """Add a node to the cluster.
618 @type node_name: C{str}
619 @ivar node_name: The name of the node to add. This can be a short name,
620 but it will be expanded to the FQDN.
621 @type primary_ip: IP address
622 @ivar primary_ip: The primary IP of the node. This will be ignored when the
623 opcode is submitted, but will be filled during the node
624 add (so it will be visible in the job query).
625 @type secondary_ip: IP address
626 @ivar secondary_ip: The secondary IP of the node. This needs to be passed
627 if the cluster has been initialized in 'dual-network'
628 mode, otherwise it must not be given.
630 @ivar readd: Whether to re-add an existing node to the cluster. If
631 this is not passed, then the operation will abort if the node
632 name is already in the cluster; use this parameter to 'repair'
633 a node that had its configuration broken, or was reinstalled
634 without removal from the cluster.
636 @ivar group: The node group to which this node will belong.
637 @type vm_capable: C{bool}
638 @ivar vm_capable: The vm_capable node attribute
639 @type master_capable: C{bool}
640 @ivar master_capable: The master_capable node attribute
643 OP_DSC_FIELD = "node_name"
646 ("primary_ip", None, ht.NoType),
647 ("secondary_ip", None, ht.TMaybeString),
648 ("readd", False, ht.TBool),
649 ("group", None, ht.TMaybeString),
650 ("master_capable", None, ht.TMaybeBool),
651 ("vm_capable", None, ht.TMaybeBool),
652 ("ndparams", None, ht.TMaybeDict),
656 class OpNodeQuery(OpCode):
657 """Compute the list of nodes."""
660 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
661 ("use_locking", False, ht.TBool),
665 class OpNodeQueryvols(OpCode):
666 """Get list of volumes on node."""
669 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
673 class OpNodeQueryStorage(OpCode):
674 """Get information on storage for node(s)."""
678 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
679 ("name", None, ht.TMaybeString),
683 class OpNodeModifyStorage(OpCode):
684 """Modifies the properies of a storage unit"""
688 ("name", ht.NoDefault, ht.TNonEmptyString),
689 ("changes", ht.NoDefault, ht.TDict),
693 class OpRepairNodeStorage(OpCode):
694 """Repairs the volume group on a node."""
695 OP_DSC_FIELD = "node_name"
699 ("name", ht.NoDefault, ht.TNonEmptyString),
700 ("ignore_consistency", False, ht.TBool),
704 class OpNodeSetParams(OpCode):
705 """Change the parameters of a node."""
706 OP_DSC_FIELD = "node_name"
710 ("master_candidate", None, ht.TMaybeBool),
711 ("offline", None, ht.TMaybeBool),
712 ("drained", None, ht.TMaybeBool),
713 ("auto_promote", False, ht.TBool),
714 ("master_capable", None, ht.TMaybeBool),
715 ("vm_capable", None, ht.TMaybeBool),
716 ("secondary_ip", None, ht.TMaybeString),
717 ("ndparams", None, ht.TMaybeDict),
718 ("powered", None, ht.TMaybeBool),
722 class OpNodePowercycle(OpCode):
723 """Tries to powercycle a node."""
724 OP_DSC_FIELD = "node_name"
731 class OpNodeMigrate(OpCode):
732 """Migrate all instances from a node."""
733 OP_DSC_FIELD = "node_name"
741 class OpNodeEvacStrategy(OpCode):
742 """Compute the evacuation strategy for a list of nodes."""
743 OP_DSC_FIELD = "nodes"
745 ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
746 ("remote_node", None, ht.TMaybeString),
747 ("iallocator", None, ht.TMaybeString),
753 class OpInstanceCreate(OpCode):
754 """Create an instance.
756 @ivar instance_name: Instance name
757 @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES})
758 @ivar source_handshake: Signed handshake from source (remote import only)
759 @ivar source_x509_ca: Source X509 CA in PEM format (remote import only)
760 @ivar source_instance_name: Previous name of instance (remote import only)
761 @ivar source_shutdown_timeout: Shutdown timeout used for source instance
765 OP_DSC_FIELD = "instance_name"
768 ("beparams", ht.EmptyDict, ht.TDict),
769 ("disks", ht.NoDefault, ht.TListOf(ht.TDict)),
770 ("disk_template", ht.NoDefault, _CheckDiskTemplate),
771 ("file_driver", None, ht.TOr(ht.TNone, ht.TElemOf(constants.FILE_DRIVER))),
772 ("file_storage_dir", None, ht.TMaybeString),
773 ("force_variant", False, ht.TBool),
774 ("hvparams", ht.EmptyDict, ht.TDict),
775 ("hypervisor", None, ht.TMaybeString),
776 ("iallocator", None, ht.TMaybeString),
777 ("identify_defaults", False, ht.TBool),
778 ("ip_check", True, ht.TBool),
779 ("mode", ht.NoDefault, ht.TElemOf(constants.INSTANCE_CREATE_MODES)),
780 ("name_check", True, ht.TBool),
781 ("nics", ht.NoDefault, ht.TListOf(ht.TDict)),
782 ("no_install", None, ht.TMaybeBool),
783 ("osparams", ht.EmptyDict, ht.TDict),
784 ("os_type", None, ht.TMaybeString),
785 ("pnode", None, ht.TMaybeString),
786 ("snode", None, ht.TMaybeString),
787 ("source_handshake", None, ht.TOr(ht.TList, ht.TNone)),
788 ("source_instance_name", None, ht.TMaybeString),
789 ("source_shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT,
791 ("source_x509_ca", None, ht.TMaybeString),
792 ("src_node", None, ht.TMaybeString),
793 ("src_path", None, ht.TMaybeString),
794 ("start", True, ht.TBool),
795 ("wait_for_sync", True, ht.TBool),
799 class OpInstanceReinstall(OpCode):
800 """Reinstall an instance's OS."""
801 OP_DSC_FIELD = "instance_name"
804 ("os_type", None, ht.TMaybeString),
805 ("force_variant", False, ht.TBool),
806 ("osparams", None, ht.TMaybeDict),
810 class OpInstanceRemove(OpCode):
811 """Remove an instance."""
812 OP_DSC_FIELD = "instance_name"
816 ("ignore_failures", False, ht.TBool),
820 class OpInstanceRename(OpCode):
821 """Rename an instance."""
824 ("new_name", ht.NoDefault, ht.TNonEmptyString),
825 ("ip_check", False, ht.TBool),
826 ("name_check", True, ht.TBool),
830 class OpInstanceStartup(OpCode):
831 """Startup an instance."""
832 OP_DSC_FIELD = "instance_name"
836 _PIgnoreOfflineNodes,
837 ("hvparams", ht.EmptyDict, ht.TDict),
838 ("beparams", ht.EmptyDict, ht.TDict),
842 class OpInstanceShutdown(OpCode):
843 """Shutdown an instance."""
844 OP_DSC_FIELD = "instance_name"
847 _PIgnoreOfflineNodes,
848 ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TPositiveInt),
852 class OpInstanceReboot(OpCode):
853 """Reboot an instance."""
854 OP_DSC_FIELD = "instance_name"
858 ("ignore_secondaries", False, ht.TBool),
859 ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES)),
863 class OpInstanceReplaceDisks(OpCode):
864 """Replace the disks of an instance."""
865 OP_DSC_FIELD = "instance_name"
868 ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES)),
869 ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt)),
870 ("remote_node", None, ht.TMaybeString),
871 ("iallocator", None, ht.TMaybeString),
872 ("early_release", False, ht.TBool),
876 class OpInstanceFailover(OpCode):
877 """Failover an instance."""
878 OP_DSC_FIELD = "instance_name"
882 ("ignore_consistency", False, ht.TBool),
886 class OpInstanceMigrate(OpCode):
887 """Migrate an instance.
889 This migrates (without shutting down an instance) to its secondary
892 @ivar instance_name: the name of the instance
893 @ivar mode: the migration mode (live, non-live or None for auto)
896 OP_DSC_FIELD = "instance_name"
901 ("cleanup", False, ht.TBool),
905 class OpInstanceMove(OpCode):
908 This move (with shutting down an instance and data copying) to an
911 @ivar instance_name: the name of the instance
912 @ivar target_node: the destination node
915 OP_DSC_FIELD = "instance_name"
919 ("target_node", ht.NoDefault, ht.TNonEmptyString),
923 class OpInstanceConsole(OpCode):
924 """Connect to an instance's console."""
925 OP_DSC_FIELD = "instance_name"
931 class OpInstanceActivateDisks(OpCode):
932 """Activate an instance's disks."""
933 OP_DSC_FIELD = "instance_name"
936 ("ignore_size", False, ht.TBool),
940 class OpInstanceDeactivateDisks(OpCode):
941 """Deactivate an instance's disks."""
942 OP_DSC_FIELD = "instance_name"
949 class OpInstanceRecreateDisks(OpCode):
950 """Deactivate an instance's disks."""
951 OP_DSC_FIELD = "instance_name"
954 ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt)),
955 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
959 class OpInstanceQuery(OpCode):
960 """Compute the list of instances."""
963 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
964 ("use_locking", False, ht.TBool),
968 class OpInstanceQueryData(OpCode):
969 """Compute the run-time status of instances."""
971 ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
972 ("static", False, ht.TBool),
973 ("use_locking", False, ht.TBool),
977 class OpInstanceSetParams(OpCode):
978 """Change the parameters of an instance."""
979 OP_DSC_FIELD = "instance_name"
983 ("nics", ht.EmptyList, ht.TList),
984 ("disks", ht.EmptyList, ht.TList),
985 ("beparams", ht.EmptyDict, ht.TDict),
986 ("hvparams", ht.EmptyDict, ht.TDict),
987 ("disk_template", None, ht.TOr(ht.TNone, _CheckDiskTemplate)),
988 ("remote_node", None, ht.TMaybeString),
989 ("os_name", None, ht.TMaybeString),
990 ("force_variant", False, ht.TBool),
991 ("osparams", None, ht.TMaybeDict),
995 class OpInstanceGrowDisk(OpCode):
996 """Grow a disk of an instance."""
997 OP_DSC_FIELD = "instance_name"
1000 ("disk", ht.NoDefault, ht.TInt),
1001 ("amount", ht.NoDefault, ht.TInt),
1002 ("wait_for_sync", True, ht.TBool),
1006 # Node group opcodes
1008 class OpGroupAdd(OpCode):
1009 """Add a node group to the cluster."""
1010 OP_DSC_FIELD = "group_name"
1013 ("ndparams", None, ht.TMaybeDict),
1014 ("alloc_policy", None,
1015 ht.TOr(ht.TNone, ht.TElemOf(constants.VALID_ALLOC_POLICIES))),
1019 class OpGroupAssignNodes(OpCode):
1020 """Assign nodes to a node group."""
1021 OP_DSC_FIELD = "group_name"
1025 ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
1029 class OpGroupQuery(OpCode):
1030 """Compute the list of node groups."""
1033 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
1037 class OpGroupSetParams(OpCode):
1038 """Change the parameters of a node group."""
1039 OP_DSC_FIELD = "group_name"
1042 ("ndparams", None, ht.TMaybeDict),
1043 ("alloc_policy", None, ht.TOr(ht.TNone,
1044 ht.TElemOf(constants.VALID_ALLOC_POLICIES))),
1048 class OpGroupRemove(OpCode):
1049 """Remove a node group from the cluster."""
1050 OP_DSC_FIELD = "group_name"
1056 class OpGroupRename(OpCode):
1057 """Rename a node group in the cluster."""
1058 OP_DSC_FIELD = "old_name"
1060 ("old_name", ht.NoDefault, ht.TNonEmptyString),
1061 ("new_name", ht.NoDefault, ht.TNonEmptyString),
1066 class OpOsDiagnose(OpCode):
1067 """Compute the list of guest operating systems."""
1070 ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
1075 class OpBackupQuery(OpCode):
1076 """Compute the list of exported images."""
1078 ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
1079 ("use_locking", False, ht.TBool),
1083 class OpBackupPrepare(OpCode):
1084 """Prepares an instance export.
1086 @ivar instance_name: Instance name
1087 @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1090 OP_DSC_FIELD = "instance_name"
1093 ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES)),
1097 class OpBackupExport(OpCode):
1098 """Export an instance.
1100 For local exports, the export destination is the node name. For remote
1101 exports, the export destination is a list of tuples, each consisting of
1102 hostname/IP address, port, HMAC and HMAC salt. The HMAC is calculated using
1103 the cluster domain secret over the value "${index}:${hostname}:${port}". The
1104 destination X509 CA must be a signed certificate.
1106 @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1107 @ivar target_node: Export destination
1108 @ivar x509_key_name: X509 key to use (remote export only)
1109 @ivar destination_x509_ca: Destination X509 CA in PEM format (remote export
1113 OP_DSC_FIELD = "instance_name"
1117 # TODO: Rename target_node as it changes meaning for different export modes
1118 # (e.g. "destination")
1119 ("target_node", ht.NoDefault, ht.TOr(ht.TNonEmptyString, ht.TList)),
1120 ("shutdown", True, ht.TBool),
1121 ("remove_instance", False, ht.TBool),
1122 ("ignore_remove_failures", False, ht.TBool),
1123 ("mode", constants.EXPORT_MODE_LOCAL, ht.TElemOf(constants.EXPORT_MODES)),
1124 ("x509_key_name", None, ht.TOr(ht.TList, ht.TNone)),
1125 ("destination_x509_ca", None, ht.TMaybeString),
1129 class OpBackupRemove(OpCode):
1130 """Remove an instance's export."""
1131 OP_DSC_FIELD = "instance_name"
1138 class OpTagsGet(OpCode):
1139 """Returns the tags of the given object."""
1140 OP_DSC_FIELD = "name"
1143 # Name is only meaningful for nodes and instances
1144 ("name", ht.NoDefault, ht.TMaybeString),
1148 class OpTagsSearch(OpCode):
1149 """Searches the tags in the cluster for a given pattern."""
1150 OP_DSC_FIELD = "pattern"
1152 ("pattern", ht.NoDefault, ht.TNonEmptyString),
1156 class OpTagsSet(OpCode):
1157 """Add a list of tags on a given object."""
1161 # Name is only meaningful for nodes and instances
1162 ("name", ht.NoDefault, ht.TMaybeString),
1166 class OpTagsDel(OpCode):
1167 """Remove a list of tags from a given object."""
1171 # Name is only meaningful for nodes and instances
1172 ("name", ht.NoDefault, ht.TMaybeString),
1176 class OpTestDelay(OpCode):
1177 """Sleeps for a configured amount of time.
1179 This is used just for debugging and testing.
1182 - duration: the time to sleep
1183 - on_master: if true, sleep on the master
1184 - on_nodes: list of nodes in which to sleep
1186 If the on_master parameter is true, it will execute a sleep on the
1187 master (before any node sleep).
1189 If the on_nodes list is not empty, it will sleep on those nodes
1190 (after the sleep on the master, if that is enabled).
1192 As an additional feature, the case of duration < 0 will be reported
1193 as an execution error, so this opcode can be used as a failure
1194 generator. The case of duration == 0 will not be treated specially.
1197 OP_DSC_FIELD = "duration"
1199 ("duration", ht.NoDefault, ht.TFloat),
1200 ("on_master", True, ht.TBool),
1201 ("on_nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
1202 ("repeat", 0, ht.TPositiveInt)
1206 class OpTestAllocator(OpCode):
1207 """Allocator framework testing.
1209 This opcode has two modes:
1210 - gather and return allocator input for a given mode (allocate new
1211 or replace secondary) and a given instance definition (direction
1213 - run a selected allocator for a given operation (as above) and
1214 return the allocator output (direction 'out')
1217 OP_DSC_FIELD = "allocator"
1219 ("direction", ht.NoDefault,
1220 ht.TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS)),
1221 ("mode", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_MODES)),
1222 ("name", ht.NoDefault, ht.TNonEmptyString),
1223 ("nics", ht.NoDefault, ht.TOr(ht.TNone, ht.TListOf(
1224 ht.TDictOf(ht.TElemOf(["mac", "ip", "bridge"]),
1225 ht.TOr(ht.TNone, ht.TNonEmptyString))))),
1226 ("disks", ht.NoDefault, ht.TOr(ht.TNone, ht.TList)),
1227 ("hypervisor", None, ht.TMaybeString),
1228 ("allocator", None, ht.TMaybeString),
1229 ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
1230 ("mem_size", None, ht.TOr(ht.TNone, ht.TPositiveInt)),
1231 ("vcpus", None, ht.TOr(ht.TNone, ht.TPositiveInt)),
1232 ("os", None, ht.TMaybeString),
1233 ("disk_template", None, ht.TMaybeString),
1234 ("evac_nodes", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString))),
1238 class OpTestJqueue(OpCode):
1239 """Utility opcode to test some aspects of the job queue.
1243 ("notify_waitlock", False, ht.TBool),
1244 ("notify_exec", False, ht.TBool),
1245 ("log_messages", ht.EmptyList, ht.TListOf(ht.TString)),
1246 ("fail", False, ht.TBool),
1250 class OpTestDummy(OpCode):
1251 """Utility opcode used by unittests.
1255 ("result", ht.NoDefault, ht.NoType),
1256 ("messages", ht.NoDefault, ht.NoType),
1257 ("fail", ht.NoDefault, ht.NoType),
1263 """Returns list of all defined opcodes.
1265 Does not eliminate duplicates by C{OP_ID}.
1268 return [v for v in globals().values()
1269 if (isinstance(v, type) and issubclass(v, OpCode) and
1270 hasattr(v, "OP_ID") and v is not OpCode)]
1273 OP_MAPPING = dict((v.OP_ID, v) for v in _GetOpList())