Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes.py @ dd076c21

History | View | Annotate | Download (65 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""OpCodes module
23

24
This module implements the data structures which define the cluster
25
operations - the so-called opcodes.
26

27
Every operation which modifies the cluster state is expressed via
28
opcodes.
29

30
"""
31

    
32
# this are practically structures, so disable the message about too
33
# few public methods:
34
# pylint: disable=R0903
35

    
36
import logging
37
import re
38
import ipaddr
39

    
40
from ganeti import constants
41
from ganeti import errors
42
from ganeti import ht
43
from ganeti import objects
44
from ganeti import objectutils
45

    
46

    
47
# Common opcode attributes
48

    
49
#: output fields for a query operation
50
_POutputFields = ("output_fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
51
                  "Selected output fields")
52

    
53
#: the shutdown timeout
54
_PShutdownTimeout = \
55
  ("shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TNonNegativeInt,
56
   "How long to wait for instance to shut down")
57

    
58
#: the force parameter
59
_PForce = ("force", False, ht.TBool, "Whether to force the operation")
60

    
61
#: a required instance name (for single-instance LUs)
62
_PInstanceName = ("instance_name", ht.NoDefault, ht.TNonEmptyString,
63
                  "Instance name")
64

    
65
#: Whether to ignore offline nodes
66
_PIgnoreOfflineNodes = ("ignore_offline_nodes", False, ht.TBool,
67
                        "Whether to ignore offline nodes")
68

    
69
#: a required node name (for single-node LUs)
70
_PNodeName = ("node_name", ht.NoDefault, ht.TNonEmptyString, "Node name")
71

    
72
#: a required node group name (for single-group LUs)
73
_PGroupName = ("group_name", ht.NoDefault, ht.TNonEmptyString, "Group name")
74

    
75
#: Migration type (live/non-live)
76
_PMigrationMode = ("mode", None,
77
                   ht.TMaybe(ht.TElemOf(constants.HT_MIGRATION_MODES)),
78
                   "Migration mode")
79

    
80
#: Obsolete 'live' migration mode (boolean)
81
_PMigrationLive = ("live", None, ht.TMaybeBool,
82
                   "Legacy setting for live migration, do not use")
83

    
84
#: Tag type
85
_PTagKind = ("kind", ht.NoDefault, ht.TElemOf(constants.VALID_TAG_TYPES),
86
             "Tag kind")
87

    
88
#: List of tag strings
89
_PTags = ("tags", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
90
          "List of tag names")
91

    
92
_PForceVariant = ("force_variant", False, ht.TBool,
93
                  "Whether to force an unknown OS variant")
94

    
95
_PWaitForSync = ("wait_for_sync", True, ht.TBool,
96
                 "Whether to wait for the disk to synchronize")
97

    
98
_PWaitForSyncFalse = ("wait_for_sync", False, ht.TBool,
99
                      "Whether to wait for the disk to synchronize"
100
                      " (defaults to false)")
101

    
102
_PIgnoreConsistency = ("ignore_consistency", False, ht.TBool,
103
                       "Whether to ignore disk consistency")
104

    
105
_PStorageName = ("name", ht.NoDefault, ht.TMaybeString, "Storage name")
106

    
107
_PUseLocking = ("use_locking", False, ht.TBool,
108
                "Whether to use synchronization")
109

    
110
_PNameCheck = ("name_check", True, ht.TBool, "Whether to check name")
111

    
112
_PNodeGroupAllocPolicy = \
113
  ("alloc_policy", None,
114
   ht.TMaybe(ht.TElemOf(constants.VALID_ALLOC_POLICIES)),
115
   "Instance allocation policy")
116

    
117
_PGroupNodeParams = ("ndparams", None, ht.TMaybeDict,
118
                     "Default node parameters for group")
119

    
120
_PQueryWhat = ("what", ht.NoDefault, ht.TElemOf(constants.QR_VIA_OP),
121
               "Resource(s) to query for")
122

    
123
_PEarlyRelease = ("early_release", False, ht.TBool,
124
                  "Whether to release locks as soon as possible")
125

    
126
_PIpCheckDoc = "Whether to ensure instance's IP address is inactive"
127

    
128
#: Do not remember instance state changes
129
_PNoRemember = ("no_remember", False, ht.TBool,
130
                "Do not remember the state change")
131

    
132
#: Target node for instance migration/failover
133
_PMigrationTargetNode = ("target_node", None, ht.TMaybeString,
134
                         "Target node for shared-storage instances")
135

    
136
_PStartupPaused = ("startup_paused", False, ht.TBool,
137
                   "Pause instance at startup")
138

    
139
_PVerbose = ("verbose", False, ht.TBool, "Verbose mode")
140

    
141
# Parameters for cluster verification
142
_PDebugSimulateErrors = ("debug_simulate_errors", False, ht.TBool,
143
                         "Whether to simulate errors (useful for debugging)")
144
_PErrorCodes = ("error_codes", False, ht.TBool, "Error codes")
145
_PSkipChecks = ("skip_checks", ht.EmptyList,
146
                ht.TListOf(ht.TElemOf(constants.VERIFY_OPTIONAL_CHECKS)),
147
                "Which checks to skip")
148
_PIgnoreErrors = ("ignore_errors", ht.EmptyList,
149
                  ht.TListOf(ht.TElemOf(constants.CV_ALL_ECODES_STRINGS)),
150
                  "List of error codes that should be treated as warnings")
151

    
152
# Disk parameters
153
_PDiskParams = \
154
  ("diskparams", None,
155
   ht.TMaybe(ht.TDictOf(ht.TElemOf(constants.DISK_TEMPLATES), ht.TDict)),
156
   "Disk templates' parameter defaults")
157

    
158
# Parameters for node resource model
159
_PHvState = ("hv_state", None, ht.TMaybeDict, "Set hypervisor states")
160
_PDiskState = ("disk_state", None, ht.TMaybeDict, "Set disk states")
161

    
162

    
163
_PIgnoreIpolicy = ("ignore_ipolicy", False, ht.TBool,
164
                   "Whether to ignore ipolicy violations")
165

    
166
# Allow runtime changes while migrating
167
_PAllowRuntimeChgs = ("allow_runtime_changes", True, ht.TBool,
168
                      "Allow runtime changes (eg. memory ballooning)")
169

    
170
#: IAllocator field builder
171
_PIAllocFromDesc = lambda desc: ("iallocator", None, ht.TMaybeString, desc)
172

    
173
#: a required network name
174
_PNetworkName = ("network_name", ht.NoDefault, ht.TNonEmptyString,
175
                 "Set network name")
176

    
177
#: OP_ID conversion regular expression
178
_OPID_RE = re.compile("([a-z])([A-Z])")
179

    
180
#: Utility function for L{OpClusterSetParams}
181
_TestClusterOsListItem = \
182
  ht.TAnd(ht.TIsLength(2), ht.TItems([
183
    ht.TElemOf(constants.DDMS_VALUES),
184
    ht.TNonEmptyString,
185
    ]))
186

    
187
_TestClusterOsList = ht.TMaybeListOf(_TestClusterOsListItem)
188

    
189
# TODO: Generate check from constants.INIC_PARAMS_TYPES
190
#: Utility function for testing NIC definitions
191
_TestNicDef = \
192
  ht.Comment("NIC parameters")(ht.TDictOf(ht.TElemOf(constants.INIC_PARAMS),
193
                                          ht.TMaybeString))
194

    
195
_TSetParamsResultItemItems = [
196
  ht.Comment("name of changed parameter")(ht.TNonEmptyString),
197
  ht.Comment("new value")(ht.TAny),
198
  ]
199

    
200
_TSetParamsResult = \
201
  ht.TListOf(ht.TAnd(ht.TIsLength(len(_TSetParamsResultItemItems)),
202
                     ht.TItems(_TSetParamsResultItemItems)))
203

    
204
# TODO: Generate check from constants.IDISK_PARAMS_TYPES (however, not all users
205
# of this check support all parameters)
206
_TDiskParams = \
207
  ht.Comment("Disk parameters")(ht.TDictOf(ht.TElemOf(constants.IDISK_PARAMS),
208
                                           ht.TOr(ht.TNonEmptyString, ht.TInt)))
209

    
210
_TQueryRow = \
211
  ht.TListOf(ht.TAnd(ht.TIsLength(2),
212
                     ht.TItems([ht.TElemOf(constants.RS_ALL),
213
                                ht.TAny])))
214

    
215
_TQueryResult = ht.TListOf(_TQueryRow)
216

    
217
_TOldQueryRow = ht.TListOf(ht.TAny)
218

    
219
_TOldQueryResult = ht.TListOf(_TOldQueryRow)
220

    
221

    
222
_SUMMARY_PREFIX = {
223
  "CLUSTER_": "C_",
224
  "GROUP_": "G_",
225
  "NODE_": "N_",
226
  "INSTANCE_": "I_",
227
  }
228

    
229
#: Attribute name for dependencies
230
DEPEND_ATTR = "depends"
231

    
232
#: Attribute name for comment
233
COMMENT_ATTR = "comment"
234

    
235

    
236
def _NameToId(name):
237
  """Convert an opcode class name to an OP_ID.
238

239
  @type name: string
240
  @param name: the class name, as OpXxxYyy
241
  @rtype: string
242
  @return: the name in the OP_XXXX_YYYY format
243

244
  """
245
  if not name.startswith("Op"):
246
    return None
247
  # Note: (?<=[a-z])(?=[A-Z]) would be ideal, since it wouldn't
248
  # consume any input, and hence we would just have all the elements
249
  # in the list, one by one; but it seems that split doesn't work on
250
  # non-consuming input, hence we have to process the input string a
251
  # bit
252
  name = _OPID_RE.sub(r"\1,\2", name)
253
  elems = name.split(",")
254
  return "_".join(n.upper() for n in elems)
255

    
256

    
257
def _GenerateObjectTypeCheck(obj, fields_types):
258
  """Helper to generate type checks for objects.
259

260
  @param obj: The object to generate type checks
261
  @param fields_types: The fields and their types as a dict
262
  @return: A ht type check function
263

264
  """
265
  assert set(obj.GetAllSlots()) == set(fields_types.keys()), \
266
    "%s != %s" % (set(obj.GetAllSlots()), set(fields_types.keys()))
267
  return ht.TStrictDict(True, True, fields_types)
268

    
269

    
270
_TQueryFieldDef = \
271
  _GenerateObjectTypeCheck(objects.QueryFieldDefinition, {
272
    "name": ht.TNonEmptyString,
273
    "title": ht.TNonEmptyString,
274
    "kind": ht.TElemOf(constants.QFT_ALL),
275
    "doc": ht.TNonEmptyString,
276
    })
277

    
278

    
279
def RequireFileStorage():
280
  """Checks that file storage is enabled.
281

282
  While it doesn't really fit into this module, L{utils} was deemed too large
283
  of a dependency to be imported for just one or two functions.
284

285
  @raise errors.OpPrereqError: when file storage is disabled
286

287
  """
288
  if not constants.ENABLE_FILE_STORAGE:
289
    raise errors.OpPrereqError("File storage disabled at configure time",
290
                               errors.ECODE_INVAL)
291

    
292

    
293
def RequireSharedFileStorage():
294
  """Checks that shared file storage is enabled.
295

296
  While it doesn't really fit into this module, L{utils} was deemed too large
297
  of a dependency to be imported for just one or two functions.
298

299
  @raise errors.OpPrereqError: when shared file storage is disabled
300

301
  """
302
  if not constants.ENABLE_SHARED_FILE_STORAGE:
303
    raise errors.OpPrereqError("Shared file storage disabled at"
304
                               " configure time", errors.ECODE_INVAL)
305

    
306

    
307
@ht.WithDesc("CheckFileStorage")
308
def _CheckFileStorage(value):
309
  """Ensures file storage is enabled if used.
310

311
  """
312
  if value == constants.DT_FILE:
313
    RequireFileStorage()
314
  elif value == constants.DT_SHARED_FILE:
315
    RequireSharedFileStorage()
316
  return True
317

    
318

    
319
def _BuildDiskTemplateCheck(accept_none):
320
  """Builds check for disk template.
321

322
  @type accept_none: bool
323
  @param accept_none: whether to accept None as a correct value
324
  @rtype: callable
325

326
  """
327
  template_check = ht.TElemOf(constants.DISK_TEMPLATES)
328

    
329
  if accept_none:
330
    template_check = ht.TMaybe(template_check)
331

    
332
  return ht.TAnd(template_check, _CheckFileStorage)
333

    
334

    
335
def _CheckStorageType(storage_type):
336
  """Ensure a given storage type is valid.
337

338
  """
339
  if storage_type not in constants.VALID_STORAGE_TYPES:
340
    raise errors.OpPrereqError("Unknown storage type: %s" % storage_type,
341
                               errors.ECODE_INVAL)
342
  if storage_type == constants.ST_FILE:
343
    # TODO: What about shared file storage?
344
    RequireFileStorage()
345
  return True
346

    
347

    
348
#: Storage type parameter
349
_PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType,
350
                 "Storage type")
351

    
352
_CheckNetworkType = ht.TElemOf(constants.NETWORK_VALID_TYPES)
353

    
354

    
355
@ht.WithDesc("IPv4 network")
356
def _CheckCIDRNetNotation(value):
357
  """Ensure a given CIDR notation type is valid.
358

359
  """
360
  try:
361
    ipaddr.IPv4Network(value)
362
  except ipaddr.AddressValueError:
363
    return False
364
  return True
365

    
366

    
367
@ht.WithDesc("IPv4 address")
368
def _CheckCIDRAddrNotation(value):
369
  """Ensure a given CIDR notation type is valid.
370

371
  """
372
  try:
373
    ipaddr.IPv4Address(value)
374
  except ipaddr.AddressValueError:
375
    return False
376
  return True
377

    
378

    
379
@ht.WithDesc("IPv6 address")
380
def _CheckCIDR6AddrNotation(value):
381
  """Ensure a given CIDR notation type is valid.
382

383
  """
384
  try:
385
    ipaddr.IPv6Address(value)
386
  except ipaddr.AddressValueError:
387
    return False
388
  return True
389

    
390

    
391
@ht.WithDesc("IPv6 network")
392
def _CheckCIDR6NetNotation(value):
393
  """Ensure a given CIDR notation type is valid.
394

395
  """
396
  try:
397
    ipaddr.IPv6Network(value)
398
  except ipaddr.AddressValueError:
399
    return False
400
  return True
401

    
402

    
403
_TIpAddress4 = ht.TAnd(ht.TString, _CheckCIDRAddrNotation)
404
_TIpAddress6 = ht.TAnd(ht.TString, _CheckCIDR6AddrNotation)
405
_TIpNetwork4 = ht.TAnd(ht.TString, _CheckCIDRNetNotation)
406
_TIpNetwork6 = ht.TAnd(ht.TString, _CheckCIDR6NetNotation)
407
_TMaybeAddr4List = ht.TMaybe(ht.TListOf(_TIpAddress4))
408

    
409

    
410
class _AutoOpParamSlots(objectutils.AutoSlots):
411
  """Meta class for opcode definitions.
412

413
  """
414
  def __new__(mcs, name, bases, attrs):
415
    """Called when a class should be created.
416

417
    @param mcs: The meta class
418
    @param name: Name of created class
419
    @param bases: Base classes
420
    @type attrs: dict
421
    @param attrs: Class attributes
422

423
    """
424
    assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
425

    
426
    slots = mcs._GetSlots(attrs)
427
    assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
428
      "Class '%s' uses unknown field in OP_DSC_FIELD" % name
429

    
430
    attrs["OP_ID"] = _NameToId(name)
431

    
432
    return objectutils.AutoSlots.__new__(mcs, name, bases, attrs)
433

    
434
  @classmethod
435
  def _GetSlots(mcs, attrs):
436
    """Build the slots out of OP_PARAMS.
437

438
    """
439
    # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams
440
    params = attrs.setdefault("OP_PARAMS", [])
441

    
442
    # Use parameter names as slots
443
    return [pname for (pname, _, _, _) in params]
444

    
445

    
446
class BaseOpCode(objectutils.ValidatedSlots):
447
  """A simple serializable object.
448

449
  This object serves as a parent class for OpCode without any custom
450
  field handling.
451

452
  """
453
  # pylint: disable=E1101
454
  # as OP_ID is dynamically defined
455
  __metaclass__ = _AutoOpParamSlots
456

    
457
  def __getstate__(self):
458
    """Generic serializer.
459

460
    This method just returns the contents of the instance as a
461
    dictionary.
462

463
    @rtype:  C{dict}
464
    @return: the instance attributes and their values
465

466
    """
467
    state = {}
468
    for name in self.GetAllSlots():
469
      if hasattr(self, name):
470
        state[name] = getattr(self, name)
471
    return state
472

    
473
  def __setstate__(self, state):
474
    """Generic unserializer.
475

476
    This method just restores from the serialized state the attributes
477
    of the current instance.
478

479
    @param state: the serialized opcode data
480
    @type state:  C{dict}
481

482
    """
483
    if not isinstance(state, dict):
484
      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
485
                       type(state))
486

    
487
    for name in self.GetAllSlots():
488
      if name not in state and hasattr(self, name):
489
        delattr(self, name)
490

    
491
    for name in state:
492
      setattr(self, name, state[name])
493

    
494
  @classmethod
495
  def GetAllParams(cls):
496
    """Compute list of all parameters for an opcode.
497

498
    """
499
    slots = []
500
    for parent in cls.__mro__:
501
      slots.extend(getattr(parent, "OP_PARAMS", []))
502
    return slots
503

    
504
  def Validate(self, set_defaults): # pylint: disable=W0221
505
    """Validate opcode parameters, optionally setting default values.
506

507
    @type set_defaults: bool
508
    @param set_defaults: Whether to set default values
509
    @raise errors.OpPrereqError: When a parameter value doesn't match
510
                                 requirements
511

512
    """
513
    for (attr_name, default, test, _) in self.GetAllParams():
514
      assert test == ht.NoType or callable(test)
515

    
516
      if not hasattr(self, attr_name):
517
        if default == ht.NoDefault:
518
          raise errors.OpPrereqError("Required parameter '%s.%s' missing" %
519
                                     (self.OP_ID, attr_name),
520
                                     errors.ECODE_INVAL)
521
        elif set_defaults:
522
          if callable(default):
523
            dval = default()
524
          else:
525
            dval = default
526
          setattr(self, attr_name, dval)
527

    
528
      if test == ht.NoType:
529
        # no tests here
530
        continue
531

    
532
      if set_defaults or hasattr(self, attr_name):
533
        attr_val = getattr(self, attr_name)
534
        if not test(attr_val):
535
          logging.error("OpCode %s, parameter %s, has invalid type %s/value"
536
                        " '%s' expecting type %s",
537
                        self.OP_ID, attr_name, type(attr_val), attr_val, test)
538
          raise errors.OpPrereqError("Parameter '%s.%s' fails validation" %
539
                                     (self.OP_ID, attr_name),
540
                                     errors.ECODE_INVAL)
541

    
542

    
543
def _BuildJobDepCheck(relative):
544
  """Builds check for job dependencies (L{DEPEND_ATTR}).
545

546
  @type relative: bool
547
  @param relative: Whether to accept relative job IDs (negative)
548
  @rtype: callable
549

550
  """
551
  if relative:
552
    job_id = ht.TOr(ht.TJobId, ht.TRelativeJobId)
553
  else:
554
    job_id = ht.TJobId
555

    
556
  job_dep = \
557
    ht.TAnd(ht.TOr(ht.TList, ht.TTuple),
558
            ht.TIsLength(2),
559
            ht.TItems([job_id,
560
                       ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))]))
561

    
562
  return ht.TMaybeListOf(job_dep)
563

    
564

    
565
TNoRelativeJobDependencies = _BuildJobDepCheck(False)
566

    
567
#: List of submission status and job ID as returned by C{SubmitManyJobs}
568
_TJobIdListItem = \
569
  ht.TAnd(ht.TIsLength(2),
570
          ht.TItems([ht.Comment("success")(ht.TBool),
571
                     ht.Comment("Job ID if successful, error message"
572
                                " otherwise")(ht.TOr(ht.TString,
573
                                                     ht.TJobId))]))
574
TJobIdList = ht.TListOf(_TJobIdListItem)
575

    
576
#: Result containing only list of submitted jobs
577
TJobIdListOnly = ht.TStrictDict(True, True, {
578
  constants.JOB_IDS_KEY: ht.Comment("List of submitted jobs")(TJobIdList),
579
  })
580

    
581

    
582
class OpCode(BaseOpCode):
583
  """Abstract OpCode.
584

585
  This is the root of the actual OpCode hierarchy. All clases derived
586
  from this class should override OP_ID.
587

588
  @cvar OP_ID: The ID of this opcode. This should be unique amongst all
589
               children of this class.
590
  @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
591
                      string returned by Summary(); see the docstring of that
592
                      method for details).
