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