Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes.py @ a138ead7

History | View | Annotate | Download (64.9 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.TOr(ht.TNone, 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.TOr(ht.TNone, 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.TOr(ht.TNone,
156
          ht.TDictOf(ht.TElemOf(constants.DISK_TEMPLATES), ht.TDict)),
157
   "Disk templates' parameter defaults")
158

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

    
163

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

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

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

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

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

    
185
_TestClusterOsList = ht.TMaybeListOf(_TestClusterOsListItem)
186

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

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

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

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

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

    
213
_TQueryResult = ht.TListOf(_TQueryRow)
214

    
215
_TOldQueryRow = ht.TListOf(ht.TAny)
216

    
217
_TOldQueryResult = ht.TListOf(_TOldQueryRow)
218

    
219

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

    
227
#: Attribute name for dependencies
228
DEPEND_ATTR = "depends"
229

    
230
#: Attribute name for comment
231
COMMENT_ATTR = "comment"
232

    
233

    
234
def _NameToId(name):
235
  """Convert an opcode class name to an OP_ID.
236

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

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

    
254

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

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

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

    
267

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

    
276

    
277
def RequireFileStorage():
278
  """Checks that file storage is enabled.
279

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

283
  @raise errors.OpPrereqError: when file storage is disabled
284

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

    
290

    
291
def RequireSharedFileStorage():
292
  """Checks that shared 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 shared file storage is disabled
298

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

    
304

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

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

    
316

    
317
def _BuildDiskTemplateCheck(accept_none):
318
  """Builds check for disk template.
319

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

324
  """
325
  template_check = ht.TElemOf(constants.DISK_TEMPLATES)
326

    
327
  if accept_none:
328
    template_check = ht.TOr(ht.TNone, template_check)
329

    
330
  return ht.TAnd(template_check, _CheckFileStorage)
331

    
332

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

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

    
345

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

    
350
_CheckNetworkType = ht.TElemOf(constants.NETWORK_VALID_TYPES)
351

    
352
#: Network type parameter
353
_PNetworkType = ("network_type", None, ht.TOr(ht.TNone, _CheckNetworkType),
354
                 "Network type")
355

    
356

    
357
def _CheckCIDRNetNotation(value):
358
  """Ensure a given cidr notation type is valid.
359

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

    
367

    
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
def _CheckCIDR6AddrNotation(value):
380
  """Ensure a given cidr notation type is valid.
381

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

    
389

    
390
def _CheckCIDR6NetNotation(value):
391
  """Ensure a given cidr notation type is valid.
392

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

    
400

    
401
_TIpAddress = ht.TOr(ht.TNone, ht.TAnd(ht.TString, _CheckCIDRNetNotation))
402
_TIpAddress6 = ht.TOr(ht.TNone, ht.TAnd(ht.TString, _CheckCIDR6NetNotation))
403

    
404

    
405
class _AutoOpParamSlots(objectutils.AutoSlots):
406
  """Meta class for opcode definitions.
407

408
  """
409
  def __new__(mcs, name, bases, attrs):
410
    """Called when a class should be created.
411

412
    @param mcs: The meta class
413
    @param name: Name of created class
414
    @param bases: Base classes
415
    @type attrs: dict
416
    @param attrs: Class attributes
417

418
    """
419
    assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
420

    
421
    slots = mcs._GetSlots(attrs)
422
    assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
423
      "Class '%s' uses unknown field in OP_DSC_FIELD" % name
424

    
425
    attrs["OP_ID"] = _NameToId(name)
426

    
427
    return objectutils.AutoSlots.__new__(mcs, name, bases, attrs)
428

    
429
  @classmethod
430
  def _GetSlots(mcs, attrs):
431
    """Build the slots out of OP_PARAMS.
432

433
    """
434
    # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams
435
    params = attrs.setdefault("OP_PARAMS", [])
436

    
437
    # Use parameter names as slots
438
    return [pname for (pname, _, _, _) in params]
439

    
440

    
441
class BaseOpCode(objectutils.ValidatedSlots):
442
  """A simple serializable object.
443

444
  This object serves as a parent class for OpCode without any custom
445
  field handling.
446

447
  """
448
  # pylint: disable=E1101
449
  # as OP_ID is dynamically defined
450
  __metaclass__ = _AutoOpParamSlots
451

    
452
  def __getstate__(self):
453
    """Generic serializer.
454

455
    This method just returns the contents of the instance as a
456
    dictionary.
457

458
    @rtype:  C{dict}
459
    @return: the instance attributes and their values
460

461
    """
462
    state = {}
463
    for name in self.GetAllSlots():
464
      if hasattr(self, name):
465
        state[name] = getattr(self, name)
466
    return state
467

    
468
  def __setstate__(self, state):
469
    """Generic unserializer.
470

471
    This method just restores from the serialized state the attributes
472
    of the current instance.
473

474
    @param state: the serialized opcode data
475
    @type state:  C{dict}
476

477
    """
478
    if not isinstance(state, dict):
479
      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
480
                       type(state))
481

    
482
    for name in self.GetAllSlots():
483
      if name not in state and hasattr(self, name):
484
        delattr(self, name)
485

    
486
    for name in state:
487
      setattr(self, name, state[name])
488

    
489
  @classmethod
490
  def GetAllParams(cls):
491
    """Compute list of all parameters for an opcode.
492

493
    """
494
    slots = []
495
    for parent in cls.__mro__:
496
      slots.extend(getattr(parent, "OP_PARAMS", []))
497
    return slots
498

    
499
  def Validate(self, set_defaults): # pylint: disable=W0221
500
    """Validate opcode parameters, optionally setting default values.
501

502
    @type set_defaults: bool
503
    @param set_defaults: Whether to set default values
504
    @raise errors.OpPrereqError: When a parameter value doesn't match
505
                                 requirements
506

507
    """
508
    for (attr_name, default, test, _) in self.GetAllParams():
509
      assert test == ht.NoType or callable(test)
510

    
511
      if not hasattr(self, attr_name):
512
        if default == ht.NoDefault:
513
          raise errors.OpPrereqError("Required parameter '%s.%s' missing" %
514
                                     (self.OP_ID, attr_name),
515
                                     errors.ECODE_INVAL)
516
        elif set_defaults:
517
          if callable(default):
518
            dval = default()
519
          else:
520
            dval = default
521
          setattr(self, attr_name, dval)
522

    
523
      if test == ht.NoType:
524
        # no tests here
525
        continue
526

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

    
537

    
538
def _BuildJobDepCheck(relative):
539
  """Builds check for job dependencies (L{DEPEND_ATTR}).
540

541
  @type relative: bool
542
  @param relative: Whether to accept relative job IDs (negative)
543
  @rtype: callable
544

545
  """
546
  if relative:
547
    job_id = ht.TOr(ht.TJobId, ht.TRelativeJobId)
548
  else:
549
    job_id = ht.TJobId
550

    
551
  job_dep = \
552
    ht.TAnd(ht.TIsLength(2),
553
            ht.TItems([job_id,
554
                       ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))]))