593
  @cvar OP_PARAMS: List of opcode attributes, the default values they should
594
                   get if not already defined, and types they must match.
595
  @cvar OP_RESULT: Callable to verify opcode result
596
  @cvar WITH_LU: Boolean that specifies whether this should be included in
597
      mcpu's dispatch table
598
  @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
599
                 the check steps
600
  @ivar priority: Opcode priority for queue
601

602
  """
603
  # pylint: disable=E1101
604
  # as OP_ID is dynamically defined
605
  WITH_LU = True
606
  OP_PARAMS = [
607
    ("dry_run", None, ht.TMaybeBool, "Run checks only, don't execute"),
608
    ("debug_level", None, ht.TMaybe(ht.TNonNegativeInt), "Debug level"),
609
    ("priority", constants.OP_PRIO_DEFAULT,
610
     ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID), "Opcode priority"),
611
    (DEPEND_ATTR, None, _BuildJobDepCheck(True),
612
     "Job dependencies; if used through ``SubmitManyJobs`` relative (negative)"
613
     " job IDs can be used; see :doc:`design document <design-chained-jobs>`"
614
     " for details"),
615
    (COMMENT_ATTR, None, ht.TMaybeString,
616
     "Comment describing the purpose of the opcode"),
617
    ]
618
  OP_RESULT = None
619

    
620
  def __getstate__(self):
621
    """Specialized getstate for opcodes.
