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