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