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