555

    
556
  return ht.TMaybeListOf(job_dep)
557

    
558

    
559
TNoRelativeJobDependencies = _BuildJobDepCheck(False)
560

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

    
570
#: Result containing only list of submitted jobs
571
TJobIdListOnly = ht.TStrictDict(True, True, {
572
  constants.JOB_IDS_KEY: ht.Comment("List of submitted jobs")(TJobIdList),
573
  })
574

    
575

    
576
class OpCode(BaseOpCode):
577
  """Abstract OpCode.
578

579
  This is the root of the actual OpCode hierarchy. All clases derived
580
  from this class should override OP_ID.
581

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

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

    
614
  def __getstate__(self):
615
    """Specialized getstate for opcodes.
616

617
    This method adds to the state dictionary the OP_ID of the class,
618
    so that on unload we can identify the correct class for
619
    instantiating the opcode.
620

621
    @rtype:   C{dict}
622
    @return:  the state as a dictionary
623

624
    """
625
    data = BaseOpCode.__getstate__(self)
626
    data["OP_ID"] = self.OP_ID
627
    return data
628

    
629
  @classmethod
630
  def LoadOpCode(cls, data):
631
    """Generic load opcode method.
632

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

637
    @type data:  C{dict}
638
    @param data: the serialized opcode
