Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes.py @ 08208574

History | View | Annotate | Download (61.2 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

    
39
from ganeti import constants
40
from ganeti import errors
41
from ganeti import ht
42
from ganeti import objects
43

    
44

    
45
# Common opcode attributes
46

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

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

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

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

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

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

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

    
73
#: Migration type (live/non-live)
74
_PMigrationMode = ("mode", None,
75
                   ht.TOr(ht.TNone, ht.TElemOf(constants.HT_MIGRATION_MODES)),
76
                   "Migration mode")
77

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

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

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

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

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

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

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

    
103
_PStorageName = ("name", ht.NoDefault, ht.TMaybeString, "Storage name")
104

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

    
108
_PNameCheck = ("name_check", True, ht.TBool, "Whether to check name")
109

    
110
_PNodeGroupAllocPolicy = \
111
  ("alloc_policy", None,
112
   ht.TOr(ht.TNone, ht.TElemOf(constants.VALID_ALLOC_POLICIES)),
113
   "Instance allocation policy")
114

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

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

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

    
124
_PIpCheckDoc = "Whether to ensure instance's IP address is inactive"
125

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

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

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

    
137
_PVerbose = ("verbose", False, ht.TBool, "Verbose mode")
138

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

    
150
# Disk parameters
151
_PDiskParams = ("diskparams", None,
152
                ht.TOr(
153
                  ht.TDictOf(ht.TElemOf(constants.DISK_TEMPLATES), ht.TDict),
154
                  ht.TNone),
155
                "Disk templates' parameter defaults")
156

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

    
161

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

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

    
169
#: a required network name
170
_PNetworkName = ("network_name", ht.NoDefault, ht.TNonEmptyString, "Set network name")
171

    
172
#: OP_ID conversion regular expression
173
_OPID_RE = re.compile("([a-z])([A-Z])")
174

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

    
182
_TestClusterOsList = ht.TMaybeListOf(_TestClusterOsListItem)
183

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

    
190
_TSetParamsResultItemItems = [
191
  ht.Comment("name of changed parameter")(ht.TNonEmptyString),
192
  ht.Comment("new value")(ht.TAny),
193
  ]
194

    
195
_TSetParamsResult = \
196
  ht.TListOf(ht.TAnd(ht.TIsLength(len(_TSetParamsResultItemItems)),
197
                     ht.TItems(_TSetParamsResultItemItems)))
198

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

    
205
_TQueryRow = \
206
  ht.TListOf(ht.TAnd(ht.TIsLength(2),
207
                     ht.TItems([ht.TElemOf(constants.RS_ALL),
208
                                ht.TAny])))
209

    
210
_TQueryResult = ht.TListOf(_TQueryRow)
211

    
212
_TOldQueryRow = ht.TListOf(ht.TAny)
213

    
214
_TOldQueryResult = ht.TListOf(_TOldQueryRow)
215

    
216

    
217
_SUMMARY_PREFIX = {
218
  "CLUSTER_": "C_",
219
  "GROUP_": "G_",
220
  "NODE_": "N_",
221
  "INSTANCE_": "I_",
222
  }
223

    
224
#: Attribute name for dependencies
225
DEPEND_ATTR = "depends"
226

    
227
#: Attribute name for comment
228
COMMENT_ATTR = "comment"
229

    
230

    
231
def _NameToId(name):
232
  """Convert an opcode class name to an OP_ID.
233

234
  @type name: string
235
  @param name: the class name, as OpXxxYyy
236
  @rtype: string
237
  @return: the name in the OP_XXXX_YYYY format
238

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

    
251

    
252
def _GenerateObjectTypeCheck(obj, fields_types):
253
  """Helper to generate type checks for objects.
254

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

259
  """
260
  assert set(obj.GetAllSlots()) == set(fields_types.keys()), \
261
    "%s != %s" % (set(obj.GetAllSlots()), set(fields_types.keys()))
262
  return ht.TStrictDict(True, True, fields_types)
263

    
264

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

    
273

    
274
def RequireFileStorage():
275
  """Checks that file storage is enabled.
276

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

280
  @raise errors.OpPrereqError: when file storage is disabled
281

282
  """
283
  if not constants.ENABLE_FILE_STORAGE:
284
    raise errors.OpPrereqError("File storage disabled at configure time",
285
                               errors.ECODE_INVAL)
286

    
287

    
288
def RequireSharedFileStorage():
289
  """Checks that shared file storage is enabled.
290

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

294
  @raise errors.OpPrereqError: when shared file storage is disabled
295

296
  """
297
  if not constants.ENABLE_SHARED_FILE_STORAGE:
298
    raise errors.OpPrereqError("Shared file storage disabled at"
299
                               " configure time", errors.ECODE_INVAL)
300

    
301

    
302
@ht.WithDesc("CheckFileStorage")
303
def _CheckFileStorage(value):
304
  """Ensures file storage is enabled if used.
305

306
  """
307
  if value == constants.DT_FILE:
308
    RequireFileStorage()
309
  elif value == constants.DT_SHARED_FILE:
310
    RequireSharedFileStorage()
311
  return True
312

    
313

    
314
def _BuildDiskTemplateCheck(accept_none):
315
  """Builds check for disk template.
316

317
  @type accept_none: bool
318
  @param accept_none: whether to accept None as a correct value
319
  @rtype: callable
320

321
  """
322
  template_check = ht.TElemOf(constants.DISK_TEMPLATES)
323

    
324
  if accept_none:
325
    template_check = ht.TOr(template_check, ht.TNone)
326

    
327
  return ht.TAnd(template_check, _CheckFileStorage)
328

    
329

    
330
def _CheckStorageType(storage_type):
331
  """Ensure a given storage type is valid.
332

333
  """
334
  if storage_type not in constants.VALID_STORAGE_TYPES:
335
    raise errors.OpPrereqError("Unknown storage type: %s" % storage_type,
336
                               errors.ECODE_INVAL)
337
  if storage_type == constants.ST_FILE:
338
    RequireFileStorage()
339
  return True
340

    
341

    
342
#: Storage type parameter
343
_PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType,
344
                 "Storage type")
345

    
346

    
347
class _AutoOpParamSlots(type):
348
  """Meta class for opcode definitions.
349

350
  """
351
  def __new__(mcs, name, bases, attrs):
352
    """Called when a class should be created.
353

354
    @param mcs: The meta class
355
    @param name: Name of created class
356
    @param bases: Base classes
357
    @type attrs: dict
358
    @param attrs: Class attributes
359

360
    """
361
    assert "__slots__" not in attrs, \
362
      "Class '%s' defines __slots__ when it should use OP_PARAMS" % name
363
    assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
364

    
365
    attrs["OP_ID"] = _NameToId(name)
366

    
367
    # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams
368
    params = attrs.setdefault("OP_PARAMS", [])
369

    
370
    # Use parameter names as slots
371
    slots = [pname for (pname, _, _, _) in params]
372

    
373
    assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
374
      "Class '%s' uses unknown field in OP_DSC_FIELD" % name
375

    
376
    attrs["__slots__"] = slots
377

    
378
    return type.__new__(mcs, name, bases, attrs)
379

    
380

    
381
class BaseOpCode(object):
382
  """A simple serializable object.
383

384
  This object serves as a parent class for OpCode without any custom
385
  field handling.
386

387
  """
388
  # pylint: disable=E1101
389
  # as OP_ID is dynamically defined
390
  __metaclass__ = _AutoOpParamSlots
391

    
392
  def __init__(self, **kwargs):
393
    """Constructor for BaseOpCode.
394

395
    The constructor takes only keyword arguments and will set
396
    attributes on this object based on the passed arguments. As such,
397
    it means that you should not pass arguments which are not in the
398
    __slots__ attribute for this class.
399

400
    """
401
    slots = self._all_slots()
402
    for key in kwargs:
403
      if key not in slots:
404
        raise TypeError("Object %s doesn't support the parameter '%s'" %
405
                        (self.__class__.__name__, key))
406
      setattr(self, key, kwargs[key])
407

    
408
  def __getstate__(self):
409
    """Generic serializer.
410

411
    This method just returns the contents of the instance as a
412
    dictionary.
413

414
    @rtype:  C{dict}
415
    @return: the instance attributes and their values
416

417
    """
418
    state = {}
419
    for name in self._all_slots():
420
      if hasattr(self, name):
421
        state[name] = getattr(self, name)
422
    return state
423

    
424
  def __setstate__(self, state):
425
    """Generic unserializer.
426

427
    This method just restores from the serialized state the attributes
428
    of the current instance.
429

430
    @param state: the serialized opcode data
431
    @type state:  C{dict}
432

433
    """
434
    if not isinstance(state, dict):
435
      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
436
                       type(state))
437

    
438
    for name in self._all_slots():
439
      if name not in state and hasattr(self, name):
440
        delattr(self, name)
441

    
442
    for name in state:
443
      setattr(self, name, state[name])
444

    
445
  @classmethod
446
  def _all_slots(cls):
447
    """Compute the list of all declared slots for a class.
448

449
    """
450
    slots = []
451
    for parent in cls.__mro__:
452
      slots.extend(getattr(parent, "__slots__", []))
453
    return slots
454

    
455
  @classmethod
456
  def GetAllParams(cls):
457
    """Compute list of all parameters for an opcode.
458

459
    """
460
    slots = []
461
    for parent in cls.__mro__:
462
      slots.extend(getattr(parent, "OP_PARAMS", []))
463
    return slots
464

    
465
  def Validate(self, set_defaults):
466
    """Validate opcode parameters, optionally setting default values.
467

468
    @type set_defaults: bool
469
    @param set_defaults: Whether to set default values
470
    @raise errors.OpPrereqError: When a parameter value doesn't match
471
                                 requirements
472

473
    """
474
    for (attr_name, default, test, _) in self.GetAllParams():
475
      assert test == ht.NoType or callable(test)
476

    
477
      if not hasattr(self, attr_name):
478
        if default == ht.NoDefault:
479
          raise errors.OpPrereqError("Required parameter '%s.%s' missing" %
480
                                     (self.OP_ID, attr_name),
481
                                     errors.ECODE_INVAL)
482
        elif set_defaults:
483
          if callable(default):
484
            dval = default()
485
          else:
486
            dval = default
487
          setattr(self, attr_name, dval)
488

    
489
      if test == ht.NoType:
490
        # no tests here
491
        continue
492

    
493
      if set_defaults or hasattr(self, attr_name):
494
        attr_val = getattr(self, attr_name)
495
        if not test(attr_val):
496
          logging.error("OpCode %s, parameter %s, has invalid type %s/value %s",
497
                        self.OP_ID, attr_name, type(attr_val), attr_val)
498
          raise errors.OpPrereqError("Parameter '%s.%s' fails validation" %
499
                                     (self.OP_ID, attr_name),
500
                                     errors.ECODE_INVAL)
501

    
502

    
503
def _BuildJobDepCheck(relative):
504
  """Builds check for job dependencies (L{DEPEND_ATTR}).
505

506
  @type relative: bool
507
  @param relative: Whether to accept relative job IDs (negative)
508
  @rtype: callable
509

510
  """
511
  if relative:
512
    job_id = ht.TOr(ht.TJobId, ht.TRelativeJobId)
513
  else:
514
    job_id = ht.TJobId
515

    
516
  job_dep = \
517
    ht.TAnd(ht.TIsLength(2),
518
            ht.TItems([job_id,
519
                       ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))]))
520

    
521
  return ht.TMaybeListOf(job_dep)
522

    
523

    
524
TNoRelativeJobDependencies = _BuildJobDepCheck(False)
525

    
526
#: List of submission status and job ID as returned by C{SubmitManyJobs}
527
_TJobIdListItem = \
528
  ht.TAnd(ht.TIsLength(2),
529
          ht.TItems([ht.Comment("success")(ht.TBool),
530
                     ht.Comment("Job ID if successful, error message"
531
                                " otherwise")(ht.TOr(ht.TString,
532
                                                     ht.TJobId))]))
533
TJobIdList = ht.TListOf(_TJobIdListItem)
534

    
535
#: Result containing only list of submitted jobs
536
TJobIdListOnly = ht.TStrictDict(True, True, {
537
  constants.JOB_IDS_KEY: ht.Comment("List of submitted jobs")(TJobIdList),
538
  })
539

    
540

    
541
class OpCode(BaseOpCode):
542
  """Abstract OpCode.
543

544
  This is the root of the actual OpCode hierarchy. All clases derived
545
  from this class should override OP_ID.
546

547
  @cvar OP_ID: The ID of this opcode. This should be unique amongst all
548
               children of this class.
549
  @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
550
                      string returned by Summary(); see the docstring of that
551
                      method for details).
