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