639

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

    
658
  def Summary(self):
659
    """Generates a summary description of this opcode.
660

661
    The summary is the value of the OP_ID attribute (without the "OP_"
662
    prefix), plus the value of the OP_DSC_FIELD attribute, if one was
663
    defined; this field should allow to easily identify the operation
664
    (for an instance creation job, e.g., it would be the instance
665
    name).
666

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

    
679
  def TinySummary(self):
680
    """Generates a compact summary description of the opcode.
681

682
    """
683
    assert self.OP_ID.startswith("OP_")
684

    
685
    text = self.OP_ID[3:]
686

    
687
    for (prefix, supplement) in _SUMMARY_PREFIX.items():
688
      if text.startswith(prefix):
689
        return supplement + text[len(prefix):]
690

    
691
    return text
692

    
693

    
694
# cluster opcodes
695

    
696
class OpClusterPostInit(OpCode):
697
  """Post cluster initialization.
698

699
  This opcode does not touch the cluster at all. Its purpose is to run hooks
700
  after the cluster has been initialized.
701

702
  """
703
  OP_RESULT = ht.TBool
704

    
705

    
706
class OpClusterDestroy(OpCode):
707
  """Destroy the cluster.
708

709
  This opcode has no other parameters. All the state is irreversibly
710
  lost after the execution of this opcode.
711

712
  """
713
  OP_RESULT = ht.TNonEmptyString
714

    
715

    
716
class OpClusterQuery(OpCode):
717
  """Query cluster information."""
718
  OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TAny)
719

    
720

    
721
class OpClusterVerify(OpCode):
722
  """Submits all jobs necessary to verify the cluster.
723

724
  """
725
  OP_PARAMS = [
726
    _PDebugSimulateErrors,
727
    _PErrorCodes,
728
    _PSkipChecks,
729
    _PIgnoreErrors,
730
    _PVerbose,
731
    ("group_name", None, ht.TMaybeString, "Group to verify"),
732
    ]
733
  OP_RESULT = TJobIdListOnly
734

    
735

    
736
class OpClusterVerifyConfig(OpCode):
737
  """Verify the cluster config.
738

739
  """
740
  OP_PARAMS = [
741
    _PDebugSimulateErrors,
742
    _PErrorCodes,
743
    _PIgnoreErrors,
744
    _PVerbose,
745
    ]
746
  OP_RESULT = ht.TBool
747

    
748

    
749
class OpClusterVerifyGroup(OpCode):
750
  """Run verify on a node group from the cluster.
751

752
  @type skip_checks: C{list}
753
  @ivar skip_checks: steps to be skipped from the verify process; this
754
                     needs to be a subset of
755
                     L{constants.VERIFY_OPTIONAL_CHECKS}; currently
756
                     only L{constants.VERIFY_NPLUSONE_MEM} can be passed
757

758
  """
759
  OP_DSC_FIELD = "group_name"
760
  OP_PARAMS = [
761
    _PGroupName,
762
    _PDebugSimulateErrors,
763
    _PErrorCodes,
764
    _PSkipChecks,
765
    _PIgnoreErrors,
766
    _PVerbose,
767
    ]
768
  OP_RESULT = ht.TBool
769

    
770

    
771
class OpClusterVerifyDisks(OpCode):
772
  """Verify the cluster disks.
773

