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