Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes.py @ 0fb66bb1

History | View | Annotate | Download (58.5 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
_PIgnoreConsistency = ("ignore_consistency", False, ht.TBool,
97
                       "Whether to ignore disk consistency")
98

    
99
_PStorageName = ("name", ht.NoDefault, ht.TMaybeString, "Storage name")
100

    
101
_PUseLocking = ("use_locking", False, ht.TBool,
102
                "Whether to use synchronization")
103

    
104
_PNameCheck = ("name_check", True, ht.TBool, "Whether to check name")
105

    
106
_PNodeGroupAllocPolicy = \
107
  ("alloc_policy", None,
108
   ht.TOr(ht.TNone, ht.TElemOf(constants.VALID_ALLOC_POLICIES)),
109
   "Instance allocation policy")
110

    
111
_PGroupNodeParams = ("ndparams", None, ht.TMaybeDict,
112
                     "Default node parameters for group")
113

    
114
_PQueryWhat = ("what", ht.NoDefault, ht.TElemOf(constants.QR_VIA_OP),
115
               "Resource(s) to query for")
116

    
117
_PEarlyRelease = ("early_release", False, ht.TBool,
118
                  "Whether to release locks as soon as possible")
119

    
120
_PIpCheckDoc = "Whether to ensure instance's IP address is inactive"
121

    
122
#: Do not remember instance state changes
123
_PNoRemember = ("no_remember", False, ht.TBool,
124
                "Do not remember the state change")
125

    
126
#: Target node for instance migration/failover
127
_PMigrationTargetNode = ("target_node", None, ht.TMaybeString,
128
                         "Target node for shared-storage instances")
129

    
130
_PStartupPaused = ("startup_paused", False, ht.TBool,
131
                   "Pause instance at startup")
132

    
133
_PVerbose = ("verbose", False, ht.TBool, "Verbose mode")
134

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

    
146
# Disk parameters
147
_PDiskParams = ("diskparams", None,
148
                ht.TOr(
149
                  ht.TDictOf(ht.TElemOf(constants.DISK_TEMPLATES), ht.TDict),
150
                  ht.TNone),
151
                "Disk templates' parameter defaults")
152

    
153
# Parameters for node resource model
154
_PHvState = ("hv_state", None, ht.TMaybeDict, "Set hypervisor states")
155
_PDiskState = ("disk_state", None, ht.TMaybeDict, "Set disk states")
156

    
157

    
158
_PIgnoreIpolicy = ("ignore_ipolicy", False, ht.TBool,
159
                   "Whether to ignore ipolicy violations")
160

    
161
# Allow runtime changes while migrating
162
_PAllowRuntimeChgs = ("allow_runtime_changes", True, ht.TBool,
163
                      "Allow runtime changes (eg. memory ballooning)")
164

    
165

    
166
#: OP_ID conversion regular expression
167
_OPID_RE = re.compile("([a-z])([A-Z])")
168

    
169
#: Utility function for L{OpClusterSetParams}
170
_TestClusterOsListItem = \
171
  ht.TAnd(ht.TIsLength(2), ht.TItems([
172
    ht.TElemOf(constants.DDMS_VALUES),
173
    ht.TNonEmptyString,
174
    ]))
175

    
176
_TestClusterOsList = ht.TMaybeListOf(_TestClusterOsListItem)
177

    
178
# TODO: Generate check from constants.INIC_PARAMS_TYPES
179
#: Utility function for testing NIC definitions
180
_TestNicDef = \
181
  ht.Comment("NIC parameters")(ht.TDictOf(ht.TElemOf(constants.INIC_PARAMS),
182
                                          ht.TOr(ht.TNone, ht.TNonEmptyString)))
183

    
184
_TSetParamsResultItemItems = [
185
  ht.Comment("name of changed parameter")(ht.TNonEmptyString),
186
  ht.Comment("new value")(ht.TAny),
187
  ]
188

    
189
_TSetParamsResult = \
190
  ht.TListOf(ht.TAnd(ht.TIsLength(len(_TSetParamsResultItemItems)),
191
                     ht.TItems(_TSetParamsResultItemItems)))
192

    
193
# TODO: Generate check from constants.IDISK_PARAMS_TYPES (however, not all users
194
# of this check support all parameters)
195
_TDiskParams = \
196
  ht.Comment("Disk parameters")(ht.TDictOf(ht.TElemOf(constants.IDISK_PARAMS),
197
                                           ht.TOr(ht.TNonEmptyString, ht.TInt)))
198

    
199
_TQueryRow = \
200
  ht.TListOf(ht.TAnd(ht.TIsLength(2),
201
                     ht.TItems([ht.TElemOf(constants.RS_ALL),
202
                                ht.TAny])))
203

    
204
_TQueryResult = ht.TListOf(_TQueryRow)
205

    
206
_TOldQueryRow = ht.TListOf(ht.TAny)
207

    
208
_TOldQueryResult = ht.TListOf(_TOldQueryRow)
209

    
210

    
211
_SUMMARY_PREFIX = {
212
  "CLUSTER_": "C_",
213
  "GROUP_": "G_",
214
  "NODE_": "N_",
215
  "INSTANCE_": "I_",
216
  }
217

    
218
#: Attribute name for dependencies
219
DEPEND_ATTR = "depends"
220

    
221
#: Attribute name for comment
222
COMMENT_ATTR = "comment"
223

    
224

    
225
def _NameToId(name):
226
  """Convert an opcode class name to an OP_ID.
227

228
  @type name: string
229
  @param name: the class name, as OpXxxYyy
230
  @rtype: string
231
  @return: the name in the OP_XXXX_YYYY format
232

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

    
245

    
246
def _GenerateObjectTypeCheck(obj, fields_types):
247
  """Helper to generate type checks for objects.
248

249
  @param obj: The object to generate type checks
250
  @param fields_types: The fields and their types as a dict
251
  @return: A ht type check function
252

253
  """
254
  assert set(obj.GetAllSlots()) == set(fields_types.keys()), \
255
    "%s != %s" % (set(obj.GetAllSlots()), set(fields_types.keys()))
256
  return ht.TStrictDict(True, True, fields_types)
257

    
258

    
259
_TQueryFieldDef = \
260
  _GenerateObjectTypeCheck(objects.QueryFieldDefinition, {
261
    "name": ht.TNonEmptyString,
262
    "title": ht.TNonEmptyString,
263
    "kind": ht.TElemOf(constants.QFT_ALL),
264
    "doc": ht.TNonEmptyString,
265
    })
266

    
267

    
268
def RequireFileStorage():
269
  """Checks that file storage is enabled.
270

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

274
  @raise errors.OpPrereqError: when file storage is disabled
275

276
  """
277
  if not constants.ENABLE_FILE_STORAGE:
278
    raise errors.OpPrereqError("File storage disabled at configure time",
279
                               errors.ECODE_INVAL)
280

    
281

    
282
def RequireSharedFileStorage():
283
  """Checks that shared file storage is enabled.
284

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

288
  @raise errors.OpPrereqError: when shared file storage is disabled
289

290
  """
291
  if not constants.ENABLE_SHARED_FILE_STORAGE:
292
    raise errors.OpPrereqError("Shared file storage disabled at"
293
                               " configure time", errors.ECODE_INVAL)
294

    
295

    
296
@ht.WithDesc("CheckFileStorage")
297
def _CheckFileStorage(value):
298
  """Ensures file storage is enabled if used.
299

300
  """
301
  if value == constants.DT_FILE:
302
    RequireFileStorage()
303
  elif value == constants.DT_SHARED_FILE:
304
    RequireSharedFileStorage()
305
  return True
306

    
307

    
308
def _BuildDiskTemplateCheck(accept_none):
309
  """Builds check for disk template.
310

311
  @type accept_none: bool
312
  @param accept_none: whether to accept None as a correct value
313
  @rtype: callable
314

315
  """
316
  template_check = ht.TElemOf(constants.DISK_TEMPLATES)
317

    
318
  if accept_none:
319
    template_check = ht.TOr(template_check, ht.TNone)
320

    
321
  return ht.TAnd(template_check, _CheckFileStorage)
322

    
323

    
324
def _CheckStorageType(storage_type):
325
  """Ensure a given storage type is valid.
326

327
  """
328
  if storage_type not in constants.VALID_STORAGE_TYPES:
329
    raise errors.OpPrereqError("Unknown storage type: %s" % storage_type,
330
                               errors.ECODE_INVAL)
331
  if storage_type == constants.ST_FILE:
332
    RequireFileStorage()
333
  return True
334

    
335

    
336
#: Storage type parameter
337
_PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType,
338
                 "Storage type")
339

    
340

    
341
class _AutoOpParamSlots(type):
342
  """Meta class for opcode definitions.
343

344
  """
345
  def __new__(mcs, name, bases, attrs):
346
    """Called when a class should be created.
347

348
    @param mcs: The meta class
349
    @param name: Name of created class
350
    @param bases: Base classes
351
    @type attrs: dict
352
    @param attrs: Class attributes
353

354
    """
355
    assert "__slots__" not in attrs, \
356
      "Class '%s' defines __slots__ when it should use OP_PARAMS" % name
357
    assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
358

    
359
    attrs["OP_ID"] = _NameToId(name)
360

    
361
    # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams
362
    params = attrs.setdefault("OP_PARAMS", [])
363

    
364
    # Use parameter names as slots
365
    slots = [pname for (pname, _, _, _) in params]
366

    
367
    assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
368
      "Class '%s' uses unknown field in OP_DSC_FIELD" % name
369

    
370
    attrs["__slots__"] = slots
371

    
372
    return type.__new__(mcs, name, bases, attrs)
373

    
374

    
375
class BaseOpCode(object):
376
  """A simple serializable object.
377

378
  This object serves as a parent class for OpCode without any custom
379
  field handling.
380

381
  """
382
  # pylint: disable=E1101
383
  # as OP_ID is dynamically defined
384
  __metaclass__ = _AutoOpParamSlots
385

    
386
  def __init__(self, **kwargs):
387
    """Constructor for BaseOpCode.
388

389
    The constructor takes only keyword arguments and will set
390
    attributes on this object based on the passed arguments. As such,
391
    it means that you should not pass arguments which are not in the
392
    __slots__ attribute for this class.
393

394
    """
395
    slots = self._all_slots()
396
    for key in kwargs:
397
      if key not in slots:
398
        raise TypeError("Object %s doesn't support the parameter '%s'" %
399
                        (self.__class__.__name__, key))
400
      setattr(self, key, kwargs[key])
401

    
402
  def __getstate__(self):
403
    """Generic serializer.
404

405
    This method just returns the contents of the instance as a
406
    dictionary.
407

408
    @rtype:  C{dict}
409
    @return: the instance attributes and their values
410

411
    """
412
    state = {}
413
    for name in self._all_slots():
414
      if hasattr(self, name):
415
        state[name] = getattr(self, name)
416
    return state
417

    
418
  def __setstate__(self, state):
419
    """Generic unserializer.
420

421
    This method just restores from the serialized state the attributes
422
    of the current instance.
423

424
    @param state: the serialized opcode data
425
    @type state:  C{dict}
426

427
    """
428
    if not isinstance(state, dict):
429
      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
430
                       type(state))
431

    
432
    for name in self._all_slots():
433
      if name not in state and hasattr(self, name):
434
        delattr(self, name)
435

    
436
    for name in state:
437
      setattr(self, name, state[name])
438

    
439
  @classmethod
440
  def _all_slots(cls):
441
    """Compute the list of all declared slots for a class.
442

443
    """
444
    slots = []
445
    for parent in cls.__mro__:
446
      slots.extend(getattr(parent, "__slots__", []))
447
    return slots
448

    
449
  @classmethod
450
  def GetAllParams(cls):
451
    """Compute list of all parameters for an opcode.
452

453
    """
454
    slots = []
455
    for parent in cls.__mro__:
456
      slots.extend(getattr(parent, "OP_PARAMS", []))
457
    return slots
458

    
459
  def Validate(self, set_defaults):
460
    """Validate opcode parameters, optionally setting default values.
461

462
    @type set_defaults: bool
463
    @param set_defaults: Whether to set default values
464
    @raise errors.OpPrereqError: When a parameter value doesn't match
465
                                 requirements
466

467
    """
468
    for (attr_name, default, test, _) in self.GetAllParams():
469
      assert test == ht.NoType or callable(test)
470

    
471
      if not hasattr(self, attr_name):
472
        if default == ht.NoDefault:
473
          raise errors.OpPrereqError("Required parameter '%s.%s' missing" %
474
                                     (self.OP_ID, attr_name),
475
                                     errors.ECODE_INVAL)
476
        elif set_defaults:
477
          if callable(default):
478
            dval = default()
479
          else:
480
            dval = default
481
          setattr(self, attr_name, dval)
482

    
483
      if test == ht.NoType:
484
        # no tests here
485
        continue
486

    
487
      if set_defaults or hasattr(self, attr_name):
488
        attr_val = getattr(self, attr_name)
489
        if not test(attr_val):
490
          logging.error("OpCode %s, parameter %s, has invalid type %s/value %s",
491
                        self.OP_ID, attr_name, type(attr_val), attr_val)
492
          raise errors.OpPrereqError("Parameter '%s.%s' fails validation" %
493
                                     (self.OP_ID, attr_name),
494
                                     errors.ECODE_INVAL)
495

    
496

    
497
def _BuildJobDepCheck(relative):
498
  """Builds check for job dependencies (L{DEPEND_ATTR}).
499

500
  @type relative: bool
501
  @param relative: Whether to accept relative job IDs (negative)
502
  @rtype: callable
503

504
  """
505
  if relative:
506
    job_id = ht.TOr(ht.TJobId, ht.TRelativeJobId)
507
  else:
508
    job_id = ht.TJobId
509

    
510
  job_dep = \
511
    ht.TAnd(ht.TIsLength(2),
512
            ht.TItems([job_id,
513
                       ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))]))
514

    
515
  return ht.TMaybeListOf(job_dep)
516

    
517

    
518
TNoRelativeJobDependencies = _BuildJobDepCheck(False)
519

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

    
529
#: Result containing only list of submitted jobs
530
TJobIdListOnly = ht.TStrictDict(True, True, {
531
  constants.JOB_IDS_KEY: ht.Comment("List of submitted jobs")(TJobIdList),
532
  })
533

    
534

    
535
class OpCode(BaseOpCode):
536
  """Abstract OpCode.
537

538
  This is the root of the actual OpCode hierarchy. All clases derived
539
  from this class should override OP_ID.
540

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

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

    
573
  def __getstate__(self):
574
    """Specialized getstate for opcodes.
575

576
    This method adds to the state dictionary the OP_ID of the class,
577
    so that on unload we can identify the correct class for
578
    instantiating the opcode.
579

580
    @rtype:   C{dict}
581
    @return:  the state as a dictionary
582

583
    """
584
    data = BaseOpCode.__getstate__(self)
585
    data["OP_ID"] = self.OP_ID
586
    return data
587

    
588
  @classmethod
589
  def LoadOpCode(cls, data):
590
    """Generic load opcode method.
591

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

596
    @type data:  C{dict}
597
    @param data: the serialized opcode
598

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

    
617
  def Summary(self):
618
    """Generates a summary description of this opcode.
619

620
    The summary is the value of the OP_ID attribute (without the "OP_"
621
    prefix), plus the value of the OP_DSC_FIELD attribute, if one was
622
    defined; this field should allow to easily identify the operation
623
    (for an instance creation job, e.g., it would be the instance
624
    name).
625

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

    
638
  def TinySummary(self):
639
    """Generates a compact summary description of the opcode.
640

641
    """
642
    assert self.OP_ID.startswith("OP_")
643

    
644
    text = self.OP_ID[3:]
645

    
646
    for (prefix, supplement) in _SUMMARY_PREFIX.items():
647
      if text.startswith(prefix):
648
        return supplement + text[len(prefix):]
649

    
650
    return text
651

    
652

    
653
# cluster opcodes
654

    
655
class OpClusterPostInit(OpCode):
656
  """Post cluster initialization.
657

658
  This opcode does not touch the cluster at all. Its purpose is to run hooks
659
  after the cluster has been initialized.
660

661
  """
662
  OP_RESULT = ht.TBool
663

    
664

    
665
class OpClusterDestroy(OpCode):
666
  """Destroy the cluster.
667

668
  This opcode has no other parameters. All the state is irreversibly
669
  lost after the execution of this opcode.
670

671
  """
672
  OP_RESULT = ht.TNonEmptyString
673

    
674

    
675
class OpClusterQuery(OpCode):
676
  """Query cluster information."""
677
  OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TAny)