774
  """
775
  OP_RESULT = TJobIdListOnly
776

    
777

    
778
class OpGroupVerifyDisks(OpCode):
779
  """Verifies the status of all disks in a node group.
780

781
  Result: a tuple of three elements:
782
    - dict of node names with issues (values: error msg)
783
    - list of instances with degraded disks (that should be activated)
784
    - dict of instances with missing logical volumes (values: (node, vol)
785
      pairs with details about the missing volumes)
786

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

792
  Note that only instances that are drbd-based are taken into
793
  consideration. This might need to be revisited in the future.
794

795
  """
796
  OP_DSC_FIELD = "group_name"
797
  OP_PARAMS = [
798
    _PGroupName,
799
    ]
800
  OP_RESULT = \
801
    ht.TAnd(ht.TIsLength(3),
802
            ht.TItems([ht.TDictOf(ht.TString, ht.TString),
803
                       ht.TListOf(ht.TString),
804
                       ht.TDictOf(ht.TString,
805
                                  ht.TListOf(ht.TListOf(ht.TString)))]))
806

    
807

    
808
class OpClusterRepairDiskSizes(OpCode):
809
  """Verify the disk sizes of the instances and fixes configuration
810
  mimatches.
811

812
  Parameters: optional instances list, in case we want to restrict the
813
  checks to only a subset of the instances.
814

815
  Result: a list of tuples, (instance, disk, new-size) for changed
816
  configurations.
817

818
  In normal operation, the list should be empty.
819

820
  @type instances: list
821
  @ivar instances: the list of instances to check, or empty for all instances
822

823
  """
824
  OP_PARAMS = [
825
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
826
    ]
827
  OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3),
828
                                 ht.TItems([ht.TNonEmptyString,
829
                                            ht.TNonNegativeInt,
830
                                            ht.TNonNegativeInt])))
831

    
832

    
833
class OpClusterConfigQuery(OpCode):
834
  """Query cluster configuration values."""
835
  OP_PARAMS = [
836
    _POutputFields,
837
    ]
838
  OP_RESULT = ht.TListOf(ht.TAny)
839

    
840

    
841
class OpClusterRename(OpCode):
842
  """Rename the cluster.
843

844
  @type name: C{str}
845
  @ivar name: The new name of the cluster. The name and/or the master IP
846
              address will be changed to match the new name and its IP
847
              address.
848

849
  """
850
  OP_DSC_FIELD = "name"
851
  OP_PARAMS = [
852
    ("name", ht.NoDefault, ht.TNonEmptyString, None),
853
    ]
854
  OP_RESULT = ht.TNonEmptyString
855

    
856

    
857
class OpClusterSetParams(OpCode):
858
  """Change the parameters of the cluster.
859

860
  @type vg_name: C{str} or C{None}
861
  @ivar vg_name: The new volume group name or None to disable LVM usage.
862

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

    
924

    
925
class OpClusterRedistConf(OpCode):
926
  """Force a full push of the cluster configuration.
927

928
  """
929
  OP_RESULT = ht.TNone
930

    
931

    
932
class OpClusterActivateMasterIp(OpCode):
933
  """Activate the master IP on the master node.
934

935
  """
936
  OP_RESULT = ht.TNone
937

    
938

    
939
class OpClusterDeactivateMasterIp(OpCode):
940
  """Deactivate the master IP on the master node.
941

942
  """
943
  OP_RESULT = ht.TNone
944

    
945

    
946
class OpQuery(OpCode):
947
  """Query for resources/items.
948

949
  @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
950
  @ivar fields: List of fields to retrieve
951
  @ivar qfilter: Query filter
952

953
  """
954
  OP_DSC_FIELD = "what"
955
  OP_PARAMS = [
956
    _PQueryWhat,
957
    _PUseLocking,
958
    ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
959
     "Requested fields"),
960
    ("qfilter", None, ht.TOr(ht.TNone, ht.TList),
961
     "Query filter"),
962
    ]
963
  OP_RESULT = \