552
  @cvar OP_PARAMS: List of opcode attributes, the default values they should
553
                   get if not already defined, and types they must match.
554
  @cvar OP_RESULT: Callable to verify opcode result
555
  @cvar WITH_LU: Boolean that specifies whether this should be included in
556
      mcpu's dispatch table
557
  @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
558
                 the check steps
559
  @ivar priority: Opcode priority for queue
560

561
  """
562
  # pylint: disable=E1101
563
  # as OP_ID is dynamically defined
564
  WITH_LU = True
565
  OP_PARAMS = [
566
    ("dry_run", None, ht.TMaybeBool, "Run checks only, don't execute"),
567
    ("debug_level", None, ht.TOr(ht.TNone, ht.TPositiveInt), "Debug level"),
568
    ("priority", constants.OP_PRIO_DEFAULT,
569
     ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID), "Opcode priority"),
570
    (DEPEND_ATTR, None, _BuildJobDepCheck(True),
571
     "Job dependencies; if used through ``SubmitManyJobs`` relative (negative)"
572
     " job IDs can be used; see :doc:`design document <design-chained-jobs>`"
573
     " for details"),
574
    (COMMENT_ATTR, None, ht.TMaybeString,
575
     "Comment describing the purpose of the opcode"),
576
    ]
577
  OP_RESULT = None
578

    
579
  def __getstate__(self):
580
    """Specialized getstate for opcodes.
581

582
    This method adds to the state dictionary the OP_ID of the class,
583
    so that on unload we can identify the correct class for
584
    instantiating the opcode.
585

586
    @rtype:   C{dict}
587
    @return:  the state as a dictionary
588

589
    """
590
    data = BaseOpCode.__getstate__(self)
591
    data["OP_ID"] = self.OP_ID
592
    return data
593

    
594
  @classmethod
595
  def LoadOpCode(cls, data):
596
    """Generic load opcode method.
597

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

602
    @type data:  C{dict}
603
    @param data: the serialized opcode
604

605
    """
606
    if not isinstance(data, dict):
607
      raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
608
    if "OP_ID" not in data:
609
      raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
610
    op_id = data["OP_ID"]
611
    op_class = None
612
    if op_id in OP_MAPPING:
613
      op_class = OP_MAPPING[op_id]
614
    else:
615
      raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
616
                       op_id)
617
    op = op_class()
618
    new_data = data.copy()
619
    del new_data["OP_ID"]
620
    op.__setstate__(new_data)
621
    return op
622

    
623
  def Summary(self):
624
    """Generates a summary description of this opcode.
625

626
    The summary is the value of the OP_ID attribute (without the "OP_"
627
    prefix), plus the value of the OP_DSC_FIELD attribute, if one was
628
    defined; this field should allow to easily identify the operation
629
    (for an instance creation job, e.g., it would be the instance
630
    name).
631

632
    """
633
    assert self.OP_ID is not None and len(self.OP_ID) > 3
634
    # all OP_ID start with OP_, we remove that
635
    txt = self.OP_ID[3:]
636
    field_name = getattr(self, "OP_DSC_FIELD", None)
637
    if field_name:
638
      field_value = getattr(self, field_name, None)
639
      if isinstance(field_value, (list, tuple)):
640
        field_value = ",".join(str(i) for i in field_value)
641
      txt = "%s(%s)" % (txt, field_value)
642
    return txt
643

    
644
  def TinySummary(self):
645
    """Generates a compact summary description of the opcode.
646

647
    """
648
    assert self.OP_ID.startswith("OP_")
649

    
650
    text = self.OP_ID[3:]
651

    
652
    for (prefix, supplement) in _SUMMARY_PREFIX.items():
653
      if text.startswith(prefix):
654
        return supplement + text[len(prefix):]
655

    
656
    return text
657

    
658

    
659
# cluster opcodes
660

    
661
class OpClusterPostInit(OpCode):
662
  """Post cluster initialization.
663

664
  This opcode does not touch the cluster at all. Its purpose is to run hooks
665
  after the cluster has been initialized.
666

667
  """
668
  OP_RESULT = ht.TBool
669

    
670

    
671
class OpClusterDestroy(OpCode):
672
  """Destroy the cluster.
673

674
  This opcode has no other parameters. All the state is irreversibly
675
  lost after the execution of this opcode.
676

677
  """
678
  OP_RESULT = ht.TNonEmptyString
679

    
680

    
681
class OpClusterQuery(OpCode):
682
  """Query cluster information."""
683
  OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TAny)
684

    
685

    
686
class OpClusterVerify(OpCode):
687
  """Submits all jobs necessary to verify the cluster.
688

689
  """
690
  OP_PARAMS = [
691
    _PDebugSimulateErrors,
692
    _PErrorCodes,
693
    _PSkipChecks,
694
    _PIgnoreErrors,
695
    _PVerbose,
696
    ("group_name", None, ht.TMaybeString, "Group to verify")
697
    ]
698
  OP_RESULT = TJobIdListOnly
699

    
700

    
701
class OpClusterVerifyConfig(OpCode):
702
  """Verify the cluster config.
