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