964
    _GenerateObjectTypeCheck(objects.QueryResponse, {
965
      "fields": ht.TListOf(_TQueryFieldDef),
966
      "data": _TQueryResult,
967
      })
968

    
969

    
970
class OpQueryFields(OpCode):
971
  """Query for available resource/item fields.
972

973
  @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
974
  @ivar fields: List of fields to retrieve
975

976
  """
977
  OP_DSC_FIELD = "what"
978
  OP_PARAMS = [
979
    _PQueryWhat,
980
    ("fields", None, ht.TMaybeListOf(ht.TNonEmptyString),
981
     "Requested fields; if not given, all are returned"),
982
    ]
983
  OP_RESULT = \
984
    _GenerateObjectTypeCheck(objects.QueryFieldsResponse, {
985
      "fields": ht.TListOf(_TQueryFieldDef),
986
      })
987

    
988

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

    
1006

    
1007
class OpRestrictedCommand(OpCode):
1008
  """Runs a restricted command on node(s).
1009

1010
  """
1011
  OP_PARAMS = [
1012
    _PUseLocking,
1013
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
1014
     "Nodes on which the command should be run (at least one)"),
1015
    ("command", ht.NoDefault, ht.TNonEmptyString,
1016
     "Command name (no parameters)"),
1017
    ]
1018

    
1019
  _RESULT_ITEMS = [
1020
    ht.Comment("success")(ht.TBool),
1021
    ht.Comment("output or error message")(ht.TString),
1022
    ]
1023

    
1024
  OP_RESULT = \
1025
    ht.TListOf(ht.TAnd(ht.TIsLength(len(_RESULT_ITEMS)),
1026
                       ht.TItems(_RESULT_ITEMS)))
1027

    
1028

    
1029
# node opcodes
1030

    
1031
class OpNodeRemove(OpCode):
1032
  """Remove a node.
1033

1034
  @type node_name: C{str}
1035
  @ivar node_name: The name of the node to remove. If the node still has
1036
                   instances on it, the operation will fail.
1037

1038
  """
1039
  OP_DSC_FIELD = "node_name"
1040
  OP_PARAMS = [
1041
    _PNodeName,
1042
    ]
1043
  OP_RESULT = ht.TNone