678

    
679

    
680
class OpClusterVerify(OpCode):
681
  """Submits all jobs necessary to verify the cluster.
682

683
  """
684
  OP_PARAMS = [
685
    _PDebugSimulateErrors,
686
    _PErrorCodes,
687
    _PSkipChecks,
688
    _PIgnoreErrors,
689
    _PVerbose,
690
    ("group_name", None, ht.TMaybeString, "Group to verify")
691
    ]
692
  OP_RESULT = TJobIdListOnly
693

    
694

    
695
class OpClusterVerifyConfig(OpCode):
696
  """Verify the cluster config.
697

698
  """
699
  OP_PARAMS = [
700
    _PDebugSimulateErrors,
701
    _PErrorCodes,
702
    _PIgnoreErrors,
703
    _PVerbose,
704
    ]
705
  OP_RESULT = ht.TBool
706

    
707

    
708
class OpClusterVerifyGroup(OpCode):
709
  """Run verify on a node group from the cluster.
710

711
  @type skip_checks: C{list}
712
  @ivar skip_checks: steps to be skipped from the verify process; this
713
                     needs to be a subset of
714
                     L{constants.VERIFY_OPTIONAL_CHECKS}; currently
715
                     only L{constants.VERIFY_NPLUSONE_MEM} can be passed
716

717
  """
