Core shared file storage support
[ganeti-local] / lib / opcodes.py
1 #
2 #
3
4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Google Inc.
5 #
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.
10 #
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.
15 #
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
19 # 02110-1301, USA.
20
21
22 """OpCodes module
23
24 This module implements the data structures which define the cluster
25 operations - the so-called opcodes.
26
27 Every operation which modifies the cluster state is expressed via
28 opcodes.
29
30 """
31
32 # this are practically structures, so disable the message about too
33 # few public methods:
34 # pylint: disable-msg=R0903
35
36 import logging
37 import re
38 import operator
39
40 from ganeti import constants
41 from ganeti import errors
42 from ganeti import ht
43
44
45 # Common opcode attributes
46
47 #: output fields for a query operation
48 _POutputFields = ("output_fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
49                   "Selected output fields")
50
51 #: the shutdown timeout
52 _PShutdownTimeout = \
53   ("shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TPositiveInt,
54    "How long to wait for instance to shut down")
55
56 #: the force parameter
57 _PForce = ("force", False, ht.TBool, "Whether to force the operation")
58
59 #: a required instance name (for single-instance LUs)
60 _PInstanceName = ("instance_name", ht.NoDefault, ht.TNonEmptyString,
61                   "Instance name")
62
63 #: Whether to ignore offline nodes
64 _PIgnoreOfflineNodes = ("ignore_offline_nodes", False, ht.TBool,
65                         "Whether to ignore offline nodes")
66
67 #: a required node name (for single-node LUs)
68 _PNodeName = ("node_name", ht.NoDefault, ht.TNonEmptyString, "Node name")
69
70 #: a required node group name (for single-group LUs)
71 _PGroupName = ("group_name", ht.NoDefault, ht.TNonEmptyString, "Group name")
72
73 #: Migration type (live/non-live)
74 _PMigrationMode = ("mode", None,
75                    ht.TOr(ht.TNone, ht.TElemOf(constants.HT_MIGRATION_MODES)),
76                    "Migration mode")
77
78 #: Obsolete 'live' migration mode (boolean)
79 _PMigrationLive = ("live", None, ht.TMaybeBool,
80                    "Legacy setting for live migration, do not use")
81
82 #: Tag type
83 _PTagKind = ("kind", ht.NoDefault, ht.TElemOf(constants.VALID_TAG_TYPES), None)
84
85 #: List of tag strings
86 _PTags = ("tags", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), None)
87
88 _PForceVariant = ("force_variant", False, ht.TBool,
89                   "Whether to force an unknown OS variant")
90
91 _PWaitForSync = ("wait_for_sync", True, ht.TBool,
92                  "Whether to wait for the disk to synchronize")
93
94 _PIgnoreConsistency = ("ignore_consistency", False, ht.TBool,
95                        "Whether to ignore disk consistency")
96
97 _PStorageName = ("name", ht.NoDefault, ht.TMaybeString, "Storage name")
98
99 _PUseLocking = ("use_locking", False, ht.TBool,
100                 "Whether to use synchronization")
101
102 _PNameCheck = ("name_check", True, ht.TBool, "Whether to check name")
103
104 _PNodeGroupAllocPolicy = \
105   ("alloc_policy", None,
106    ht.TOr(ht.TNone, ht.TElemOf(constants.VALID_ALLOC_POLICIES)),
107    "Instance allocation policy")
108
109 _PGroupNodeParams = ("ndparams", None, ht.TMaybeDict,
110                      "Default node parameters for group")
111
112 _PIpCheckDoc = "Whether to ensure instance's IP address is inactive"
113
114 #: OP_ID conversion regular expression
115 _OPID_RE = re.compile("([a-z])([A-Z])")
116
117 #: Utility function for L{OpClusterSetParams}
118 _TestClusterOsList = ht.TOr(ht.TNone,
119   ht.TListOf(ht.TAnd(ht.TList, ht.TIsLength(2),
120     ht.TMap(ht.WithDesc("GetFirstItem")(operator.itemgetter(0)),
121             ht.TElemOf(constants.DDMS_VALUES)))))
122
123
124 # TODO: Generate check from constants.INIC_PARAMS_TYPES
125 #: Utility function for testing NIC definitions
126 _TestNicDef = ht.TDictOf(ht.TElemOf(constants.INIC_PARAMS),
127                          ht.TOr(ht.TNone, ht.TNonEmptyString))
128
129
130 def _NameToId(name):
131   """Convert an opcode class name to an OP_ID.
132
133   @type name: string
134   @param name: the class name, as OpXxxYyy
135   @rtype: string
136   @return: the name in the OP_XXXX_YYYY format
137
138   """
139   if not name.startswith("Op"):
140     return None
141   # Note: (?<=[a-z])(?=[A-Z]) would be ideal, since it wouldn't
142   # consume any input, and hence we would just have all the elements
143   # in the list, one by one; but it seems that split doesn't work on
144   # non-consuming input, hence we have to process the input string a
145   # bit
146   name = _OPID_RE.sub(r"\1,\2", name)
147   elems = name.split(",")
148   return "_".join(n.upper() for n in elems)
149
150
151 def RequireFileStorage():
152   """Checks that file storage is enabled.
153
154   While it doesn't really fit into this module, L{utils} was deemed too large
155   of a dependency to be imported for just one or two functions.
156
157   @raise errors.OpPrereqError: when file storage is disabled
158
159   """
160   if not constants.ENABLE_FILE_STORAGE:
161     raise errors.OpPrereqError("File storage disabled at configure time",
162                                errors.ECODE_INVAL)
163
164
165 def RequireSharedFileStorage():
166   """Checks that shared file storage is enabled.
167
168   While it doesn't really fit into this module, L{utils} was deemed too large
169   of a dependency to be imported for just one or two functions.
170
171   @raise errors.OpPrereqError: when shared file storage is disabled
172
173   """
174   if not constants.ENABLE_SHARED_FILE_STORAGE:
175     raise errors.OpPrereqError("Shared file storage disabled at"
176                                " configure time", errors.ECODE_INVAL)
177
178
179 @ht.WithDesc("CheckFileStorage")
180 def _CheckFileStorage(value):
181   """Ensures file storage is enabled if used.
182
183   """
184   if value == constants.DT_FILE:
185     RequireFileStorage()
186   elif value == constants.DT_SHARED_FILE:
187     RequireSharedFileStorage()
188   return True
189
190
191 _CheckDiskTemplate = ht.TAnd(ht.TElemOf(constants.DISK_TEMPLATES),
192                              _CheckFileStorage)
193
194
195 def _CheckStorageType(storage_type):
196   """Ensure a given storage type is valid.
197
198   """
199   if storage_type not in constants.VALID_STORAGE_TYPES:
200     raise errors.OpPrereqError("Unknown storage type: %s" % storage_type,
201                                errors.ECODE_INVAL)
202   if storage_type == constants.ST_FILE:
203     RequireFileStorage()
204   return True
205
206
207 #: Storage type parameter
208 _PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType,
209                  "Storage type")
210
211
212 class _AutoOpParamSlots(type):
213   """Meta class for opcode definitions.
214
215   """
216   def __new__(mcs, name, bases, attrs):
217     """Called when a class should be created.
218
219     @param mcs: The meta class
220     @param name: Name of created class
221     @param bases: Base classes
222     @type attrs: dict
223     @param attrs: Class attributes
224
225     """
226     assert "__slots__" not in attrs, \
227       "Class '%s' defines __slots__ when it should use OP_PARAMS" % name
228     assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
229
230     attrs["OP_ID"] = _NameToId(name)
231
232     # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams
233     params = attrs.setdefault("OP_PARAMS", [])
234
235     # Use parameter names as slots
236     slots = [pname for (pname, _, _, _) in params]
237
238     assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
239       "Class '%s' uses unknown field in OP_DSC_FIELD" % name
240
241     attrs["__slots__"] = slots
242
243     return type.__new__(mcs, name, bases, attrs)
244
245
246 class BaseOpCode(object):
247   """A simple serializable object.
248
249   This object serves as a parent class for OpCode without any custom
250   field handling.
251
252   """
253   # pylint: disable-msg=E1101
254   # as OP_ID is dynamically defined
255   __metaclass__ = _AutoOpParamSlots
256
257   def __init__(self, **kwargs):
258     """Constructor for BaseOpCode.
259
260     The constructor takes only keyword arguments and will set
261     attributes on this object based on the passed arguments. As such,
262     it means that you should not pass arguments which are not in the
263     __slots__ attribute for this class.
264
265     """
266     slots = self._all_slots()
267     for key in kwargs:
268       if key not in slots:
269         raise TypeError("Object %s doesn't support the parameter '%s'" %
270                         (self.__class__.__name__, key))
271       setattr(self, key, kwargs[key])
272
273   def __getstate__(self):
274     """Generic serializer.
275
276     This method just returns the contents of the instance as a
277     dictionary.
278
279     @rtype:  C{dict}
280     @return: the instance attributes and their values
281
282     """
283     state = {}
284     for name in self._all_slots():
285       if hasattr(self, name):
286         state[name] = getattr(self, name)
287     return state
288
289   def __setstate__(self, state):
290     """Generic unserializer.
291
292     This method just restores from the serialized state the attributes
293     of the current instance.
294
295     @param state: the serialized opcode data
296     @type state:  C{dict}
297
298     """
299     if not isinstance(state, dict):
300       raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
301                        type(state))
302
303     for name in self._all_slots():
304       if name not in state and hasattr(self, name):
305         delattr(self, name)
306
307     for name in state:
308       setattr(self, name, state[name])
309
310   @classmethod
311   def _all_slots(cls):
312     """Compute the list of all declared slots for a class.
313
314     """
315     slots = []
316     for parent in cls.__mro__:
317       slots.extend(getattr(parent, "__slots__", []))
318     return slots
319
320   @classmethod
321   def GetAllParams(cls):
322     """Compute list of all parameters for an opcode.
323
324     """
325     slots = []
326     for parent in cls.__mro__:
327       slots.extend(getattr(parent, "OP_PARAMS", []))
328     return slots
329
330   def Validate(self, set_defaults):
331     """Validate opcode parameters, optionally setting default values.
332
333     @type set_defaults: bool
334     @param set_defaults: Whether to set default values
335     @raise errors.OpPrereqError: When a parameter value doesn't match
336                                  requirements
337
338     """
339     for (attr_name, default, test, _) in self.GetAllParams():
340       assert test == ht.NoType or callable(test)
341
342       if not hasattr(self, attr_name):
343         if default == ht.NoDefault:
344           raise errors.OpPrereqError("Required parameter '%s.%s' missing" %
345                                      (self.OP_ID, attr_name),
346                                      errors.ECODE_INVAL)
347         elif set_defaults:
348           if callable(default):
349             dval = default()
350           else:
351             dval = default
352           setattr(self, attr_name, dval)
353
354       if test == ht.NoType:
355         # no tests here
356         continue
357
358       if set_defaults or hasattr(self, attr_name):
359         attr_val = getattr(self, attr_name)
360         if not test(attr_val):
361           logging.error("OpCode %s, parameter %s, has invalid type %s/value %s",
362                         self.OP_ID, attr_name, type(attr_val), attr_val)
363           raise errors.OpPrereqError("Parameter '%s.%s' fails validation" %
364                                      (self.OP_ID, attr_name),
365                                      errors.ECODE_INVAL)
366
367
368 class OpCode(BaseOpCode):
369   """Abstract OpCode.
370
371   This is the root of the actual OpCode hierarchy. All clases derived
372   from this class should override OP_ID.
373
374   @cvar OP_ID: The ID of this opcode. This should be unique amongst all
375                children of this class.
376   @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
377                       string returned by Summary(); see the docstring of that
378                       method for details).
379   @cvar OP_PARAMS: List of opcode attributes, the default values they should
380                    get if not already defined, and types they must match.
381   @cvar WITH_LU: Boolean that specifies whether this should be included in
382       mcpu's dispatch table
383   @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
384                  the check steps
385   @ivar priority: Opcode priority for queue
386
387   """
388   # pylint: disable-msg=E1101
389   # as OP_ID is dynamically defined
390   WITH_LU = True
391   OP_PARAMS = [
392     ("dry_run", None, ht.TMaybeBool, "Run checks only, don't execute"),
393     ("debug_level", None, ht.TOr(ht.TNone, ht.TPositiveInt), "Debug level"),
394     ("priority", constants.OP_PRIO_DEFAULT,
395      ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID), "Opcode priority"),
396     ]
397
398   def __getstate__(self):
399     """Specialized getstate for opcodes.
400
401     This method adds to the state dictionary the OP_ID of the class,
402     so that on unload we can identify the correct class for
403     instantiating the opcode.
404
405     @rtype:   C{dict}
406     @return:  the state as a dictionary
407
408     """
409     data = BaseOpCode.__getstate__(self)
410     data["OP_ID"] = self.OP_ID
411     return data
412
413   @classmethod
414   def LoadOpCode(cls, data):
415     """Generic load opcode method.
416
417     The method identifies the correct opcode class from the dict-form
418     by looking for a OP_ID key, if this is not found, or its value is
419     not available in this module as a child of this class, we fail.
420
421     @type data:  C{dict}
422     @param data: the serialized opcode
423
424     """
425     if not isinstance(data, dict):
426       raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
427     if "OP_ID" not in data:
428       raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
429     op_id = data["OP_ID"]
430     op_class = None
431     if op_id in OP_MAPPING:
432       op_class = OP_MAPPING[op_id]
433     else:
434       raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
435                        op_id)
436     op = op_class()
437     new_data = data.copy()
438     del new_data["OP_ID"]
439     op.__setstate__(new_data)
440     return op
441
442   def Summary(self):
443     """Generates a summary description of this opcode.
444
445     The summary is the value of the OP_ID attribute (without the "OP_"
446     prefix), plus the value of the OP_DSC_FIELD attribute, if one was
447     defined; this field should allow to easily identify the operation
448     (for an instance creation job, e.g., it would be the instance
449     name).
450
451     """
452     assert self.OP_ID is not None and len(self.OP_ID) > 3
453     # all OP_ID start with OP_, we remove that
454     txt = self.OP_ID[3:]
455     field_name = getattr(self, "OP_DSC_FIELD", None)
456     if field_name:
457       field_value = getattr(self, field_name, None)
458       if isinstance(field_value, (list, tuple)):
459         field_value = ",".join(str(i) for i in field_value)
460       txt = "%s(%s)" % (txt, field_value)
461     return txt
462
463
464 # cluster opcodes
465
466 class OpClusterPostInit(OpCode):
467   """Post cluster initialization.
468
469   This opcode does not touch the cluster at all. Its purpose is to run hooks
470   after the cluster has been initialized.
471
472   """
473
474
475 class OpClusterDestroy(OpCode):
476   """Destroy the cluster.
477
478   This opcode has no other parameters. All the state is irreversibly
479   lost after the execution of this opcode.
480
481   """
482
483
484 class OpClusterQuery(OpCode):
485   """Query cluster information."""
486
487
488 class OpClusterVerify(OpCode):
489   """Verify the cluster state.
490
491   @type skip_checks: C{list}
492   @ivar skip_checks: steps to be skipped from the verify process; this
493                      needs to be a subset of
494                      L{constants.VERIFY_OPTIONAL_CHECKS}; currently
495                      only L{constants.VERIFY_NPLUSONE_MEM} can be passed
496
497   """
498   OP_PARAMS = [
499     ("skip_checks", ht.EmptyList,
500      ht.TListOf(ht.TElemOf(constants.VERIFY_OPTIONAL_CHECKS)), None),
501     ("verbose", False, ht.TBool, None),
502     ("error_codes", False, ht.TBool, None),
503     ("debug_simulate_errors", False, ht.TBool, None),
504     ]
505
506
507 class OpClusterVerifyDisks(OpCode):
508   """Verify the cluster disks.
509
510   Parameters: none
511
512   Result: a tuple of four elements:
513     - list of node names with bad data returned (unreachable, etc.)
514     - dict of node names with broken volume groups (values: error msg)
515     - list of instances with degraded disks (that should be activated)
516     - dict of instances with missing logical volumes (values: (node, vol)
517       pairs with details about the missing volumes)
518
519   In normal operation, all lists should be empty. A non-empty instance
520   list (3rd element of the result) is still ok (errors were fixed) but
521   non-empty node list means some node is down, and probably there are
522   unfixable drbd errors.
523
524   Note that only instances that are drbd-based are taken into
525   consideration. This might need to be revisited in the future.
526
527   """
528
529
530 class OpClusterRepairDiskSizes(OpCode):
531   """Verify the disk sizes of the instances and fixes configuration
532   mimatches.
533
534   Parameters: optional instances list, in case we want to restrict the
535   checks to only a subset of the instances.
536
537   Result: a list of tuples, (instance, disk, new-size) for changed
538   configurations.
539
540   In normal operation, the list should be empty.
541
542   @type instances: list
543   @ivar instances: the list of instances to check, or empty for all instances
544
545   """
546   OP_PARAMS = [
547     ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
548     ]
549
550
551 class OpClusterConfigQuery(OpCode):
552   """Query cluster configuration values."""
553   OP_PARAMS = [
554     _POutputFields
555     ]
556
557
558 class OpClusterRename(OpCode):
559   """Rename the cluster.
560
561   @type name: C{str}
562   @ivar name: The new name of the cluster. The name and/or the master IP
563               address will be changed to match the new name and its IP
564               address.
565
566   """
567   OP_DSC_FIELD = "name"
568   OP_PARAMS = [
569     ("name", ht.NoDefault, ht.TNonEmptyString, None),
570     ]
571
572
573 class OpClusterSetParams(OpCode):
574   """Change the parameters of the cluster.
575
576   @type vg_name: C{str} or C{None}
577   @ivar vg_name: The new volume group name or None to disable LVM usage.
578
579   """
580   OP_PARAMS = [
581     ("vg_name", None, ht.TMaybeString, "Volume group name"),
582     ("enabled_hypervisors", None,
583      ht.TOr(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)), ht.TTrue),
584             ht.TNone),
585      "List of enabled hypervisors"),
586     ("hvparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
587                               ht.TNone),
588      "Cluster-wide hypervisor parameter defaults, hypervisor-dependent"),
589     ("beparams", None, ht.TOr(ht.TDict, ht.TNone),
590      "Cluster-wide backend parameter defaults"),
591     ("os_hvp", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
592                             ht.TNone),
593      "Cluster-wide per-OS hypervisor parameter defaults"),
594     ("osparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
595                               ht.TNone),
596      "Cluster-wide OS parameter defaults"),
597     ("candidate_pool_size", None, ht.TOr(ht.TStrictPositiveInt, ht.TNone),
598      "Master candidate pool size"),
599     ("uid_pool", None, ht.NoType,
600      "Set UID pool, must be list of lists describing UID ranges (two items,"
601      " start and end inclusive)"),
602     ("add_uids", None, ht.NoType,
603      "Extend UID pool, must be list of lists describing UID ranges (two"
604      " items, start and end inclusive) to be added"),
605     ("remove_uids", None, ht.NoType,
606      "Shrink UID pool, must be list of lists describing UID ranges (two"
607      " items, start and end inclusive) to be removed"),
608     ("maintain_node_health", None, ht.TMaybeBool,
609      "Whether to automatically maintain node health"),
610     ("prealloc_wipe_disks", None, ht.TMaybeBool,
611      "Whether to wipe disks before allocating them to instances"),
612     ("nicparams", None, ht.TMaybeDict, "Cluster-wide NIC parameter defaults"),
613     ("ndparams", None, ht.TMaybeDict, "Cluster-wide node parameter defaults"),
614     ("drbd_helper", None, ht.TOr(ht.TString, ht.TNone), "DRBD helper program"),
615     ("default_iallocator", None, ht.TOr(ht.TString, ht.TNone),
616      "Default iallocator for cluster"),
617     ("master_netdev", None, ht.TOr(ht.TString, ht.TNone),
618      "Master network device"),
619     ("reserved_lvs", None, ht.TOr(ht.TListOf(ht.TNonEmptyString), ht.TNone),
620      "List of reserved LVs"),
621     ("hidden_os", None, _TestClusterOsList,
622      "Modify list of hidden operating systems. Each modification must have"
623      " two items, the operation and the OS name. The operation can be"
624      " ``%s`` or ``%s``." % (constants.DDM_ADD, constants.DDM_REMOVE)),
625     ("blacklisted_os", None, _TestClusterOsList,
626      "Modify list of blacklisted operating systems. Each modification must have"
627      " two items, the operation and the OS name. The operation can be"
628      " ``%s`` or ``%s``." % (constants.DDM_ADD, constants.DDM_REMOVE)),
629     ]
630
631
632 class OpClusterRedistConf(OpCode):
633   """Force a full push of the cluster configuration.
634
635   """
636
637
638 class OpQuery(OpCode):
639   """Query for resources/items.
640
641   @ivar what: Resources to query for, must be one of L{constants.QR_OP_QUERY}
642   @ivar fields: List of fields to retrieve
643   @ivar filter: Query filter
644
645   """
646   OP_PARAMS = [
647     ("what", ht.NoDefault, ht.TElemOf(constants.QR_OP_QUERY),
648      "Resource(s) to query for"),
649     ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
650      "Requested fields"),
651     ("filter", None, ht.TOr(ht.TNone,
652                             ht.TListOf(ht.TOr(ht.TNonEmptyString, ht.TList))),
653      "Query filter"),
654     ]
655
656
657 class OpQueryFields(OpCode):
658   """Query for available resource/item fields.
659
660   @ivar what: Resources to query for, must be one of L{constants.QR_OP_QUERY}
661   @ivar fields: List of fields to retrieve
662
663   """
664   OP_PARAMS = [
665     ("what", ht.NoDefault, ht.TElemOf(constants.QR_OP_QUERY), None),
666     ("fields", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString)), None),
667     ]
668
669
670 class OpOobCommand(OpCode):
671   """Interact with OOB."""
672   OP_PARAMS = [
673     ("node_names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
674     ("command", None, ht.TElemOf(constants.OOB_COMMANDS), None),
675     ("timeout", constants.OOB_TIMEOUT, ht.TInt, None),
676     ("ignore_status", False, ht.TBool, None),
677     ]
678
679
680 # node opcodes
681
682 class OpNodeRemove(OpCode):
683   """Remove a node.
684
685   @type node_name: C{str}
686   @ivar node_name: The name of the node to remove. If the node still has
687                    instances on it, the operation will fail.
688
689   """
690   OP_DSC_FIELD = "node_name"
691   OP_PARAMS = [
692     _PNodeName,
693     ]
694
695
696 class OpNodeAdd(OpCode):
697   """Add a node to the cluster.
698
699   @type node_name: C{str}
700   @ivar node_name: The name of the node to add. This can be a short name,
701                    but it will be expanded to the FQDN.
702   @type primary_ip: IP address
703   @ivar primary_ip: The primary IP of the node. This will be ignored when the
704                     opcode is submitted, but will be filled during the node
705                     add (so it will be visible in the job query).
706   @type secondary_ip: IP address
707   @ivar secondary_ip: The secondary IP of the node. This needs to be passed
708                       if the cluster has been initialized in 'dual-network'
709                       mode, otherwise it must not be given.
710   @type readd: C{bool}
711   @ivar readd: Whether to re-add an existing node to the cluster. If
712                this is not passed, then the operation will abort if the node
713                name is already in the cluster; use this parameter to 'repair'
714                a node that had its configuration broken, or was reinstalled
715                without removal from the cluster.
716   @type group: C{str}
717   @ivar group: The node group to which this node will belong.
718   @type vm_capable: C{bool}
719   @ivar vm_capable: The vm_capable node attribute
720   @type master_capable: C{bool}
721   @ivar master_capable: The master_capable node attribute
722
723   """
724   OP_DSC_FIELD = "node_name"
725   OP_PARAMS = [
726     _PNodeName,
727     ("primary_ip", None, ht.NoType, "Primary IP address"),
728     ("secondary_ip", None, ht.TMaybeString, "Secondary IP address"),
729     ("readd", False, ht.TBool, "Whether node is re-added to cluster"),
730     ("group", None, ht.TMaybeString, "Initial node group"),
731     ("master_capable", None, ht.TMaybeBool,
732      "Whether node can become master or master candidate"),
733     ("vm_capable", None, ht.TMaybeBool,
734      "Whether node can host instances"),
735     ("ndparams", None, ht.TMaybeDict, "Node parameters"),
736     ]
737
738
739 class OpNodeQuery(OpCode):
740   """Compute the list of nodes."""
741   OP_PARAMS = [
742     _POutputFields,
743     _PUseLocking,
744     ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
745      "Empty list to query all nodes, node names otherwise"),
746     ]
747
748
749 class OpNodeQueryvols(OpCode):
750   """Get list of volumes on node."""
751   OP_PARAMS = [
752     _POutputFields,
753     ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
754      "Empty list to query all nodes, node names otherwise"),
755     ]
756
757
758 class OpNodeQueryStorage(OpCode):
759   """Get information on storage for node(s)."""
760   OP_PARAMS = [
761     _POutputFields,
762     _PStorageType,
763     ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "List of nodes"),
764     ("name", None, ht.TMaybeString, "Storage name"),
765     ]
766
767
768 class OpNodeModifyStorage(OpCode):
769   """Modifies the properies of a storage unit"""
770   OP_PARAMS = [
771     _PNodeName,
772     _PStorageType,
773     _PStorageName,
774     ("changes", ht.NoDefault, ht.TDict, "Requested changes"),
775     ]
776
777
778 class OpRepairNodeStorage(OpCode):
779   """Repairs the volume group on a node."""
780   OP_DSC_FIELD = "node_name"
781   OP_PARAMS = [
782     _PNodeName,
783     _PStorageType,
784     _PStorageName,
785     _PIgnoreConsistency,
786     ]
787
788
789 class OpNodeSetParams(OpCode):
790   """Change the parameters of a node."""
791   OP_DSC_FIELD = "node_name"
792   OP_PARAMS = [
793     _PNodeName,
794     _PForce,
795     ("master_candidate", None, ht.TMaybeBool,
796      "Whether the node should become a master candidate"),
797     ("offline", None, ht.TMaybeBool,
798      "Whether the node should be marked as offline"),
799     ("drained", None, ht.TMaybeBool,
800      "Whether the node should be marked as drained"),
801     ("auto_promote", False, ht.TBool,
802      "Whether node(s) should be promoted to master candidate if necessary"),
803     ("master_capable", None, ht.TMaybeBool,
804      "Denote whether node can become master or master candidate"),
805     ("vm_capable", None, ht.TMaybeBool,
806      "Denote whether node can host instances"),
807     ("secondary_ip", None, ht.TMaybeString,
808      "Change node's secondary IP address"),
809     ("ndparams", None, ht.TMaybeDict, "Set node parameters"),
810     ("powered", None, ht.TMaybeBool,
811      "Whether the node should be marked as powered"),
812     ]
813
814
815 class OpNodePowercycle(OpCode):
816   """Tries to powercycle a node."""
817   OP_DSC_FIELD = "node_name"
818   OP_PARAMS = [
819     _PNodeName,
820     _PForce,
821     ]
822
823
824 class OpNodeMigrate(OpCode):
825   """Migrate all instances from a node."""
826   OP_DSC_FIELD = "node_name"
827   OP_PARAMS = [
828     _PNodeName,
829     _PMigrationMode,
830     _PMigrationLive,
831     ]
832
833
834 class OpNodeEvacStrategy(OpCode):
835   """Compute the evacuation strategy for a list of nodes."""
836   OP_DSC_FIELD = "nodes"
837   OP_PARAMS = [
838     ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), None),
839     ("remote_node", None, ht.TMaybeString, None),
840     ("iallocator", None, ht.TMaybeString, None),
841     ]
842
843
844 # instance opcodes
845
846 class OpInstanceCreate(OpCode):
847   """Create an instance.
848
849   @ivar instance_name: Instance name
850   @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES})
851   @ivar source_handshake: Signed handshake from source (remote import only)
852   @ivar source_x509_ca: Source X509 CA in PEM format (remote import only)
853   @ivar source_instance_name: Previous name of instance (remote import only)
854   @ivar source_shutdown_timeout: Shutdown timeout used for source instance
855     (remote import only)
856
857   """
858   OP_DSC_FIELD = "instance_name"
859   OP_PARAMS = [
860     _PInstanceName,
861     _PForceVariant,
862     _PWaitForSync,
863     _PNameCheck,
864     ("beparams", ht.EmptyDict, ht.TDict, "Backend parameters for instance"),
865     ("disks", ht.NoDefault,
866      # TODO: Generate check from constants.IDISK_PARAMS_TYPES
867      ht.TListOf(ht.TDictOf(ht.TElemOf(constants.IDISK_PARAMS),
868                            ht.TOr(ht.TNonEmptyString, ht.TInt))),
869      "Disk descriptions, for example ``[{\"%s\": 100}, {\"%s\": 5}]``;"
870      " each disk definition must contain a ``%s`` value and"
871      " can contain an optional ``%s`` value denoting the disk access mode"
872      " (%s)" %
873      (constants.IDISK_SIZE, constants.IDISK_SIZE, constants.IDISK_SIZE,
874       constants.IDISK_MODE,
875       " or ".join("``%s``" % i for i in sorted(constants.DISK_ACCESS_SET)))),
876     ("disk_template", ht.NoDefault, _CheckDiskTemplate, "Disk template"),
877     ("file_driver", None, ht.TOr(ht.TNone, ht.TElemOf(constants.FILE_DRIVER)),
878      "Driver for file-backed disks"),
879     ("file_storage_dir", None, ht.TMaybeString,
880      "Directory for storing file-backed disks"),
881     ("hvparams", ht.EmptyDict, ht.TDict,
882      "Hypervisor parameters for instance, hypervisor-dependent"),
883     ("hypervisor", None, ht.TMaybeString, "Hypervisor"),
884     ("iallocator", None, ht.TMaybeString,
885      "Iallocator for deciding which node(s) to use"),
886     ("identify_defaults", False, ht.TBool,
887      "Reset instance parameters to default if equal"),
888     ("ip_check", True, ht.TBool, _PIpCheckDoc),
889     ("mode", ht.NoDefault, ht.TElemOf(constants.INSTANCE_CREATE_MODES),
890      "Instance creation mode"),
891     ("nics", ht.NoDefault, ht.TListOf(_TestNicDef),
892      "List of NIC (network interface) definitions, for example"
893      " ``[{}, {}, {\"%s\": \"198.51.100.4\"}]``; each NIC definition can"
894      " contain the optional values %s" %
895      (constants.INIC_IP,
896       ", ".join("``%s``" % i for i in sorted(constants.INIC_PARAMS)))),
897     ("no_install", None, ht.TMaybeBool,
898      "Do not install the OS (will disable automatic start)"),
899     ("osparams", ht.EmptyDict, ht.TDict, "OS parameters for instance"),
900     ("os_type", None, ht.TMaybeString, "Operating system"),
901     ("pnode", None, ht.TMaybeString, "Primary node"),
902     ("snode", None, ht.TMaybeString, "Secondary node"),
903     ("source_handshake", None, ht.TOr(ht.TList, ht.TNone),
904      "Signed handshake from source (remote import only)"),
905     ("source_instance_name", None, ht.TMaybeString,
906      "Source instance name (remote import only)"),
907     ("source_shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT,
908      ht.TPositiveInt,
909      "How long source instance was given to shut down (remote import only)"),
910     ("source_x509_ca", None, ht.TMaybeString,
911      "Source X509 CA in PEM format (remote import only)"),
912     ("src_node", None, ht.TMaybeString, "Source node for import"),
913     ("src_path", None, ht.TMaybeString, "Source directory for import"),
914     ("start", True, ht.TBool, "Whether to start instance after creation"),
915     ]
916
917
918 class OpInstanceReinstall(OpCode):
919   """Reinstall an instance's OS."""
920   OP_DSC_FIELD = "instance_name"
921   OP_PARAMS = [
922     _PInstanceName,
923     _PForceVariant,
924     ("os_type", None, ht.TMaybeString, "Instance operating system"),
925     ("osparams", None, ht.TMaybeDict, "Temporary OS parameters"),
926     ]
927
928
929 class OpInstanceRemove(OpCode):
930   """Remove an instance."""
931   OP_DSC_FIELD = "instance_name"
932   OP_PARAMS = [
933     _PInstanceName,
934     _PShutdownTimeout,
935     ("ignore_failures", False, ht.TBool,
936      "Whether to ignore failures during removal"),
937     ]
938
939
940 class OpInstanceRename(OpCode):
941   """Rename an instance."""
942   OP_PARAMS = [
943     _PInstanceName,
944     _PNameCheck,
945     ("new_name", ht.NoDefault, ht.TNonEmptyString, "New instance name"),
946     ("ip_check", False, ht.TBool, _PIpCheckDoc),
947     ]
948
949
950 class OpInstanceStartup(OpCode):
951   """Startup an instance."""
952   OP_DSC_FIELD = "instance_name"
953   OP_PARAMS = [
954     _PInstanceName,
955     _PForce,
956     _PIgnoreOfflineNodes,
957     ("hvparams", ht.EmptyDict, ht.TDict,
958      "Temporary hypervisor parameters, hypervisor-dependent"),
959     ("beparams", ht.EmptyDict, ht.TDict, "Temporary backend parameters"),
960     ]
961
962
963 class OpInstanceShutdown(OpCode):
964   """Shutdown an instance."""
965   OP_DSC_FIELD = "instance_name"
966   OP_PARAMS = [
967     _PInstanceName,
968     _PIgnoreOfflineNodes,
969     ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TPositiveInt,
970      "How long to wait for instance to shut down"),
971     ]
972
973
974 class OpInstanceReboot(OpCode):
975   """Reboot an instance."""
976   OP_DSC_FIELD = "instance_name"
977   OP_PARAMS = [
978     _PInstanceName,
979     _PShutdownTimeout,
980     ("ignore_secondaries", False, ht.TBool,
981      "Whether to start the instance even if secondary disks are failing"),
982     ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES),
983      "How to reboot instance"),
984     ]
985
986
987 class OpInstanceReplaceDisks(OpCode):
988   """Replace the disks of an instance."""
989   OP_DSC_FIELD = "instance_name"
990   OP_PARAMS = [
991     _PInstanceName,
992     ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES),
993      "Replacement mode"),
994     ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt),
995      "Disk indexes"),
996     ("remote_node", None, ht.TMaybeString, "New secondary node"),
997     ("iallocator", None, ht.TMaybeString,
998      "Iallocator for deciding new secondary node"),
999     ("early_release", False, ht.TBool,
1000      "Whether to release locks as soon as possible"),
1001     ]
1002
1003
1004 class OpInstanceFailover(OpCode):
1005   """Failover an instance."""
1006   OP_DSC_FIELD = "instance_name"
1007   OP_PARAMS = [
1008     _PInstanceName,
1009     _PShutdownTimeout,
1010     _PIgnoreConsistency,
1011     ]
1012
1013
1014 class OpInstanceMigrate(OpCode):
1015   """Migrate an instance.
1016
1017   This migrates (without shutting down an instance) to its secondary
1018   node.
1019
1020   @ivar instance_name: the name of the instance
1021   @ivar mode: the migration mode (live, non-live or None for auto)
1022
1023   """
1024   OP_DSC_FIELD = "instance_name"
1025   OP_PARAMS = [
1026     _PInstanceName,
1027     _PMigrationMode,
1028     _PMigrationLive,
1029     ("cleanup", False, ht.TBool,
1030      "Whether a previously failed migration should be cleaned up"),
1031     ]
1032
1033
1034 class OpInstanceMove(OpCode):
1035   """Move an instance.
1036
1037   This move (with shutting down an instance and data copying) to an
1038   arbitrary node.
1039
1040   @ivar instance_name: the name of the instance
1041   @ivar target_node: the destination node
1042
1043   """
1044   OP_DSC_FIELD = "instance_name"
1045   OP_PARAMS = [
1046     _PInstanceName,
1047     _PShutdownTimeout,
1048     ("target_node", ht.NoDefault, ht.TNonEmptyString, "Target node"),
1049     ]
1050
1051
1052 class OpInstanceConsole(OpCode):
1053   """Connect to an instance's console."""
1054   OP_DSC_FIELD = "instance_name"
1055   OP_PARAMS = [
1056     _PInstanceName
1057     ]
1058
1059
1060 class OpInstanceActivateDisks(OpCode):
1061   """Activate an instance's disks."""
1062   OP_DSC_FIELD = "instance_name"
1063   OP_PARAMS = [
1064     _PInstanceName,
1065     ("ignore_size", False, ht.TBool, "Whether to ignore recorded size"),
1066     ]
1067
1068
1069 class OpInstanceDeactivateDisks(OpCode):
1070   """Deactivate an instance's disks."""
1071   OP_DSC_FIELD = "instance_name"
1072   OP_PARAMS = [
1073     _PInstanceName,
1074     _PForce,
1075     ]
1076
1077
1078 class OpInstanceRecreateDisks(OpCode):
1079   """Deactivate an instance's disks."""
1080   OP_DSC_FIELD = "instance_name"
1081   OP_PARAMS = [
1082     _PInstanceName,
1083     ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt),
1084      "List of disk indexes"),
1085     ]
1086
1087
1088 class OpInstanceQuery(OpCode):
1089   """Compute the list of instances."""
1090   OP_PARAMS = [
1091     _POutputFields,
1092     _PUseLocking,
1093     ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1094      "Empty list to query all instances, instance names otherwise"),
1095     ]
1096
1097
1098 class OpInstanceQueryData(OpCode):
1099   """Compute the run-time status of instances."""
1100   OP_PARAMS = [
1101     ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
1102     ("static", False, ht.TBool, None),
1103     ]
1104
1105
1106 class OpInstanceSetParams(OpCode):
1107   """Change the parameters of an instance."""
1108   OP_DSC_FIELD = "instance_name"
1109   OP_PARAMS = [
1110     _PInstanceName,
1111     _PForce,
1112     _PForceVariant,
1113     # TODO: Use _TestNicDef
1114     ("nics", ht.EmptyList, ht.TList,
1115      "List of NIC changes. Each item is of the form ``(op, settings)``."
1116      " ``op`` can be ``%s`` to add a new NIC with the specified settings,"
1117      " ``%s`` to remove the last NIC or a number to modify the settings"
1118      " of the NIC with that index." %
1119      (constants.DDM_ADD, constants.DDM_REMOVE)),
1120     ("disks", ht.EmptyList, ht.TList, "List of disk changes. See ``nics``."),
1121     ("beparams", ht.EmptyDict, ht.TDict, "Per-instance backend parameters"),
1122     ("hvparams", ht.EmptyDict, ht.TDict,
1123      "Per-instance hypervisor parameters, hypervisor-dependent"),
1124     ("disk_template", None, ht.TOr(ht.TNone, _CheckDiskTemplate),
1125      "Disk template for instance"),
1126     ("remote_node", None, ht.TMaybeString,
1127      "Secondary node (used when changing disk template)"),
1128     ("os_name", None, ht.TMaybeString,
1129      "Change instance's OS name. Does not reinstall the instance."),
1130     ("osparams", None, ht.TMaybeDict, "Per-instance OS parameters"),
1131     ]
1132
1133
1134 class OpInstanceGrowDisk(OpCode):
1135   """Grow a disk of an instance."""
1136   OP_DSC_FIELD = "instance_name"
1137   OP_PARAMS = [
1138     _PInstanceName,
1139     _PWaitForSync,
1140     ("disk", ht.NoDefault, ht.TInt, "Disk index"),
1141     ("amount", ht.NoDefault, ht.TInt,
1142      "Amount of disk space to add (megabytes)"),
1143     ]
1144
1145
1146 # Node group opcodes
1147
1148 class OpGroupAdd(OpCode):
1149   """Add a node group to the cluster."""
1150   OP_DSC_FIELD = "group_name"
1151   OP_PARAMS = [
1152     _PGroupName,
1153     _PNodeGroupAllocPolicy,
1154     _PGroupNodeParams,
1155     ]
1156
1157
1158 class OpGroupAssignNodes(OpCode):
1159   """Assign nodes to a node group."""
1160   OP_DSC_FIELD = "group_name"
1161   OP_PARAMS = [
1162     _PGroupName,
1163     _PForce,
1164     ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
1165      "List of nodes to assign"),
1166     ]
1167
1168
1169 class OpGroupQuery(OpCode):
1170   """Compute the list of node groups."""
1171   OP_PARAMS = [
1172     _POutputFields,
1173     ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1174      "Empty list to query all groups, group names otherwise"),
1175     ]
1176
1177
1178 class OpGroupSetParams(OpCode):
1179   """Change the parameters of a node group."""
1180   OP_DSC_FIELD = "group_name"
1181   OP_PARAMS = [
1182     _PGroupName,
1183     _PNodeGroupAllocPolicy,
1184     _PGroupNodeParams,
1185     ]
1186
1187
1188 class OpGroupRemove(OpCode):
1189   """Remove a node group from the cluster."""
1190   OP_DSC_FIELD = "group_name"
1191   OP_PARAMS = [
1192     _PGroupName,
1193     ]
1194
1195
1196 class OpGroupRename(OpCode):
1197   """Rename a node group in the cluster."""
1198   OP_PARAMS = [
1199     _PGroupName,
1200     ("new_name", ht.NoDefault, ht.TNonEmptyString, "New group name"),
1201     ]
1202
1203
1204 # OS opcodes
1205 class OpOsDiagnose(OpCode):
1206   """Compute the list of guest operating systems."""
1207   OP_PARAMS = [
1208     _POutputFields,
1209     ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1210      "Which operating systems to diagnose"),
1211     ]
1212
1213
1214 # Exports opcodes
1215 class OpBackupQuery(OpCode):
1216   """Compute the list of exported images."""
1217   OP_PARAMS = [
1218     _PUseLocking,
1219     ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1220      "Empty list to query all nodes, node names otherwise"),
1221     ]
1222
1223
1224 class OpBackupPrepare(OpCode):
1225   """Prepares an instance export.
1226
1227   @ivar instance_name: Instance name
1228   @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1229
1230   """
1231   OP_DSC_FIELD = "instance_name"
1232   OP_PARAMS = [
1233     _PInstanceName,
1234     ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES),
1235      "Export mode"),
1236     ]
1237
1238
1239 class OpBackupExport(OpCode):
1240   """Export an instance.
1241
1242   For local exports, the export destination is the node name. For remote
1243   exports, the export destination is a list of tuples, each consisting of
1244   hostname/IP address, port, HMAC and HMAC salt. The HMAC is calculated using
1245   the cluster domain secret over the value "${index}:${hostname}:${port}". The
1246   destination X509 CA must be a signed certificate.
1247
1248   @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1249   @ivar target_node: Export destination
1250   @ivar x509_key_name: X509 key to use (remote export only)
1251   @ivar destination_x509_ca: Destination X509 CA in PEM format (remote export
1252                              only)
1253
1254   """
1255   OP_DSC_FIELD = "instance_name"
1256   OP_PARAMS = [
1257     _PInstanceName,
1258     _PShutdownTimeout,
1259     # TODO: Rename target_node as it changes meaning for different export modes
1260     # (e.g. "destination")
1261     ("target_node", ht.NoDefault, ht.TOr(ht.TNonEmptyString, ht.TList),
1262      "Destination information, depends on export mode"),
1263     ("shutdown", True, ht.TBool, "Whether to shutdown instance before export"),
1264     ("remove_instance", False, ht.TBool,
1265      "Whether to remove instance after export"),
1266     ("ignore_remove_failures", False, ht.TBool,
1267      "Whether to ignore failures while removing instances"),
1268     ("mode", constants.EXPORT_MODE_LOCAL, ht.TElemOf(constants.EXPORT_MODES),
1269      "Export mode"),
1270     ("x509_key_name", None, ht.TOr(ht.TList, ht.TNone),
1271      "Name of X509 key (remote export only)"),
1272     ("destination_x509_ca", None, ht.TMaybeString,
1273      "Destination X509 CA (remote export only)"),
1274     ]
1275
1276
1277 class OpBackupRemove(OpCode):
1278   """Remove an instance's export."""
1279   OP_DSC_FIELD = "instance_name"
1280   OP_PARAMS = [
1281     _PInstanceName,
1282     ]
1283
1284
1285 # Tags opcodes
1286 class OpTagsGet(OpCode):
1287   """Returns the tags of the given object."""
1288   OP_DSC_FIELD = "name"
1289   OP_PARAMS = [
1290     _PTagKind,
1291     # Name is only meaningful for nodes and instances
1292     ("name", ht.NoDefault, ht.TMaybeString, None),
1293     ]
1294
1295
1296 class OpTagsSearch(OpCode):
1297   """Searches the tags in the cluster for a given pattern."""
1298   OP_DSC_FIELD = "pattern"
1299   OP_PARAMS = [
1300     ("pattern", ht.NoDefault, ht.TNonEmptyString, None),
1301     ]
1302
1303
1304 class OpTagsSet(OpCode):
1305   """Add a list of tags on a given object."""
1306   OP_PARAMS = [
1307     _PTagKind,
1308     _PTags,
1309     # Name is only meaningful for nodes and instances
1310     ("name", ht.NoDefault, ht.TMaybeString, None),
1311     ]
1312
1313
1314 class OpTagsDel(OpCode):
1315   """Remove a list of tags from a given object."""
1316   OP_PARAMS = [
1317     _PTagKind,
1318     _PTags,
1319     # Name is only meaningful for nodes and instances
1320     ("name", ht.NoDefault, ht.TMaybeString, None),
1321     ]
1322
1323 # Test opcodes
1324 class OpTestDelay(OpCode):
1325   """Sleeps for a configured amount of time.
1326
1327   This is used just for debugging and testing.
1328
1329   Parameters:
1330     - duration: the time to sleep
1331     - on_master: if true, sleep on the master
1332     - on_nodes: list of nodes in which to sleep
1333
1334   If the on_master parameter is true, it will execute a sleep on the
1335   master (before any node sleep).
1336
1337   If the on_nodes list is not empty, it will sleep on those nodes
1338   (after the sleep on the master, if that is enabled).
1339
1340   As an additional feature, the case of duration < 0 will be reported
1341   as an execution error, so this opcode can be used as a failure
1342   generator. The case of duration == 0 will not be treated specially.
1343
1344   """
1345   OP_DSC_FIELD = "duration"
1346   OP_PARAMS = [
1347     ("duration", ht.NoDefault, ht.TFloat, None),
1348     ("on_master", True, ht.TBool, None),
1349     ("on_nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
1350     ("repeat", 0, ht.TPositiveInt, None),
1351     ]
1352
1353
1354 class OpTestAllocator(OpCode):
1355   """Allocator framework testing.
1356
1357   This opcode has two modes:
1358     - gather and return allocator input for a given mode (allocate new
1359       or replace secondary) and a given instance definition (direction
1360       'in')
1361     - run a selected allocator for a given operation (as above) and
1362       return the allocator output (direction 'out')
1363
1364   """
1365   OP_DSC_FIELD = "allocator"
1366   OP_PARAMS = [
1367     ("direction", ht.NoDefault,
1368      ht.TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS), None),
1369     ("mode", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_MODES), None),
1370     ("name", ht.NoDefault, ht.TNonEmptyString, None),
1371     ("nics", ht.NoDefault, ht.TOr(ht.TNone, ht.TListOf(
1372      ht.TDictOf(ht.TElemOf(["mac", "ip", "bridge"]),
1373                 ht.TOr(ht.TNone, ht.TNonEmptyString)))), None),
1374     ("disks", ht.NoDefault, ht.TOr(ht.TNone, ht.TList), None),
1375     ("hypervisor", None, ht.TMaybeString, None),
1376     ("allocator", None, ht.TMaybeString, None),
1377     ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
1378     ("mem_size", None, ht.TOr(ht.TNone, ht.TPositiveInt), None),
1379     ("vcpus", None, ht.TOr(ht.TNone, ht.TPositiveInt), None),
1380     ("os", None, ht.TMaybeString, None),
1381     ("disk_template", None, ht.TMaybeString, None),
1382     ("evac_nodes", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString)),
1383      None),
1384     ]
1385
1386
1387 class OpTestJqueue(OpCode):
1388   """Utility opcode to test some aspects of the job queue.
1389
1390   """
1391   OP_PARAMS = [
1392     ("notify_waitlock", False, ht.TBool, None),
1393     ("notify_exec", False, ht.TBool, None),
1394     ("log_messages", ht.EmptyList, ht.TListOf(ht.TString), None),
1395     ("fail", False, ht.TBool, None),
1396     ]
1397
1398
1399 class OpTestDummy(OpCode):
1400   """Utility opcode used by unittests.
1401
1402   """
1403   OP_PARAMS = [
1404     ("result", ht.NoDefault, ht.NoType, None),
1405     ("messages", ht.NoDefault, ht.NoType, None),
1406     ("fail", ht.NoDefault, ht.NoType, None),
1407     ]
1408   WITH_LU = False
1409
1410
1411 def _GetOpList():
1412   """Returns list of all defined opcodes.
1413
1414   Does not eliminate duplicates by C{OP_ID}.
1415
1416   """
1417   return [v for v in globals().values()
1418           if (isinstance(v, type) and issubclass(v, OpCode) and
1419               hasattr(v, "OP_ID") and v is not OpCode)]
1420
1421
1422 OP_MAPPING = dict((v.OP_ID, v) for v in _GetOpList())