1044

    
1045

    
1046
class OpNodeAdd(OpCode):
1047
  """Add a node to the cluster.
1048

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

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

    
1091

    
1092
class OpNodeQuery(OpCode):
1093
  """Compute the list of nodes."""
1094
  OP_PARAMS = [
1095
    _POutputFields,
1096
    _PUseLocking,
1097
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1098
     "Empty list to query all nodes, node names otherwise"),
1099
    ]
1100
  OP_RESULT = _TOldQueryResult
1101

    
1102

    
1103
class OpNodeQueryvols(OpCode):
1104
  """Get list of volumes on node."""
1105
  OP_PARAMS = [
1106
    _POutputFields,
1107
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1108
     "Empty list to query all nodes, node names otherwise"),
1109
    ]
1110
  OP_RESULT = ht.TListOf(ht.TAny)
1111

    
1112

    
1113
class OpNodeQueryStorage(OpCode):
1114
  """Get information on storage for node(s)."""
1115
  OP_PARAMS = [
1116
    _POutputFields,
1117
    _PStorageType,
1118
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "List of nodes"),
1119
    ("name", None, ht.TMaybeString, "Storage name"),
1120
    ]
1121
  OP_RESULT = _TOldQueryResult
1122

    
1123

    
1124
class OpNodeModifyStorage(OpCode):
1125
  """Modifies the properies of a storage unit"""
1126
  OP_DSC_FIELD = "node_name"
1127
  OP_PARAMS = [
1128
    _PNodeName,
1129
    _PStorageType,
1130
    _PStorageName,
1131
    ("changes", ht.NoDefault, ht.TDict, "Requested changes"),
1132
    ]
1133
  OP_RESULT = ht.TNone
1134

    
1135

    
1136
class OpRepairNodeStorage(OpCode):
1137
  """Repairs the volume group on a node."""
1138
  OP_DSC_FIELD = "node_name"
1139
  OP_PARAMS = [
1140
    _PNodeName,
1141
    _PStorageType,
1142
    _PStorageName,
1143
    _PIgnoreConsistency,
1144
    ]
1145
  OP_RESULT = ht.TNone
1146

    
1147

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

    
1176

    
1177
class OpNodePowercycle(OpCode):
1178
  """Tries to powercycle a node."""
1179
  OP_DSC_FIELD = "node_name"
1180
  OP_PARAMS = [
1181
    _PNodeName,
1182
    _PForce,
1183
    ]
1184
  OP_RESULT = ht.TMaybeString
1185

    
1186

    
1187
class OpNodeMigrate(OpCode):
1188
  """Migrate all instances from a node."""
1189
  OP_DSC_FIELD = "node_name"
1190
  OP_PARAMS = [
1191
    _PNodeName,
1192
    _PMigrationMode,
1193
    _PMigrationLive,
1194
    _PMigrationTargetNode,
1195
    _PAllowRuntimeChgs,
1196
    _PIgnoreIpolicy,
1197
    ("iallocator", None, ht.TMaybeString,
1198
     "Iallocator for deciding the target node for shared-storage instances"),
1199
    ]
1200
  OP_RESULT = TJobIdListOnly
1201

    
1202

    
1203
class OpNodeEvacuate(OpCode):
1204
  """Evacuate instances off a number of nodes."""
1205
  OP_DSC_FIELD = "node_name"
1206
  OP_PARAMS = [
1207
    _PEarlyRelease,
1208
    _PNodeName,
1209
    ("remote_node", None, ht.TMaybeString, "New secondary node"),
1210
    ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"),
1211
    ("mode", ht.NoDefault, ht.TElemOf(constants.NODE_EVAC_MODES),
1212
     "Node evacuation mode"),
1213
    ]
1214
  OP_RESULT = TJobIdListOnly
1215

    
1216

    
1217
# instance opcodes
1218

    
1219
class OpInstanceCreate(OpCode):
1220
  """Create an instance.
1221

1222
  @ivar instance_name: Instance name
1223
  @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES})
1224
  @ivar source_handshake: Signed handshake from source (remote import only)
1225
  @ivar source_x509_ca: Source X509 CA in PEM format (remote import only)
1226
  @ivar source_instance_name: Previous name of instance (remote import only)
1227
  @ivar source_shutdown_timeout: Shutdown timeout used for source instance
1228
    (remote import only)
1229

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

    
1292

    
1293
class OpInstanceMultiAlloc(OpCode):
1294
  """Allocates multiple instances.
1295

1296
  """
1297
  OP_PARAMS = [
1298
    ("iallocator", None, ht.TMaybeString,
1299
     "Iallocator used to allocate all the instances"),
1300
    ("instances", [], ht.TListOf(ht.TInstanceOf(OpInstanceCreate)),
1301
     "List of instance create opcodes describing the instances to allocate"),
1302
    ]
1303
  _JOB_LIST = ht.Comment("List of submitted jobs")(TJobIdList)
1304
  ALLOCATABLE_KEY = "allocatable"
1305
  FAILED_KEY = "allocatable"
1306
  OP_RESULT = ht.TStrictDict(True, True, {
1307
    constants.JOB_IDS_KEY: _JOB_LIST,
1308
    ALLOCATABLE_KEY: ht.TListOf(ht.TNonEmptyString),
1309
    FAILED_KEY: ht.TListOf(ht.TNonEmptyString),
1310
    })
1311

    
1312
  def __getstate__(self):
1313
    """Generic serializer.
1314

1315
    """
1316
    state = OpCode.__getstate__(self)
1317
    if hasattr(self, "instances"):
1318
      # pylint: disable=E1101