718
  OP_DSC_FIELD = "group_name"
719
  OP_PARAMS = [
720
    _PGroupName,
721
    _PDebugSimulateErrors,
722
    _PErrorCodes,
723
    _PSkipChecks,
724
    _PIgnoreErrors,
725
    _PVerbose,
726
    ]
727
  OP_RESULT = ht.TBool
728

    
729

    
730
class OpClusterVerifyDisks(OpCode):
731
  """Verify the cluster disks.
732

733
  """
734
  OP_RESULT = TJobIdListOnly
735

    
736

    
737
class OpGroupVerifyDisks(OpCode):
738
  """Verifies the status of all disks in a node group.
739

740
  Result: a tuple of three elements:
741
    - dict of node names with issues (values: error msg)
742
    - list of instances with degraded disks (that should be activated)
743
    - dict of instances with missing logical volumes (values: (node, vol)
744
      pairs with details about the missing volumes)
745

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

751
  Note that only instances that are drbd-based are taken into
752
  consideration. This might need to be revisited in the future.
753

754
  """
755
  OP_DSC_FIELD = "group_name"
756
  OP_PARAMS = [
757
    _PGroupName,
758
    ]
759
  OP_RESULT = \
760
    ht.TAnd(ht.TIsLength(3),
761
            ht.TItems([ht.TDictOf(ht.TString, ht.TString),
762
                       ht.TListOf(ht.TString),
763
                       ht.TDictOf(ht.TString,
764
                                  ht.TListOf(ht.TListOf(ht.TString)))]))
