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