1319
      state["instances"] = [inst.__getstate__() for inst in self.instances]
1320
    return state
1321

    
1322
  def __setstate__(self, state):
1323
    """Generic unserializer.
1324

1325
    This method just restores from the serialized state the attributes
1326
    of the current instance.
1327

1328
    @param state: the serialized opcode data
1329
    @type state: C{dict}
1330

1331
    """
1332
    if not isinstance(state, dict):
1333
      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
1334
                       type(state))
1335

    
1336
    if "instances" in state:
1337
      insts = [OpCode.LoadOpCode(inst) for inst in state["instances"]]
1338
      state["instances"] = insts
1339
    return OpCode.__setstate__(self, state)
1340

    
1341
  def Validate(self, set_defaults):
1342
    """Validates this opcode.
1343

1344
    We do this recursively.
1345

1346
    """
1347
    OpCode.Validate(self, set_defaults)
1348

    
1349
    for inst in self.instances: # pylint: disable=E1101
1350
      inst.Validate(set_defaults)
1351

    
1352

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

    
1364

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

    
1376

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

    
1387

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

    
1403

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

    
1416

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

    
1430

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

    
1448

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

    
1463

    
1464
class OpInstanceMigrate(OpCode):
1465
  """Migrate an instance.
1466

1467
  This migrates (without shutting down an instance) to its secondary
1468
  node.
1469

1470
  @ivar instance_name: the name of the instance
1471
  @ivar mode: the migration mode (live, non-live or None for auto)
1472

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

    
1491

    
1492
class OpInstanceMove(OpCode):
1493
  """Move an instance.
1494

1495
  This move (with shutting down an instance and data copying) to an
1496
  arbitrary node.
1497

1498
  @ivar instance_name: the name of the instance
1499
  @ivar target_node: the destination node
1500

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

    
1512

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

    
1521

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

    
1535

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

    
1545

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

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

    
1567

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

    
1578

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

    
1591

    
1592
def _TestInstSetParamsModList(fn):
1593
  """Generates a check for modification lists.
1594

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

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

    
1612
  return ht.TOr(ht.Comment("Recommended")(ht.TListOf(mod_item_fn)),
1613
                ht.Comment("Deprecated")(ht.TListOf(old_mod_item_fn)))
1614

    
1615

    
1616
class OpInstanceSetParams(OpCode):
1617
  """Change the parameters of an instance.
1618

1619
  """
1620
  TestNicModifications = _TestInstSetParamsModList(_TestNicDef)
1621
  TestDiskModifications = _TestInstSetParamsModList(_TDiskParams)
1622

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

    
1659

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

    
1674

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

    
1687

    
1688
# Node group opcodes
1689

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

    
1705

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

    
1717

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

    
1727

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

    
1742

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

    
1751

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

    
1760

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

    
1773

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

    
1784

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

    
1797

    
1798
class OpBackupPrepare(OpCode):
1799
  """Prepares an instance export.
1800

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

1804
  """
1805
  OP_DSC_FIELD = "instance_name"
1806
  OP_PARAMS = [
1807
    _PInstanceName,
1808
    ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES),
1809
     "Export mode"),
1810
    ]
1811
  OP_RESULT = ht.TOr(ht.TNone, ht.TDict)
1812

    
1813

    
1814
class OpBackupExport(OpCode):
1815
  """Export an instance.
1816

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

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

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

    
1856

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

    
1865

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

    
1880

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

    
1893

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

    
1905

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

    
1917

    
1918
# Test opcodes
1919
class OpTestDelay(OpCode):
1920
  """Sleeps for a configured amount of time.
1921

1922
  This is used just for debugging and testing.
1923

1924
  Parameters:
1925
    - duration: the time to sleep
1926
    - on_master: if true, sleep on the master
1927
    - on_nodes: list of nodes in which to sleep
1928

1929
  If the on_master parameter is true, it will execute a sleep on the
1930
  master (before any node sleep).
1931

1932
  If the on_nodes list is not empty, it will sleep on those nodes
1933
  (after the sleep on the master, if that is enabled).
1934

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

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

    
1948

    
1949
class OpTestAllocator(OpCode):
1950
  """Allocator framework testing.