765

    
766

    
767
class OpClusterRepairDiskSizes(OpCode):
768
  """Verify the disk sizes of the instances and fixes configuration
769
  mimatches.
770

771
  Parameters: optional instances list, in case we want to restrict the
772
  checks to only a subset of the instances.
773

774
  Result: a list of tuples, (instance, disk, new-size) for changed
775
  configurations.
776

777
  In normal operation, the list should be empty.
778

779
  @type instances: list
780
  @ivar instances: the list of instances to check, or empty for all instances
781

782
  """
783
  OP_PARAMS = [
784
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
785
    ]
786
  OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3),
787
                                 ht.TItems([ht.TNonEmptyString,
788
                                            ht.TPositiveInt,
789
                                            ht.TPositiveInt])))
790

    
791

    
792
class OpClusterConfigQuery(OpCode):
793
  """Query cluster configuration values."""
794
  OP_PARAMS = [
795
    _POutputFields
796
    ]
797
  OP_RESULT = ht.TListOf(ht.TAny)
798

    
799

    
800
class OpClusterRename(OpCode):
801
  """Rename the cluster.
802

803
  @type name: C{str}
804
  @ivar name: The new name of the cluster. The name and/or the master IP
805
              address will be changed to match the new name and its IP
806
              address.
807

808
  """
809
  OP_DSC_FIELD = "name"
810
  OP_PARAMS = [
811
    ("name", ht.NoDefault, ht.TNonEmptyString, None),
812
    ]
813
  OP_RESULT = ht.TNonEmptyString
814

    
815

    
816
class OpClusterSetParams(OpCode):
817
  """Change the parameters of the cluster.
818

819
  @type vg_name: C{str} or C{None}
820
  @ivar vg_name: The new volume group name or None to disable LVM usage.
821

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

    
884

    
885
class OpClusterRedistConf(OpCode):
886
  """Force a full push of the cluster configuration.
887

888
  """
889
  OP_RESULT = ht.TNone
890

    
891

    
892
class OpClusterActivateMasterIp(OpCode):
893
  """Activate the master IP on the master node.
894

895
  """
896
  OP_RESULT = ht.TNone
897

    
898

    
899
class OpClusterDeactivateMasterIp(OpCode):
900
  """Deactivate the master IP on the master node.
901

902
  """
903
  OP_RESULT = ht.TNone
904

    
905

    
906
class OpQuery(OpCode):
907
  """Query for resources/items.
908

909
  @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
910
  @ivar fields: List of fields to retrieve
911
  @ivar qfilter: Query filter
912

913
  """
914
  OP_DSC_FIELD = "what"
915
  OP_PARAMS = [
916
    _PQueryWhat,
917
    _PUseLocking,
918
    ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
919
     "Requested fields"),
920
    ("qfilter", None, ht.TOr(ht.TNone, ht.TList),
921
     "Query filter"),
922
    ]
923
  OP_RESULT = \
924
    _GenerateObjectTypeCheck(objects.QueryResponse, {
925
      "fields": ht.TListOf(_TQueryFieldDef),
926
      "data": _TQueryResult,
927
      })
928

    
929

    
930
class OpQueryFields(OpCode):
931
  """Query for available resource/item fields.
932

933
  @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
934
  @ivar fields: List of fields to retrieve
935

936
  """
937
  OP_DSC_FIELD = "what"
938
  OP_PARAMS = [
939
    _PQueryWhat,
940
    ("fields", None, ht.TMaybeListOf(ht.TNonEmptyString),
941
     "Requested fields; if not given, all are returned"),
942
    ]
943
  OP_RESULT = \
944
    _GenerateObjectTypeCheck(objects.QueryFieldsResponse, {
945
      "fields": ht.TListOf(_TQueryFieldDef),
946
      })
947

    
948

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

    
966

    
967
# node opcodes
968

    
969
class OpNodeRemove(OpCode):
970
  """Remove a node.
971

972
  @type node_name: C{str}
973
  @ivar node_name: The name of the node to remove. If the node still has
974
                   instances on it, the operation will fail.
975

976
  """
977
  OP_DSC_FIELD = "node_name"
978
  OP_PARAMS = [
979
    _PNodeName,
980
    ]
981
  OP_RESULT = ht.TNone
982

    
983

    
984
class OpNodeAdd(OpCode):
985
  """Add a node to the cluster.
986

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

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

    
1029

    
1030
class OpNodeQuery(OpCode):
1031
  """Compute the list of nodes."""
1032
  OP_PARAMS = [
1033
    _POutputFields,
1034
    _PUseLocking,
1035
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1036
     "Empty list to query all nodes, node names otherwise"),
1037
    ]
1038
  OP_RESULT = _TOldQueryResult
1039

    
1040

    
1041
class OpNodeQueryvols(OpCode):
1042
  """Get list of volumes on node."""
1043
  OP_PARAMS = [
1044
    _POutputFields,
1045
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1046
     "Empty list to query all nodes, node names otherwise"),
1047
    ]
1048
  OP_RESULT = ht.TListOf(ht.TAny)
1049

    
1050

    
1051
class OpNodeQueryStorage(OpCode):
1052
  """Get information on storage for node(s)."""
1053
  OP_PARAMS = [
1054
    _POutputFields,
1055
    _PStorageType,
1056
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "List of nodes"),
1057
    ("name", None, ht.TMaybeString, "Storage name"),
1058
    ]
1059
  OP_RESULT = _TOldQueryResult
1060

    
1061

    
1062
class OpNodeModifyStorage(OpCode):
1063
  """Modifies the properies of a storage unit"""
1064
  OP_PARAMS = [
1065
    _PNodeName,
1066
    _PStorageType,
1067
    _PStorageName,
1068
    ("changes", ht.NoDefault, ht.TDict, "Requested changes"),
1069
    ]