703

704
  """
705
  OP_PARAMS = [
706
    _PDebugSimulateErrors,
707
    _PErrorCodes,
708
    _PIgnoreErrors,
709
    _PVerbose,
710
    ]
711
  OP_RESULT = ht.TBool
712

    
713

    
714
class OpClusterVerifyGroup(OpCode):
715
  """Run verify on a node group from the cluster.
716

717
  @type skip_checks: C{list}
718
  @ivar skip_checks: steps to be skipped from the verify process; this
719
                     needs to be a subset of
720
                     L{constants.VERIFY_OPTIONAL_CHECKS}; currently
721
                     only L{constants.VERIFY_NPLUSONE_MEM} can be passed
722

723
  """
724
  OP_DSC_FIELD = "group_name"
725
  OP_PARAMS = [
726
    _PGroupName,
727
    _PDebugSimulateErrors,
728
    _PErrorCodes,
729
    _PSkipChecks,
730
    _PIgnoreErrors,
731
    _PVerbose,
732
    ]
733
  OP_RESULT = ht.TBool
734

    
735

    
736
class OpClusterVerifyDisks(OpCode):
737
  """Verify the cluster disks.
738

739
  """
740
  OP_RESULT = TJobIdListOnly
741

    
742

    
743
class OpGroupVerifyDisks(OpCode):
744
  """Verifies the status of all disks in a node group.
745

746
  Result: a tuple of three elements:
747
    - dict of node names with issues (values: error msg)
748
    - list of instances with degraded disks (that should be activated)
749
    - dict of instances with missing logical volumes (values: (node, vol)
750
      pairs with details about the missing volumes)
751

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

757
  Note that only instances that are drbd-based are taken into
758
  consideration. This might need to be revisited in the future.
759

760
  """
761
  OP_DSC_FIELD = "group_name"
762
  OP_PARAMS = [
763
    _PGroupName,
764
    ]
765
  OP_RESULT = \
766
    ht.TAnd(ht.TIsLength(3),
767
            ht.TItems([ht.TDictOf(ht.TString, ht.TString),
768
                       ht.TListOf(ht.TString),
769
                       ht.TDictOf(ht.TString,
770
                                  ht.TListOf(ht.TListOf(ht.TString)))]))
771

    
772

    
773
class OpClusterRepairDiskSizes(OpCode):
774
  """Verify the disk sizes of the instances and fixes configuration
775
  mimatches.
776

777
  Parameters: optional instances list, in case we want to restrict the
778
  checks to only a subset of the instances.
779

780
  Result: a list of tuples, (instance, disk, new-size) for changed
781
  configurations.
782

783
  In normal operation, the list should be empty.
784

785
  @type instances: list
786
  @ivar instances: the list of instances to check, or empty for all instances
787

788
  """
789
  OP_PARAMS = [
790
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
791
    ]
792
  OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3),
793
                                 ht.TItems([ht.TNonEmptyString,
794
                                            ht.TPositiveInt,
795
                                            ht.TPositiveInt])))
796

    
797

    
798
class OpClusterConfigQuery(OpCode):
799
  """Query cluster configuration values."""
800
  OP_PARAMS = [
801
    _POutputFields
802
    ]
803
  OP_RESULT = ht.TListOf(ht.TAny)
804

    
805

    
806
class OpClusterRename(OpCode):
807
  """Rename the cluster.
808

809
  @type name: C{str}
810
  @ivar name: The new name of the cluster. The name and/or the master IP
811
              address will be changed to match the new name and its IP
812
              address.
813

814
  """
815
  OP_DSC_FIELD = "name"
816
  OP_PARAMS = [
817
    ("name", ht.NoDefault, ht.TNonEmptyString, None),
818
    ]
819
  OP_RESULT = ht.TNonEmptyString
820

    
821

    
822
class OpClusterSetParams(OpCode):
823
  """Change the parameters of the cluster.
824

825
  @type vg_name: C{str} or C{None}
826
  @ivar vg_name: The new volume group name or None to disable LVM usage.
827

828
  """
829
  OP_PARAMS = [
830
    _PHvState,
831
    _PDiskState,
832
    ("vg_name", None, ht.TMaybeString, "Volume group name"),
833
    ("enabled_hypervisors", None,
834
     ht.TOr(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)), ht.TTrue),
835
            ht.TNone),
836
     "List of enabled hypervisors"),
837
    ("hvparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
838
                              ht.TNone),
839
     "Cluster-wide hypervisor parameter defaults, hypervisor-dependent"),
840
    ("beparams", None, ht.TOr(ht.TDict, ht.TNone),
841
     "Cluster-wide backend parameter defaults"),
842
    ("os_hvp", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
843
                            ht.TNone),
844
     "Cluster-wide per-OS hypervisor parameter defaults"),
845
    ("osparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
846
                              ht.TNone),
847
     "Cluster-wide OS parameter defaults"),
848
    _PDiskParams,
849
    ("candidate_pool_size", None, ht.TOr(ht.TStrictPositiveInt, ht.TNone),
850
     "Master candidate pool size"),
851
    ("uid_pool", None, ht.NoType,
852
     "Set UID pool, must be list of lists describing UID ranges (two items,"
853
     " start and end inclusive)"),
854
    ("add_uids", None, ht.NoType,
855
     "Extend UID pool, must be list of lists describing UID ranges (two"
856
     " items, start and end inclusive) to be added"),
857
    ("remove_uids", None, ht.NoType,
858
     "Shrink UID pool, must be list of lists describing UID ranges (two"
859
     " items, start and end inclusive) to be removed"),
860
    ("maintain_node_health", None, ht.TMaybeBool,
861
     "Whether to automatically maintain node health"),
862
    ("prealloc_wipe_disks", None, ht.TMaybeBool,
863
     "Whether to wipe disks before allocating them to instances"),
864
    ("nicparams", None, ht.TMaybeDict, "Cluster-wide NIC parameter defaults"),
865
    ("ndparams", None, ht.TMaybeDict, "Cluster-wide node parameter defaults"),
866
    ("ipolicy", None, ht.TMaybeDict,
867
     "Cluster-wide :ref:`instance policy <rapi-ipolicy>` specs"),
868
    ("drbd_helper", None, ht.TOr(ht.TString, ht.TNone), "DRBD helper program"),
869
    ("default_iallocator", None, ht.TOr(ht.TString, ht.TNone),
870
     "Default iallocator for cluster"),
871
    ("master_netdev", None, ht.TOr(ht.TString, ht.TNone),
872
     "Master network device"),
873
    ("master_netmask", None, ht.TOr(ht.TInt, ht.TNone),
874
     "Netmask of the master IP"),
875
    ("reserved_lvs", None, ht.TMaybeListOf(ht.TNonEmptyString),
876
     "List of reserved LVs"),
877
    ("hidden_os", None, _TestClusterOsList,
878
     "Modify list of hidden operating systems: each modification must have"
879
     " two items, the operation and the OS name; the operation can be"
880
     " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)),
881
    ("blacklisted_os", None, _TestClusterOsList,
882
     "Modify list of blacklisted operating systems: each modification must"
883
     " have two items, the operation and the OS name; the operation can be"
884
     " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)),
885
    ("use_external_mip_script", None, ht.TMaybeBool,
886
     "Whether to use an external master IP address setup script"),
887
    ]
888
  OP_RESULT = ht.TNone
889

    
890

    
891
class OpClusterRedistConf(OpCode):
892
  """Force a full push of the cluster configuration.
893

894
  """
895
  OP_RESULT = ht.TNone
896

    
897

    
898
class OpClusterActivateMasterIp(OpCode):
899
  """Activate the master IP on the master node.
900

901
  """
902
  OP_RESULT = ht.TNone
903

    
904

    
905
class OpClusterDeactivateMasterIp(OpCode):
906
  """Deactivate the master IP on the master node.
907

908
  """
909
  OP_RESULT = ht.TNone
910

    
911

    
912
class OpQuery(OpCode):
913
  """Query for resources/items.
914

915
  @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
916
  @ivar fields: List of fields to retrieve
917
  @ivar qfilter: Query filter
918

919
  """
920
  OP_DSC_FIELD = "what"
921
  OP_PARAMS = [
922
    _PQueryWhat,
923
    _PUseLocking,
924
    ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
925
     "Requested fields"),
926
    ("qfilter", None, ht.TOr(ht.TNone, ht.TList),
927
     "Query filter"),
928
    ]
929
  OP_RESULT = \
930
    _GenerateObjectTypeCheck(objects.QueryResponse, {
931
      "fields": ht.TListOf(_TQueryFieldDef),
932
      "data": _TQueryResult,
933
      })
934

    
935

    
936
class OpQueryFields(OpCode):
937
  """Query for available resource/item fields.
938

939
  @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
