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