1070
  OP_RESULT = ht.TNone
1071

    
1072

    
1073
class OpRepairNodeStorage(OpCode):
1074
  """Repairs the volume group on a node."""
1075
  OP_DSC_FIELD = "node_name"
1076
  OP_PARAMS = [
1077
    _PNodeName,
1078
    _PStorageType,
1079
    _PStorageName,
1080
    _PIgnoreConsistency,
1081
    ]
1082
  OP_RESULT = ht.TNone
1083

    
1084

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

    
1113

    
1114
class OpNodePowercycle(OpCode):
1115
  """Tries to powercycle a node."""
1116
  OP_DSC_FIELD = "node_name"
1117
  OP_PARAMS = [
1118
    _PNodeName,
1119
    _PForce,
1120
    ]
1121
  OP_RESULT = ht.TMaybeString
1122

    
1123

    
1124
class OpNodeMigrate(OpCode):
1125
  """Migrate all instances from a node."""
1126
  OP_DSC_FIELD = "node_name"
1127
  OP_PARAMS = [
1128
    _PNodeName,
1129
    _PMigrationMode,
1130
    _PMigrationLive,
1131
    _PMigrationTargetNode,
1132
    _PAllowRuntimeChgs,
1133
    _PIgnoreIpolicy,
1134
    ("iallocator", None, ht.TMaybeString,
1135
     "Iallocator for deciding the target node for shared-storage instances"),
1136
    ]
1137
  OP_RESULT = TJobIdListOnly
1138

    
1139

    
1140
class OpNodeEvacuate(OpCode):
1141
  """Evacuate instances off a number of nodes."""
1142
  OP_DSC_FIELD = "node_name"
1143
  OP_PARAMS = [
1144
    _PEarlyRelease,
1145
    _PNodeName,
1146
    ("remote_node", None, ht.TMaybeString, "New secondary node"),
1147
    ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"),
1148
    ("mode", ht.NoDefault, ht.TElemOf(constants.NODE_EVAC_MODES),
1149
     "Node evacuation mode"),
1150
    ]
1151
  OP_RESULT = TJobIdListOnly
1152

    
1153

    
1154
# instance opcodes
1155

    
1156
class OpInstanceCreate(OpCode):
1157
  """Create an instance.
1158

1159
  @ivar instance_name: Instance name
1160
  @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES})
1161
  @ivar source_handshake: Signed handshake from source (remote import only)
1162
  @ivar source_x509_ca: Source X509 CA in PEM format (remote import only)
1163
  @ivar source_instance_name: Previous name of instance (remote import only)
1164
  @ivar source_shutdown_timeout: Shutdown timeout used for source instance
1165
    (remote import only)
1166

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

    
1228

    
1229
class OpInstanceReinstall(OpCode):
1230
  """Reinstall an instance's OS."""
1231
  OP_DSC_FIELD = "instance_name"
1232
  OP_PARAMS = [
1233
    _PInstanceName,
1234
    _PForceVariant,
1235
    ("os_type", None, ht.TMaybeString, "Instance operating system"),
1236
    ("osparams", None, ht.TMaybeDict, "Temporary OS parameters"),
1237
    ]
1238
  OP_RESULT = ht.TNone
1239

    
1240

    
1241
class OpInstanceRemove(OpCode):
1242
  """Remove an instance."""
1243
  OP_DSC_FIELD = "instance_name"
1244
  OP_PARAMS = [
1245
    _PInstanceName,
1246
    _PShutdownTimeout,
1247
    ("ignore_failures", False, ht.TBool,
1248
     "Whether to ignore failures during removal"),
1249
    ]
1250
  OP_RESULT = ht.TNone
1251

    
1252

    
1253
class OpInstanceRename(OpCode):
1254
  """Rename an instance."""
1255
  OP_PARAMS = [
1256
    _PInstanceName,
1257
    _PNameCheck,
1258
    ("new_name", ht.NoDefault, ht.TNonEmptyString, "New instance name"),
1259
    ("ip_check", False, ht.TBool, _PIpCheckDoc),
1260
    ]
1261
  OP_RESULT = ht.Comment("New instance name")(ht.TNonEmptyString)
1262

    
1263

    
1264
class OpInstanceStartup(OpCode):
1265
  """Startup an instance."""
1266
  OP_DSC_FIELD = "instance_name"
1267
  OP_PARAMS = [
1268
    _PInstanceName,
1269
    _PForce,
1270
    _PIgnoreOfflineNodes,
1271
    ("hvparams", ht.EmptyDict, ht.TDict,
1272
     "Temporary hypervisor parameters, hypervisor-dependent"),
1273
    ("beparams", ht.EmptyDict, ht.TDict, "Temporary backend parameters"),
1274
    _PNoRemember,
1275
    _PStartupPaused,
1276
    ]
1277
  OP_RESULT = ht.TNone
1278

    
1279

    
1280
class OpInstanceShutdown(OpCode):
1281
  """Shutdown an instance."""
1282
  OP_DSC_FIELD = "instance_name"
1283
  OP_PARAMS = [
1284
    _PInstanceName,
1285
    _PIgnoreOfflineNodes,
1286
    ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TPositiveInt,
1287
     "How long to wait for instance to shut down"),
1288
    _PNoRemember,
1289
    ]
1290
  OP_RESULT = ht.TNone
1291

    
1292

    
1293
class OpInstanceReboot(OpCode):
1294
  """Reboot an instance."""
1295
  OP_DSC_FIELD = "instance_name"
1296
  OP_PARAMS = [
1297
    _PInstanceName,
1298
    _PShutdownTimeout,
1299
    ("ignore_secondaries", False, ht.TBool,
1300
     "Whether to start the instance even if secondary disks are failing"),
1301
    ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES),
1302
     "How to reboot instance"),
1303
    ]
1304
  OP_RESULT = ht.TNone
1305

    
1306

    
1307
class OpInstanceReplaceDisks(OpCode):
1308
  """Replace the disks of an instance."""
1309
  OP_DSC_FIELD = "instance_name"
1310
  OP_PARAMS = [
1311
    _PInstanceName,
1312
    _PEarlyRelease,
1313
    _PIgnoreIpolicy,
1314
    ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES),
1315
     "Replacement mode"),
1316
    ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt),
1317
     "Disk indexes"),