940
  @ivar fields: List of fields to retrieve
941

942
  """
943
  OP_DSC_FIELD = "what"
944
  OP_PARAMS = [
945
    _PQueryWhat,
946
    ("fields", None, ht.TMaybeListOf(ht.TNonEmptyString),
947
     "Requested fields; if not given, all are returned"),
948
    ]
949
  OP_RESULT = \
950
    _GenerateObjectTypeCheck(objects.QueryFieldsResponse, {
951
      "fields": ht.TListOf(_TQueryFieldDef),
952
      })
953

    
954

    
955
class OpOobCommand(OpCode):
956
  """Interact with OOB."""
957
  OP_PARAMS = [
958
    ("node_names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
959
     "List of nodes to run the OOB command against"),
960
    ("command", None, ht.TElemOf(constants.OOB_COMMANDS),
961
     "OOB command to be run"),
962
    ("timeout", constants.OOB_TIMEOUT, ht.TInt,
963
     "Timeout before the OOB helper will be terminated"),
964
    ("ignore_status", False, ht.TBool,
965
     "Ignores the node offline status for power off"),
966
    ("power_delay", constants.OOB_POWER_DELAY, ht.TPositiveFloat,
967
     "Time in seconds to wait between powering on nodes"),
968
    ]
969
  # Fixme: Make it more specific with all the special cases in LUOobCommand
970
  OP_RESULT = _TQueryResult
971

    
972

    
973
# node opcodes
974

    
975
class OpNodeRemove(OpCode):
976
  """Remove a node.
977

978
  @type node_name: C{str}
979
  @ivar node_name: The name of the node to remove. If the node still has
980
                   instances on it, the operation will fail.
981

982
  """
983
  OP_DSC_FIELD = "node_name"
984
  OP_PARAMS = [
985
    _PNodeName,
986
    ]
987
  OP_RESULT = ht.TNone
988

    
989

    
990
class OpNodeAdd(OpCode):
991
  """Add a node to the cluster.
992

993
  @type node_name: C{str}
994
  @ivar node_name: The name of the node to add. This can be a short name,
995
                   but it will be expanded to the FQDN.
996
  @type primary_ip: IP address
997
  @ivar primary_ip: The primary IP of the node. This will be ignored when the
998
                    opcode is submitted, but will be filled during the node
999
                    add (so it will be visible in the job query).
1000
  @type secondary_ip: IP address
1001
  @ivar secondary_ip: The secondary IP of the node. This needs to be passed
1002
                      if the cluster has been initialized in 'dual-network'
1003
                      mode, otherwise it must not be given.
1004
  @type readd: C{bool}
1005
  @ivar readd: Whether to re-add an existing node to the cluster. If
1006
               this is not passed, then the operation will abort if the node
1007
               name is already in the cluster; use this parameter to 'repair'
1008
               a node that had its configuration broken, or was reinstalled
1009
               without removal from the cluster.
1010
  @type group: C{str}
1011
  @ivar group: The node group to which this node will belong.
1012
  @type vm_capable: C{bool}
1013
  @ivar vm_capable: The vm_capable node attribute
1014
  @type master_capable: C{bool}
1015
  @ivar master_capable: The master_capable node attribute
1016

1017
  """
1018
  OP_DSC_FIELD = "node_name"
1019
  OP_PARAMS = [
1020
    _PNodeName,
1021
    _PHvState,
1022
    _PDiskState,
1023
    ("primary_ip", None, ht.NoType, "Primary IP address"),
1024
    ("secondary_ip", None, ht.TMaybeString, "Secondary IP address"),
1025
    ("readd", False, ht.TBool, "Whether node is re-added to cluster"),
1026
    ("group", None, ht.TMaybeString, "Initial node group"),
1027
    ("master_capable", None, ht.TMaybeBool,
1028
     "Whether node can become master or master candidate"),
1029
    ("vm_capable", None, ht.TMaybeBool,
1030
     "Whether node can host instances"),
1031
    ("ndparams", None, ht.TMaybeDict, "Node parameters"),
1032
    ]
1033
  OP_RESULT = ht.TNone
1034

    
1035

    
1036
class OpNodeQuery(OpCode):
1037
  """Compute the list of nodes."""
1038
  OP_PARAMS = [
1039
    _POutputFields,
1040
    _PUseLocking,
1041
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1042
     "Empty list to query all nodes, node names otherwise"),
1043
    ]
1044
  OP_RESULT = _TOldQueryResult
1045

    
1046

    
1047
class OpNodeQueryvols(OpCode):
1048
  """Get list of volumes on node."""
1049
  OP_PARAMS = [
1050
    _POutputFields,
1051
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1052
     "Empty list to query all nodes, node names otherwise"),
1053
    ]
1054
  OP_RESULT = ht.TListOf(ht.TAny)
1055

    
1056

    
1057
class OpNodeQueryStorage(OpCode):
1058
  """Get information on storage for node(s)."""
1059
  OP_PARAMS = [
1060
    _POutputFields,
1061
    _PStorageType,
1062
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "List of nodes"),
1063
    ("name", None, ht.TMaybeString, "Storage name"),
1064
    ]
1065
  OP_RESULT = _TOldQueryResult
1066

    
1067

    
1068
class OpNodeModifyStorage(OpCode):
1069
  """Modifies the properies of a storage unit"""
1070
  OP_DSC_FIELD = "node_name"
1071
  OP_PARAMS = [
1072
    _PNodeName,
1073
    _PStorageType,
1074
    _PStorageName,
1075
    ("changes", ht.NoDefault, ht.TDict, "Requested changes"),
1076
    ]
1077
  OP_RESULT = ht.TNone
1078

    
1079

    
1080
class OpRepairNodeStorage(OpCode):
1081
  """Repairs the volume group on a node."""
1082
  OP_DSC_FIELD = "node_name"
1083
  OP_PARAMS = [
1084
    _PNodeName,
1085
    _PStorageType,
1086
    _PStorageName,
1087
    _PIgnoreConsistency,
1088
    ]
1089
  OP_RESULT = ht.TNone
1090

    
1091

    
1092
class OpNodeSetParams(OpCode):
1093
  """Change the parameters of a node."""
1094
  OP_DSC_FIELD = "node_name"
1095
  OP_PARAMS = [
1096
    _PNodeName,
1097
    _PForce,
1098
    _PHvState,
1099
    _PDiskState,
1100
    ("master_candidate", None, ht.TMaybeBool,
1101
     "Whether the node should become a master candidate"),
1102
    ("offline", None, ht.TMaybeBool,
1103
     "Whether the node should be marked as offline"),
1104
    ("drained", None, ht.TMaybeBool,
1105
     "Whether the node should be marked as drained"),
1106
    ("auto_promote", False, ht.TBool,
1107
     "Whether node(s) should be promoted to master candidate if necessary"),
1108
    ("master_capable", None, ht.TMaybeBool,
1109
     "Denote whether node can become master or master candidate"),
1110
    ("vm_capable", None, ht.TMaybeBool,
1111
     "Denote whether node can host instances"),
1112
    ("secondary_ip", None, ht.TMaybeString,
1113
     "Change node's secondary IP address"),
1114
    ("ndparams", None, ht.TMaybeDict, "Set node parameters"),
1115
    ("powered", None, ht.TMaybeBool,
1116
     "Whether the node should be marked as powered"),
1117
    ]
1118
  OP_RESULT = _TSetParamsResult
1119

    
1120

    
1121
class OpNodePowercycle(OpCode):
1122
  """Tries to powercycle a node."""
1123
  OP_DSC_FIELD = "node_name"
1124
  OP_PARAMS = [
1125
    _PNodeName,
1126
    _PForce,
1127
    ]
1128
  OP_RESULT = ht.TMaybeString
1129

    
1130

    
1131
class OpNodeMigrate(OpCode):
1132
  """Migrate all instances from a node."""
1133
  OP_DSC_FIELD = "node_name"
1134
  OP_PARAMS = [
1135
    _PNodeName,
1136
    _PMigrationMode,
1137
    _PMigrationLive,
1138
    _PMigrationTargetNode,
1139
    _PAllowRuntimeChgs,
1140
    _PIgnoreIpolicy,
1141
    ("iallocator", None, ht.TMaybeString,
1142
     "Iallocator for deciding the target node for shared-storage instances"),
1143
    ]
1144
  OP_RESULT = TJobIdListOnly
1145

    
1146

    
1147
class OpNodeEvacuate(OpCode):
1148
  """Evacuate instances off a number of nodes."""
1149
  OP_DSC_FIELD = "node_name"
1150
  OP_PARAMS = [
1151
    _PEarlyRelease,
1152
    _PNodeName,
1153
    ("remote_node", None, ht.TMaybeString, "New secondary node"),
1154
    ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"),
1155
    ("mode", ht.NoDefault, ht.TElemOf(constants.NODE_EVAC_MODES),
1156
     "Node evacuation mode"),
1157
    ]
1158
  OP_RESULT = TJobIdListOnly
1159

    
1160

    
1161
# instance opcodes
1162

    
1163
class OpInstanceCreate(OpCode):
1164
  """Create an instance.
