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