622

623
    This method adds to the state dictionary the OP_ID of the class,
624
    so that on unload we can identify the correct class for
625
    instantiating the opcode.
626

627
    @rtype:   C{dict}
628
    @return:  the state as a dictionary
629

630
    """
631
    data = BaseOpCode.__getstate__(self)
632
    data["OP_ID"] = self.OP_ID
633
    return data
634

    
635
  @classmethod
636
  def LoadOpCode(cls, data):
637
    """Generic load opcode method.
638

639
    The method identifies the correct opcode class from the dict-form
640
    by looking for a OP_ID key, if this is not found, or its value is
641
    not available in this module as a child of this class, we fail.
642

643
    @type data:  C{dict}
644
    @param data: the serialized opcode
645

646
    """
647
    if not isinstance(data, dict):
648
      raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
649
    if "OP_ID" not in data:
650
      raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
651
    op_id = data["OP_ID"]
652
    op_class = None
653
    if op_id in OP_MAPPING:
654
      op_class = OP_MAPPING[op_id]
655
    else:
656
      raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
657
                       op_id)
658
    op = op_class()
659
    new_data = data.copy()
660
    del new_data["OP_ID"]
661
    op.__setstate__(new_data)
662
    return op
663

    
664
  def Summary(self):
665
    """Generates a summary description of this opcode.
666

667
    The summary is the value of the OP_ID attribute (without the "OP_"
668
    prefix), plus the value of the OP_DSC_FIELD attribute, if one was
669
    defined; this field should allow to easily identify the operation
670
    (for an instance creation job, e.g., it would be the instance
671
    name).
672

673
    """
674
    assert self.OP_ID is not None and len(self.OP_ID) > 3
675
    # all OP_ID start with OP_, we remove that
676
    txt = self.OP_ID[3:]
677
    field_name = getattr(self, "OP_DSC_FIELD", None)
678
    if field_name:
679
      field_value = getattr(self, field_name, None)
680
      if isinstance(field_value, (list, tuple)):
681
        field_value = ",".join(str(i) for i in field_value)
682
      txt = "%s(%s)" % (txt, field_value)
683
    return txt
684

    
685
  def TinySummary(self):
686
    """Generates a compact summary description of the opcode.
687

688
    """
689
    assert self.OP_ID.startswith("OP_")
690

    
691
    text = self.OP_ID[3:]
692

    
693
    for (prefix, supplement) in _SUMMARY_PREFIX.items():
694
      if text.startswith(prefix):
695
        return supplement + text[len(prefix):]
696

    
697
    return text
698

    
699

    
700
# cluster opcodes
701

    
702
class OpClusterPostInit(OpCode):
703
  """Post cluster initialization.
704

705
  This opcode does not touch the cluster at all. Its purpose is to run hooks
706
  after the cluster has been initialized.
707

708
  """
709
  OP_RESULT = ht.TBool
710

    
711

    
712
class OpClusterDestroy(OpCode):
713
  """Destroy the cluster.
714

715
  This opcode has no other parameters. All the state is irreversibly
716
  lost after the execution of this opcode.
717

718
  """
719
  OP_RESULT = ht.TNonEmptyString
720

    
721

    
722
class OpClusterQuery(OpCode):
723
  """Query cluster information."""
724
  OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TAny)
725

    
726

    
727
class OpClusterVerify(OpCode):
728
  """Submits all jobs necessary to verify the cluster.
729

730
  """
731
  OP_PARAMS = [
732
    _PDebugSimulateErrors,
733
    _PErrorCodes,
734
    _PSkipChecks,
735
    _PIgnoreErrors,
736
    _PVerbose,
737
    ("group_name", None, ht.TMaybeString, "Group to verify"),
738
    ]
739
  OP_RESULT = TJobIdListOnly
740

    
741

    
742
class OpClusterVerifyConfig(OpCode):
743
  """Verify the cluster config.
744

745
  """
746
  OP_PARAMS = [
747
    _PDebugSimulateErrors,
748
    _PErrorCodes,
749
    _PIgnoreErrors,
750
    _PVerbose,
751
    ]
752
  OP_RESULT = ht.TBool
753

    
754

    
755
class OpClusterVerifyGroup(OpCode):
756
  """Run verify on a node group from the cluster.
757

758
  @type skip_checks: C{list}
759
  @ivar skip_checks: steps to be skipped from the verify process; this
760
                     needs to be a subset of
761
                     L{constants.VERIFY_OPTIONAL_CHECKS}; currently
762
                     only L{constants.VERIFY_NPLUSONE_MEM} can be passed
763

764
  """
765
  OP_DSC_FIELD = "group_name"
766
  OP_PARAMS = [
767
    _PGroupName,
768
    _PDebugSimulateErrors,
769
    _PErrorCodes,
770
    _PSkipChecks,
771
    _PIgnoreErrors,
772
    _PVerbose,
773
    ]
774
  OP_RESULT = ht.TBool
775

    
776

    
777
class OpClusterVerifyDisks(OpCode):
778
  """Verify the cluster disks.
779

780
  """
781
  OP_RESULT = TJobIdListOnly
782

    
783

    
784
class OpGroupVerifyDisks(OpCode):
785
  """Verifies the status of all disks in a node group.
786

787
  Result: a tuple of three elements:
788
    - dict of node names with issues (values: error msg)
789
    - list of instances with degraded disks (that should be activated)
790
    - dict of instances with missing logical volumes (values: (node, vol)
791
      pairs with details about the missing volumes)
792

793
  In normal operation, all lists should be empty. A non-empty instance
794
  list (3rd element of the result) is still ok (errors were fixed) but
795
  non-empty node list means some node is down, and probably there are
796
  unfixable drbd errors.
797

798
  Note that only instances that are drbd-based are taken into
799
  consideration. This might need to be revisited in the future.
800

801
  """
802
  OP_DSC_FIELD = "group_name"
803
  OP_PARAMS = [
804
    _PGroupName,
805
    ]
806
  OP_RESULT = \
807
    ht.TAnd(ht.TIsLength(3),
808
            ht.TItems([ht.TDictOf(ht.TString, ht.TString),
809
                       ht.TListOf(ht.TString),
810
                       ht.TDictOf(ht.TString,
811
                                  ht.TListOf(ht.TListOf(ht.TString)))]))
812

    
813

    
814
class OpClusterRepairDiskSizes(OpCode):
815
  """Verify the disk sizes of the instances and fixes configuration
816
  mimatches.
817

818
  Parameters: optional instances list, in case we want to restrict the
819
  checks to only a subset of the instances.
820

821
  Result: a list of tuples, (instance, disk, new-size) for changed
822
  configurations.
823

824
  In normal operation, the list should be empty.
825

826
  @type instances: list
827
  @ivar instances: the list of instances to check, or empty for all instances
828

829
  """
830
  OP_PARAMS = [
831
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
832
    ]
833
  OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3),
834
                                 ht.TItems([ht.TNonEmptyString,
835
                                            ht.TNonNegativeInt,
836
                                            ht.TNonNegativeInt])))
837

    
838

    
839
class OpClusterConfigQuery(OpCode):
840
  """Query cluster configuration values."""
841
  OP_PARAMS = [
842
    _POutputFields,
843
    ]
844
  OP_RESULT = ht.TListOf(ht.TAny)
845

    
846

    
847
class OpClusterRename(OpCode):
848
  """Rename the cluster.
849

850
  @type name: C{str}
851
  @ivar name: The new name of the cluster. The name and/or the master IP
852
              address will be changed to match the new name and its IP
853
              address.
854

855
  """
856
  OP_DSC_FIELD = "name"
857
  OP_PARAMS = [
858
    ("name", ht.NoDefault, ht.TNonEmptyString, None),
859
    ]
860
  OP_RESULT = ht.TNonEmptyString
861

    
862

    
863
class OpClusterSetParams(OpCode):
864
  """Change the parameters of the cluster.
865

866
  @type vg_name: C{str} or C{None}
867
  @ivar vg_name: The new volume group name or None to disable LVM usage.
868

