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