1318
    ("remote_node", None, ht.TMaybeString, "New secondary node"),
1319
    ("iallocator", None, ht.TMaybeString,
1320
     "Iallocator for deciding new secondary node"),
1321
    ]
1322
  OP_RESULT = ht.TNone
1323

    
1324

    
1325
class OpInstanceFailover(OpCode):
1326
  """Failover an instance."""
1327
  OP_DSC_FIELD = "instance_name"
1328
  OP_PARAMS = [
1329
    _PInstanceName,
1330
    _PShutdownTimeout,
1331
    _PIgnoreConsistency,
1332
    _PMigrationTargetNode,
1333
    _PIgnoreIpolicy,
1334
    ("iallocator", None, ht.TMaybeString,
1335
     "Iallocator for deciding the target node for shared-storage instances"),
1336
    ]
1337
  OP_RESULT = ht.TNone
1338

    
1339

    
1340
class OpInstanceMigrate(OpCode):
1341
  """Migrate an instance.
1342

1343
  This migrates (without shutting down an instance) to its secondary
1344
  node.
1345

1346
  @ivar instance_name: the name of the instance
1347
  @ivar mode: the migration mode (live, non-live or None for auto)
1348

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

    
1367

    
1368
class OpInstanceMove(OpCode):
1369
  """Move an instance.
1370

1371
  This move (with shutting down an instance and data copying) to an
1372
  arbitrary node.
1373

1374
  @ivar instance_name: the name of the instance
1375
  @ivar target_node: the destination node
1376

1377
  """
1378
  OP_DSC_FIELD = "instance_name"
1379
  OP_PARAMS = [
1380
    _PInstanceName,
1381
    _PShutdownTimeout,
1382
    _PIgnoreIpolicy,
1383
    ("target_node", ht.NoDefault, ht.TNonEmptyString, "Target node"),
1384
    _PIgnoreConsistency,
1385
    ]
1386
  OP_RESULT = ht.TNone
1387

    
1388

    
1389
class OpInstanceConsole(OpCode):
1390
  """Connect to an instance's console."""
1391
  OP_DSC_FIELD = "instance_name"
1392
  OP_PARAMS = [
1393
    _PInstanceName
1394
    ]
1395
  OP_RESULT = ht.TDict
1396

    
1397

    
1398
class OpInstanceActivateDisks(OpCode):
1399
  """Activate an instance's disks."""
1400
  OP_DSC_FIELD = "instance_name"
1401
  OP_PARAMS = [
1402
    _PInstanceName,
1403
    ("ignore_size", False, ht.TBool, "Whether to ignore recorded size"),
1404
    ]
1405
  OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3),
1406
                                 ht.TItems([ht.TNonEmptyString,
1407
                                            ht.TNonEmptyString,
1408
                                            ht.TNonEmptyString])))
1409

    
1410

    
1411
class OpInstanceDeactivateDisks(OpCode):
1412
  """Deactivate an instance's disks."""
1413
  OP_DSC_FIELD = "instance_name"
1414
  OP_PARAMS = [
1415
    _PInstanceName,
1416
    _PForce,
1417
    ]
1418
  OP_RESULT = ht.TNone
1419

    
1420

    
1421
class OpInstanceRecreateDisks(OpCode):
1422
  """Recreate an instance's disks."""
1423
  _TDiskChanges = \
1424
    ht.TAnd(ht.TIsLength(2),
1425
            ht.TItems([ht.Comment("Disk index")(ht.TPositiveInt),
1426
                       ht.Comment("Parameters")(_TDiskParams)]))
1427

    
1428
  OP_DSC_FIELD = "instance_name"
1429
  OP_PARAMS = [
1430
    _PInstanceName,
1431
    ("disks", ht.EmptyList,
1432
     ht.TOr(ht.TListOf(ht.TPositiveInt), ht.TListOf(_TDiskChanges)),
1433
     "List of disk indexes (deprecated) or a list of tuples containing a disk"
1434
     " index and a possibly empty dictionary with disk parameter changes"),
1435
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1436
     "New instance nodes, if relocation is desired"),
1437
    ]
1438
  OP_RESULT = ht.TNone
1439

    
1440

    
1441
class OpInstanceQuery(OpCode):
1442
  """Compute the list of instances."""
1443
  OP_PARAMS = [
1444
    _POutputFields,
1445
    _PUseLocking,
1446
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1447
     "Empty list to query all instances, instance names otherwise"),
1448
    ]
1449
  OP_RESULT = _TOldQueryResult
1450

    
1451

    
1452
class OpInstanceQueryData(OpCode):
1453
  """Compute the run-time status of instances."""
1454
  OP_PARAMS = [
1455
    _PUseLocking,
1456
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1457
     "Instance names"),
1458
    ("static", False, ht.TBool,
1459
     "Whether to only return configuration data without querying"
1460
     " nodes"),
1461
    ]
1462
  OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TDict)
1463

    
1464

    
1465
def _TestInstSetParamsModList(fn):
1466
  """Generates a check for modification lists.
1467

1468
  """
1469
  # Old format
1470
  # TODO: Remove in version 2.8 including support in LUInstanceSetParams
1471
  old_mod_item_fn = \
1472
    ht.TAnd(ht.TIsLength(2), ht.TItems([
1473
      ht.TOr(ht.TElemOf(constants.DDMS_VALUES), ht.TPositiveInt),
1474
      fn,
1475
      ]))
1476

    
1477
  # New format, supporting adding/removing disks/NICs at arbitrary indices
1478
  mod_item_fn = \
1479
    ht.TAnd(ht.TIsLength(3), ht.TItems([
1480
      ht.TElemOf(constants.DDMS_VALUES_WITH_MODIFY),
1481
      ht.Comment("Disk index, can be negative, e.g. -1 for last disk")(ht.TInt),
1482
      fn,
1483
      ]))
1484

    
1485
  return ht.TOr(ht.Comment("Recommended")(ht.TListOf(mod_item_fn)),
1486
                ht.Comment("Deprecated")(ht.TListOf(old_mod_item_fn)))
1487

    
1488

    
1489
class OpInstanceSetParams(OpCode):
1490
  """Change the parameters of an instance.
1491

1492
  """
1493
  TestNicModifications = _TestInstSetParamsModList(_TestNicDef)
1494
  TestDiskModifications = _TestInstSetParamsModList(_TDiskParams)