869
  """
870
  OP_PARAMS = [
871
    _PHvState,
872
    _PDiskState,
873
    ("vg_name", None, ht.TMaybe(ht.TString), "Volume group name"),
874
    ("enabled_hypervisors", None,
875
     ht.TMaybe(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)),
876
                       ht.TTrue)),
877
     "List of enabled hypervisors"),
878
    ("hvparams", None,
879
     ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)),
880
     "Cluster-wide hypervisor parameter defaults, hypervisor-dependent"),
881
    ("beparams", None, ht.TMaybeDict,
882
     "Cluster-wide backend parameter defaults"),
883
    ("os_hvp", None, ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)),
884
     "Cluster-wide per-OS hypervisor parameter defaults"),
885
    ("osparams", None,
886
     ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)),
887
     "Cluster-wide OS parameter defaults"),
888
    _PDiskParams,
889
    ("candidate_pool_size", None, ht.TMaybe(ht.TPositiveInt),
890
     "Master candidate pool size"),
891
    ("uid_pool", None, ht.NoType,
892
     "Set UID pool, must be list of lists describing UID ranges (two items,"
893
     " start and end inclusive)"),
894
    ("add_uids", None, ht.NoType,
895
     "Extend UID pool, must be list of lists describing UID ranges (two"
896
     " items, start and end inclusive) to be added"),
897
    ("remove_uids", None, ht.NoType,
898
     "Shrink UID pool, must be list of lists describing UID ranges (two"
899
     " items, start and end inclusive) to be removed"),
900
    ("maintain_node_health", None, ht.TMaybeBool,
901
     "Whether to automatically maintain node health"),
902
    ("prealloc_wipe_disks", None, ht.TMaybeBool,
903
     "Whether to wipe disks before allocating them to instances"),
904
    ("nicparams", None, ht.TMaybeDict, "Cluster-wide NIC parameter defaults"),
905
    ("ndparams", None, ht.TMaybeDict, "Cluster-wide node parameter defaults"),
906
    ("ipolicy", None, ht.TMaybeDict,
907
     "Cluster-wide :ref:`instance policy <rapi-ipolicy>` specs"),
908
    ("drbd_helper", None, ht.TMaybe(ht.TString), "DRBD helper program"),
909
    ("default_iallocator", None, ht.TMaybe(ht.TString),
910
     "Default iallocator for cluster"),
911
    ("master_netdev", None, ht.TMaybe(ht.TString),
912
     "Master network device"),
913
    ("master_netmask", None, ht.TMaybe(ht.TNonNegativeInt),
914
     "Netmask of the master IP"),
915
    ("reserved_lvs", None, ht.TMaybeListOf(ht.TNonEmptyString),
916
     "List of reserved LVs"),
917
    ("hidden_os", None, _TestClusterOsList,
918
     "Modify list of hidden operating systems: each modification must have"
919
     " two items, the operation and the OS name; the operation can be"
920
     " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)),
921
    ("blacklisted_os", None, _TestClusterOsList,
922
     "Modify list of blacklisted operating systems: each modification must"
923
     " have two items, the operation and the OS name; the operation can be"
924
     " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)),
925
    ("use_external_mip_script", None, ht.TMaybeBool,
926
     "Whether to use an external master IP address setup script"),
927
    ]
928
  OP_RESULT = ht.TNone
929

    
930

    
931
class OpClusterRedistConf(OpCode):
932
  """Force a full push of the cluster configuration.
933

934
  """
935
  OP_RESULT = ht.TNone
936

    
937

    
938
class OpClusterActivateMasterIp(OpCode):
939
  """Activate the master IP on the master node.
940

941
  """
942
  OP_RESULT = ht.TNone
943

    
944

    
945
class OpClusterDeactivateMasterIp(OpCode):
946
  """Deactivate the master IP on the master node.
947

948
  """
949
  OP_RESULT = ht.TNone
950

    
951

    
952
class OpQuery(OpCode):
953
  """Query for resources/items.
954

955
  @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
956
  @ivar fields: List of fields to retrieve
957
  @ivar qfilter: Query filter
958

959
  """
960
  OP_DSC_FIELD = "what"
961
  OP_PARAMS = [
962
    _PQueryWhat,
963
    _PUseLocking,
964
    ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
965
     "Requested fields"),
966
    ("qfilter", None, ht.TMaybe(ht.TList),
967
     "Query filter"),
968
    ]
969
  OP_RESULT = \
970
    _GenerateObjectTypeCheck(objects.QueryResponse, {
971
      "fields": ht.TListOf(_TQueryFieldDef),
972
      "data": _TQueryResult,
973
      })
974

    
975

    
976
class OpQueryFields(OpCode):
977
  """Query for available resource/item fields.
978

979
  @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
980
  @ivar fields: List of fields to retrieve
981

982
  """
983
  OP_DSC_FIELD = "what"
984
  OP_PARAMS = [
985
    _PQueryWhat,
986
    ("fields", None, ht.TMaybeListOf(ht.TNonEmptyString),
987
     "Requested fields; if not given, all are returned"),
988
    ]
989
  OP_RESULT = \
990
    _GenerateObjectTypeCheck(objects.QueryFieldsResponse, {
991
      "fields": ht.TListOf(_TQueryFieldDef),
992
      })
993

    
994

    
995
class OpOobCommand(OpCode):
996
  """Interact with OOB."""
997
  OP_PARAMS = [
998
    ("node_names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
999
     "List of nodes to run the OOB command against"),
1000
    ("command", ht.NoDefault, ht.TElemOf(constants.OOB_COMMANDS),
1001
     "OOB command to be run"),
1002
    ("timeout", constants.OOB_TIMEOUT, ht.TInt,
1003
     "Timeout before the OOB helper will be terminated"),
1004
    ("ignore_status", False, ht.TBool,
1005
     "Ignores the node offline status for power off"),
1006
    ("power_delay", constants.OOB_POWER_DELAY, ht.TNonNegativeFloat,
1007
     "Time in seconds to wait between powering on nodes"),
1008
    ]
1009
  # Fixme: Make it more specific with all the special cases in LUOobCommand
1010
  OP_RESULT = _TQueryResult
1011

    
1012

    
1013
class OpRestrictedCommand(OpCode):
1014
  """Runs a restricted command on node(s).
1015

1016
  """
1017
  OP_PARAMS = [
1018
    _PUseLocking,
1019
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
1020
     "Nodes on which the command should be run (at least one)"),
1021
    ("command", ht.NoDefault, ht.TNonEmptyString,
1022
     "Command name (no parameters)"),
1023
    ]
1024

    
1025
  _RESULT_ITEMS = [
1026
    ht.Comment("success")(ht.TBool),
1027
    ht.Comment("output or error message")(ht.TString),
1028
    ]
1029

    
1030
  OP_RESULT = \
1031
    ht.TListOf(ht.TAnd(ht.TIsLength(len(_RESULT_ITEMS)),
1032
                       ht.TItems(_RESULT_ITEMS)))
1033

    
1034

    
1035
# node opcodes
1036

    
1037
class OpNodeRemove(OpCode):
1038
  """Remove a node.
1039

1040
  @type node_name: C{str}
1041
  @ivar node_name: The name of the node to remove. If the node still has
1042
                   instances on it, the operation will fail.
1043

1044
  """
1045
  OP_DSC_FIELD = "node_name"
1046
  OP_PARAMS = [
1047
    _PNodeName,
1048
    ]
1049
  OP_RESULT = ht.TNone
1050

    
1051

    
1052
class OpNodeAdd(OpCode):
1053
  """Add a node to the cluster.
1054

1055
  @type node_name: C{str}
1056
  @ivar node_name: The name of the node to add. This can be a short name,
1057
                   but it will be expanded to the FQDN.
1058
  @type primary_ip: IP address
1059
  @ivar primary_ip: The primary IP of the node. This will be ignored when the
1060
                    opcode is submitted, but will be filled during the node
1061
                    add (so it will be visible in the job query).
1062
  @type secondary_ip: IP address
1063
  @ivar secondary_ip: The secondary IP of the node. This needs to be passed
1064
                      if the cluster has been initialized in 'dual-network'
1065
                      mode, otherwise it must not be given.
1066
  @type readd: C{bool}
1067
  @ivar readd: Whether to re-add an existing node to the cluster. If
1068
               this is not passed, then the operation will abort if the node
1069
               name is already in the cluster; use this parameter to 'repair'
1070
               a node that had its configuration broken, or was reinstalled
1071
               without removal from the cluster.
1072
  @type group: C{str}
1073
  @ivar group: The node group to which this node will belong.
1074
  @type vm_capable: C{bool}
1075
  @ivar vm_capable: The vm_capable node attribute
1076
  @type master_capable: C{bool}
1077
  @ivar master_capable: The master_capable node attribute
1078