1165

1166
  @ivar instance_name: Instance name
1167
  @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES})
1168
  @ivar source_handshake: Signed handshake from source (remote import only)
1169
  @ivar source_x509_ca: Source X509 CA in PEM format (remote import only)
1170
  @ivar source_instance_name: Previous name of instance (remote import only)
1171
  @ivar source_shutdown_timeout: Shutdown timeout used for source instance
1172
    (remote import only)
1173

1174
  """
1175
  OP_DSC_FIELD = "instance_name"
1176
  OP_PARAMS = [
1177
    _PInstanceName,
1178
    _PForceVariant,
1179
    _PWaitForSync,
1180
    _PNameCheck,
1181
    _PIgnoreIpolicy,
1182
    ("beparams", ht.EmptyDict, ht.TDict, "Backend parameters for instance"),
1183
    ("disks", ht.NoDefault, ht.TListOf(_TDiskParams),
1184
     "Disk descriptions, for example ``[{\"%s\": 100}, {\"%s\": 5}]``;"
1185
     " each disk definition must contain a ``%s`` value and"
1186
     " can contain an optional ``%s`` value denoting the disk access mode"
1187
     " (%s)" %
1188
     (constants.IDISK_SIZE, constants.IDISK_SIZE, constants.IDISK_SIZE,
1189
      constants.IDISK_MODE,
1190
      " or ".join("``%s``" % i for i in sorted(constants.DISK_ACCESS_SET)))),
1191
    ("disk_template", ht.NoDefault, _BuildDiskTemplateCheck(True),
1192
     "Disk template"),
1193
    ("file_driver", None, ht.TOr(ht.TNone, ht.TElemOf(constants.FILE_DRIVER)),
1194
     "Driver for file-backed disks"),
1195
    ("file_storage_dir", None, ht.TMaybeString,
1196
     "Directory for storing file-backed disks"),
1197
    ("hvparams", ht.EmptyDict, ht.TDict,
1198
     "Hypervisor parameters for instance, hypervisor-dependent"),
1199
    ("hypervisor", None, ht.TMaybeString, "Hypervisor"),
1200
    ("iallocator", None, ht.TMaybeString,
1201
     "Iallocator for deciding which node(s) to use"),
1202
    ("identify_defaults", False, ht.TBool,
1203
     "Reset instance parameters to default if equal"),
1204
    ("ip_check", True, ht.TBool, _PIpCheckDoc),
1205
    ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"),
1206
    ("mode", ht.NoDefault, ht.TElemOf(constants.INSTANCE_CREATE_MODES),
1207
     "Instance creation mode"),
1208
    ("nics", ht.NoDefault, ht.TListOf(_TestNicDef),
1209
     "List of NIC (network interface) definitions, for example"
1210
     " ``[{}, {}, {\"%s\": \"198.51.100.4\"}]``; each NIC definition can"
1211
     " contain the optional values %s" %
1212
     (constants.INIC_IP,
1213
      ", ".join("``%s``" % i for i in sorted(constants.INIC_PARAMS)))),
1214
    ("no_install", None, ht.TMaybeBool,
1215
     "Do not install the OS (will disable automatic start)"),
1216
    ("osparams", ht.EmptyDict, ht.TDict, "OS parameters for instance"),
1217
    ("os_type", None, ht.TMaybeString, "Operating system"),
1218
    ("pnode", None, ht.TMaybeString, "Primary node"),
1219
    ("snode", None, ht.TMaybeString, "Secondary node"),
1220
    ("source_handshake", None, ht.TOr(ht.TList, ht.TNone),
1221
     "Signed handshake from source (remote import only)"),
1222
    ("source_instance_name", None, ht.TMaybeString,
1223
     "Source instance name (remote import only)"),
1224
    ("source_shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT,
1225
     ht.TPositiveInt,
1226
     "How long source instance was given to shut down (remote import only)"),
1227
    ("source_x509_ca", None, ht.TMaybeString,
1228
     "Source X509 CA in PEM format (remote import only)"),
1229
    ("src_node", None, ht.TMaybeString, "Source node for import"),
1230
    ("src_path", None, ht.TMaybeString, "Source directory for import"),
1231
    ("start", True, ht.TBool, "Whether to start instance after creation"),
1232
    ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Instance tags"),
1233
    ]
1234
  OP_RESULT = ht.Comment("instance nodes")(ht.TListOf(ht.TNonEmptyString))
1235

    
1236

    
1237
class OpInstanceReinstall(OpCode):
1238
  """Reinstall an instance's OS."""
1239
  OP_DSC_FIELD = "instance_name"
1240
  OP_PARAMS = [
1241
    _PInstanceName,
1242
    _PForceVariant,
1243
    ("os_type", None, ht.TMaybeString, "Instance operating system"),
1244
    ("osparams", None, ht.TMaybeDict, "Temporary OS parameters"),
1245
    ]
1246
  OP_RESULT = ht.TNone
1247

    
1248

    
1249
class OpInstanceRemove(OpCode):
1250
  """Remove an instance."""
1251
  OP_DSC_FIELD = "instance_name"
1252
  OP_PARAMS = [
1253
    _PInstanceName,
1254
    _PShutdownTimeout,
1255
    ("ignore_failures", False, ht.TBool,
1256
     "Whether to ignore failures during removal"),
1257
    ]
1258
  OP_RESULT = ht.TNone
1259

    
1260

    
1261
class OpInstanceRename(OpCode):
1262
  """Rename an instance."""
1263
  OP_PARAMS = [
1264
    _PInstanceName,
1265
    _PNameCheck,
1266
    ("new_name", ht.NoDefault, ht.TNonEmptyString, "New instance name"),
1267
    ("ip_check", False, ht.TBool, _PIpCheckDoc),
1268
    ]
1269
  OP_RESULT = ht.Comment("New instance name")(ht.TNonEmptyString)
1270

    
1271

    
1272
class OpInstanceStartup(OpCode):
1273
  """Startup an instance."""
1274
  OP_DSC_FIELD = "instance_name"
1275
  OP_PARAMS = [
1276
    _PInstanceName,
1277
    _PForce,
1278
    _PIgnoreOfflineNodes,
1279
    ("hvparams", ht.EmptyDict, ht.TDict,
1280
     "Temporary hypervisor parameters, hypervisor-dependent"),
1281
    ("beparams", ht.EmptyDict, ht.TDict, "Temporary backend parameters"),
1282
    _PNoRemember,
1283
    _PStartupPaused,
1284
    ]
1285
  OP_RESULT = ht.TNone
1286

    
1287

    
1288
class OpInstanceShutdown(OpCode):
1289
  """Shutdown an instance."""
1290
  OP_DSC_FIELD = "instance_name"
1291
  OP_PARAMS = [
1292
    _PInstanceName,
1293
    _PIgnoreOfflineNodes,
1294
    ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TPositiveInt,
1295
     "How long to wait for instance to shut down"),
1296
    _PNoRemember,
1297
    ]
1298
  OP_RESULT = ht.TNone
1299

    
1300

    
1301
class OpInstanceReboot(OpCode):
1302
  """Reboot an instance."""
1303
  OP_DSC_FIELD = "instance_name"
1304
  OP_PARAMS = [
1305
    _PInstanceName,
1306
    _PShutdownTimeout,
1307
    ("ignore_secondaries", False, ht.TBool,
1308
     "Whether to start the instance even if secondary disks are failing"),
1309
    ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES),
1310
     "How to reboot instance"),
1311
    ]
1312
  OP_RESULT = ht.TNone
1313

    
1314

    
1315
class OpInstanceReplaceDisks(OpCode):
1316
  """Replace the disks of an instance."""
1317
  OP_DSC_FIELD = "instance_name"
1318
  OP_PARAMS = [
1319
    _PInstanceName,
1320
    _PEarlyRelease,
1321
    _PIgnoreIpolicy,
1322
    ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES),
1323
     "Replacement mode"),
1324
    ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt),
1325
     "Disk indexes"),
1326
    ("remote_node", None, ht.TMaybeString, "New secondary node"),
1327
    ("iallocator", None, ht.TMaybeString,
1328
     "Iallocator for deciding new secondary node"),
1329
    ]
1330
  OP_RESULT = ht.TNone
1331

    
1332

    
1333
class OpInstanceFailover(OpCode):
1334
  """Failover an instance."""
1335
  OP_DSC_FIELD = "instance_name"
1336
  OP_PARAMS = [
1337
    _PInstanceName,
1338
    _PShutdownTimeout,
1339
    _PIgnoreConsistency,
1340
    _PMigrationTargetNode,
1341
    _PIgnoreIpolicy,
1342
    ("iallocator", None, ht.TMaybeString,
1343
     "Iallocator for deciding the target node for shared-storage instances"),
1344
    ]