1495

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

    
1531

    
1532
class OpInstanceGrowDisk(OpCode):
1533
  """Grow a disk of an instance."""
1534
  OP_DSC_FIELD = "instance_name"
1535
  OP_PARAMS = [
1536
    _PInstanceName,
1537
    _PWaitForSync,
1538
    ("disk", ht.NoDefault, ht.TInt, "Disk index"),
1539
    ("amount", ht.NoDefault, ht.TPositiveInt,
1540
     "Amount of disk space to add (megabytes)"),
1541
    ("absolute", False, ht.TBool,
1542
     "Whether the amount parameter is an absolute target or a relative one"),
1543
    ]
1544
  OP_RESULT = ht.TNone
1545

    
1546

    
1547
class OpInstanceChangeGroup(OpCode):
1548
  """Moves an instance to another node group."""
1549
  OP_DSC_FIELD = "instance_name"
1550
  OP_PARAMS = [
1551
    _PInstanceName,
1552
    _PEarlyRelease,
1553
    ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"),
1554
    ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString),
1555
     "Destination group names or UUIDs (defaults to \"all but current group\""),
1556
    ]
1557
  OP_RESULT = TJobIdListOnly
1558

    
1559

    
1560
# Node group opcodes
1561

    
1562
class OpGroupAdd(OpCode):
1563
  """Add a node group to the cluster."""
1564
  OP_DSC_FIELD = "group_name"
1565
  OP_PARAMS = [
1566
    _PGroupName,
1567
    _PNodeGroupAllocPolicy,
1568
    _PGroupNodeParams,
1569
    _PDiskParams,
1570
    _PHvState,
1571
    _PDiskState,
1572
    ("ipolicy", None, ht.TMaybeDict,
1573
     "Group-wide :ref:`instance policy <rapi-ipolicy>` specs"),
1574
    ]
1575
  OP_RESULT = ht.TNone
1576

    
1577

    
1578
class OpGroupAssignNodes(OpCode):
1579
  """Assign nodes to a node group."""
1580
  OP_DSC_FIELD = "group_name"
1581
  OP_PARAMS = [
1582
    _PGroupName,
1583
    _PForce,
1584
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
1585
     "List of nodes to assign"),
1586
    ]
1587
  OP_RESULT = ht.TNone
1588

    
1589

    
1590
class OpGroupQuery(OpCode):
1591
  """Compute the list of node groups."""
1592
  OP_PARAMS = [
1593
    _POutputFields,
1594
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1595
     "Empty list to query all groups, group names otherwise"),
1596
    ]
1597
  OP_RESULT = _TOldQueryResult
1598

    
1599

    
1600
class OpGroupSetParams(OpCode):
1601
  """Change the parameters of a node group."""
1602
  OP_DSC_FIELD = "group_name"
1603
  OP_PARAMS = [
1604
    _PGroupName,
1605
    _PNodeGroupAllocPolicy,
1606
    _PGroupNodeParams,
1607
    _PDiskParams,
1608
    _PHvState,
1609
    _PDiskState,
1610
    ("ipolicy", None, ht.TMaybeDict, "Group-wide instance policy specs"),
1611
    ]
1612
  OP_RESULT = _TSetParamsResult
1613

    
1614

    
1615
class OpGroupRemove(OpCode):
1616
  """Remove a node group from the cluster."""
1617
  OP_DSC_FIELD = "group_name"
1618
  OP_PARAMS = [
1619
    _PGroupName,
1620
    ]
1621
  OP_RESULT = ht.TNone
1622

    
1623

    
1624
class OpGroupRename(OpCode):
1625
  """Rename a node group in the cluster."""
1626
  OP_PARAMS = [
1627
    _PGroupName,
1628
    ("new_name", ht.NoDefault, ht.TNonEmptyString, "New group name"),
1629
    ]
1630
  OP_RESULT = ht.Comment("New group name")(ht.TNonEmptyString)
1631

    
1632

    
1633
class OpGroupEvacuate(OpCode):
1634
  """Evacuate a node group in the cluster."""
1635
  OP_DSC_FIELD = "group_name"
1636
  OP_PARAMS = [
1637
    _PGroupName,
1638
    _PEarlyRelease,
1639
    ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"),
1640
    ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString),
1641
     "Destination group names or UUIDs"),
1642
    ]
1643
  OP_RESULT = TJobIdListOnly
1644

    
1645

    
1646
# OS opcodes
1647
class OpOsDiagnose(OpCode):
1648
  """Compute the list of guest operating systems."""
1649
  OP_PARAMS = [
1650
    _POutputFields,
1651
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1652
     "Which operating systems to diagnose"),
1653
    ]
1654
  OP_RESULT = _TOldQueryResult
1655

    
1656

    
1657
# Exports opcodes
1658
class OpBackupQuery(OpCode):
1659
  """Compute the list of exported images."""
1660
  OP_PARAMS = [
1661
    _PUseLocking,
1662
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1663
     "Empty list to query all nodes, node names otherwise"),
1664
    ]
1665
  OP_RESULT = ht.TDictOf(ht.TNonEmptyString,
1666
                         ht.TOr(ht.Comment("False on error")(ht.TBool),
1667
                                ht.TListOf(ht.TNonEmptyString)))
1668

    
1669

    
1670
class OpBackupPrepare(OpCode):
1671
  """Prepares an instance export.
1672

1673
  @ivar instance_name: Instance name
1674
  @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1675

1676
  """
1677
  OP_DSC_FIELD = "instance_name"
1678
  OP_PARAMS = [
1679
    _PInstanceName,
1680
    ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES),
1681
     "Export mode"),
1682
    ]
1683
  OP_RESULT = ht.TOr(ht.TNone, ht.TDict)
1684

    
1685

    
1686
class OpBackupExport(OpCode):
1687
  """Export an instance.
1688

1689
  For local exports, the export destination is the node name. For remote
1690
  exports, the export destination is a list of tuples, each consisting of
1691
  hostname/IP address, port, HMAC and HMAC salt. The HMAC is calculated using
1692
  the cluster domain secret over the value "${index}:${hostname}:${port}". The
1693
  destination X509 CA must be a signed certificate.
1694

1695
  @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1696
  @ivar target_node: Export destination
1697
  @ivar x509_key_name: X509 key to use (remote export only)
1698
  @ivar destination_x509_ca: Destination X509 CA in PEM format (remote export
1699
                             only)
1700

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

    
1728

    
1729
class OpBackupRemove(OpCode):
1730
  """Remove an instance's export."""