1079
  """
1080
  OP_DSC_FIELD = "node_name"
1081
  OP_PARAMS = [
1082
    _PNodeName,
1083
    _PHvState,
1084
    _PDiskState,
1085
    ("primary_ip", None, ht.NoType, "Primary IP address"),
1086
    ("secondary_ip", None, ht.TMaybeString, "Secondary IP address"),
1087
    ("readd", False, ht.TBool, "Whether node is re-added to cluster"),
1088
    ("group", None, ht.TMaybeString, "Initial node group"),
1089
    ("master_capable", None, ht.TMaybeBool,
1090
     "Whether node can become master or master candidate"),
1091
    ("vm_capable", None, ht.TMaybeBool,
1092
     "Whether node can host instances"),
1093
    ("ndparams", None, ht.TMaybeDict, "Node parameters"),
1094
    ]
1095
  OP_RESULT = ht.TNone
1096

    
1097

    
1098
class OpNodeQuery(OpCode):
1099
  """Compute the list of nodes."""
1100
  OP_PARAMS = [
1101
    _POutputFields,
1102
    _PUseLocking,
1103
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1104
     "Empty list to query all nodes, node names otherwise"),
1105
    ]
1106
  OP_RESULT = _TOldQueryResult
1107

    
1108

    
1109
class OpNodeQueryvols(OpCode):
1110
  """Get list of volumes on node."""
1111
  OP_PARAMS = [
1112
    _POutputFields,
1113
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1114
     "Empty list to query all nodes, node names otherwise"),
1115
    ]
1116
  OP_RESULT = ht.TListOf(ht.TAny)
1117

    
1118

    
1119
class OpNodeQueryStorage(OpCode):
1120
  """Get information on storage for node(s)."""
1121
  OP_PARAMS = [
1122
    _POutputFields,
1123
    _PStorageType,
1124
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "List of nodes"),
1125
    ("name", None, ht.TMaybeString, "Storage name"),
1126
    ]
1127
  OP_RESULT = _TOldQueryResult
1128

    
1129

    
1130
class OpNodeModifyStorage(OpCode):
1131
  """Modifies the properies of a storage unit"""
1132
  OP_DSC_FIELD = "node_name"
1133
  OP_PARAMS = [
1134
    _PNodeName,
1135
    _PStorageType,
1136
    _PStorageName,
1137
    ("changes", ht.NoDefault, ht.TDict, "Requested changes"),
1138
    ]
1139
  OP_RESULT = ht.TNone
1140

    
1141

    
1142
class OpRepairNodeStorage(OpCode):
1143
  """Repairs the volume group on a node."""
1144
  OP_DSC_FIELD = "node_name"
1145
  OP_PARAMS = [
1146
    _PNodeName,
1147
    _PStorageType,
1148
    _PStorageName,
1149
    _PIgnoreConsistency,
1150
    ]
1151
  OP_RESULT = ht.TNone
1152

    
1153

    
1154
class OpNodeSetParams(OpCode):
1155
  """Change the parameters of a node."""
1156
  OP_DSC_FIELD = "node_name"
1157
  OP_PARAMS = [
1158
    _PNodeName,
1159
    _PForce,
1160
    _PHvState,
1161
    _PDiskState,
1162
    ("master_candidate", None, ht.TMaybeBool,
1163
     "Whether the node should become a master candidate"),
1164
    ("offline", None, ht.TMaybeBool,
1165
     "Whether the node should be marked as offline"),
1166
    ("drained", None, ht.TMaybeBool,
1167
     "Whether the node should be marked as drained"),
1168
    ("auto_promote", False, ht.TBool,
1169
     "Whether node(s) should be promoted to master candidate if necessary"),
1170
    ("master_capable", None, ht.TMaybeBool,
1171
     "Denote whether node can become master or master candidate"),
1172
    ("vm_capable", None, ht.TMaybeBool,
1173
     "Denote whether node can host instances"),
1174
    ("secondary_ip", None, ht.TMaybeString,
1175
     "Change node's secondary IP address"),
1176
    ("ndparams", None, ht.TMaybeDict, "Set node parameters"),
1177
    ("powered", None, ht.TMaybeBool,
1178
     "Whether the node should be marked as powered"),
1179
    ]
1180
  OP_RESULT = _TSetParamsResult
1181

    
1182

    
1183
class OpNodePowercycle(OpCode):
1184
  """Tries to powercycle a node."""
1185
  OP_DSC_FIELD = "node_name"
1186
  OP_PARAMS = [
1187
    _PNodeName,
1188
    _PForce,
1189
    ]
1190
  OP_RESULT = ht.TMaybeString
1191

    
1192

    
1193
class OpNodeMigrate(OpCode):
1194
  """Migrate all instances from a node."""
1195
  OP_DSC_FIELD = "node_name"
1196
  OP_PARAMS = [
1197
    _PNodeName,
1198
    _PMigrationMode,
1199
    _PMigrationLive,
1200
    _PMigrationTargetNode,
1201
    _PAllowRuntimeChgs,
1202
    _PIgnoreIpolicy,
1203
    _PIAllocFromDesc("Iallocator for deciding the target node"
1204
                     " for shared-storage instances"),
1205
    ]
1206
  OP_RESULT = TJobIdListOnly
1207

    
1208

    
1209
class OpNodeEvacuate(OpCode):
1210
  """Evacuate instances off a number of nodes."""
1211
  OP_DSC_FIELD = "node_name"
1212
  OP_PARAMS = [
1213
    _PEarlyRelease,
1214
    _PNodeName,
1215
    ("remote_node", None, ht.TMaybeString, "New secondary node"),
1216
    _PIAllocFromDesc("Iallocator for computing solution"),
1217
    ("mode", ht.NoDefault, ht.TElemOf(constants.NODE_EVAC_MODES),
1218
     "Node evacuation mode"),
1219
    ]
1220
  OP_RESULT = TJobIdListOnly
1221

    
1222

    
1223
# instance opcodes
1224

    
1225
class OpInstanceCreate(OpCode):
1226
  """Create an instance.
1227

1228
  @ivar instance_name: Instance name
1229
  @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES})
1230
  @ivar source_handshake: Signed handshake from source (remote import only)
1231
  @ivar source_x509_ca: Source X509 CA in PEM format (remote import only)
1232
  @ivar source_instance_name: Previous name of instance (remote import only)
1233
  @ivar source_shutdown_timeout: Shutdown timeout used for source instance
1234
    (remote import only)
1235

1236
  """
1237
  OP_DSC_FIELD = "instance_name"
1238
  OP_PARAMS = [
1239
    _PInstanceName,
1240
    _PForceVariant,
1241
    _PWaitForSync,
1242
    _PNameCheck,
1243
    _PIgnoreIpolicy,
1244
    ("beparams", ht.EmptyDict, ht.TDict, "Backend parameters for instance"),
1245
    ("disks", ht.NoDefault, ht.TListOf(_TDiskParams),
1246
     "Disk descriptions, for example ``[{\"%s\": 100}, {\"%s\": 5}]``;"
1247
     " each disk definition must contain a ``%s`` value and"
1248
     " can contain an optional ``%s`` value denoting the disk access mode"
1249
     " (%s)" %
1250
     (constants.IDISK_SIZE, constants.IDISK_SIZE, constants.IDISK_SIZE,
1251
      constants.IDISK_MODE,
1252
      " or ".join("``%s``" % i for i in sorted(constants.DISK_ACCESS_SET)))),
1253
    ("disk_template", ht.NoDefault, _BuildDiskTemplateCheck(True),
1254
     "Disk template"),
1255
    ("file_driver", None, ht.TMaybe(ht.TElemOf(constants.FILE_DRIVER)),
1256
     "Driver for file-backed disks"),
1257
    ("file_storage_dir", None, ht.TMaybeString,
1258
     "Directory for storing file-backed disks"),
1259
    ("hvparams", ht.EmptyDict, ht.TDict,
1260
     "Hypervisor parameters for instance, hypervisor-dependent"),
1261
    ("hypervisor", None, ht.TMaybeString, "Hypervisor"),
1262
    _PIAllocFromDesc("Iallocator for deciding which node(s) to use"),
1263
    ("identify_defaults", False, ht.TBool,
1264
     "Reset instance parameters to default if equal"),
1265
    ("ip_check", True, ht.TBool, _PIpCheckDoc),
1266
    ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"),
1267
    ("mode", ht.NoDefault, ht.TElemOf(constants.INSTANCE_CREATE_MODES),
1268
     "Instance creation mode"),
1269
    ("nics", ht.NoDefault, ht.TListOf(_TestNicDef),
1270
     "List of NIC (network interface) definitions, for example"
1271
     " ``[{}, {}, {\"%s\": \"198.51.100.4\"}]``; each NIC definition can"
1272
     " contain the optional values %s" %
1273
     (constants.INIC_IP,
1274
      ", ".join("``%s``" % i for i in sorted(constants.INIC_PARAMS)))),
1275
    ("no_install", None, ht.TMaybeBool,
1276
     "Do not install the OS (will disable automatic start)"),
1277
    ("osparams", ht.EmptyDict, ht.TDict, "OS parameters for instance"),
1278
    ("os_type", None, ht.TMaybeString, "Operating system"),
1279
    ("pnode", None, ht.TMaybeString, "Primary node"),
1280
    ("snode", None, ht.TMaybeString, "Secondary node"),
1281
    ("source_handshake", None, ht.TMaybe(ht.TList),
1282
     "Signed handshake from source (remote import only)"),
1283
    ("source_instance_name", None, ht.TMaybeString,
1284
     "Source instance name (remote import only)"),
1285
    ("source_shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT,
1286
     ht.TNonNegativeInt,
1287
     "How long source instance was given to shut down (remote import only)"),
1288
    ("source_x509_ca", None, ht.TMaybeString,
1289
     "Source X509 CA in PEM format (remote import only)"),
1290
    ("src_node", None, ht.TMaybeString, "Source node for import"),
1291
    ("src_path", None, ht.TMaybeString, "Source directory for import"),
1292
    ("start", True, ht.TBool, "Whether to start instance after creation"),
1293
    ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Instance tags"),
1294
    ]
1295
  OP_RESULT = ht.Comment("instance nodes")(ht.TListOf(ht.TNonEmptyString))
1296

    
1297

    
1298
class OpInstanceMultiAlloc(OpCode):
1299
  """Allocates multiple instances.
1300

1301
  """
1302
  OP_PARAMS = [
1303
    _PIAllocFromDesc("Iallocator used to allocate all the instances"),
1304
    ("instances", ht.EmptyList, ht.TListOf(ht.TInstanceOf(OpInstanceCreate)),
1305
     "List of instance create opcodes describing the instances to allocate"),
1306
    ]
1307
  _JOB_LIST = ht.Comment("List of submitted jobs")(TJobIdList)
1308
  ALLOCATABLE_KEY = "allocatable"
1309
  FAILED_KEY = "allocatable"
1310
  OP_RESULT = ht.TStrictDict(True, True, {
1311
    constants.JOB_IDS_KEY: _JOB_LIST,
1312
    ALLOCATABLE_KEY: ht.TListOf(ht.TNonEmptyString),
1313
    FAILED_KEY: ht.TListOf(ht.TNonEmptyString),
1314
    })
1315

    
1316
  def __getstate__(self):
1317
    """Generic serializer.
1318

1319
    """
1320
    state = OpCode.__getstate__(self)
1321
    if hasattr(self, "instances"):
1322
      # pylint: disable=E1101
1323
      state["instances"] = [inst.__getstate__() for inst in self.instances]
1324
    return state
1325

    
1326
  def __setstate__(self, state):
1327
    """Generic unserializer.
1328

1329
    This method just restores from the serialized state the attributes
1330
    of the current instance.
1331

1332
    @param state: the serialized opcode data
1333
    @type state: C{dict}
1334

1335
    """
1336
    if not isinstance(state, dict):
1337
      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
1338
                       type(state))
1339

    
1340
    if "instances" in state:
1341
      state["instances"] = map(OpCode.LoadOpCode, state["instances"])
1342

    
1343
    return OpCode.__setstate__(self, state)
1344

    
1345
  def Validate(self, set_defaults):
1346
    """Validates this opcode.
1347

1348
    We do this recursively.
1349

1350
    """
1351
    OpCode.Validate(self, set_defaults)
1352

    
1353
    for inst in self.instances: # pylint: disable=E1101
1354
      inst.Validate(set_defaults)
1355

    
1356

    
1357
class OpInstanceReinstall(OpCode):
1358
  """Reinstall an instance's OS."""
1359
  OP_DSC_FIELD = "instance_name"
1360
  OP_PARAMS = [
1361
    _PInstanceName,
1362
    _PForceVariant,
1363
    ("os_type", None, ht.TMaybeString, "Instance operating system"),
1364
    ("osparams", None, ht.TMaybeDict, "Temporary OS parameters"),
1365
    ]
1366
  OP_RESULT = ht.TNone
1367

    
1368

    
1369
class OpInstanceRemove(OpCode):
1370
  """Remove an instance."""
1371
  OP_DSC_FIELD = "instance_name"
1372
  OP_PARAMS = [
1373
    _PInstanceName,
1374
    _PShutdownTimeout,
1375
    ("ignore_failures", False, ht.TBool,
1376
     "Whether to ignore failures during removal"),
1377
    ]