1345
  OP_RESULT = ht.TNone
1346

    
1347

    
1348
class OpInstanceMigrate(OpCode):
1349
  """Migrate an instance.
1350

1351
  This migrates (without shutting down an instance) to its secondary
1352
  node.
1353

1354
  @ivar instance_name: the name of the instance
1355
  @ivar mode: the migration mode (live, non-live or None for auto)
1356

1357
  """
1358
  OP_DSC_FIELD = "instance_name"
1359
  OP_PARAMS = [
1360
    _PInstanceName,
1361
    _PMigrationMode,
1362
    _PMigrationLive,
1363
    _PMigrationTargetNode,
1364
    _PAllowRuntimeChgs,
1365
    _PIgnoreIpolicy,
1366
    ("cleanup", False, ht.TBool,
1367
     "Whether a previously failed migration should be cleaned up"),
1368
    ("iallocator", None, ht.TMaybeString,
1369
     "Iallocator for deciding the target node for shared-storage instances"),
1370
    ("allow_failover", False, ht.TBool,
1371
     "Whether we can fallback to failover if migration is not possible"),
1372
    ]
1373
  OP_RESULT = ht.TNone
1374

    
1375

    
1376
class OpInstanceMove(OpCode):
1377
  """Move an instance.
1378

1379
  This move (with shutting down an instance and data copying) to an
1380
  arbitrary node.
1381

1382
  @ivar instance_name: the name of the instance
1383
  @ivar target_node: the destination node
1384

1385
  """
1386
  OP_DSC_FIELD = "instance_name"
1387
  OP_PARAMS = [
1388
    _PInstanceName,
1389
    _PShutdownTimeout,
1390
    _PIgnoreIpolicy,
1391
    ("target_node", ht.NoDefault, ht.TNonEmptyString, "Target node"),
1392
    _PIgnoreConsistency,
1393
    ]
1394
  OP_RESULT = ht.TNone
1395

    
1396

    
1397
class OpInstanceConsole(OpCode):
1398
  """Connect to an instance's console."""
1399
  OP_DSC_FIELD = "instance_name"
1400
  OP_PARAMS = [
1401
    _PInstanceName
1402
    ]
1403
  OP_RESULT = ht.TDict
1404

    
1405

    
1406
class OpInstanceActivateDisks(OpCode):
1407
  """Activate an instance's disks."""
1408
  OP_DSC_FIELD = "instance_name"
1409
  OP_PARAMS = [
1410
    _PInstanceName,
1411
    ("ignore_size", False, ht.TBool, "Whether to ignore recorded size"),
1412
    _PWaitForSyncFalse,
1413
    ]
1414
  OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3),
1415
                                 ht.TItems([ht.TNonEmptyString,
1416
                                            ht.TNonEmptyString,
1417
                                            ht.TNonEmptyString])))
1418

    
1419

    
1420
class OpInstanceDeactivateDisks(OpCode):
1421
  """Deactivate an instance's disks."""
1422
  OP_DSC_FIELD = "instance_name"
1423
  OP_PARAMS = [
1424
    _PInstanceName,
1425
    _PForce,
1426
    ]
1427
  OP_RESULT = ht.TNone
1428

    
1429

    
1430
class OpInstanceRecreateDisks(OpCode):
1431
  """Recreate an instance's disks."""
1432
  _TDiskChanges = \
1433
    ht.TAnd(ht.TIsLength(2),
1434
            ht.TItems([ht.Comment("Disk index")(ht.TPositiveInt),
1435
                       ht.Comment("Parameters")(_TDiskParams)]))
1436

    
1437
  OP_DSC_FIELD = "instance_name"
1438
  OP_PARAMS = [
1439
    _PInstanceName,
1440
    ("disks", ht.EmptyList,
1441
     ht.TOr(ht.TListOf(ht.TPositiveInt), ht.TListOf(_TDiskChanges)),
1442
     "List of disk indexes (deprecated) or a list of tuples containing a disk"
1443
     " index and a possibly empty dictionary with disk parameter changes"),
1444
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1445
     "New instance nodes, if relocation is desired"),
1446
    ]
1447
  OP_RESULT = ht.TNone
1448

    
1449

    
1450
class OpInstanceQuery(OpCode):
1451
  """Compute the list of instances."""
1452
  OP_PARAMS = [
1453
    _POutputFields,
1454
    _PUseLocking,
1455
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1456
     "Empty list to query all instances, instance names otherwise"),
1457
    ]
1458
  OP_RESULT = _TOldQueryResult
1459

    
1460

    
1461
class OpInstanceQueryData(OpCode):
1462
  """Compute the run-time status of instances."""
1463
  OP_PARAMS = [
1464
    _PUseLocking,
1465
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1466
     "Instance names"),
1467
    ("static", False, ht.TBool,
1468
     "Whether to only return configuration data without querying"
1469
     " nodes"),
1470
    ]
1471
  OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TDict)
1472

    
1473

    
1474
def _TestInstSetParamsModList(fn):
1475
  """Generates a check for modification lists.
1476

1477
  """
1478
  # Old format
1479
  # TODO: Remove in version 2.8 including support in LUInstanceSetParams
1480
  old_mod_item_fn = \
1481
    ht.TAnd(ht.TIsLength(2), ht.TItems([
1482
      ht.TOr(ht.TElemOf(constants.DDMS_VALUES), ht.TPositiveInt),
1483
      fn,
1484
      ]))
1485

    
1486
  # New format, supporting adding/removing disks/NICs at arbitrary indices
1487
  mod_item_fn = \
1488
    ht.TAnd(ht.TIsLength(3), ht.TItems([
1489
      ht.TElemOf(constants.DDMS_VALUES_WITH_MODIFY),
1490
      ht.Comment("Disk index, can be negative, e.g. -1 for last disk")(ht.TInt),
1491
      fn,
1492
      ]))
1493

    
1494
  return ht.TOr(ht.Comment("Recommended")(ht.TListOf(mod_item_fn)),
1495
                ht.Comment("Deprecated")(ht.TListOf(old_mod_item_fn)))
1496

    
1497

    
1498
class OpInstanceSetParams(OpCode):
1499
  """Change the parameters of an instance.
1500

1501
  """
1502
  TestNicModifications = _TestInstSetParamsModList(_TestNicDef)
1503
  TestDiskModifications = _TestInstSetParamsModList(_TDiskParams)
1504

    
1505
  OP_DSC_FIELD = "instance_name"
1506
  OP_PARAMS = [
1507
    _PInstanceName,
1508
    _PForce,
1509
    _PForceVariant,
1510
    _PIgnoreIpolicy,
1511
    ("nics", ht.EmptyList, TestNicModifications,
1512
     "List of NIC changes: each item is of the form ``(op, index, settings)``,"
1513
     " ``op`` is one of ``%s``, ``%s`` or ``%s``, ``index`` can be either -1"
1514
     " to refer to the last position, or a zero-based index number; a"
1515
     " deprecated version of this parameter used the form ``(op, settings)``,"
1516
     " where ``op`` can be ``%s`` to add a new NIC with the specified"
1517
     " settings, ``%s`` to remove the last NIC or a number to modify the"
1518
     " settings of the NIC with that index" %
1519
     (constants.DDM_ADD, constants.DDM_MODIFY, constants.DDM_REMOVE,
1520
      constants.DDM_ADD, constants.DDM_REMOVE)),
1521
    ("disks", ht.EmptyList, TestDiskModifications,
1522
     "List of disk changes; see ``nics``"),
1523
    ("beparams", ht.EmptyDict, ht.TDict, "Per-instance backend parameters"),
1524
    ("runtime_mem", None, ht.TMaybeStrictPositiveInt, "New runtime memory"),
1525
    ("hvparams", ht.EmptyDict, ht.TDict,
1526
     "Per-instance hypervisor parameters, hypervisor-dependent"),
1527
    ("disk_template", None, ht.TOr(ht.TNone, _BuildDiskTemplateCheck(False)),
1528
     "Disk template for instance"),
1529
    ("remote_node", None, ht.TMaybeString,
1530
     "Secondary node (used when changing disk template)"),
1531
    ("os_name", None, ht.TMaybeString,
1532
     "Change the instance's OS without reinstalling the instance"),
1533
    ("osparams", None, ht.TMaybeDict, "Per-instance OS parameters"),
1534
    ("wait_for_sync", True, ht.TBool,
1535
     "Whether to wait for the disk to synchronize, when changing template"),
1536
    ("offline", None, ht.TMaybeBool, "Whether to mark instance as offline"),
1537
    ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"),
1538
    ]
1539
  OP_RESULT = _TSetParamsResult
1540

    
1541

    
1542
class OpInstanceGrowDisk(OpCode):
1543
  """Grow a disk of an instance."""