1731
  OP_DSC_FIELD = "instance_name"
1732
  OP_PARAMS = [
1733
    _PInstanceName,
1734
    ]
1735
  OP_RESULT = ht.TNone
1736

    
1737

    
1738
# Tags opcodes
1739
class OpTagsGet(OpCode):
1740
  """Returns the tags of the given object."""
1741
  OP_DSC_FIELD = "name"
1742
  OP_PARAMS = [
1743
    _PTagKind,
1744
    # Not using _PUseLocking as the default is different for historical reasons
1745
    ("use_locking", True, ht.TBool, "Whether to use synchronization"),
1746
    # Name is only meaningful for nodes and instances
1747
    ("name", ht.NoDefault, ht.TMaybeString,
1748
     "Name of object to retrieve tags from"),
1749
    ]
1750
  OP_RESULT = ht.TListOf(ht.TNonEmptyString)
1751

    
1752

    
1753
class OpTagsSearch(OpCode):
1754
  """Searches the tags in the cluster for a given pattern."""
1755
  OP_DSC_FIELD = "pattern"
1756
  OP_PARAMS = [
1757
    ("pattern", ht.NoDefault, ht.TNonEmptyString,
1758
     "Search pattern (regular expression)"),
1759
    ]
1760
  OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(2), ht.TItems([
1761
    ht.TNonEmptyString,
1762
    ht.TNonEmptyString,
1763
    ])))
1764

    
1765

    
1766
class OpTagsSet(OpCode):
1767
  """Add a list of tags on a given object."""
1768
  OP_PARAMS = [
1769
    _PTagKind,
1770
    _PTags,
1771
    # Name is only meaningful for nodes and instances
1772
    ("name", ht.NoDefault, ht.TMaybeString,
1773
     "Name of object where tag(s) should be added"),
1774
    ]
1775
  OP_RESULT = ht.TNone
1776

    
1777

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

    
1789

    
1790
# Test opcodes
1791
class OpTestDelay(OpCode):
1792
  """Sleeps for a configured amount of time.
1793

1794
  This is used just for debugging and testing.
1795

1796
  Parameters:
1797
    - duration: the time to sleep
1798
    - on_master: if true, sleep on the master
1799
    - on_nodes: list of nodes in which to sleep
1800

1801
  If the on_master parameter is true, it will execute a sleep on the
1802
  master (before any node sleep).
1803

1804
  If the on_nodes list is not empty, it will sleep on those nodes
1805
  (after the sleep on the master, if that is enabled).
1806

1807
  As an additional feature, the case of duration < 0 will be reported
1808
  as an execution error, so this opcode can be used as a failure
1809
  generator. The case of duration == 0 will not be treated specially.
1810

1811
  """
1812
  OP_DSC_FIELD = "duration"
1813
  OP_PARAMS = [
1814
    ("duration", ht.NoDefault, ht.TNumber, None),
1815
    ("on_master", True, ht.TBool, None),
1816
    ("on_nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
1817
    ("repeat", 0, ht.TPositiveInt, None),
1818
    ]
1819

    
1820

    
1821
class OpTestAllocator(OpCode):
1822
  """Allocator framework testing.
1823

1824
  This opcode has two modes:
1825
    - gather and return allocator input for a given mode (allocate new
1826
      or replace secondary) and a given instance definition (direction
1827
      'in')
1828
    - run a selected allocator for a given operation (as above) and
1829
      return the allocator output (direction 'out')
1830

1831
  """
1832
  OP_DSC_FIELD = "allocator"
1833
  OP_PARAMS = [
1834
    ("direction", ht.NoDefault,
1835
     ht.TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS), None),
1836
    ("mode", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_MODES), None),
1837
    ("name", ht.NoDefault, ht.TNonEmptyString, None),
1838
    ("nics", ht.NoDefault,
1839
     ht.TMaybeListOf(ht.TDictOf(ht.TElemOf([constants.INIC_MAC,
1840
                                            constants.INIC_IP,
1841
                                            "bridge"]),
1842
                                ht.TOr(ht.TNone, ht.TNonEmptyString))),
1843
     None),
1844
    ("disks", ht.NoDefault, ht.TOr(ht.TNone, ht.TList), None),
1845
    ("hypervisor", None, ht.TMaybeString, None),
1846
    ("allocator", None, ht.TMaybeString, None),
1847
    ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
1848
    ("memory", None, ht.TOr(ht.TNone, ht.TPositiveInt), None),
1849
    ("vcpus", None, ht.TOr(ht.TNone, ht.TPositiveInt), None),
1850
    ("os", None, ht.TMaybeString, None),
1851
    ("disk_template", None, ht.TMaybeString, None),
1852
    ("instances", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
1853
    ("evac_mode", None,
1854
     ht.TOr(ht.TNone, ht.TElemOf(constants.IALLOCATOR_NEVAC_MODES)), None),
1855
    ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
1856
    ]
1857

    
1858

    
1859
class OpTestJqueue(OpCode):
1860
  """Utility opcode to test some aspects of the job queue.
1861

1862
  """
1863
  OP_PARAMS = [
1864
    ("notify_waitlock", False, ht.TBool, None),
1865
    ("notify_exec", False, ht.TBool, None),
1866
    ("log_messages", ht.EmptyList, ht.TListOf(ht.TString), None),
1867
    ("fail", False, ht.TBool, None),
1868
    ]
1869

    
1870

    
1871
class OpTestDummy(OpCode):
1872
  """Utility opcode used by unittests.
1873

1874
  """
1875
  OP_PARAMS = [
1876
    ("result", ht.NoDefault, ht.NoType, None),
1877
    ("messages", ht.NoDefault, ht.NoType, None),
1878
    ("fail", ht.NoDefault, ht.NoType, None),
1879
    ("submit_jobs", None, ht.NoType, None),
1880
    ]
1881
  WITH_LU = False
1882

    
1883

    
1884
def _GetOpList():
1885
  """Returns list of all defined opcodes.
1886

1887
  Does not eliminate duplicates by C{OP_ID}.
1888

1889
  """
1890
  return [v for v in globals().values()
1891
          if (isinstance(v, type) and issubclass(v, OpCode) and
1892
              hasattr(v, "OP_ID") and v is not OpCode)]
1893

    
1894

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