1378
  OP_RESULT = ht.TNone
1379

    
1380

    
1381
class OpInstanceRename(OpCode):
1382
  """Rename an instance."""
1383
  OP_PARAMS = [
1384
    _PInstanceName,
1385
    _PNameCheck,
1386
    ("new_name", ht.NoDefault, ht.TNonEmptyString, "New instance name"),
1387
    ("ip_check", False, ht.TBool, _PIpCheckDoc),
1388
    ]
1389
  OP_RESULT = ht.Comment("New instance name")(ht.TNonEmptyString)
1390

    
1391

    
1392
class OpInstanceStartup(OpCode):
1393
  """Startup an instance."""
1394
  OP_DSC_FIELD = "instance_name"
1395
  OP_PARAMS = [
1396
    _PInstanceName,
1397
    _PForce,
1398
    _PIgnoreOfflineNodes,
1399
    ("hvparams", ht.EmptyDict, ht.TDict,
1400
     "Temporary hypervisor parameters, hypervisor-dependent"),
1401
    ("beparams", ht.EmptyDict, ht.TDict, "Temporary backend parameters"),
1402
    _PNoRemember,
1403
    _PStartupPaused,
1404
    ]
1405
  OP_RESULT = ht.TNone
1406

    
1407

    
1408
class OpInstanceShutdown(OpCode):
1409
  """Shutdown an instance."""
1410
  OP_DSC_FIELD = "instance_name"
1411
  OP_PARAMS = [
1412
    _PInstanceName,
1413
    _PIgnoreOfflineNodes,
1414
    ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TNonNegativeInt,
1415
     "How long to wait for instance to shut down"),
1416
    _PNoRemember,
1417
    ]
1418
  OP_RESULT = ht.TNone
1419

    
1420

    
1421
class OpInstanceReboot(OpCode):
1422
  """Reboot an instance."""
1423
  OP_DSC_FIELD = "instance_name"
1424
  OP_PARAMS = [
1425
    _PInstanceName,
1426
    _PShutdownTimeout,
1427
    ("ignore_secondaries", False, ht.TBool,
1428
     "Whether to start the instance even if secondary disks are failing"),
1429
    ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES),
1430
     "How to reboot instance"),
1431
    ]
1432
  OP_RESULT = ht.TNone
1433

    
1434

    
1435
class OpInstanceReplaceDisks(OpCode):
1436
  """Replace the disks of an instance."""
1437
  OP_DSC_FIELD = "instance_name"
1438
  OP_PARAMS = [
1439
    _PInstanceName,
1440
    _PEarlyRelease,
1441
    _PIgnoreIpolicy,
1442
    ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES),
1443
     "Replacement mode"),
1444
    ("disks", ht.EmptyList, ht.TListOf(ht.TNonNegativeInt),
1445
     "Disk indexes"),
1446
    ("remote_node", None, ht.TMaybeString, "New secondary node"),
1447
    _PIAllocFromDesc("Iallocator for deciding new secondary node"),
1448
    ]
1449
  OP_RESULT = ht.TNone
1450

    
1451

    
1452
class OpInstanceFailover(OpCode):
1453
  """Failover an instance."""
1454
  OP_DSC_FIELD = "instance_name"
1455
  OP_PARAMS = [
1456
    _PInstanceName,
1457
    _PShutdownTimeout,
1458
    _PIgnoreConsistency,
1459
    _PMigrationTargetNode,
1460
    _PIgnoreIpolicy,
1461
    _PIAllocFromDesc("Iallocator for deciding the target node for"
1462
                     " shared-storage instances"),
1463
    ]
1464
  OP_RESULT = ht.TNone
1465

    
1466

    
1467
class OpInstanceMigrate(OpCode):
1468
  """Migrate an instance.
1469

1470
  This migrates (without shutting down an instance) to its secondary
1471
  node.
1472

1473
  @ivar instance_name: the name of the instance
1474
  @ivar mode: the migration mode (live, non-live or None for auto)
1475

1476
  """
1477
  OP_DSC_FIELD = "instance_name"
1478
  OP_PARAMS = [
1479
    _PInstanceName,
1480
    _PMigrationMode,
1481
    _PMigrationLive,
1482
    _PMigrationTargetNode,
1483
    _PAllowRuntimeChgs,
1484
    _PIgnoreIpolicy,
1485
    ("cleanup", False, ht.TBool,
1486
     "Whether a previously failed migration should be cleaned up"),
1487
    _PIAllocFromDesc("Iallocator for deciding the target node for"
1488
                     " shared-storage instances"),
1489
    ("allow_failover", False, ht.TBool,
1490
     "Whether we can fallback to failover if migration is not possible"),
1491
    ]
1492
  OP_RESULT = ht.TNone
1493

    
1494

    
1495
class OpInstanceMove(OpCode):
1496
  """Move an instance.
1497

1498
  This move (with shutting down an instance and data copying) to an
1499
  arbitrary node.
1500

1501
  @ivar instance_name: the name of the instance
1502
  @ivar target_node: the destination node
1503

1504
  """
1505
  OP_DSC_FIELD = "instance_name"
1506
  OP_PARAMS = [
1507
    _PInstanceName,
1508
    _PShutdownTimeout,
1509
    _PIgnoreIpolicy,
1510
    ("target_node", ht.NoDefault, ht.TNonEmptyString, "Target node"),
1511
    _PIgnoreConsistency,
1512
    ]
1513
  OP_RESULT = ht.TNone
1514

    
1515

    
1516
class OpInstanceConsole(OpCode):
1517
  """Connect to an instance's console."""
1518
  OP_DSC_FIELD = "instance_name"
1519
  OP_PARAMS = [
1520
    _PInstanceName,
1521
    ]
1522
  OP_RESULT = ht.TDict
1523

    
1524

    
1525
class OpInstanceActivateDisks(OpCode):
1526
  """Activate an instance's disks."""
1527
  OP_DSC_FIELD = "instance_name"
1528
  OP_PARAMS = [
1529
    _PInstanceName,
1530
    ("ignore_size", False, ht.TBool, "Whether to ignore recorded size"),
1531
    _PWaitForSyncFalse,
1532
    ]
1533
  OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3),
1534
                                 ht.TItems([ht.TNonEmptyString,
1535
                                            ht.TNonEmptyString,
1536
                                            ht.TNonEmptyString])))
1537

    
1538

    
1539
class OpInstanceDeactivateDisks(OpCode):
1540
  """Deactivate an instance's disks."""
1541
  OP_DSC_FIELD = "instance_name"
1542
  OP_PARAMS = [
1543
    _PInstanceName,
1544
    _PForce,
1545
    ]
1546
  OP_RESULT = ht.TNone
1547

    
1548

    
1549
class OpInstanceRecreateDisks(OpCode):
1550
  """Recreate an instance's disks."""
1551
  _TDiskChanges = \
1552
    ht.TAnd(ht.TIsLength(2),
1553
            ht.TItems([ht.Comment("Disk index")(ht.TNonNegativeInt),
1554
                       ht.Comment("Parameters")(_TDiskParams)]))
1555

    
1556
  OP_DSC_FIELD = "instance_name"
1557
  OP_PARAMS = [
1558
    _PInstanceName,
1559
    ("disks", ht.EmptyList,
1560
     ht.TOr(ht.TListOf(ht.TNonNegativeInt), ht.TListOf(_TDiskChanges)),
1561
     "List of disk indexes (deprecated) or a list of tuples containing a disk"
1562
     " index and a possibly empty dictionary with disk parameter changes"),
1563
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1564
     "New instance nodes, if relocation is desired"),
1565
    _PIAllocFromDesc("Iallocator for deciding new nodes"),
1566
    ]
1567
  OP_RESULT = ht.TNone
1568

    
1569

    
1570
class OpInstanceQuery(OpCode):
1571
  """Compute the list of instances."""
1572
  OP_PARAMS = [
1573
    _POutputFields,
1574
    _PUseLocking,
1575
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1576
     "Empty list to query all instances, instance names otherwise"),
1577
    ]
1578
  OP_RESULT = _TOldQueryResult
1579

    
1580

    
1581
class OpInstanceQueryData(OpCode):
1582
  """Compute the run-time status of instances."""
1583
  OP_PARAMS = [
1584
    _PUseLocking,
1585
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1586
     "Instance names"),
1587
    ("static", False, ht.TBool,
1588
     "Whether to only return configuration data without querying"
1589
     " nodes"),
1590
    ]
1591
  OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TDict)
1592

    
1593

    
1594
def _TestInstSetParamsModList(fn):
1595
  """Generates a check for modification lists.
1596

1597
  """
1598
  # Old format
1599
  # TODO: Remove in version 2.8 including support in LUInstanceSetParams
1600
  old_mod_item_fn = \
1601
    ht.TAnd(ht.TIsLength(2), ht.TItems([
1602
      ht.TOr(ht.TElemOf(constants.DDMS_VALUES), ht.TNonNegativeInt),
1603
      fn,
1604
      ]))
1605

    
1606
  # New format, supporting adding/removing disks/NICs at arbitrary indices
1607
  mod_item_fn = \
1608
    ht.TAnd(ht.TIsLength(3), ht.TItems([
1609
      ht.TElemOf(constants.DDMS_VALUES_WITH_MODIFY),
1610
      ht.Comment("Disk index, can be negative, e.g. -1 for last disk")(ht.TInt),
1611
      fn,
1612
      ]))
1613

    
1614
  return ht.TOr(ht.Comment("Recommended")(ht.TListOf(mod_item_fn)),
1615
                ht.Comment("Deprecated")(ht.TListOf(old_mod_item_fn)))
1616

    
1617

    
1618
class OpInstanceSetParams(OpCode):
1619
  """Change the parameters of an instance.
1620

1621
  """
1622
  TestNicModifications = _TestInstSetParamsModList(_TestNicDef)
1623
  TestDiskModifications = _TestInstSetParamsModList(_TDiskParams)
1624

    
1625
  OP_DSC_FIELD = "instance_name"
