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