1544
  OP_DSC_FIELD = "instance_name"
1545
  OP_PARAMS = [
1546
    _PInstanceName,
1547
    _PWaitForSync,
1548
    ("disk", ht.NoDefault, ht.TInt, "Disk index"),
1549
    ("amount", ht.NoDefault, ht.TPositiveInt,
1550
     "Amount of disk space to add (megabytes)"),
1551
    ("absolute", False, ht.TBool,
1552
     "Whether the amount parameter is an absolute target or a relative one"),
1553
    ]
1554
  OP_RESULT = ht.TNone
1555

    
1556

    
1557
class OpInstanceChangeGroup(OpCode):
1558
  """Moves an instance to another node group."""
1559
  OP_DSC_FIELD = "instance_name"
1560
  OP_PARAMS = [
1561
    _PInstanceName,
1562
    _PEarlyRelease,
1563
    ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"),
1564
    ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString),
1565
     "Destination group names or UUIDs (defaults to \"all but current group\""),
1566
    ]
1567
  OP_RESULT = TJobIdListOnly
1568

    
1569

    
1570
# Node group opcodes
1571

    
1572
class OpGroupAdd(OpCode):
1573
  """Add a node group to the cluster."""
1574
  OP_DSC_FIELD = "group_name"
1575
  OP_PARAMS = [
1576
    _PGroupName,
1577
    _PNodeGroupAllocPolicy,
1578
    _PGroupNodeParams,
1579
    _PDiskParams,
1580
    _PHvState,
1581
    _PDiskState,
1582
    ("ipolicy", None, ht.TMaybeDict,
1583
     "Group-wide :ref:`instance policy <rapi-ipolicy>` specs"),
1584
    ]
1585
  OP_RESULT = ht.TNone
1586

    
1587

    
1588
class OpGroupAssignNodes(OpCode):
1589
  """Assign nodes to a node group."""
1590
  OP_DSC_FIELD = "group_name"
1591
  OP_PARAMS = [
1592
    _PGroupName,
1593
    _PForce,
1594
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
1595
     "List of nodes to assign"),
1596
    ]
1597
  OP_RESULT = ht.TNone
1598

    
1599

    
1600
class OpGroupQuery(OpCode):
1601
  """Compute the list of node groups."""
1602
  OP_PARAMS = [
1603
    _POutputFields,
1604
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1605
     "Empty list to query all groups, group names otherwise"),
1606
    ]
1607
  OP_RESULT = _TOldQueryResult
1608

    
1609

    
1610
class OpGroupSetParams(OpCode):
1611
  """Change the parameters of a node group."""
1612
  OP_DSC_FIELD = "group_name"
1613
  OP_PARAMS = [
1614
    _PGroupName,
1615
    _PNodeGroupAllocPolicy,
1616
    _PGroupNodeParams,
1617
    _PDiskParams,
1618
    _PHvState,
1619
    _PDiskState,
1620
    ("ipolicy", None, ht.TMaybeDict, "Group-wide instance policy specs"),
1621
    ]
1622
  OP_RESULT = _TSetParamsResult
1623

    
1624

    
1625
class OpGroupRemove(OpCode):
1626
  """Remove a node group from the cluster."""
1627
  OP_DSC_FIELD = "group_name"
1628
  OP_PARAMS = [
1629
    _PGroupName,
1630
    ]
1631
  OP_RESULT = ht.TNone
1632

    
1633

    
1634
class OpGroupRename(OpCode):
1635
  """Rename a node group in the cluster."""
1636
  OP_PARAMS = [
1637
    _PGroupName,
1638
    ("new_name", ht.NoDefault, ht.TNonEmptyString, "New group name"),
1639
    ]
1640
  OP_RESULT = ht.Comment("New group name")(ht.TNonEmptyString)
1641

    
1642

    
1643
class OpGroupEvacuate(OpCode):
1644
  """Evacuate a node group in the cluster."""
1645
  OP_DSC_FIELD = "group_name"
1646
  OP_PARAMS = [
1647
    _PGroupName,
1648
    _PEarlyRelease,
1649
    ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"),
1650
    ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString),
1651
     "Destination group names or UUIDs"),
1652
    ]
1653
  OP_RESULT = TJobIdListOnly
1654

    
1655

    
1656
# OS opcodes
1657
class OpOsDiagnose(OpCode):
1658
  """Compute the list of guest operating systems."""
1659
  OP_PARAMS = [
1660
    _POutputFields,
1661
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1662
     "Which operating systems to diagnose"),
1663
    ]
1664
  OP_RESULT = _TOldQueryResult
1665

    
1666

    
1667
# Exports opcodes
1668
class OpBackupQuery(OpCode):
1669
  """Compute the list of exported images."""
1670
  OP_PARAMS = [
1671
    _PUseLocking,
1672
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1673
     "Empty list to query all nodes, node names otherwise"),
1674
    ]
1675
  OP_RESULT = ht.TDictOf(ht.TNonEmptyString,
1676
                         ht.TOr(ht.Comment("False on error")(ht.TBool),
1677
                                ht.TListOf(ht.TNonEmptyString)))
1678

    
1679

    
1680
class OpBackupPrepare(OpCode):
1681
  """Prepares an instance export.
1682

1683
  @ivar instance_name: Instance name
1684
  @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1685

1686
  """
1687
  OP_DSC_FIELD = "instance_name"
1688
  OP_PARAMS = [
1689
    _PInstanceName,
1690
    ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES),
1691
     "Export mode"),
1692
    ]
1693
  OP_RESULT = ht.TOr(ht.TNone, ht.TDict)
1694

    
1695

    
1696
class OpBackupExport(OpCode):
1697
  """Export an instance.
1698

1699
  For local exports, the export destination is the node name. For remote
1700
  exports, the export destination is a list of tuples, each consisting of
1701
  hostname/IP address, port, HMAC and HMAC salt. The HMAC is calculated using
1702
  the cluster domain secret over the value "${index}:${hostname}:${port}". The
1703
  destination X509 CA must be a signed certificate.
1704

1705
  @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1706
  @ivar target_node: Export destination
1707
  @ivar x509_key_name: X509 key to use (remote export only)
1708
  @ivar destination_x509_ca: Destination X509 CA in PEM format (remote export
1709
                             only)
1710

1711
  """
1712
  OP_DSC_FIELD = "instance_name"
1713
  OP_PARAMS = [
1714
    _PInstanceName,
1715
    _PShutdownTimeout,
1716
    # TODO: Rename target_node as it changes meaning for different export modes
1717
    # (e.g. "destination")
1718
    ("target_node", ht.NoDefault, ht.TOr(ht.TNonEmptyString, ht.TList),
1719
     "Destination information, depends on export mode"),
1720
    ("shutdown", True, ht.TBool, "Whether to shutdown instance before export"),
1721
    ("remove_instance", False, ht.TBool,
1722
     "Whether to remove instance after export"),
1723
    ("ignore_remove_failures", False, ht.TBool,
1724
     "Whether to ignore failures while removing instances"),
1725
    ("mode", constants.EXPORT_MODE_LOCAL, ht.TElemOf(constants.EXPORT_MODES),
1726
     "Export mode"),
1727
    ("x509_key_name", None, ht.TOr(ht.TList, ht.TNone),
1728
     "Name of X509 key (remote export only)"),
1729
    ("destination_x509_ca", None, ht.TMaybeString,
1730
     "Destination X509 CA (remote export only)"),
1731
    ]
1732
  OP_RESULT = \
1733
    ht.TAnd(ht.TIsLength(2), ht.TItems([
1734
      ht.Comment("Finalizing status")(ht.TBool),
1735
      ht.Comment("Status for every exported disk")(ht.TListOf(ht.TBool)),
1736
      ]))
1737

    
1738

    
1739
class OpBackupRemove(OpCode):
1740
  """Remove an instance's export."""
1741
  OP_DSC_FIELD = "instance_name"
1742
  OP_PARAMS = [
1743
    _PInstanceName,
1744
    ]
1745
  OP_RESULT = ht.TNone
1746

    
1747

    
1748
# Tags opcodes
1749
class OpTagsGet(OpCode):
1750
  """Returns the tags of the given object."""
1751
  OP_DSC_FIELD = "name"
1752
  OP_PARAMS = [
1753
    _PTagKind,
1754
    # Not using _PUseLocking as the default is different for historical reasons
1755
    ("use_locking", True, ht.TBool, "Whether to use synchronization"),
1756
    # Name is only meaningful for nodes and instances
1757
    ("name", ht.NoDefault, ht.TMaybeString,
1758
     "Name of object to retrieve tags from"),
1759
    ]
1760
  OP_RESULT = ht.TListOf(ht.TNonEmptyString)
1761

    
1762

    
1763
class OpTagsSearch(OpCode):
1764
  """Searches the tags in the cluster for a given pattern."""
