Fix the node powered field
[ganeti-local] / lib / opcodes.py
1 #
2 #
3
4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 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=R0903
35
36 import logging
37 import re
38
39 from ganeti import constants
40 from ganeti import errors
41 from ganeti import ht
42 from ganeti import objects
43 from ganeti import objectutils
44
45
46 # Common opcode attributes
47
48 #: output fields for a query operation
49 _POutputFields = ("output_fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
50                   "Selected output fields")
51
52 #: the shutdown timeout
53 _PShutdownTimeout = \
54   ("shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TPositiveInt,
55    "How long to wait for instance to shut down")
56
57 #: the force parameter
58 _PForce = ("force", False, ht.TBool, "Whether to force the operation")
59
60 #: a required instance name (for single-instance LUs)
61 _PInstanceName = ("instance_name", ht.NoDefault, ht.TNonEmptyString,
62                   "Instance name")
63
64 #: Whether to ignore offline nodes
65 _PIgnoreOfflineNodes = ("ignore_offline_nodes", False, ht.TBool,
66                         "Whether to ignore offline nodes")
67
68 #: a required node name (for single-node LUs)
69 _PNodeName = ("node_name", ht.NoDefault, ht.TNonEmptyString, "Node name")
70
71 #: a required node group name (for single-group LUs)
72 _PGroupName = ("group_name", ht.NoDefault, ht.TNonEmptyString, "Group name")
73
74 #: Migration type (live/non-live)
75 _PMigrationMode = ("mode", None,
76                    ht.TOr(ht.TNone, ht.TElemOf(constants.HT_MIGRATION_MODES)),
77                    "Migration mode")
78
79 #: Obsolete 'live' migration mode (boolean)
80 _PMigrationLive = ("live", None, ht.TMaybeBool,
81                    "Legacy setting for live migration, do not use")
82
83 #: Tag type
84 _PTagKind = ("kind", ht.NoDefault, ht.TElemOf(constants.VALID_TAG_TYPES),
85              "Tag kind")
86
87 #: List of tag strings
88 _PTags = ("tags", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
89           "List of tag names")
90
91 _PForceVariant = ("force_variant", False, ht.TBool,
92                   "Whether to force an unknown OS variant")
93
94 _PWaitForSync = ("wait_for_sync", True, ht.TBool,
95                  "Whether to wait for the disk to synchronize")
96
97 _PWaitForSyncFalse = ("wait_for_sync", False, ht.TBool,
98                       "Whether to wait for the disk to synchronize"
99                       " (defaults to false)")
100
101 _PIgnoreConsistency = ("ignore_consistency", False, ht.TBool,
102                        "Whether to ignore disk consistency")
103
104 _PStorageName = ("name", ht.NoDefault, ht.TMaybeString, "Storage name")
105
106 _PUseLocking = ("use_locking", False, ht.TBool,
107                 "Whether to use synchronization")
108
109 _PNameCheck = ("name_check", True, ht.TBool, "Whether to check name")
110
111 _PNodeGroupAllocPolicy = \
112   ("alloc_policy", None,
113    ht.TOr(ht.TNone, ht.TElemOf(constants.VALID_ALLOC_POLICIES)),
114    "Instance allocation policy")
115
116 _PGroupNodeParams = ("ndparams", None, ht.TMaybeDict,
117                      "Default node parameters for group")
118
119 _PQueryWhat = ("what", ht.NoDefault, ht.TElemOf(constants.QR_VIA_OP),
120                "Resource(s) to query for")
121
122 _PEarlyRelease = ("early_release", False, ht.TBool,
123                   "Whether to release locks as soon as possible")
124
125 _PIpCheckDoc = "Whether to ensure instance's IP address is inactive"
126
127 #: Do not remember instance state changes
128 _PNoRemember = ("no_remember", False, ht.TBool,
129                 "Do not remember the state change")
130
131 #: Target node for instance migration/failover
132 _PMigrationTargetNode = ("target_node", None, ht.TMaybeString,
133                          "Target node for shared-storage instances")
134
135 _PStartupPaused = ("startup_paused", False, ht.TBool,
136                    "Pause instance at startup")
137
138 _PVerbose = ("verbose", False, ht.TBool, "Verbose mode")
139
140 # Parameters for cluster verification
141 _PDebugSimulateErrors = ("debug_simulate_errors", False, ht.TBool,
142                          "Whether to simulate errors (useful for debugging)")
143 _PErrorCodes = ("error_codes", False, ht.TBool, "Error codes")
144 _PSkipChecks = ("skip_checks", ht.EmptyList,
145                 ht.TListOf(ht.TElemOf(constants.VERIFY_OPTIONAL_CHECKS)),
146                 "Which checks to skip")
147 _PIgnoreErrors = ("ignore_errors", ht.EmptyList,
148                   ht.TListOf(ht.TElemOf(constants.CV_ALL_ECODES_STRINGS)),
149                   "List of error codes that should be treated as warnings")
150
151 # Disk parameters
152 _PDiskParams = ("diskparams", None,
153                 ht.TOr(
154                   ht.TDictOf(ht.TElemOf(constants.DISK_TEMPLATES), ht.TDict),
155                   ht.TNone),
156                 "Disk templates' parameter defaults")
157
158 # Parameters for node resource model
159 _PHvState = ("hv_state", None, ht.TMaybeDict, "Set hypervisor states")
160 _PDiskState = ("disk_state", None, ht.TMaybeDict, "Set disk states")
161
162
163 _PIgnoreIpolicy = ("ignore_ipolicy", False, ht.TBool,
164                    "Whether to ignore ipolicy violations")
165
166 # Allow runtime changes while migrating
167 _PAllowRuntimeChgs = ("allow_runtime_changes", True, ht.TBool,
168                       "Allow runtime changes (eg. memory ballooning)")
169
170
171 #: OP_ID conversion regular expression
172 _OPID_RE = re.compile("([a-z])([A-Z])")
173
174 #: Utility function for L{OpClusterSetParams}
175 _TestClusterOsListItem = \
176   ht.TAnd(ht.TIsLength(2), ht.TItems([
177     ht.TElemOf(constants.DDMS_VALUES),
178     ht.TNonEmptyString,
179     ]))
180
181 _TestClusterOsList = ht.TMaybeListOf(_TestClusterOsListItem)
182
183 # TODO: Generate check from constants.INIC_PARAMS_TYPES
184 #: Utility function for testing NIC definitions
185 _TestNicDef = \
186   ht.Comment("NIC parameters")(ht.TDictOf(ht.TElemOf(constants.INIC_PARAMS),
187                                           ht.TOr(ht.TNone, ht.TNonEmptyString)))
188
189 _TSetParamsResultItemItems = [
190   ht.Comment("name of changed parameter")(ht.TNonEmptyString),
191   ht.Comment("new value")(ht.TAny),
192   ]
193
194 _TSetParamsResult = \
195   ht.TListOf(ht.TAnd(ht.TIsLength(len(_TSetParamsResultItemItems)),
196                      ht.TItems(_TSetParamsResultItemItems)))
197
198 # TODO: Generate check from constants.IDISK_PARAMS_TYPES (however, not all users
199 # of this check support all parameters)
200 _TDiskParams = \
201   ht.Comment("Disk parameters")(ht.TDictOf(ht.TElemOf(constants.IDISK_PARAMS),
202                                            ht.TOr(ht.TNonEmptyString, ht.TInt)))
203
204 _TQueryRow = \
205   ht.TListOf(ht.TAnd(ht.TIsLength(2),
206                      ht.TItems([ht.TElemOf(constants.RS_ALL),
207                                 ht.TAny])))
208
209 _TQueryResult = ht.TListOf(_TQueryRow)
210
211 _TOldQueryRow = ht.TListOf(ht.TAny)
212
213 _TOldQueryResult = ht.TListOf(_TOldQueryRow)
214
215
216 _SUMMARY_PREFIX = {
217   "CLUSTER_": "C_",
218   "GROUP_": "G_",
219   "NODE_": "N_",
220   "INSTANCE_": "I_",
221   }
222
223 #: Attribute name for dependencies
224 DEPEND_ATTR = "depends"
225
226 #: Attribute name for comment
227 COMMENT_ATTR = "comment"
228
229
230 def _NameToId(name):
231   """Convert an opcode class name to an OP_ID.
232
233   @type name: string
234   @param name: the class name, as OpXxxYyy
235   @rtype: string
236   @return: the name in the OP_XXXX_YYYY format
237
238   """
239   if not name.startswith("Op"):
240     return None
241   # Note: (?<=[a-z])(?=[A-Z]) would be ideal, since it wouldn't
242   # consume any input, and hence we would just have all the elements
243   # in the list, one by one; but it seems that split doesn't work on
244   # non-consuming input, hence we have to process the input string a
245   # bit
246   name = _OPID_RE.sub(r"\1,\2", name)
247   elems = name.split(",")
248   return "_".join(n.upper() for n in elems)
249
250
251 def _GenerateObjectTypeCheck(obj, fields_types):
252   """Helper to generate type checks for objects.
253
254   @param obj: The object to generate type checks
255   @param fields_types: The fields and their types as a dict
256   @return: A ht type check function
257
258   """
259   assert set(obj.GetAllSlots()) == set(fields_types.keys()), \
260     "%s != %s" % (set(obj.GetAllSlots()), set(fields_types.keys()))
261   return ht.TStrictDict(True, True, fields_types)
262
263
264 _TQueryFieldDef = \
265   _GenerateObjectTypeCheck(objects.QueryFieldDefinition, {
266     "name": ht.TNonEmptyString,
267     "title": ht.TNonEmptyString,
268     "kind": ht.TElemOf(constants.QFT_ALL),
269     "doc": ht.TNonEmptyString,
270     })
271
272
273 def RequireFileStorage():
274   """Checks that file storage is enabled.
275
276   While it doesn't really fit into this module, L{utils} was deemed too large
277   of a dependency to be imported for just one or two functions.
278
279   @raise errors.OpPrereqError: when file storage is disabled
280
281   """
282   if not constants.ENABLE_FILE_STORAGE:
283     raise errors.OpPrereqError("File storage disabled at configure time",
284                                errors.ECODE_INVAL)
285
286
287 def RequireSharedFileStorage():
288   """Checks that shared file storage is enabled.
289
290   While it doesn't really fit into this module, L{utils} was deemed too large
291   of a dependency to be imported for just one or two functions.
292
293   @raise errors.OpPrereqError: when shared file storage is disabled
294
295   """
296   if not constants.ENABLE_SHARED_FILE_STORAGE:
297     raise errors.OpPrereqError("Shared file storage disabled at"
298                                " configure time", errors.ECODE_INVAL)
299
300
301 @ht.WithDesc("CheckFileStorage")
302 def _CheckFileStorage(value):
303   """Ensures file storage is enabled if used.
304
305   """
306   if value == constants.DT_FILE:
307     RequireFileStorage()
308   elif value == constants.DT_SHARED_FILE:
309     RequireSharedFileStorage()
310   return True
311
312
313 def _BuildDiskTemplateCheck(accept_none):
314   """Builds check for disk template.
315
316   @type accept_none: bool
317   @param accept_none: whether to accept None as a correct value
318   @rtype: callable
319
320   """
321   template_check = ht.TElemOf(constants.DISK_TEMPLATES)
322
323   if accept_none:
324     template_check = ht.TOr(template_check, ht.TNone)
325
326   return ht.TAnd(template_check, _CheckFileStorage)
327
328
329 def _CheckStorageType(storage_type):
330   """Ensure a given storage type is valid.
331
332   """
333   if storage_type not in constants.VALID_STORAGE_TYPES:
334     raise errors.OpPrereqError("Unknown storage type: %s" % storage_type,
335                                errors.ECODE_INVAL)
336   if storage_type == constants.ST_FILE:
337     # TODO: What about shared file storage?
338     RequireFileStorage()
339   return True
340
341
342 #: Storage type parameter
343 _PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType,
344                  "Storage type")
345
346
347 class _AutoOpParamSlots(objectutils.AutoSlots):
348   """Meta class for opcode definitions.
349
350   """
351   def __new__(mcs, name, bases, attrs):
352     """Called when a class should be created.
353
354     @param mcs: The meta class
355     @param name: Name of created class
356     @param bases: Base classes
357     @type attrs: dict
358     @param attrs: Class attributes
359
360     """
361     assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
362
363     slots = mcs._GetSlots(attrs)
364     assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
365       "Class '%s' uses unknown field in OP_DSC_FIELD" % name
366
367     attrs["OP_ID"] = _NameToId(name)
368
369     return objectutils.AutoSlots.__new__(mcs, name, bases, attrs)
370
371   @classmethod
372   def _GetSlots(mcs, attrs):
373     """Build the slots out of OP_PARAMS.
374
375     """
376     # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams
377     params = attrs.setdefault("OP_PARAMS", [])
378
379     # Use parameter names as slots
380     return [pname for (pname, _, _, _) in params]
381
382
383 class BaseOpCode(objectutils.ValidatedSlots):
384   """A simple serializable object.
385
386   This object serves as a parent class for OpCode without any custom
387   field handling.
388
389   """
390   # pylint: disable=E1101
391   # as OP_ID is dynamically defined
392   __metaclass__ = _AutoOpParamSlots
393
394   def __getstate__(self):
395     """Generic serializer.
396
397     This method just returns the contents of the instance as a
398     dictionary.
399
400     @rtype:  C{dict}
401     @return: the instance attributes and their values
402
403     """
404     state = {}
405     for name in self.GetAllSlots():
406       if hasattr(self, name):
407         state[name] = getattr(self, name)
408     return state
409
410   def __setstate__(self, state):
411     """Generic unserializer.
412
413     This method just restores from the serialized state the attributes
414     of the current instance.
415
416     @param state: the serialized opcode data
417     @type state:  C{dict}
418
419     """
420     if not isinstance(state, dict):
421       raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
422                        type(state))
423
424     for name in self.GetAllSlots():
425       if name not in state and hasattr(self, name):
426         delattr(self, name)
427
428     for name in state:
429       setattr(self, name, state[name])
430
431   @classmethod
432   def GetAllParams(cls):
433     """Compute list of all parameters for an opcode.
434
435     """
436     slots = []
437     for parent in cls.__mro__:
438       slots.extend(getattr(parent, "OP_PARAMS", []))
439     return slots
440
441   def Validate(self, set_defaults): # pylint: disable=W0221
442     """Validate opcode parameters, optionally setting default values.
443
444     @type set_defaults: bool
445     @param set_defaults: Whether to set default values
446     @raise errors.OpPrereqError: When a parameter value doesn't match
447                                  requirements
448
449     """
450     for (attr_name, default, test, _) in self.GetAllParams():
451       assert test == ht.NoType or callable(test)
452
453       if not hasattr(self, attr_name):
454         if default == ht.NoDefault:
455           raise errors.OpPrereqError("Required parameter '%s.%s' missing" %
456                                      (self.OP_ID, attr_name),
457                                      errors.ECODE_INVAL)
458         elif set_defaults:
459           if callable(default):
460             dval = default()
461           else:
462             dval = default
463           setattr(self, attr_name, dval)
464
465       if test == ht.NoType:
466         # no tests here
467         continue
468
469       if set_defaults or hasattr(self, attr_name):
470         attr_val = getattr(self, attr_name)
471         if not test(attr_val):
472           logging.error("OpCode %s, parameter %s, has invalid type %s/value %s"
473                         " expecting type %s",
474                         self.OP_ID, attr_name, type(attr_val), attr_val, test)
475           raise errors.OpPrereqError("Parameter '%s.%s' fails validation" %
476                                      (self.OP_ID, attr_name),
477                                      errors.ECODE_INVAL)
478
479
480 def _BuildJobDepCheck(relative):
481   """Builds check for job dependencies (L{DEPEND_ATTR}).
482
483   @type relative: bool
484   @param relative: Whether to accept relative job IDs (negative)
485   @rtype: callable
486
487   """
488   if relative:
489     job_id = ht.TOr(ht.TJobId, ht.TRelativeJobId)
490   else:
491     job_id = ht.TJobId
492
493   job_dep = \
494     ht.TAnd(ht.TIsLength(2),
495             ht.TItems([job_id,
496                        ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))]))
497
498   return ht.TMaybeListOf(job_dep)
499
500
501 TNoRelativeJobDependencies = _BuildJobDepCheck(False)
502
503 #: List of submission status and job ID as returned by C{SubmitManyJobs}
504 _TJobIdListItem = \
505   ht.TAnd(ht.TIsLength(2),
506           ht.TItems([ht.Comment("success")(ht.TBool),
507                      ht.Comment("Job ID if successful, error message"
508                                 " otherwise")(ht.TOr(ht.TString,
509                                                      ht.TJobId))]))
510 TJobIdList = ht.TListOf(_TJobIdListItem)
511
512 #: Result containing only list of submitted jobs
513 TJobIdListOnly = ht.TStrictDict(True, True, {
514   constants.JOB_IDS_KEY: ht.Comment("List of submitted jobs")(TJobIdList),
515   })
516
517
518 class OpCode(BaseOpCode):
519   """Abstract OpCode.
520
521   This is the root of the actual OpCode hierarchy. All clases derived
522   from this class should override OP_ID.
523
524   @cvar OP_ID: The ID of this opcode. This should be unique amongst all
525                children of this class.
526   @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
527                       string returned by Summary(); see the docstring of that
528                       method for details).
529   @cvar OP_PARAMS: List of opcode attributes, the default values they should
530                    get if not already defined, and types they must match.
531   @cvar OP_RESULT: Callable to verify opcode result
532   @cvar WITH_LU: Boolean that specifies whether this should be included in
533       mcpu's dispatch table
534   @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
535                  the check steps
536   @ivar priority: Opcode priority for queue
537
538   """
539   # pylint: disable=E1101
540   # as OP_ID is dynamically defined
541   WITH_LU = True
542   OP_PARAMS = [
543     ("dry_run", None, ht.TMaybeBool, "Run checks only, don't execute"),
544     ("debug_level", None, ht.TOr(ht.TNone, ht.TPositiveInt), "Debug level"),
545     ("priority", constants.OP_PRIO_DEFAULT,
546      ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID), "Opcode priority"),
547     (DEPEND_ATTR, None, _BuildJobDepCheck(True),
548      "Job dependencies; if used through ``SubmitManyJobs`` relative (negative)"
549      " job IDs can be used; see :doc:`design document <design-chained-jobs>`"
550      " for details"),
551     (COMMENT_ATTR, None, ht.TMaybeString,
552      "Comment describing the purpose of the opcode"),
553     ]
554   OP_RESULT = None
555
556   def __getstate__(self):
557     """Specialized getstate for opcodes.
558
559     This method adds to the state dictionary the OP_ID of the class,
560     so that on unload we can identify the correct class for
561     instantiating the opcode.
562
563     @rtype:   C{dict}
564     @return:  the state as a dictionary
565
566     """
567     data = BaseOpCode.__getstate__(self)
568     data["OP_ID"] = self.OP_ID
569     return data
570
571   @classmethod
572   def LoadOpCode(cls, data):
573     """Generic load opcode method.
574
575     The method identifies the correct opcode class from the dict-form
576     by looking for a OP_ID key, if this is not found, or its value is
577     not available in this module as a child of this class, we fail.
578
579     @type data:  C{dict}
580     @param data: the serialized opcode
581
582     """
583     if not isinstance(data, dict):
584       raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
585     if "OP_ID" not in data:
586       raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
587     op_id = data["OP_ID"]
588     op_class = None
589     if op_id in OP_MAPPING:
590       op_class = OP_MAPPING[op_id]
591     else:
592       raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
593                        op_id)
594     op = op_class()
595     new_data = data.copy()
596     del new_data["OP_ID"]
597     op.__setstate__(new_data)
598     return op
599
600   def Summary(self):
601     """Generates a summary description of this opcode.
602
603     The summary is the value of the OP_ID attribute (without the "OP_"
604     prefix), plus the value of the OP_DSC_FIELD attribute, if one was
605     defined; this field should allow to easily identify the operation
606     (for an instance creation job, e.g., it would be the instance
607     name).
608
609     """
610     assert self.OP_ID is not None and len(self.OP_ID) > 3
611     # all OP_ID start with OP_, we remove that
612     txt = self.OP_ID[3:]
613     field_name = getattr(self, "OP_DSC_FIELD", None)
614     if field_name:
615       field_value = getattr(self, field_name, None)
616       if isinstance(field_value, (list, tuple)):
617         field_value = ",".join(str(i) for i in field_value)
618       txt = "%s(%s)" % (txt, field_value)
619     return txt
620
621   def TinySummary(self):
622     """Generates a compact summary description of the opcode.
623
624     """
625     assert self.OP_ID.startswith("OP_")
626
627     text = self.OP_ID[3:]
628
629     for (prefix, supplement) in _SUMMARY_PREFIX.items():
630       if text.startswith(prefix):
631         return supplement + text[len(prefix):]
632
633     return text
634
635
636 # cluster opcodes
637
638 class OpClusterPostInit(OpCode):
639   """Post cluster initialization.
640
641   This opcode does not touch the cluster at all. Its purpose is to run hooks
642   after the cluster has been initialized.
643
644   """
645   OP_RESULT = ht.TBool
646
647
648 class OpClusterDestroy(OpCode):
649   """Destroy the cluster.
650
651   This opcode has no other parameters. All the state is irreversibly
652   lost after the execution of this opcode.
653
654   """
655   OP_RESULT = ht.TNonEmptyString
656
657
658 class OpClusterQuery(OpCode):
659   """Query cluster information."""
660   OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TAny)
661
662
663 class OpClusterVerify(OpCode):
664   """Submits all jobs necessary to verify the cluster.
665
666   """
667   OP_PARAMS = [
668     _PDebugSimulateErrors,
669     _PErrorCodes,
670     _PSkipChecks,
671     _PIgnoreErrors,
672     _PVerbose,
673     ("group_name", None, ht.TMaybeString, "Group to verify")
674     ]
675   OP_RESULT = TJobIdListOnly
676
677
678 class OpClusterVerifyConfig(OpCode):
679   """Verify the cluster config.
680
681   """
682   OP_PARAMS = [
683     _PDebugSimulateErrors,
684     _PErrorCodes,
685     _PIgnoreErrors,
686     _PVerbose,
687     ]
688   OP_RESULT = ht.TBool
689
690
691 class OpClusterVerifyGroup(OpCode):
692   """Run verify on a node group from the cluster.
693
694   @type skip_checks: C{list}
695   @ivar skip_checks: steps to be skipped from the verify process; this
696                      needs to be a subset of
697                      L{constants.VERIFY_OPTIONAL_CHECKS}; currently
698                      only L{constants.VERIFY_NPLUSONE_MEM} can be passed
699
700   """
701   OP_DSC_FIELD = "group_name"
702   OP_PARAMS = [
703     _PGroupName,
704     _PDebugSimulateErrors,
705     _PErrorCodes,
706     _PSkipChecks,
707     _PIgnoreErrors,
708     _PVerbose,
709     ]
710   OP_RESULT = ht.TBool
711
712
713 class OpClusterVerifyDisks(OpCode):
714   """Verify the cluster disks.
715
716   """
717   OP_RESULT = TJobIdListOnly
718
719
720 class OpGroupVerifyDisks(OpCode):
721   """Verifies the status of all disks in a node group.
722
723   Result: a tuple of three elements:
724     - dict of node names with issues (values: error msg)
725     - list of instances with degraded disks (that should be activated)
726     - dict of instances with missing logical volumes (values: (node, vol)
727       pairs with details about the missing volumes)
728
729   In normal operation, all lists should be empty. A non-empty instance
730   list (3rd element of the result) is still ok (errors were fixed) but
731   non-empty node list means some node is down, and probably there are
732   unfixable drbd errors.
733
734   Note that only instances that are drbd-based are taken into
735   consideration. This might need to be revisited in the future.
736
737   """
738   OP_DSC_FIELD = "group_name"
739   OP_PARAMS = [
740     _PGroupName,
741     ]
742   OP_RESULT = \
743     ht.TAnd(ht.TIsLength(3),
744             ht.TItems([ht.TDictOf(ht.TString, ht.TString),
745                        ht.TListOf(ht.TString),
746                        ht.TDictOf(ht.TString,
747                                   ht.TListOf(ht.TListOf(ht.TString)))]))
748
749
750 class OpClusterRepairDiskSizes(OpCode):
751   """Verify the disk sizes of the instances and fixes configuration
752   mimatches.
753
754   Parameters: optional instances list, in case we want to restrict the
755   checks to only a subset of the instances.
756
757   Result: a list of tuples, (instance, disk, new-size) for changed
758   configurations.
759
760   In normal operation, the list should be empty.
761
762   @type instances: list
763   @ivar instances: the list of instances to check, or empty for all instances
764
765   """
766   OP_PARAMS = [
767     ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
768     ]
769   OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3),
770                                  ht.TItems([ht.TNonEmptyString,
771                                             ht.TPositiveInt,
772                                             ht.TPositiveInt])))
773
774
775 class OpClusterConfigQuery(OpCode):
776   """Query cluster configuration values."""
777   OP_PARAMS = [
778     _POutputFields
779     ]
780   OP_RESULT = ht.TListOf(ht.TAny)
781
782
783 class OpClusterRename(OpCode):
784   """Rename the cluster.
785
786   @type name: C{str}
787   @ivar name: The new name of the cluster. The name and/or the master IP
788               address will be changed to match the new name and its IP
789               address.
790
791   """
792   OP_DSC_FIELD = "name"
793   OP_PARAMS = [
794     ("name", ht.NoDefault, ht.TNonEmptyString, None),
795     ]
796   OP_RESULT = ht.TNonEmptyString
797
798
799 class OpClusterSetParams(OpCode):
800   """Change the parameters of the cluster.
801
802   @type vg_name: C{str} or C{None}
803   @ivar vg_name: The new volume group name or None to disable LVM usage.
804
805   """
806   OP_PARAMS = [
807     _PHvState,
808     _PDiskState,
809     ("vg_name", None, ht.TMaybeString, "Volume group name"),
810     ("enabled_hypervisors", None,
811      ht.TOr(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)), ht.TTrue),
812             ht.TNone),
813      "List of enabled hypervisors"),
814     ("hvparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
815                               ht.TNone),
816      "Cluster-wide hypervisor parameter defaults, hypervisor-dependent"),
817     ("beparams", None, ht.TOr(ht.TDict, ht.TNone),
818      "Cluster-wide backend parameter defaults"),
819     ("os_hvp", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
820                             ht.TNone),
821      "Cluster-wide per-OS hypervisor parameter defaults"),
822     ("osparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
823                               ht.TNone),
824      "Cluster-wide OS parameter defaults"),
825     _PDiskParams,
826     ("candidate_pool_size", None, ht.TOr(ht.TStrictPositiveInt, ht.TNone),
827      "Master candidate pool size"),
828     ("uid_pool", None, ht.NoType,
829      "Set UID pool, must be list of lists describing UID ranges (two items,"
830      " start and end inclusive)"),
831     ("add_uids", None, ht.NoType,
832      "Extend UID pool, must be list of lists describing UID ranges (two"
833      " items, start and end inclusive) to be added"),
834     ("remove_uids", None, ht.NoType,
835      "Shrink UID pool, must be list of lists describing UID ranges (two"
836      " items, start and end inclusive) to be removed"),
837     ("maintain_node_health", None, ht.TMaybeBool,
838      "Whether to automatically maintain node health"),
839     ("prealloc_wipe_disks", None, ht.TMaybeBool,
840      "Whether to wipe disks before allocating them to instances"),
841     ("nicparams", None, ht.TMaybeDict, "Cluster-wide NIC parameter defaults"),
842     ("ndparams", None, ht.TMaybeDict, "Cluster-wide node parameter defaults"),
843     ("ipolicy", None, ht.TMaybeDict,
844      "Cluster-wide :ref:`instance policy <rapi-ipolicy>` specs"),
845     ("drbd_helper", None, ht.TOr(ht.TString, ht.TNone), "DRBD helper program"),
846     ("default_iallocator", None, ht.TOr(ht.TString, ht.TNone),
847      "Default iallocator for cluster"),
848     ("master_netdev", None, ht.TOr(ht.TString, ht.TNone),
849      "Master network device"),
850     ("master_netmask", None, ht.TOr(ht.TInt, ht.TNone),
851      "Netmask of the master IP"),
852     ("reserved_lvs", None, ht.TMaybeListOf(ht.TNonEmptyString),
853      "List of reserved LVs"),
854     ("hidden_os", None, _TestClusterOsList,
855      "Modify list of hidden operating systems: each modification must have"
856      " two items, the operation and the OS name; the operation can be"
857      " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)),
858     ("blacklisted_os", None, _TestClusterOsList,
859      "Modify list of blacklisted operating systems: each modification must"
860      " have two items, the operation and the OS name; the operation can be"
861      " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)),
862     ("use_external_mip_script", None, ht.TMaybeBool,
863      "Whether to use an external master IP address setup script"),
864     ]
865   OP_RESULT = ht.TNone
866
867
868 class OpClusterRedistConf(OpCode):
869   """Force a full push of the cluster configuration.
870
871   """
872   OP_RESULT = ht.TNone
873
874
875 class OpClusterActivateMasterIp(OpCode):
876   """Activate the master IP on the master node.
877
878   """
879   OP_RESULT = ht.TNone
880
881
882 class OpClusterDeactivateMasterIp(OpCode):
883   """Deactivate the master IP on the master node.
884
885   """
886   OP_RESULT = ht.TNone
887
888
889 class OpQuery(OpCode):
890   """Query for resources/items.
891
892   @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
893   @ivar fields: List of fields to retrieve
894   @ivar qfilter: Query filter
895
896   """
897   OP_DSC_FIELD = "what"
898   OP_PARAMS = [
899     _PQueryWhat,
900     _PUseLocking,
901     ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
902      "Requested fields"),
903     ("qfilter", None, ht.TOr(ht.TNone, ht.TList),
904      "Query filter"),
905     ]
906   OP_RESULT = \
907     _GenerateObjectTypeCheck(objects.QueryResponse, {
908       "fields": ht.TListOf(_TQueryFieldDef),
909       "data": _TQueryResult,
910       })
911
912
913 class OpQueryFields(OpCode):
914   """Query for available resource/item fields.
915
916   @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
917   @ivar fields: List of fields to retrieve
918
919   """
920   OP_DSC_FIELD = "what"
921   OP_PARAMS = [
922     _PQueryWhat,
923     ("fields", None, ht.TMaybeListOf(ht.TNonEmptyString),
924      "Requested fields; if not given, all are returned"),
925     ]
926   OP_RESULT = \
927     _GenerateObjectTypeCheck(objects.QueryFieldsResponse, {
928       "fields": ht.TListOf(_TQueryFieldDef),
929       })
930
931
932 class OpOobCommand(OpCode):
933   """Interact with OOB."""
934   OP_PARAMS = [
935     ("node_names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
936      "List of nodes to run the OOB command against"),
937     ("command", None, ht.TElemOf(constants.OOB_COMMANDS),
938      "OOB command to be run"),
939     ("timeout", constants.OOB_TIMEOUT, ht.TInt,
940      "Timeout before the OOB helper will be terminated"),
941     ("ignore_status", False, ht.TBool,
942      "Ignores the node offline status for power off"),
943     ("power_delay", constants.OOB_POWER_DELAY, ht.TPositiveFloat,
944      "Time in seconds to wait between powering on nodes"),
945     ]
946   # Fixme: Make it more specific with all the special cases in LUOobCommand
947   OP_RESULT = _TQueryResult
948
949
950 # node opcodes
951
952 class OpNodeRemove(OpCode):
953   """Remove a node.
954
955   @type node_name: C{str}
956   @ivar node_name: The name of the node to remove. If the node still has
957                    instances on it, the operation will fail.
958
959   """
960   OP_DSC_FIELD = "node_name"
961   OP_PARAMS = [
962     _PNodeName,
963     ]
964   OP_RESULT = ht.TNone
965
966
967 class OpNodeAdd(OpCode):
968   """Add a node to the cluster.
969
970   @type node_name: C{str}
971   @ivar node_name: The name of the node to add. This can be a short name,
972                    but it will be expanded to the FQDN.
973   @type primary_ip: IP address
974   @ivar primary_ip: The primary IP of the node. This will be ignored when the
975                     opcode is submitted, but will be filled during the node
976                     add (so it will be visible in the job query).
977   @type secondary_ip: IP address
978   @ivar secondary_ip: The secondary IP of the node. This needs to be passed
979                       if the cluster has been initialized in 'dual-network'
980                       mode, otherwise it must not be given.
981   @type readd: C{bool}
982   @ivar readd: Whether to re-add an existing node to the cluster. If
983                this is not passed, then the operation will abort if the node
984                name is already in the cluster; use this parameter to 'repair'
985                a node that had its configuration broken, or was reinstalled
986                without removal from the cluster.
987   @type group: C{str}
988   @ivar group: The node group to which this node will belong.
989   @type vm_capable: C{bool}
990   @ivar vm_capable: The vm_capable node attribute
991   @type master_capable: C{bool}
992   @ivar master_capable: The master_capable node attribute
993
994   """
995   OP_DSC_FIELD = "node_name"
996   OP_PARAMS = [
997     _PNodeName,
998     _PHvState,
999     _PDiskState,
1000     ("primary_ip", None, ht.NoType, "Primary IP address"),
1001     ("secondary_ip", None, ht.TMaybeString, "Secondary IP address"),
1002     ("readd", False, ht.TBool, "Whether node is re-added to cluster"),
1003     ("group", None, ht.TMaybeString, "Initial node group"),
1004     ("master_capable", None, ht.TMaybeBool,
1005      "Whether node can become master or master candidate"),
1006     ("vm_capable", None, ht.TMaybeBool,
1007      "Whether node can host instances"),
1008     ("ndparams", None, ht.TMaybeDict, "Node parameters"),
1009     ]
1010   OP_RESULT = ht.TNone
1011
1012
1013 class OpNodeQuery(OpCode):
1014   """Compute the list of nodes."""
1015   OP_PARAMS = [
1016     _POutputFields,
1017     _PUseLocking,
1018     ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1019      "Empty list to query all nodes, node names otherwise"),
1020     ]
1021   OP_RESULT = _TOldQueryResult
1022
1023
1024 class OpNodeQueryvols(OpCode):
1025   """Get list of volumes on node."""
1026   OP_PARAMS = [
1027     _POutputFields,
1028     ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1029      "Empty list to query all nodes, node names otherwise"),
1030     ]
1031   OP_RESULT = ht.TListOf(ht.TAny)
1032
1033
1034 class OpNodeQueryStorage(OpCode):
1035   """Get information on storage for node(s)."""
1036   OP_PARAMS = [
1037     _POutputFields,
1038     _PStorageType,
1039     ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "List of nodes"),
1040     ("name", None, ht.TMaybeString, "Storage name"),
1041     ]
1042   OP_RESULT = _TOldQueryResult
1043
1044
1045 class OpNodeModifyStorage(OpCode):
1046   """Modifies the properies of a storage unit"""
1047   OP_DSC_FIELD = "node_name"
1048   OP_PARAMS = [
1049     _PNodeName,
1050     _PStorageType,
1051     _PStorageName,
1052     ("changes", ht.NoDefault, ht.TDict, "Requested changes"),
1053     ]
1054   OP_RESULT = ht.TNone
1055
1056
1057 class OpRepairNodeStorage(OpCode):
1058   """Repairs the volume group on a node."""
1059   OP_DSC_FIELD = "node_name"
1060   OP_PARAMS = [
1061     _PNodeName,
1062     _PStorageType,
1063     _PStorageName,
1064     _PIgnoreConsistency,
1065     ]
1066   OP_RESULT = ht.TNone
1067
1068
1069 class OpNodeSetParams(OpCode):
1070   """Change the parameters of a node."""
1071   OP_DSC_FIELD = "node_name"
1072   OP_PARAMS = [
1073     _PNodeName,
1074     _PForce,
1075     _PHvState,
1076     _PDiskState,
1077     ("master_candidate", None, ht.TMaybeBool,
1078      "Whether the node should become a master candidate"),
1079     ("offline", None, ht.TMaybeBool,
1080      "Whether the node should be marked as offline"),
1081     ("drained", None, ht.TMaybeBool,
1082      "Whether the node should be marked as drained"),
1083     ("auto_promote", False, ht.TBool,
1084      "Whether node(s) should be promoted to master candidate if necessary"),
1085     ("master_capable", None, ht.TMaybeBool,
1086      "Denote whether node can become master or master candidate"),
1087     ("vm_capable", None, ht.TMaybeBool,
1088      "Denote whether node can host instances"),
1089     ("secondary_ip", None, ht.TMaybeString,
1090      "Change node's secondary IP address"),
1091     ("ndparams", None, ht.TMaybeDict, "Set node parameters"),
1092     ("powered", None, ht.TMaybeBool,
1093      "Whether the node should be marked as powered"),
1094     ]
1095   OP_RESULT = _TSetParamsResult
1096
1097
1098 class OpNodePowercycle(OpCode):
1099   """Tries to powercycle a node."""
1100   OP_DSC_FIELD = "node_name"
1101   OP_PARAMS = [
1102     _PNodeName,
1103     _PForce,
1104     ]
1105   OP_RESULT = ht.TMaybeString
1106
1107
1108 class OpNodeMigrate(OpCode):
1109   """Migrate all instances from a node."""
1110   OP_DSC_FIELD = "node_name"
1111   OP_PARAMS = [
1112     _PNodeName,
1113     _PMigrationMode,
1114     _PMigrationLive,
1115     _PMigrationTargetNode,
1116     _PAllowRuntimeChgs,
1117     _PIgnoreIpolicy,
1118     ("iallocator", None, ht.TMaybeString,
1119      "Iallocator for deciding the target node for shared-storage instances"),
1120     ]
1121   OP_RESULT = TJobIdListOnly
1122
1123
1124 class OpNodeEvacuate(OpCode):
1125   """Evacuate instances off a number of nodes."""
1126   OP_DSC_FIELD = "node_name"
1127   OP_PARAMS = [
1128     _PEarlyRelease,
1129     _PNodeName,
1130     ("remote_node", None, ht.TMaybeString, "New secondary node"),
1131     ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"),
1132     ("mode", ht.NoDefault, ht.TElemOf(constants.NODE_EVAC_MODES),
1133      "Node evacuation mode"),
1134     ]
1135   OP_RESULT = TJobIdListOnly
1136
1137
1138 # instance opcodes
1139
1140 class OpInstanceCreate(OpCode):
1141   """Create an instance.
1142
1143   @ivar instance_name: Instance name
1144   @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES})
1145   @ivar source_handshake: Signed handshake from source (remote import only)
1146   @ivar source_x509_ca: Source X509 CA in PEM format (remote import only)
1147   @ivar source_instance_name: Previous name of instance (remote import only)
1148   @ivar source_shutdown_timeout: Shutdown timeout used for source instance
1149     (remote import only)
1150
1151   """
1152   OP_DSC_FIELD = "instance_name"
1153   OP_PARAMS = [
1154     _PInstanceName,
1155     _PForceVariant,
1156     _PWaitForSync,
1157     _PNameCheck,
1158     _PIgnoreIpolicy,
1159     ("beparams", ht.EmptyDict, ht.TDict, "Backend parameters for instance"),
1160     ("disks", ht.NoDefault, ht.TListOf(_TDiskParams),
1161      "Disk descriptions, for example ``[{\"%s\": 100}, {\"%s\": 5}]``;"
1162      " each disk definition must contain a ``%s`` value and"
1163      " can contain an optional ``%s`` value denoting the disk access mode"
1164      " (%s)" %
1165      (constants.IDISK_SIZE, constants.IDISK_SIZE, constants.IDISK_SIZE,
1166       constants.IDISK_MODE,
1167       " or ".join("``%s``" % i for i in sorted(constants.DISK_ACCESS_SET)))),
1168     ("disk_template", ht.NoDefault, _BuildDiskTemplateCheck(True),
1169      "Disk template"),
1170     ("file_driver", None, ht.TOr(ht.TNone, ht.TElemOf(constants.FILE_DRIVER)),
1171      "Driver for file-backed disks"),
1172     ("file_storage_dir", None, ht.TMaybeString,
1173      "Directory for storing file-backed disks"),
1174     ("hvparams", ht.EmptyDict, ht.TDict,
1175      "Hypervisor parameters for instance, hypervisor-dependent"),
1176     ("hypervisor", None, ht.TMaybeString, "Hypervisor"),
1177     ("iallocator", None, ht.TMaybeString,
1178      "Iallocator for deciding which node(s) to use"),
1179     ("identify_defaults", False, ht.TBool,
1180      "Reset instance parameters to default if equal"),
1181     ("ip_check", True, ht.TBool, _PIpCheckDoc),
1182     ("mode", ht.NoDefault, ht.TElemOf(constants.INSTANCE_CREATE_MODES),
1183      "Instance creation mode"),
1184     ("nics", ht.NoDefault, ht.TListOf(_TestNicDef),
1185      "List of NIC (network interface) definitions, for example"
1186      " ``[{}, {}, {\"%s\": \"198.51.100.4\"}]``; each NIC definition can"
1187      " contain the optional values %s" %
1188      (constants.INIC_IP,
1189       ", ".join("``%s``" % i for i in sorted(constants.INIC_PARAMS)))),
1190     ("no_install", None, ht.TMaybeBool,
1191      "Do not install the OS (will disable automatic start)"),
1192     ("osparams", ht.EmptyDict, ht.TDict, "OS parameters for instance"),
1193     ("os_type", None, ht.TMaybeString, "Operating system"),
1194     ("pnode", None, ht.TMaybeString, "Primary node"),
1195     ("snode", None, ht.TMaybeString, "Secondary node"),
1196     ("source_handshake", None, ht.TOr(ht.TList, ht.TNone),
1197      "Signed handshake from source (remote import only)"),
1198     ("source_instance_name", None, ht.TMaybeString,
1199      "Source instance name (remote import only)"),
1200     ("source_shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT,
1201      ht.TPositiveInt,
1202      "How long source instance was given to shut down (remote import only)"),
1203     ("source_x509_ca", None, ht.TMaybeString,
1204      "Source X509 CA in PEM format (remote import only)"),
1205     ("src_node", None, ht.TMaybeString, "Source node for import"),
1206     ("src_path", None, ht.TMaybeString, "Source directory for import"),
1207     ("start", True, ht.TBool, "Whether to start instance after creation"),
1208     ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Instance tags"),
1209     ]
1210   OP_RESULT = ht.Comment("instance nodes")(ht.TListOf(ht.TNonEmptyString))
1211
1212
1213 class OpInstanceMultiAlloc(OpCode):
1214   """Allocates multiple instances.
1215
1216   """
1217   OP_PARAMS = [
1218     ("iallocator", None, ht.TMaybeString,
1219      "Iallocator used to allocate all the instances"),
1220     ("instances", [], ht.TListOf(ht.TInstanceOf(OpInstanceCreate)),
1221      "List of instance create opcodes describing the instances to allocate"),
1222     ]
1223   _JOB_LIST = ht.Comment("List of submitted jobs")(TJobIdList)
1224   ALLOCATABLE_KEY = "allocatable"
1225   FAILED_KEY = "allocatable"
1226   OP_RESULT = ht.TStrictDict(True, True, {
1227     constants.JOB_IDS_KEY: _JOB_LIST,
1228     ALLOCATABLE_KEY: ht.TListOf(ht.TNonEmptyString),
1229     FAILED_KEY: ht.TListOf(ht.TNonEmptyString)
1230     })
1231
1232   def __getstate__(self):
1233     """Generic serializer.
1234
1235     """
1236     state = OpCode.__getstate__(self)
1237     if hasattr(self, "instances"):
1238       # pylint: disable=E1101
1239       state["instances"] = [inst.__getstate__() for inst in self.instances]
1240     return state
1241
1242   def __setstate__(self, state):
1243     """Generic unserializer.
1244
1245     This method just restores from the serialized state the attributes
1246     of the current instance.
1247
1248     @param state: the serialized opcode data
1249     @type state: C{dict}
1250
1251     """
1252     if not isinstance(state, dict):
1253       raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
1254                        type(state))
1255
1256     if "instances" in state:
1257       insts = [OpCode.LoadOpCode(inst) for inst in state["instances"]]
1258       state["instances"] = insts
1259     return OpCode.__setstate__(self, state)
1260
1261   def Validate(self, set_defaults):
1262     """Validates this opcode.
1263
1264     We do this recursively.
1265
1266     """
1267     OpCode.Validate(self, set_defaults)
1268
1269     for inst in self.instances: # pylint: disable=E1101
1270       inst.Validate(set_defaults)
1271
1272
1273 class OpInstanceReinstall(OpCode):
1274   """Reinstall an instance's OS."""
1275   OP_DSC_FIELD = "instance_name"
1276   OP_PARAMS = [
1277     _PInstanceName,
1278     _PForceVariant,
1279     ("os_type", None, ht.TMaybeString, "Instance operating system"),
1280     ("osparams", None, ht.TMaybeDict, "Temporary OS parameters"),
1281     ]
1282   OP_RESULT = ht.TNone
1283
1284
1285 class OpInstanceRemove(OpCode):
1286   """Remove an instance."""
1287   OP_DSC_FIELD = "instance_name"
1288   OP_PARAMS = [
1289     _PInstanceName,
1290     _PShutdownTimeout,
1291     ("ignore_failures", False, ht.TBool,
1292      "Whether to ignore failures during removal"),
1293     ]
1294   OP_RESULT = ht.TNone
1295
1296
1297 class OpInstanceRename(OpCode):
1298   """Rename an instance."""
1299   OP_PARAMS = [
1300     _PInstanceName,
1301     _PNameCheck,
1302     ("new_name", ht.NoDefault, ht.TNonEmptyString, "New instance name"),
1303     ("ip_check", False, ht.TBool, _PIpCheckDoc),
1304     ]
1305   OP_RESULT = ht.Comment("New instance name")(ht.TNonEmptyString)
1306
1307
1308 class OpInstanceStartup(OpCode):
1309   """Startup an instance."""
1310   OP_DSC_FIELD = "instance_name"
1311   OP_PARAMS = [
1312     _PInstanceName,
1313     _PForce,
1314     _PIgnoreOfflineNodes,
1315     ("hvparams", ht.EmptyDict, ht.TDict,
1316      "Temporary hypervisor parameters, hypervisor-dependent"),
1317     ("beparams", ht.EmptyDict, ht.TDict, "Temporary backend parameters"),
1318     _PNoRemember,
1319     _PStartupPaused,
1320     ]
1321   OP_RESULT = ht.TNone
1322
1323
1324 class OpInstanceShutdown(OpCode):
1325   """Shutdown an instance."""
1326   OP_DSC_FIELD = "instance_name"
1327   OP_PARAMS = [
1328     _PInstanceName,
1329     _PIgnoreOfflineNodes,
1330     ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TPositiveInt,
1331      "How long to wait for instance to shut down"),
1332     _PNoRemember,
1333     ]
1334   OP_RESULT = ht.TNone
1335
1336
1337 class OpInstanceReboot(OpCode):
1338   """Reboot an instance."""
1339   OP_DSC_FIELD = "instance_name"
1340   OP_PARAMS = [
1341     _PInstanceName,
1342     _PShutdownTimeout,
1343     ("ignore_secondaries", False, ht.TBool,
1344      "Whether to start the instance even if secondary disks are failing"),
1345     ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES),
1346      "How to reboot instance"),
1347     ]
1348   OP_RESULT = ht.TNone
1349
1350
1351 class OpInstanceReplaceDisks(OpCode):
1352   """Replace the disks of an instance."""
1353   OP_DSC_FIELD = "instance_name"
1354   OP_PARAMS = [
1355     _PInstanceName,
1356     _PEarlyRelease,
1357     _PIgnoreIpolicy,
1358     ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES),
1359      "Replacement mode"),
1360     ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt),
1361      "Disk indexes"),
1362     ("remote_node", None, ht.TMaybeString, "New secondary node"),
1363     ("iallocator", None, ht.TMaybeString,
1364      "Iallocator for deciding new secondary node"),
1365     ]
1366   OP_RESULT = ht.TNone
1367
1368
1369 class OpInstanceFailover(OpCode):
1370   """Failover an instance."""
1371   OP_DSC_FIELD = "instance_name"
1372   OP_PARAMS = [
1373     _PInstanceName,
1374     _PShutdownTimeout,
1375     _PIgnoreConsistency,
1376     _PMigrationTargetNode,
1377     _PIgnoreIpolicy,
1378     ("iallocator", None, ht.TMaybeString,
1379      "Iallocator for deciding the target node for shared-storage instances"),
1380     ]
1381   OP_RESULT = ht.TNone
1382
1383
1384 class OpInstanceMigrate(OpCode):
1385   """Migrate an instance.
1386
1387   This migrates (without shutting down an instance) to its secondary
1388   node.
1389
1390   @ivar instance_name: the name of the instance
1391   @ivar mode: the migration mode (live, non-live or None for auto)
1392
1393   """
1394   OP_DSC_FIELD = "instance_name"
1395   OP_PARAMS = [
1396     _PInstanceName,
1397     _PMigrationMode,
1398     _PMigrationLive,
1399     _PMigrationTargetNode,
1400     _PAllowRuntimeChgs,
1401     _PIgnoreIpolicy,
1402     ("cleanup", False, ht.TBool,
1403      "Whether a previously failed migration should be cleaned up"),
1404     ("iallocator", None, ht.TMaybeString,
1405      "Iallocator for deciding the target node for shared-storage instances"),
1406     ("allow_failover", False, ht.TBool,
1407      "Whether we can fallback to failover if migration is not possible"),
1408     ]
1409   OP_RESULT = ht.TNone
1410
1411
1412 class OpInstanceMove(OpCode):
1413   """Move an instance.
1414
1415   This move (with shutting down an instance and data copying) to an
1416   arbitrary node.
1417
1418   @ivar instance_name: the name of the instance
1419   @ivar target_node: the destination node
1420
1421   """
1422   OP_DSC_FIELD = "instance_name"
1423   OP_PARAMS = [
1424     _PInstanceName,
1425     _PShutdownTimeout,
1426     _PIgnoreIpolicy,
1427     ("target_node", ht.NoDefault, ht.TNonEmptyString, "Target node"),
1428     _PIgnoreConsistency,
1429     ]
1430   OP_RESULT = ht.TNone
1431
1432
1433 class OpInstanceConsole(OpCode):
1434   """Connect to an instance's console."""
1435   OP_DSC_FIELD = "instance_name"
1436   OP_PARAMS = [
1437     _PInstanceName
1438     ]
1439   OP_RESULT = ht.TDict
1440
1441
1442 class OpInstanceActivateDisks(OpCode):
1443   """Activate an instance's disks."""
1444   OP_DSC_FIELD = "instance_name"
1445   OP_PARAMS = [
1446     _PInstanceName,
1447     ("ignore_size", False, ht.TBool, "Whether to ignore recorded size"),
1448     _PWaitForSyncFalse,
1449     ]
1450   OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3),
1451                                  ht.TItems([ht.TNonEmptyString,
1452                                             ht.TNonEmptyString,
1453                                             ht.TNonEmptyString])))
1454
1455
1456 class OpInstanceDeactivateDisks(OpCode):
1457   """Deactivate an instance's disks."""
1458   OP_DSC_FIELD = "instance_name"
1459   OP_PARAMS = [
1460     _PInstanceName,
1461     _PForce,
1462     ]
1463   OP_RESULT = ht.TNone
1464
1465
1466 class OpInstanceRecreateDisks(OpCode):
1467   """Recreate an instance's disks."""
1468   _TDiskChanges = \
1469     ht.TAnd(ht.TIsLength(2),
1470             ht.TItems([ht.Comment("Disk index")(ht.TPositiveInt),
1471                        ht.Comment("Parameters")(_TDiskParams)]))
1472
1473   OP_DSC_FIELD = "instance_name"
1474   OP_PARAMS = [
1475     _PInstanceName,
1476     ("disks", ht.EmptyList,
1477      ht.TOr(ht.TListOf(ht.TPositiveInt), ht.TListOf(_TDiskChanges)),
1478      "List of disk indexes (deprecated) or a list of tuples containing a disk"
1479      " index and a possibly empty dictionary with disk parameter changes"),
1480     ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1481      "New instance nodes, if relocation is desired"),
1482     ("iallocator", None, ht.TMaybeString,
1483      "Iallocator for deciding new nodes"),
1484     ]
1485   OP_RESULT = ht.TNone
1486
1487
1488 class OpInstanceQuery(OpCode):
1489   """Compute the list of instances."""
1490   OP_PARAMS = [
1491     _POutputFields,
1492     _PUseLocking,
1493     ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1494      "Empty list to query all instances, instance names otherwise"),
1495     ]
1496   OP_RESULT = _TOldQueryResult
1497
1498
1499 class OpInstanceQueryData(OpCode):
1500   """Compute the run-time status of instances."""
1501   OP_PARAMS = [
1502     _PUseLocking,
1503     ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1504      "Instance names"),
1505     ("static", False, ht.TBool,
1506      "Whether to only return configuration data without querying"
1507      " nodes"),
1508     ]
1509   OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TDict)
1510
1511
1512 def _TestInstSetParamsModList(fn):
1513   """Generates a check for modification lists.
1514
1515   """
1516   # Old format
1517   # TODO: Remove in version 2.8 including support in LUInstanceSetParams
1518   old_mod_item_fn = \
1519     ht.TAnd(ht.TIsLength(2), ht.TItems([
1520       ht.TOr(ht.TElemOf(constants.DDMS_VALUES), ht.TPositiveInt),
1521       fn,
1522       ]))
1523
1524   # New format, supporting adding/removing disks/NICs at arbitrary indices
1525   mod_item_fn = \
1526     ht.TAnd(ht.TIsLength(3), ht.TItems([
1527       ht.TElemOf(constants.DDMS_VALUES_WITH_MODIFY),
1528       ht.Comment("Disk index, can be negative, e.g. -1 for last disk")(ht.TInt),
1529       fn,
1530       ]))
1531
1532   return ht.TOr(ht.Comment("Recommended")(ht.TListOf(mod_item_fn)),
1533                 ht.Comment("Deprecated")(ht.TListOf(old_mod_item_fn)))
1534
1535
1536 class OpInstanceSetParams(OpCode):
1537   """Change the parameters of an instance.
1538
1539   """
1540   TestNicModifications = _TestInstSetParamsModList(_TestNicDef)
1541   TestDiskModifications = _TestInstSetParamsModList(_TDiskParams)
1542
1543   OP_DSC_FIELD = "instance_name"
1544   OP_PARAMS = [
1545     _PInstanceName,
1546     _PForce,
1547     _PForceVariant,
1548     _PIgnoreIpolicy,
1549     ("nics", ht.EmptyList, TestNicModifications,
1550      "List of NIC changes: each item is of the form ``(op, index, settings)``,"
1551      " ``op`` is one of ``%s``, ``%s`` or ``%s``, ``index`` can be either -1"
1552      " to refer to the last position, or a zero-based index number; a"
1553      " deprecated version of this parameter used the form ``(op, settings)``,"
1554      " where ``op`` can be ``%s`` to add a new NIC with the specified"
1555      " settings, ``%s`` to remove the last NIC or a number to modify the"
1556      " settings of the NIC with that index" %
1557      (constants.DDM_ADD, constants.DDM_MODIFY, constants.DDM_REMOVE,
1558       constants.DDM_ADD, constants.DDM_REMOVE)),
1559     ("disks", ht.EmptyList, TestDiskModifications,
1560      "List of disk changes; see ``nics``"),
1561     ("beparams", ht.EmptyDict, ht.TDict, "Per-instance backend parameters"),
1562     ("runtime_mem", None, ht.TMaybeStrictPositiveInt, "New runtime memory"),
1563     ("hvparams", ht.EmptyDict, ht.TDict,
1564      "Per-instance hypervisor parameters, hypervisor-dependent"),
1565     ("disk_template", None, ht.TOr(ht.TNone, _BuildDiskTemplateCheck(False)),
1566      "Disk template for instance"),
1567     ("remote_node", None, ht.TMaybeString,
1568      "Secondary node (used when changing disk template)"),
1569     ("os_name", None, ht.TMaybeString,
1570      "Change the instance's OS without reinstalling the instance"),
1571     ("osparams", None, ht.TMaybeDict, "Per-instance OS parameters"),
1572     ("wait_for_sync", True, ht.TBool,
1573      "Whether to wait for the disk to synchronize, when changing template"),
1574     ("offline", None, ht.TMaybeBool, "Whether to mark instance as offline"),
1575     ]
1576   OP_RESULT = _TSetParamsResult
1577
1578
1579 class OpInstanceGrowDisk(OpCode):
1580   """Grow a disk of an instance."""
1581   OP_DSC_FIELD = "instance_name"
1582   OP_PARAMS = [
1583     _PInstanceName,
1584     _PWaitForSync,
1585     ("disk", ht.NoDefault, ht.TInt, "Disk index"),
1586     ("amount", ht.NoDefault, ht.TPositiveInt,
1587      "Amount of disk space to add (megabytes)"),
1588     ("absolute", False, ht.TBool,
1589      "Whether the amount parameter is an absolute target or a relative one"),
1590     ]
1591   OP_RESULT = ht.TNone
1592
1593
1594 class OpInstanceChangeGroup(OpCode):
1595   """Moves an instance to another node group."""
1596   OP_DSC_FIELD = "instance_name"
1597   OP_PARAMS = [
1598     _PInstanceName,
1599     _PEarlyRelease,
1600     ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"),
1601     ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString),
1602      "Destination group names or UUIDs (defaults to \"all but current group\""),
1603     ]
1604   OP_RESULT = TJobIdListOnly
1605
1606
1607 # Node group opcodes
1608
1609 class OpGroupAdd(OpCode):
1610   """Add a node group to the cluster."""
1611   OP_DSC_FIELD = "group_name"
1612   OP_PARAMS = [
1613     _PGroupName,
1614     _PNodeGroupAllocPolicy,
1615     _PGroupNodeParams,
1616     _PDiskParams,
1617     _PHvState,
1618     _PDiskState,
1619     ("ipolicy", None, ht.TMaybeDict,
1620      "Group-wide :ref:`instance policy <rapi-ipolicy>` specs"),
1621     ]
1622   OP_RESULT = ht.TNone
1623
1624
1625 class OpGroupAssignNodes(OpCode):
1626   """Assign nodes to a node group."""
1627   OP_DSC_FIELD = "group_name"
1628   OP_PARAMS = [
1629     _PGroupName,
1630     _PForce,
1631     ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
1632      "List of nodes to assign"),
1633     ]
1634   OP_RESULT = ht.TNone
1635
1636
1637 class OpGroupQuery(OpCode):
1638   """Compute the list of node groups."""
1639   OP_PARAMS = [
1640     _POutputFields,
1641     ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1642      "Empty list to query all groups, group names otherwise"),
1643     ]
1644   OP_RESULT = _TOldQueryResult
1645
1646
1647 class OpGroupSetParams(OpCode):
1648   """Change the parameters of a node group."""
1649   OP_DSC_FIELD = "group_name"
1650   OP_PARAMS = [
1651     _PGroupName,
1652     _PNodeGroupAllocPolicy,
1653     _PGroupNodeParams,
1654     _PDiskParams,
1655     _PHvState,
1656     _PDiskState,
1657     ("ipolicy", None, ht.TMaybeDict, "Group-wide instance policy specs"),
1658     ]
1659   OP_RESULT = _TSetParamsResult
1660
1661
1662 class OpGroupRemove(OpCode):
1663   """Remove a node group from the cluster."""
1664   OP_DSC_FIELD = "group_name"
1665   OP_PARAMS = [
1666     _PGroupName,
1667     ]
1668   OP_RESULT = ht.TNone
1669
1670
1671 class OpGroupRename(OpCode):
1672   """Rename a node group in the cluster."""
1673   OP_PARAMS = [
1674     _PGroupName,
1675     ("new_name", ht.NoDefault, ht.TNonEmptyString, "New group name"),
1676     ]
1677   OP_RESULT = ht.Comment("New group name")(ht.TNonEmptyString)
1678
1679
1680 class OpGroupEvacuate(OpCode):
1681   """Evacuate a node group in the cluster."""
1682   OP_DSC_FIELD = "group_name"
1683   OP_PARAMS = [
1684     _PGroupName,
1685     _PEarlyRelease,
1686     ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"),
1687     ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString),
1688      "Destination group names or UUIDs"),
1689     ]
1690   OP_RESULT = TJobIdListOnly
1691
1692
1693 # OS opcodes
1694 class OpOsDiagnose(OpCode):
1695   """Compute the list of guest operating systems."""
1696   OP_PARAMS = [
1697     _POutputFields,
1698     ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1699      "Which operating systems to diagnose"),
1700     ]
1701   OP_RESULT = _TOldQueryResult
1702
1703
1704 # Exports opcodes
1705 class OpBackupQuery(OpCode):
1706   """Compute the list of exported images."""
1707   OP_PARAMS = [
1708     _PUseLocking,
1709     ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1710      "Empty list to query all nodes, node names otherwise"),
1711     ]
1712   OP_RESULT = ht.TDictOf(ht.TNonEmptyString,
1713                          ht.TOr(ht.Comment("False on error")(ht.TBool),
1714                                 ht.TListOf(ht.TNonEmptyString)))
1715
1716
1717 class OpBackupPrepare(OpCode):
1718   """Prepares an instance export.
1719
1720   @ivar instance_name: Instance name
1721   @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1722
1723   """
1724   OP_DSC_FIELD = "instance_name"
1725   OP_PARAMS = [
1726     _PInstanceName,
1727     ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES),
1728      "Export mode"),
1729     ]
1730   OP_RESULT = ht.TOr(ht.TNone, ht.TDict)
1731
1732
1733 class OpBackupExport(OpCode):
1734   """Export an instance.
1735
1736   For local exports, the export destination is the node name. For remote
1737   exports, the export destination is a list of tuples, each consisting of
1738   hostname/IP address, port, HMAC and HMAC salt. The HMAC is calculated using
1739   the cluster domain secret over the value "${index}:${hostname}:${port}". The
1740   destination X509 CA must be a signed certificate.
1741
1742   @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1743   @ivar target_node: Export destination
1744   @ivar x509_key_name: X509 key to use (remote export only)
1745   @ivar destination_x509_ca: Destination X509 CA in PEM format (remote export
1746                              only)
1747
1748   """
1749   OP_DSC_FIELD = "instance_name"
1750   OP_PARAMS = [
1751     _PInstanceName,
1752     _PShutdownTimeout,
1753     # TODO: Rename target_node as it changes meaning for different export modes
1754     # (e.g. "destination")
1755     ("target_node", ht.NoDefault, ht.TOr(ht.TNonEmptyString, ht.TList),
1756      "Destination information, depends on export mode"),
1757     ("shutdown", True, ht.TBool, "Whether to shutdown instance before export"),
1758     ("remove_instance", False, ht.TBool,
1759      "Whether to remove instance after export"),
1760     ("ignore_remove_failures", False, ht.TBool,
1761      "Whether to ignore failures while removing instances"),
1762     ("mode", constants.EXPORT_MODE_LOCAL, ht.TElemOf(constants.EXPORT_MODES),
1763      "Export mode"),
1764     ("x509_key_name", None, ht.TOr(ht.TList, ht.TNone),
1765      "Name of X509 key (remote export only)"),
1766     ("destination_x509_ca", None, ht.TMaybeString,
1767      "Destination X509 CA (remote export only)"),
1768     ]
1769   OP_RESULT = \
1770     ht.TAnd(ht.TIsLength(2), ht.TItems([
1771       ht.Comment("Finalizing status")(ht.TBool),
1772       ht.Comment("Status for every exported disk")(ht.TListOf(ht.TBool)),
1773       ]))
1774
1775
1776 class OpBackupRemove(OpCode):
1777   """Remove an instance's export."""
1778   OP_DSC_FIELD = "instance_name"
1779   OP_PARAMS = [
1780     _PInstanceName,
1781     ]
1782   OP_RESULT = ht.TNone
1783
1784
1785 # Tags opcodes
1786 class OpTagsGet(OpCode):
1787   """Returns the tags of the given object."""
1788   OP_DSC_FIELD = "name"
1789   OP_PARAMS = [
1790     _PTagKind,
1791     # Not using _PUseLocking as the default is different for historical reasons
1792     ("use_locking", True, ht.TBool, "Whether to use synchronization"),
1793     # Name is only meaningful for nodes and instances
1794     ("name", ht.NoDefault, ht.TMaybeString,
1795      "Name of object to retrieve tags from"),
1796     ]
1797   OP_RESULT = ht.TListOf(ht.TNonEmptyString)
1798
1799
1800 class OpTagsSearch(OpCode):
1801   """Searches the tags in the cluster for a given pattern."""
1802   OP_DSC_FIELD = "pattern"
1803   OP_PARAMS = [
1804     ("pattern", ht.NoDefault, ht.TNonEmptyString,
1805      "Search pattern (regular expression)"),
1806     ]
1807   OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(2), ht.TItems([
1808     ht.TNonEmptyString,
1809     ht.TNonEmptyString,
1810     ])))
1811
1812
1813 class OpTagsSet(OpCode):
1814   """Add a list of tags on a given object."""
1815   OP_PARAMS = [
1816     _PTagKind,
1817     _PTags,
1818     # Name is only meaningful for nodes and instances
1819     ("name", ht.NoDefault, ht.TMaybeString,
1820      "Name of object where tag(s) should be added"),
1821     ]
1822   OP_RESULT = ht.TNone
1823
1824
1825 class OpTagsDel(OpCode):
1826   """Remove a list of tags from a given object."""
1827   OP_PARAMS = [
1828     _PTagKind,
1829     _PTags,
1830     # Name is only meaningful for nodes and instances
1831     ("name", ht.NoDefault, ht.TMaybeString,
1832      "Name of object where tag(s) should be deleted"),
1833     ]
1834   OP_RESULT = ht.TNone
1835
1836
1837 # Test opcodes
1838 class OpTestDelay(OpCode):
1839   """Sleeps for a configured amount of time.
1840
1841   This is used just for debugging and testing.
1842
1843   Parameters:
1844     - duration: the time to sleep
1845     - on_master: if true, sleep on the master
1846     - on_nodes: list of nodes in which to sleep
1847
1848   If the on_master parameter is true, it will execute a sleep on the
1849   master (before any node sleep).
1850
1851   If the on_nodes list is not empty, it will sleep on those nodes
1852   (after the sleep on the master, if that is enabled).
1853
1854   As an additional feature, the case of duration < 0 will be reported
1855   as an execution error, so this opcode can be used as a failure
1856   generator. The case of duration == 0 will not be treated specially.
1857
1858   """
1859   OP_DSC_FIELD = "duration"
1860   OP_PARAMS = [
1861     ("duration", ht.NoDefault, ht.TNumber, None),
1862     ("on_master", True, ht.TBool, None),
1863     ("on_nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
1864     ("repeat", 0, ht.TPositiveInt, None),
1865     ]
1866
1867
1868 class OpTestAllocator(OpCode):
1869   """Allocator framework testing.
1870
1871   This opcode has two modes:
1872     - gather and return allocator input for a given mode (allocate new
1873       or replace secondary) and a given instance definition (direction
1874       'in')
1875     - run a selected allocator for a given operation (as above) and
1876       return the allocator output (direction 'out')
1877
1878   """
1879   OP_DSC_FIELD = "allocator"
1880   OP_PARAMS = [
1881     ("direction", ht.NoDefault,
1882      ht.TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS), None),
1883     ("mode", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_MODES), None),
1884     ("name", ht.NoDefault, ht.TNonEmptyString, None),
1885     ("nics", ht.NoDefault,
1886      ht.TMaybeListOf(ht.TDictOf(ht.TElemOf([constants.INIC_MAC,
1887                                             constants.INIC_IP,
1888                                             "bridge"]),
1889                                 ht.TOr(ht.TNone, ht.TNonEmptyString))),
1890      None),
1891     ("disks", ht.NoDefault, ht.TOr(ht.TNone, ht.TList), None),
1892     ("hypervisor", None, ht.TMaybeString, None),
1893     ("allocator", None, ht.TMaybeString, None),
1894     ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
1895     ("memory", None, ht.TOr(ht.TNone, ht.TPositiveInt), None),
1896     ("vcpus", None, ht.TOr(ht.TNone, ht.TPositiveInt), None),
1897     ("os", None, ht.TMaybeString, None),
1898     ("disk_template", None, ht.TMaybeString, None),
1899     ("instances", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
1900     ("evac_mode", None,
1901      ht.TOr(ht.TNone, ht.TElemOf(constants.IALLOCATOR_NEVAC_MODES)), None),
1902     ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
1903     ("spindle_use", 1, ht.TPositiveInt, None),
1904     ("count", 1, ht.TPositiveInt, None),
1905     ]
1906
1907
1908 class OpTestJqueue(OpCode):
1909   """Utility opcode to test some aspects of the job queue.
1910
1911   """
1912   OP_PARAMS = [
1913     ("notify_waitlock", False, ht.TBool, None),
1914     ("notify_exec", False, ht.TBool, None),
1915     ("log_messages", ht.EmptyList, ht.TListOf(ht.TString), None),
1916     ("fail", False, ht.TBool, None),
1917     ]
1918
1919
1920 class OpTestDummy(OpCode):
1921   """Utility opcode used by unittests.
1922
1923   """
1924   OP_PARAMS = [
1925     ("result", ht.NoDefault, ht.NoType, None),
1926     ("messages", ht.NoDefault, ht.NoType, None),
1927     ("fail", ht.NoDefault, ht.NoType, None),
1928     ("submit_jobs", None, ht.NoType, None),
1929     ]
1930   WITH_LU = False
1931
1932
1933 def _GetOpList():
1934   """Returns list of all defined opcodes.
1935
1936   Does not eliminate duplicates by C{OP_ID}.
1937
1938   """
1939   return [v for v in globals().values()
1940           if (isinstance(v, type) and issubclass(v, OpCode) and
1941               hasattr(v, "OP_ID") and v is not OpCode)]
1942
1943
1944 OP_MAPPING = dict((v.OP_ID, v) for v in _GetOpList())