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