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