1951

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

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

    
1988

    
1989
class OpTestJqueue(OpCode):
1990
  """Utility opcode to test some aspects of the job queue.
1991

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

    
2000

    
2001
class OpTestDummy(OpCode):
2002
  """Utility opcode used by unittests.
2003

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

    
2013

    
2014
# Network opcodes
2015
# Add a new network in the cluster
2016
class OpNetworkAdd(OpCode):
2017
  """Add an IP network to the cluster."""
2018
  OP_DSC_FIELD = "network_name"
2019
  OP_PARAMS = [
2020
    _PNetworkName,
2021
    _PNetworkType,
2022
    ("network", None, _TIpAddress, "IPv4 subnet"),
2023
    ("gateway", None, _TIpAddress, "IPv4 gateway"),
2024
    ("network6", None, _TIpAddress6, "IPv6 subnet"),
2025
    ("gateway6", None, _TIpAddress6, "IPv6 gateway"),
2026
    ("mac_prefix", None, ht.TMaybeString,
2027
     "MAC address prefix that overrides cluster one"),
2028
    ("add_reserved_ips", None,
2029
     ht.TOr(ht.TNone, ht.TListOf(_CheckCIDRAddrNotation)),
2030
     "Which IP addresses to reserve"),
2031
    ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Network tags"),
2032
    ]
2033
  OP_RESULT = ht.TNone
2034

    
2035

    
2036
class OpNetworkRemove(OpCode):
2037
  """Remove an existing network from the cluster.
2038
     Must not be connected to any nodegroup.
2039

2040
  """
2041
  OP_DSC_FIELD = "network_name"
2042
  OP_PARAMS = [
2043
    _PNetworkName,
2044
    _PForce,
2045
    ]
2046
  OP_RESULT = ht.TNone
2047

    
2048

    
2049
class OpNetworkSetParams(OpCode):
2050
  """Modify Network's parameters except for IPv4 subnet"""
2051
  OP_DSC_FIELD = "network_name"
2052
  OP_PARAMS = [
2053
    _PNetworkName,
2054
    _PNetworkType,
2055
    ("gateway", None, _TIpAddress, "IPv4 gateway"),
2056
    ("network6", None, _TIpAddress6, "IPv6 subnet"),
2057
    ("gateway6", None, _TIpAddress6, "IPv6 gateway"),
2058
    ("mac_prefix", None, ht.TMaybeString,
2059
     "MAC address prefix that overrides cluster one"),
2060
    ("add_reserved_ips", None,
2061
     ht.TOr(ht.TNone, ht.TListOf(_CheckCIDRAddrNotation)),
2062
     "Which external IP addresses to reserve"),
2063
    ("remove_reserved_ips", None,
2064
     ht.TOr(ht.TNone, ht.TListOf(_CheckCIDRAddrNotation)),
2065
     "Which external IP addresses to release"),
2066
    ]
2067
  OP_RESULT = ht.TNone
2068

    
2069

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

2077
  """
2078
  OP_DSC_FIELD = "network_name"
2079
  OP_PARAMS = [
2080
    _PGroupName,
2081
    _PNetworkName,
2082
    ("network_mode", None, ht.TMaybeString, "Connectivity mode"),
2083
    ("network_link", None, ht.TMaybeString, "Connectivity link"),
2084
    ("conflicts_check", True, ht.TBool, "Whether to check for conflicting IPs"),
2085
    ]
2086
  OP_RESULT = ht.TNone
2087

    
2088

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

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

    
2102

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

    
2112

    
2113
def _GetOpList():
2114
  """Returns list of all defined opcodes.
2115

2116
  Does not eliminate duplicates by C{OP_ID}.
2117

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

    
2123

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