1765
  OP_DSC_FIELD = "pattern"
1766
  OP_PARAMS = [
1767
    ("pattern", ht.NoDefault, ht.TNonEmptyString,
1768
     "Search pattern (regular expression)"),
1769
    ]
1770
  OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(2), ht.TItems([
1771
    ht.TNonEmptyString,
1772
    ht.TNonEmptyString,
1773
    ])))
1774

    
1775

    
1776
class OpTagsSet(OpCode):
1777
  """Add a list of tags on a given object."""
1778
  OP_PARAMS = [
1779
    _PTagKind,
1780
    _PTags,
1781
    # Name is only meaningful for nodes and instances
1782
    ("name", ht.NoDefault, ht.TMaybeString,
1783
     "Name of object where tag(s) should be added"),
1784
    ]
1785
  OP_RESULT = ht.TNone
1786

    
1787

    
1788
class OpTagsDel(OpCode):
1789
  """Remove a list of tags from a given object."""
1790
  OP_PARAMS = [
1791
    _PTagKind,
1792
    _PTags,
1793
    # Name is only meaningful for nodes and instances
1794
    ("name", ht.NoDefault, ht.TMaybeString,
1795
     "Name of object where tag(s) should be deleted"),
1796
    ]
1797
  OP_RESULT = ht.TNone
1798

    
1799

    
1800
# Test opcodes
1801
class OpTestDelay(OpCode):
1802
  """Sleeps for a configured amount of time.
1803

1804
  This is used just for debugging and testing.
1805

1806
  Parameters:
1807
    - duration: the time to sleep
1808
    - on_master: if true, sleep on the master
1809
    - on_nodes: list of nodes in which to sleep
1810

1811
  If the on_master parameter is true, it will execute a sleep on the
1812
  master (before any node sleep).
1813

1814
  If the on_nodes list is not empty, it will sleep on those nodes
1815
  (after the sleep on the master, if that is enabled).
1816

1817
  As an additional feature, the case of duration < 0 will be reported
1818
  as an execution error, so this opcode can be used as a failure
1819
  generator. The case of duration == 0 will not be treated specially.
1820

1821
  """
1822
  OP_DSC_FIELD = "duration"
1823
  OP_PARAMS = [
1824
    ("duration", ht.NoDefault, ht.TNumber, None),
1825
    ("on_master", True, ht.TBool, None),
1826
    ("on_nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
1827
    ("repeat", 0, ht.TPositiveInt, None),
1828
    ]
1829

    
1830

    
1831
class OpTestAllocator(OpCode):
1832
  """Allocator framework testing.
1833

1834
  This opcode has two modes:
1835
    - gather and return allocator input for a given mode (allocate new
1836
      or replace secondary) and a given instance definition (direction
1837
      'in')
1838
    - run a selected allocator for a given operation (as above) and
1839
      return the allocator output (direction 'out')
1840

1841
  """
1842
  OP_DSC_FIELD = "allocator"
1843
  OP_PARAMS = [
1844
    ("direction", ht.NoDefault,
1845
     ht.TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS), None),
1846
    ("mode", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_MODES), None),
1847
    ("name", ht.NoDefault, ht.TNonEmptyString, None),
1848
    ("nics", ht.NoDefault,
1849
     ht.TMaybeListOf(ht.TDictOf(ht.TElemOf([constants.INIC_MAC,
1850
                                            constants.INIC_IP,
1851
                                            "bridge"]),
1852
                                ht.TOr(ht.TNone, ht.TNonEmptyString))),
1853
     None),
1854
    ("disks", ht.NoDefault, ht.TOr(ht.TNone, ht.TList), None),
1855
    ("hypervisor", None, ht.TMaybeString, None),
1856
    ("allocator", None, ht.TMaybeString, None),
1857
    ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
1858
    ("memory", None, ht.TOr(ht.TNone, ht.TPositiveInt), None),
1859
    ("vcpus", None, ht.TOr(ht.TNone, ht.TPositiveInt), None),
1860
    ("os", None, ht.TMaybeString, None),
1861
    ("disk_template", None, ht.TMaybeString, None),
1862
    ("instances", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
1863
    ("evac_mode", None,
1864
     ht.TOr(ht.TNone, ht.TElemOf(constants.IALLOCATOR_NEVAC_MODES)), None),
1865
    ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
1866
    ]
1867

    
1868

    
1869
class OpTestJqueue(OpCode):
1870
  """Utility opcode to test some aspects of the job queue.
1871

1872
  """
1873
  OP_PARAMS = [
1874
    ("notify_waitlock", False, ht.TBool, None),
1875
    ("notify_exec", False, ht.TBool, None),
1876
    ("log_messages", ht.EmptyList, ht.TListOf(ht.TString), None),
1877
    ("fail", False, ht.TBool, None),
1878
    ]
1879

    
1880

    
1881
class OpTestDummy(OpCode):
1882
  """Utility opcode used by unittests.
1883

1884
  """
1885
  OP_PARAMS = [
1886
    ("result", ht.NoDefault, ht.NoType, None),
1887
    ("messages", ht.NoDefault, ht.NoType, None),
1888
    ("fail", ht.NoDefault, ht.NoType, None),
1889
    ("submit_jobs", None, ht.NoType, None),
1890
    ]
1891
  WITH_LU = False
1892

    
1893

    
1894
# Network opcodes
1895
class OpNetworkAdd(OpCode):
1896
  """Add an IP network to the cluster."""
1897
  OP_DSC_FIELD = "network_name"
1898
  OP_PARAMS = [
1899
    _PNetworkName,
1900
    ("network", None, ht.TString, None),
1901
    ("gateway", None, ht.TMaybeString, None),
1902
    ("network6", None, ht.TMaybeString, None),
1903
    ("gateway6", None, ht.TMaybeString, None),
1904
    ("mac_prefix", None, ht.TMaybeString, None),
1905
    ("network_type", None, ht.TMaybeString, None),
1906
    ("reserved_ips", None, ht.TOr(ht.TListOf(ht.TNonEmptyString), ht.TNone), None),
1907
    ]
1908

    
1909
class OpNetworkRemove(OpCode):
1910
  """Remove an IP network from the cluster."""
1911
  OP_DSC_FIELD = "network_name"
1912
  OP_PARAMS = [
1913
    _PNetworkName,
1914
    _PForce,
1915
    ]
1916

    
1917
class OpNetworkSetParams(OpCode):
1918
  """Set parameters of an IP network"""
1919
  OP_DSC_FIELD = "network_name"
1920
  OP_PARAMS = [
1921
    _PNetworkName,
1922
    ("gateway", None, ht.TMaybeString, None),
1923
    ("reserved_ips", None, ht.TOr(ht.TListOf(ht.TNonEmptyString), ht.TNone), None),
1924
    ("network6", None, ht.TMaybeString, None),
1925
    ("gateway6", None, ht.TMaybeString, None),
1926
    ("mac_prefix", None, ht.TMaybeString, None),
1927
    ("network_type", None, ht.TMaybeString, None),
1928
    ]
1929

    
1930
class OpNetworkConnect(OpCode):
1931
  OP_DSC_FIELD = "network_name"
1932
  OP_PARAMS = [
1933
    _PGroupName,
1934
    _PNetworkName,
1935
    ("network_mode", None, ht.TString, None),
1936
    ("network_link", None, ht.TString, None),
1937
    ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"),
1938
    ]
1939

    
1940
class OpNetworkDisconnect(OpCode):
1941
  OP_DSC_FIELD = "network_name"
1942
  OP_PARAMS = [
1943
    _PGroupName,
1944
    _PNetworkName,
1945
    ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"),
1946
    ]
1947

    
1948
class OpNetworkConnectAll(OpCode):
1949
  OP_DSC_FIELD = "network_name"
1950
  OP_PARAMS = [
1951
    _PNetworkName,
1952
    ("network_mode", None, ht.TString, None),
1953
    ("network_link", None, ht.TString, None),
1954
    ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"),
1955
    ]
1956

    
1957
class OpNetworkDisconnectAll(OpCode):
1958
  OP_DSC_FIELD = "network_name"
1959
  OP_PARAMS = [
1960
    _PNetworkName,
1961
    ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"),
1962
    ]
1963

    
1964

    
1965
class OpNetworkQuery(OpCode):
1966
  """Compute the list of networks."""
1967
  OP_PARAMS = [
1968
    _POutputFields,
1969
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1970
     "Empty list to query all groups, group names otherwise"),
1971
    ]
1972

    
1973

    
1974
def _GetOpList():
1975
  """Returns list of all defined opcodes.
1976

1977
  Does not eliminate duplicates by C{OP_ID}.
1978

1979
  """
1980
  return [v for v in globals().values()
1981
          if (isinstance(v, type) and issubclass(v, OpCode) and
1982
              hasattr(v, "OP_ID") and v is not OpCode)]
1983

    
1984

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