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