1626
  OP_PARAMS = [
1627
    _PInstanceName,
1628
    _PForce,
1629
    _PForceVariant,
1630
    _PIgnoreIpolicy,
1631
    ("nics", ht.EmptyList, TestNicModifications,
1632
     "List of NIC changes: each item is of the form ``(op, index, settings)``,"
1633
     " ``op`` is one of ``%s``, ``%s`` or ``%s``, ``index`` can be either -1"
1634
     " to refer to the last position, or a zero-based index number; a"
1635
     " deprecated version of this parameter used the form ``(op, settings)``,"
1636
     " where ``op`` can be ``%s`` to add a new NIC with the specified"
1637
     " settings, ``%s`` to remove the last NIC or a number to modify the"
1638
     " settings of the NIC with that index" %
1639
     (constants.DDM_ADD, constants.DDM_MODIFY, constants.DDM_REMOVE,
1640
      constants.DDM_ADD, constants.DDM_REMOVE)),
1641
    ("disks", ht.EmptyList, TestDiskModifications,
1642
     "List of disk changes; see ``nics``"),
1643
    ("beparams", ht.EmptyDict, ht.TDict, "Per-instance backend parameters"),
1644
    ("runtime_mem", None, ht.TMaybePositiveInt, "New runtime memory"),
1645
    ("hvparams", ht.EmptyDict, ht.TDict,
1646
     "Per-instance hypervisor parameters, hypervisor-dependent"),
1647
    ("disk_template", None, ht.TMaybe(_BuildDiskTemplateCheck(False)),
1648
     "Disk template for instance"),
1649
    ("remote_node", None, ht.TMaybeString,
1650
     "Secondary node (used when changing disk template)"),
1651
    ("os_name", None, ht.TMaybeString,
1652
     "Change the instance's OS without reinstalling the instance"),
1653
    ("osparams", None, ht.TMaybeDict, "Per-instance OS parameters"),
1654
    ("wait_for_sync", True, ht.TBool,
1655
     "Whether to wait for the disk to synchronize, when changing template"),
1656
    ("offline", None, ht.TMaybeBool, "Whether to mark instance as offline"),
1657
    ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"),
1658
    ]
1659
  OP_RESULT = _TSetParamsResult
1660

    
1661

    
1662
class OpInstanceGrowDisk(OpCode):
1663
  """Grow a disk of an instance."""
1664
  OP_DSC_FIELD = "instance_name"
1665
  OP_PARAMS = [
1666
    _PInstanceName,
1667
    _PWaitForSync,
1668
    ("disk", ht.NoDefault, ht.TInt, "Disk index"),
1669
    ("amount", ht.NoDefault, ht.TNonNegativeInt,
1670
     "Amount of disk space to add (megabytes)"),
1671
    ("absolute", False, ht.TBool,
1672
     "Whether the amount parameter is an absolute target or a relative one"),
1673
    ]
1674
  OP_RESULT = ht.TNone
1675

    
1676

    
1677
class OpInstanceChangeGroup(OpCode):
1678
  """Moves an instance to another node group."""
1679
  OP_DSC_FIELD = "instance_name"
1680
  OP_PARAMS = [
1681
    _PInstanceName,
1682
    _PEarlyRelease,
1683
    _PIAllocFromDesc("Iallocator for computing solution"),
1684
    ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString),
1685
     "Destination group names or UUIDs (defaults to \"all but current group\""),
1686
    ]
1687
  OP_RESULT = TJobIdListOnly
1688

    
1689

    
1690
# Node group opcodes
1691

    
1692
class OpGroupAdd(OpCode):
1693
  """Add a node group to the cluster."""
1694
  OP_DSC_FIELD = "group_name"
1695
  OP_PARAMS = [
1696
    _PGroupName,
1697
    _PNodeGroupAllocPolicy,
1698
    _PGroupNodeParams,
1699
    _PDiskParams,
1700
    _PHvState,
1701
    _PDiskState,
1702
    ("ipolicy", None, ht.TMaybeDict,
1703
     "Group-wide :ref:`instance policy <rapi-ipolicy>` specs"),
1704
    ]
1705
  OP_RESULT = ht.TNone
1706

    
1707

    
1708
class OpGroupAssignNodes(OpCode):
1709
  """Assign nodes to a node group."""
1710
  OP_DSC_FIELD = "group_name"
1711
  OP_PARAMS = [
1712
    _PGroupName,
1713
    _PForce,
1714
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
1715
     "List of nodes to assign"),
1716
    ]
1717
  OP_RESULT = ht.TNone
1718

    
1719

    
1720
class OpGroupQuery(OpCode):
1721
  """Compute the list of node groups."""
1722
  OP_PARAMS = [
1723
    _POutputFields,
1724
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1725
     "Empty list to query all groups, group names otherwise"),
1726
    ]
1727
  OP_RESULT = _TOldQueryResult
1728

    
1729

    
1730
class OpGroupSetParams(OpCode):
1731
  """Change the parameters of a node group."""
1732
  OP_DSC_FIELD = "group_name"
1733
  OP_PARAMS = [
1734
    _PGroupName,
1735
    _PNodeGroupAllocPolicy,
1736
    _PGroupNodeParams,
1737
    _PDiskParams,
1738
    _PHvState,
1739
    _PDiskState,
1740
    ("ipolicy", None, ht.TMaybeDict, "Group-wide instance policy specs"),
1741
    ]
1742
  OP_RESULT = _TSetParamsResult
1743

    
1744

    
1745
class OpGroupRemove(OpCode):
1746
  """Remove a node group from the cluster."""
1747
  OP_DSC_FIELD = "group_name"
1748
  OP_PARAMS = [
1749
    _PGroupName,
1750
    ]
1751
  OP_RESULT = ht.TNone
1752

    
1753

    
1754
class OpGroupRename(OpCode):
1755
  """Rename a node group in the cluster."""
1756
  OP_PARAMS = [
1757
    _PGroupName,
1758
    ("new_name", ht.NoDefault, ht.TNonEmptyString, "New group name"),
1759
    ]
1760
  OP_RESULT = ht.Comment("New group name")(ht.TNonEmptyString)
1761

    
1762

    
1763
class OpGroupEvacuate(OpCode):
1764
  """Evacuate a node group in the cluster."""
1765
  OP_DSC_FIELD = "group_name"
1766
  OP_PARAMS = [
1767
    _PGroupName,
1768
    _PEarlyRelease,
1769
    _PIAllocFromDesc("Iallocator for computing solution"),
1770
    ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString),
1771
     "Destination group names or UUIDs"),
1772
    ]
1773
  OP_RESULT = TJobIdListOnly
1774

    
1775

    
1776
# OS opcodes
1777
class OpOsDiagnose(OpCode):
1778
  """Compute the list of guest operating systems."""
1779
  OP_PARAMS = [
1780
    _POutputFields,
1781
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1782
     "Which operating systems to diagnose"),
1783
    ]
1784
  OP_RESULT = _TOldQueryResult
1785

    
1786

    
1787
# Exports opcodes
1788
class OpBackupQuery(OpCode):
1789
  """Compute the list of exported images."""
1790
  OP_PARAMS = [
1791
    _PUseLocking,
1792
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1793
     "Empty list to query all nodes, node names otherwise"),
1794
    ]
1795
  OP_RESULT = ht.TDictOf(ht.TNonEmptyString,
1796
                         ht.TOr(ht.Comment("False on error")(ht.TBool),
1797
                                ht.TListOf(ht.TNonEmptyString)))
1798

    
1799

    
1800
class OpBackupPrepare(OpCode):
1801
  """Prepares an instance export.
1802

1803
  @ivar instance_name: Instance name
1804
  @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1805

1806
  """
1807
  OP_DSC_FIELD = "instance_name"
1808
  OP_PARAMS = [
1809
    _PInstanceName,
1810
    ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES),
1811
     "Export mode"),
1812
    ]
1813
  OP_RESULT = ht.TMaybeDict
1814

    
1815

    
1816
class OpBackupExport(OpCode):
1817
  """Export an instance.
1818

1819
  For local exports, the export destination is the node name. For
1820
  remote exports, the export destination is a list of tuples, each
1821
  consisting of hostname/IP address, port, magic, HMAC and HMAC
1822
  salt. The HMAC is calculated using the cluster domain secret over
1823
  the value "${index}:${hostname}:${port}". The destination X509 CA
1824
  must be a signed certificate.
1825

1826
  @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1827
  @ivar target_node: Export destination
1828
  @ivar x509_key_name: X509 key to use (remote export only)
1829
  @ivar destination_x509_ca: Destination X509 CA in PEM format (remote export
1830
                             only)
1831

1832
  """
1833
  OP_DSC_FIELD = "instance_name"
1834
  OP_PARAMS = [
1835
    _PInstanceName,
1836
    _PShutdownTimeout,
1837
    # TODO: Rename target_node as it changes meaning for different export modes
1838
    # (e.g. "destination")
1839
    ("target_node", ht.NoDefault, ht.TOr(ht.TNonEmptyString, ht.TList),
1840
     "Destination information, depends on export mode"),
1841
    ("shutdown", True, ht.TBool, "Whether to shutdown instance before export"),
1842
    ("remove_instance", False, ht.TBool,
1843
     "Whether to remove instance after export"),
1844
    ("ignore_remove_failures", False, ht.TBool,
1845
     "Whether to ignore failures while removing instances"),
1846
    ("mode", constants.EXPORT_MODE_LOCAL, ht.TElemOf(constants.EXPORT_MODES),
1847
     "Export mode"),
1848
    ("x509_key_name", None, ht.TMaybe(ht.TList),
1849
     "Name of X509 key (remote export only)"),
1850
    ("destination_x509_ca", None, ht.TMaybeString,
1851
     "Destination X509 CA (remote export only)"),
1852
    ]
1853
  OP_RESULT = \
1854
    ht.TAnd(ht.TIsLength(2), ht.TItems([
1855
      ht.Comment("Finalizing status")(ht.TBool),
1856
      ht.Comment("Status for every exported disk")(ht.TListOf(ht.TBool)),
1857
      ]))
1858

    
1859

    
1860
class OpBackupRemove(OpCode):
1861
  """Remove an instance's export."""
1862
  OP_DSC_FIELD = "instance_name"
1863
  OP_PARAMS = [
1864
    _PInstanceName,
1865
    ]
1866
  OP_RESULT = ht.TNone
1867

    
1868

    
1869
# Tags opcodes
1870
class OpTagsGet(OpCode):
1871
  """Returns the tags of the given object."""
1872
  OP_DSC_FIELD = "name"
1873
  OP_PARAMS = [
1874
    _PTagKind,
1875
    # Not using _PUseLocking as the default is different for historical reasons
1876
    ("use_locking", True, ht.TBool, "Whether to use synchronization"),
1877
    # Name is only meaningful for nodes and instances
1878
    ("name", ht.NoDefault, ht.TMaybeString,
1879
     "Name of object to retrieve tags from"),
1880
    ]
1881
  OP_RESULT = ht.TListOf(ht.TNonEmptyString)
1882

    
1883

    
1884
class OpTagsSearch(OpCode):
1885
  """Searches the tags in the cluster for a given pattern."""
1886
  OP_DSC_FIELD = "pattern"
1887
  OP_PARAMS = [
1888
    ("pattern", ht.NoDefault, ht.TNonEmptyString,
1889
     "Search pattern (regular expression)"),
1890
    ]
1891
  OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(2), ht.TItems([
1892
    ht.TNonEmptyString,
1893
    ht.TNonEmptyString,
1894
    ])))
1895

    
1896

    
1897
class OpTagsSet(OpCode):
1898
  """Add a list of tags on a given object."""
1899
  OP_PARAMS = [
1900
    _PTagKind,
1901
    _PTags,
1902
    # Name is only meaningful for groups, nodes and instances
1903
    ("name", ht.NoDefault, ht.TMaybeString,
1904
     "Name of object where tag(s) should be added"),
1905
    ]
1906
  OP_RESULT = ht.TNone
1907

    
1908

    
1909
class OpTagsDel(OpCode):
1910
  """Remove a list of tags from a given object."""
1911
  OP_PARAMS = [
1912
    _PTagKind,
1913
    _PTags,
1914
    # Name is only meaningful for groups, nodes and instances
1915
    ("name", ht.NoDefault, ht.TMaybeString,
1916
     "Name of object where tag(s) should be deleted"),
1917
    ]
1918
  OP_RESULT = ht.TNone
1919

    
1920

    
1921
# Test opcodes
1922
class OpTestDelay(OpCode):
1923
  """Sleeps for a configured amount of time.
1924

1925
  This is used just for debugging and testing.
1926

1927
  Parameters:
1928
    - duration: the time to sleep
1929
    - on_master: if true, sleep on the master
1930
    - on_nodes: list of nodes in which to sleep
1931

1932
  If the on_master parameter is true, it will execute a sleep on the
1933
  master (before any node sleep).
1934

1935
  If the on_nodes list is not empty, it will sleep on those nodes
1936
  (after the sleep on the master, if that is enabled).
1937

1938
  As an additional feature, the case of duration < 0 will be reported
1939
  as an execution error, so this opcode can be used as a failure
1940
  generator. The case of duration == 0 will not be treated specially.
1941

1942
  """
1943
  OP_DSC_FIELD = "duration"
1944
  OP_PARAMS = [
1945
    ("duration", ht.NoDefault, ht.TNumber, None),
1946
    ("on_master", True, ht.TBool, None),
1947
    ("on_nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
1948
    ("repeat", 0, ht.TNonNegativeInt, None),
1949
    ]
1950

    
1951

    
1952
class OpTestAllocator(OpCode):
1953
  """Allocator framework testing.
1954

1955
  This opcode has two modes:
1956
    - gather and return allocator input for a given mode (allocate new
1957
      or replace secondary) and a given instance definition (direction
1958
      'in')
1959
    - run a selected allocator for a given operation (as above) and
1960
      return the allocator output (direction 'out')
1961

1962
  """
1963
  OP_DSC_FIELD = "iallocator"
1964
  OP_PARAMS = [
1965
    ("direction", ht.NoDefault,
1966
     ht.TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS), None),
1967
    ("mode", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_MODES), None),
1968
    ("name", ht.NoDefault, ht.TNonEmptyString, None),
1969
    ("nics", ht.NoDefault,
1970
     ht.TMaybeListOf(ht.TDictOf(ht.TElemOf([constants.INIC_MAC,
1971
                                            constants.INIC_IP,
1972
                                            "bridge"]),
1973
                                ht.TMaybeString)),
1974
     None),
1975
    ("disks", ht.NoDefault, ht.TMaybe(ht.TList), None),
1976
    ("hypervisor", None, ht.TMaybeString, None),
1977
    _PIAllocFromDesc(None),
1978
    ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
1979
    ("memory", None, ht.TMaybe(ht.TNonNegativeInt), None),
1980
    ("vcpus", None, ht.TMaybe(ht.TNonNegativeInt), None),
1981
    ("os", None, ht.TMaybeString, None),
1982
    ("disk_template", None, ht.TMaybeString, None),
1983
    ("instances", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
1984
    ("evac_mode", None,
1985
     ht.TMaybe(ht.TElemOf(constants.IALLOCATOR_NEVAC_MODES)), None),
1986
    ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
1987
    ("spindle_use", 1, ht.TNonNegativeInt, None),
1988
    ("count", 1, ht.TNonNegativeInt, None),
1989
    ]
1990

    
1991

    
1992
class OpTestJqueue(OpCode):
1993
  """Utility opcode to test some aspects of the job queue.
1994

1995
  """
1996
  OP_PARAMS = [
1997
    ("notify_waitlock", False, ht.TBool, None),
1998
    ("notify_exec", False, ht.TBool, None),
1999
    ("log_messages", ht.EmptyList, ht.TListOf(ht.TString), None),
2000
    ("fail", False, ht.TBool, None),
2001
    ]
2002

    
2003

    
2004
class OpTestDummy(OpCode):
2005
  """Utility opcode used by unittests.
2006

2007
  """
2008
  OP_PARAMS = [
2009
    ("result", ht.NoDefault, ht.NoType, None),
2010
    ("messages", ht.NoDefault, ht.NoType, None),
2011
    ("fail", ht.NoDefault, ht.NoType, None),
2012
    ("submit_jobs", None, ht.NoType, None),
2013
    ]
2014
  WITH_LU = False
2015

    
2016

    
2017
# Network opcodes
2018
# Add a new network in the cluster
2019
class OpNetworkAdd(OpCode):
2020
  """Add an IP network to the cluster."""
2021
  OP_DSC_FIELD = "network_name"
2022
  OP_PARAMS = [
2023
    _PNetworkName,
2024
    ("network_type", None, ht.TMaybe(_CheckNetworkType), "Network type"),
2025
    ("network", ht.NoDefault, _TIpNetwork4, "IPv4 subnet"),
2026
    ("gateway", None, ht.TMaybe(_TIpAddress4), "IPv4 gateway"),
2027
    ("network6", None, ht.TMaybe(_TIpNetwork6), "IPv6 subnet"),
2028
    ("gateway6", None, ht.TMaybe(_TIpAddress6), "IPv6 gateway"),
2029
    ("mac_prefix", None, ht.TMaybeString,
2030
     "MAC address prefix that overrides cluster one"),
2031
    ("add_reserved_ips", None, _TMaybeAddr4List,
2032
     "Which IP addresses to reserve"),
2033
    ("conflicts_check", True, ht.TBool,
2034
     "Whether to check for conflicting IP addresses"),
2035
    ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Network tags"),
2036
    ]
2037
  OP_RESULT = ht.TNone
2038

    
2039

    
2040
class OpNetworkRemove(OpCode):
2041
  """Remove an existing network from the cluster.
2042
     Must not be connected to any nodegroup.
2043

2044
  """
2045
  OP_DSC_FIELD = "network_name"
2046
  OP_PARAMS = [
2047
    _PNetworkName,
2048
    _PForce,
2049
    ]
2050
  OP_RESULT = ht.TNone
2051

    
2052

    
2053
class OpNetworkSetParams(OpCode):
2054
  """Modify Network's parameters except for IPv4 subnet"""
2055
  OP_DSC_FIELD = "network_name"
2056
  OP_PARAMS = [
2057
    _PNetworkName,
2058
    ("network_type", None, ht.TMaybeValueNone(_CheckNetworkType),
2059
     "Network type"),
2060
    ("gateway", None, ht.TMaybeValueNone(_TIpAddress4), "IPv4 gateway"),
2061
    ("network6", None, ht.TMaybeValueNone(_TIpNetwork6), "IPv6 subnet"),
2062
    ("gateway6", None, ht.TMaybeValueNone(_TIpAddress6), "IPv6 gateway"),
2063
    ("mac_prefix", None, ht.TMaybeValueNone(ht.TString),
2064
     "MAC address prefix that overrides cluster one"),
2065
    ("add_reserved_ips", None, _TMaybeAddr4List,
2066
     "Which external IP addresses to reserve"),
2067
    ("remove_reserved_ips", None, _TMaybeAddr4List,
2068
     "Which external IP addresses to release"),
2069
    ]
2070
  OP_RESULT = ht.TNone
2071

    
2072

    
2073
class OpNetworkConnect(OpCode):
2074
  """Connect a Network to a specific Nodegroup with the defined netparams
2075
     (mode, link). Nics in this Network will inherit those params.
2076
     Produce errors if a NIC (that its not already assigned to a network)
2077
     has an IP that is contained in the Network this will produce error unless
2078
     --no-conflicts-check is passed.
2079

2080
  """
2081
  OP_DSC_FIELD = "network_name"
2082
  OP_PARAMS = [
2083
    _PGroupName,
2084
    _PNetworkName,
2085
    ("network_mode", ht.NoDefault, ht.TString, "Connectivity mode"),
2086
    ("network_link", ht.NoDefault, ht.TString, "Connectivity link"),
2087
    ("conflicts_check", True, ht.TBool, "Whether to check for conflicting IPs"),
2088
    ]
2089
  OP_RESULT = ht.TNone
2090

    
2091

    
2092
class OpNetworkDisconnect(OpCode):
2093
  """Disconnect a Network from a Nodegroup. Produce errors if NICs are
2094
     present in the Network unless --no-conficts-check option is passed.
2095

2096
  """
2097
  OP_DSC_FIELD = "network_name"
2098
  OP_PARAMS = [
2099
    _PGroupName,
2100
    _PNetworkName,
2101
    ("conflicts_check", True, ht.TBool, "Whether to check for conflicting IPs"),
2102
    ]
2103
  OP_RESULT = ht.TNone
2104

    
2105

    
2106
class OpNetworkQuery(OpCode):
2107
  """Compute the list of networks."""
2108
  OP_PARAMS = [
2109
    _POutputFields,
2110
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
2111
     "Empty list to query all groups, group names otherwise"),
2112
    ]
2113
  OP_RESULT = _TOldQueryResult
2114

    
2115

    
2116
def _GetOpList():
2117
  """Returns list of all defined opcodes.
2118

2119
  Does not eliminate duplicates by C{OP_ID}.
2120

2121
  """
2122
  return [v for v in globals().values()
2123
          if (isinstance(v, type) and issubclass(v, OpCode) and
2124
              hasattr(v, "OP_ID") and v is not OpCode)]
2125

    
2126

    
2127
OP_MAPPING = dict((v.OP_ID, v) for v in _GetOpList())