Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes.py @ bee581e2

History | View | Annotate | Download (45.6 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 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-msg=R0903
35

    
36
import logging
37
import re
38
import operator
39

    
40
from ganeti import constants
41
from ganeti import errors
42
from ganeti import ht
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), None)
84

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

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

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

    
94
_PIgnoreConsistency = ("ignore_consistency", False, ht.TBool,
95
                       "Whether to ignore disk consistency")
96

    
97
_PStorageName = ("name", ht.NoDefault, ht.TMaybeString, "Storage name")
98

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

    
102
_PNameCheck = ("name_check", True, ht.TBool, "Whether to check name")
103

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

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

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

    
115
_PIpCheckDoc = "Whether to ensure instance's IP address is inactive"
116

    
117
#: OP_ID conversion regular expression
118
_OPID_RE = re.compile("([a-z])([A-Z])")
119

    
120
#: Utility function for L{OpClusterSetParams}
121
_TestClusterOsList = ht.TOr(ht.TNone,
122
  ht.TListOf(ht.TAnd(ht.TList, ht.TIsLength(2),
123
    ht.TMap(ht.WithDesc("GetFirstItem")(operator.itemgetter(0)),
124
            ht.TElemOf(constants.DDMS_VALUES)))))
125

    
126

    
127
# TODO: Generate check from constants.INIC_PARAMS_TYPES
128
#: Utility function for testing NIC definitions
129
_TestNicDef = ht.TDictOf(ht.TElemOf(constants.INIC_PARAMS),
130
                         ht.TOr(ht.TNone, ht.TNonEmptyString))
131

    
132
_SUMMARY_PREFIX = {
133
  "CLUSTER_": "C_",
134
  "GROUP_": "G_",
135
  "NODE_": "N_",
136
  "INSTANCE_": "I_",
137
  }
138

    
139

    
140
def _NameToId(name):
141
  """Convert an opcode class name to an OP_ID.
142

143
  @type name: string
144
  @param name: the class name, as OpXxxYyy
145
  @rtype: string
146
  @return: the name in the OP_XXXX_YYYY format
147

148
  """
149
  if not name.startswith("Op"):
150
    return None
151
  # Note: (?<=[a-z])(?=[A-Z]) would be ideal, since it wouldn't
152
  # consume any input, and hence we would just have all the elements
153
  # in the list, one by one; but it seems that split doesn't work on
154
  # non-consuming input, hence we have to process the input string a
155
  # bit
156
  name = _OPID_RE.sub(r"\1,\2", name)
157
  elems = name.split(",")
158
  return "_".join(n.upper() for n in elems)
159

    
160

    
161
def RequireFileStorage():
162
  """Checks that file storage is enabled.
163

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

167
  @raise errors.OpPrereqError: when file storage is disabled
168

169
  """
170
  if not constants.ENABLE_FILE_STORAGE:
171
    raise errors.OpPrereqError("File storage disabled at configure time",
172
                               errors.ECODE_INVAL)
173

    
174

    
175
def RequireSharedFileStorage():
176
  """Checks that shared file storage is enabled.
177

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

181
  @raise errors.OpPrereqError: when shared file storage is disabled
182

183
  """
184
  if not constants.ENABLE_SHARED_FILE_STORAGE:
185
    raise errors.OpPrereqError("Shared file storage disabled at"
186
                               " configure time", errors.ECODE_INVAL)
187

    
188

    
189
@ht.WithDesc("CheckFileStorage")
190
def _CheckFileStorage(value):
191
  """Ensures file storage is enabled if used.
192

193
  """
194
  if value == constants.DT_FILE:
195
    RequireFileStorage()
196
  elif value == constants.DT_SHARED_FILE:
197
    RequireSharedFileStorage()
198
  return True
199

    
200

    
201
_CheckDiskTemplate = ht.TAnd(ht.TElemOf(constants.DISK_TEMPLATES),
202
                             _CheckFileStorage)
203

    
204

    
205
def _CheckStorageType(storage_type):
206
  """Ensure a given storage type is valid.
207

208
  """
209
  if storage_type not in constants.VALID_STORAGE_TYPES:
210
    raise errors.OpPrereqError("Unknown storage type: %s" % storage_type,
211
                               errors.ECODE_INVAL)
212
  if storage_type == constants.ST_FILE:
213
    RequireFileStorage()
214
  return True
215

    
216

    
217
#: Storage type parameter
218
_PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType,
219
                 "Storage type")
220

    
221

    
222
class _AutoOpParamSlots(type):
223
  """Meta class for opcode definitions.
224

225
  """
226
  def __new__(mcs, name, bases, attrs):
227
    """Called when a class should be created.
228

229
    @param mcs: The meta class
230
    @param name: Name of created class
231
    @param bases: Base classes
232
    @type attrs: dict
233
    @param attrs: Class attributes
234

235
    """
236
    assert "__slots__" not in attrs, \
237
      "Class '%s' defines __slots__ when it should use OP_PARAMS" % name
238
    assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
239

    
240
    attrs["OP_ID"] = _NameToId(name)
241

    
242
    # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams
243
    params = attrs.setdefault("OP_PARAMS", [])
244

    
245
    # Use parameter names as slots
246
    slots = [pname for (pname, _, _, _) in params]
247

    
248
    assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
249
      "Class '%s' uses unknown field in OP_DSC_FIELD" % name
250

    
251
    attrs["__slots__"] = slots
252

    
253
    return type.__new__(mcs, name, bases, attrs)
254

    
255

    
256
class BaseOpCode(object):
257
  """A simple serializable object.
258

259
  This object serves as a parent class for OpCode without any custom
260
  field handling.
261

262
  """
263
  # pylint: disable-msg=E1101
264
  # as OP_ID is dynamically defined
265
  __metaclass__ = _AutoOpParamSlots
266

    
267
  def __init__(self, **kwargs):
268
    """Constructor for BaseOpCode.
269

270
    The constructor takes only keyword arguments and will set
271
    attributes on this object based on the passed arguments. As such,
272
    it means that you should not pass arguments which are not in the
273
    __slots__ attribute for this class.
274

275
    """
276
    slots = self._all_slots()
277
    for key in kwargs:
278
      if key not in slots:
279
        raise TypeError("Object %s doesn't support the parameter '%s'" %
280
                        (self.__class__.__name__, key))
281
      setattr(self, key, kwargs[key])
282

    
283
  def __getstate__(self):
284
    """Generic serializer.
285

286
    This method just returns the contents of the instance as a
287
    dictionary.
288

289
    @rtype:  C{dict}
290
    @return: the instance attributes and their values
291

292
    """
293
    state = {}
294
    for name in self._all_slots():
295
      if hasattr(self, name):
296
        state[name] = getattr(self, name)
297
    return state
298

    
299
  def __setstate__(self, state):
300
    """Generic unserializer.
301

302
    This method just restores from the serialized state the attributes
303
    of the current instance.
304

305
    @param state: the serialized opcode data
306
    @type state:  C{dict}
307

308
    """
309
    if not isinstance(state, dict):
310
      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
311
                       type(state))
312

    
313
    for name in self._all_slots():
314
      if name not in state and hasattr(self, name):
315
        delattr(self, name)
316

    
317
    for name in state:
318
      setattr(self, name, state[name])
319

    
320
  @classmethod
321
  def _all_slots(cls):
322
    """Compute the list of all declared slots for a class.
323

324
    """
325
    slots = []
326
    for parent in cls.__mro__:
327
      slots.extend(getattr(parent, "__slots__", []))
328
    return slots
329

    
330
  @classmethod
331
  def GetAllParams(cls):
332
    """Compute list of all parameters for an opcode.
333

334
    """
335
    slots = []
336
    for parent in cls.__mro__:
337
      slots.extend(getattr(parent, "OP_PARAMS", []))
338
    return slots
339

    
340
  def Validate(self, set_defaults):
341
    """Validate opcode parameters, optionally setting default values.
342

343
    @type set_defaults: bool
344
    @param set_defaults: Whether to set default values
345
    @raise errors.OpPrereqError: When a parameter value doesn't match
346
                                 requirements
347

348
    """
349
    for (attr_name, default, test, _) in self.GetAllParams():
350
      assert test == ht.NoType or callable(test)
351

    
352
      if not hasattr(self, attr_name):
353
        if default == ht.NoDefault:
354
          raise errors.OpPrereqError("Required parameter '%s.%s' missing" %
355
                                     (self.OP_ID, attr_name),
356
                                     errors.ECODE_INVAL)
357
        elif set_defaults:
358
          if callable(default):
359
            dval = default()
360
          else:
361
            dval = default
362
          setattr(self, attr_name, dval)
363

    
364
      if test == ht.NoType:
365
        # no tests here
366
        continue
367

    
368
      if set_defaults or hasattr(self, attr_name):
369
        attr_val = getattr(self, attr_name)
370
        if not test(attr_val):
371
          logging.error("OpCode %s, parameter %s, has invalid type %s/value %s",
372
                        self.OP_ID, attr_name, type(attr_val), attr_val)
373
          raise errors.OpPrereqError("Parameter '%s.%s' fails validation" %
374
                                     (self.OP_ID, attr_name),
375
                                     errors.ECODE_INVAL)
376

    
377

    
378
class OpCode(BaseOpCode):
379
  """Abstract OpCode.
380

381
  This is the root of the actual OpCode hierarchy. All clases derived
382
  from this class should override OP_ID.
383

384
  @cvar OP_ID: The ID of this opcode. This should be unique amongst all
385
               children of this class.
386
  @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
387
                      string returned by Summary(); see the docstring of that
388
                      method for details).
389
  @cvar OP_PARAMS: List of opcode attributes, the default values they should
390
                   get if not already defined, and types they must match.
391
  @cvar WITH_LU: Boolean that specifies whether this should be included in
392
      mcpu's dispatch table
393
  @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
394
                 the check steps
395
  @ivar priority: Opcode priority for queue
396

397
  """
398
  # pylint: disable-msg=E1101
399
  # as OP_ID is dynamically defined
400
  WITH_LU = True
401
  OP_PARAMS = [
402
    ("dry_run", None, ht.TMaybeBool, "Run checks only, don't execute"),
403
    ("debug_level", None, ht.TOr(ht.TNone, ht.TPositiveInt), "Debug level"),
404
    ("priority", constants.OP_PRIO_DEFAULT,
405
     ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID), "Opcode priority"),
406
    ]
407

    
408
  def __getstate__(self):
409
    """Specialized getstate for opcodes.
410

411
    This method adds to the state dictionary the OP_ID of the class,
412
    so that on unload we can identify the correct class for
413
    instantiating the opcode.
414

415
    @rtype:   C{dict}
416
    @return:  the state as a dictionary
417

418
    """
419
    data = BaseOpCode.__getstate__(self)
420
    data["OP_ID"] = self.OP_ID
421
    return data
422

    
423
  @classmethod
424
  def LoadOpCode(cls, data):
425
    """Generic load opcode method.
426

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

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

434
    """
435
    if not isinstance(data, dict):
436
      raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
437
    if "OP_ID" not in data:
438
      raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
439
    op_id = data["OP_ID"]
440
    op_class = None
441
    if op_id in OP_MAPPING:
442
      op_class = OP_MAPPING[op_id]
443
    else:
444
      raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
445
                       op_id)
446
    op = op_class()
447
    new_data = data.copy()
448
    del new_data["OP_ID"]
449
    op.__setstate__(new_data)
450
    return op
451

    
452
  def Summary(self):
453
    """Generates a summary description of this opcode.
454

455
    The summary is the value of the OP_ID attribute (without the "OP_"
456
    prefix), plus the value of the OP_DSC_FIELD attribute, if one was
457
    defined; this field should allow to easily identify the operation
458
    (for an instance creation job, e.g., it would be the instance
459
    name).
460

461
    """
462
    assert self.OP_ID is not None and len(self.OP_ID) > 3
463
    # all OP_ID start with OP_, we remove that
464
    txt = self.OP_ID[3:]
465
    field_name = getattr(self, "OP_DSC_FIELD", None)
466
    if field_name:
467
      field_value = getattr(self, field_name, None)
468
      if isinstance(field_value, (list, tuple)):
469
        field_value = ",".join(str(i) for i in field_value)
470
      txt = "%s(%s)" % (txt, field_value)
471
    return txt
472

    
473
  def TinySummary(self):
474
    """Generates a compact summary description of the opcode.
475

476
    """
477
    assert self.OP_ID.startswith("OP_")
478

    
479
    text = self.OP_ID[3:]
480

    
481
    for (prefix, supplement) in _SUMMARY_PREFIX.items():
482
      if text.startswith(prefix):
483
        return supplement + text[len(prefix):]
484

    
485
    return text
486

    
487

    
488
# cluster opcodes
489

    
490
class OpClusterPostInit(OpCode):
491
  """Post cluster initialization.
492

493
  This opcode does not touch the cluster at all. Its purpose is to run hooks
494
  after the cluster has been initialized.
495

496
  """
497

    
498

    
499
class OpClusterDestroy(OpCode):
500
  """Destroy the cluster.
501

502
  This opcode has no other parameters. All the state is irreversibly
503
  lost after the execution of this opcode.
504

505
  """
506

    
507

    
508
class OpClusterQuery(OpCode):
509
  """Query cluster information."""
510

    
511

    
512
class OpClusterVerify(OpCode):
513
  """Verify the cluster state.
514

515
  @type skip_checks: C{list}
516
  @ivar skip_checks: steps to be skipped from the verify process; this
517
                     needs to be a subset of
518
                     L{constants.VERIFY_OPTIONAL_CHECKS}; currently
519
                     only L{constants.VERIFY_NPLUSONE_MEM} can be passed
520

521
  """
522
  OP_PARAMS = [
523
    ("skip_checks", ht.EmptyList,
524
     ht.TListOf(ht.TElemOf(constants.VERIFY_OPTIONAL_CHECKS)), None),
525
    ("verbose", False, ht.TBool, None),
526
    ("error_codes", False, ht.TBool, None),
527
    ("debug_simulate_errors", False, ht.TBool, None),
528
    ]
529

    
530

    
531
class OpClusterVerifyDisks(OpCode):
532
  """Verify the cluster disks.
533

534
  Parameters: none
535

536
  Result: a tuple of four elements:
537
    - list of node names with bad data returned (unreachable, etc.)
538
    - dict of node names with broken volume groups (values: error msg)
539
    - list of instances with degraded disks (that should be activated)
540
    - dict of instances with missing logical volumes (values: (node, vol)
541
      pairs with details about the missing volumes)
542

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

548
  Note that only instances that are drbd-based are taken into
549
  consideration. This might need to be revisited in the future.
550

551
  """
552

    
553

    
554
class OpClusterRepairDiskSizes(OpCode):
555
  """Verify the disk sizes of the instances and fixes configuration
556
  mimatches.
557

558
  Parameters: optional instances list, in case we want to restrict the
559
  checks to only a subset of the instances.
560

561
  Result: a list of tuples, (instance, disk, new-size) for changed
562
  configurations.
563

564
  In normal operation, the list should be empty.
565

566
  @type instances: list
567
  @ivar instances: the list of instances to check, or empty for all instances
568

569
  """
570
  OP_PARAMS = [
571
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
572
    ]
573

    
574

    
575
class OpClusterConfigQuery(OpCode):
576
  """Query cluster configuration values."""
577
  OP_PARAMS = [
578
    _POutputFields
579
    ]
580

    
581

    
582
class OpClusterRename(OpCode):
583
  """Rename the cluster.
584

585
  @type name: C{str}
586
  @ivar name: The new name of the cluster. The name and/or the master IP
587
              address will be changed to match the new name and its IP
588
              address.
589

590
  """
591
  OP_DSC_FIELD = "name"
592
  OP_PARAMS = [
593
    ("name", ht.NoDefault, ht.TNonEmptyString, None),
594
    ]
595

    
596

    
597
class OpClusterSetParams(OpCode):
598
  """Change the parameters of the cluster.
599

600
  @type vg_name: C{str} or C{None}
601
  @ivar vg_name: The new volume group name or None to disable LVM usage.
602

603
  """
604
  OP_PARAMS = [
605
    ("vg_name", None, ht.TMaybeString, "Volume group name"),
606
    ("enabled_hypervisors", None,
607
     ht.TOr(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)), ht.TTrue),
608
            ht.TNone),
609
     "List of enabled hypervisors"),
610
    ("hvparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
611
                              ht.TNone),
612
     "Cluster-wide hypervisor parameter defaults, hypervisor-dependent"),
613
    ("beparams", None, ht.TOr(ht.TDict, ht.TNone),
614
     "Cluster-wide backend parameter defaults"),
615
    ("os_hvp", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
616
                            ht.TNone),
617
     "Cluster-wide per-OS hypervisor parameter defaults"),
618
    ("osparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
619
                              ht.TNone),
620
     "Cluster-wide OS parameter defaults"),
621
    ("candidate_pool_size", None, ht.TOr(ht.TStrictPositiveInt, ht.TNone),
622
     "Master candidate pool size"),
623
    ("uid_pool", None, ht.NoType,
624
     "Set UID pool, must be list of lists describing UID ranges (two items,"
625
     " start and end inclusive)"),
626
    ("add_uids", None, ht.NoType,
627
     "Extend UID pool, must be list of lists describing UID ranges (two"
628
     " items, start and end inclusive) to be added"),
629
    ("remove_uids", None, ht.NoType,
630
     "Shrink UID pool, must be list of lists describing UID ranges (two"
631
     " items, start and end inclusive) to be removed"),
632
    ("maintain_node_health", None, ht.TMaybeBool,
633
     "Whether to automatically maintain node health"),
634
    ("prealloc_wipe_disks", None, ht.TMaybeBool,
635
     "Whether to wipe disks before allocating them to instances"),
636
    ("nicparams", None, ht.TMaybeDict, "Cluster-wide NIC parameter defaults"),
637
    ("ndparams", None, ht.TMaybeDict, "Cluster-wide node parameter defaults"),
638
    ("drbd_helper", None, ht.TOr(ht.TString, ht.TNone), "DRBD helper program"),
639
    ("default_iallocator", None, ht.TOr(ht.TString, ht.TNone),
640
     "Default iallocator for cluster"),
641
    ("master_netdev", None, ht.TOr(ht.TString, ht.TNone),
642
     "Master network device"),
643
    ("reserved_lvs", None, ht.TOr(ht.TListOf(ht.TNonEmptyString), ht.TNone),
644
     "List of reserved LVs"),
645
    ("hidden_os", None, _TestClusterOsList,
646
     "Modify list of hidden operating systems. Each modification must have"
647
     " two items, the operation and the OS name. The operation can be"
648
     " ``%s`` or ``%s``." % (constants.DDM_ADD, constants.DDM_REMOVE)),
649
    ("blacklisted_os", None, _TestClusterOsList,
650
     "Modify list of blacklisted operating systems. Each modification must have"
651
     " two items, the operation and the OS name. The operation can be"
652
     " ``%s`` or ``%s``." % (constants.DDM_ADD, constants.DDM_REMOVE)),
653
    ]
654

    
655

    
656
class OpClusterRedistConf(OpCode):
657
  """Force a full push of the cluster configuration.
658

659
  """
660

    
661

    
662
class OpQuery(OpCode):
663
  """Query for resources/items.
664

665
  @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
666
  @ivar fields: List of fields to retrieve
667
  @ivar filter: Query filter
668

669
  """
670
  OP_PARAMS = [
671
    _PQueryWhat,
672
    ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
673
     "Requested fields"),
674
    ("filter", None, ht.TOr(ht.TNone, ht.TListOf),
675
     "Query filter"),
676
    ]
677

    
678

    
679
class OpQueryFields(OpCode):
680
  """Query for available resource/item fields.
681

682
  @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
683
  @ivar fields: List of fields to retrieve
684

685
  """
686
  OP_PARAMS = [
687
    _PQueryWhat,
688
    ("fields", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString)),
689
     "Requested fields; if not given, all are returned"),
690
    ]
691

    
692

    
693
class OpOobCommand(OpCode):
694
  """Interact with OOB."""
695
  OP_PARAMS = [
696
    ("node_names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
697
     "List of nodes to run the OOB command against"),
698
    ("command", None, ht.TElemOf(constants.OOB_COMMANDS),
699
     "OOB command to be run"),
700
    ("timeout", constants.OOB_TIMEOUT, ht.TInt,
701
     "Timeout before the OOB helper will be terminated"),
702
    ("ignore_status", False, ht.TBool,
703
     "Ignores the node offline status for power off"),
704
    ("power_delay", constants.OOB_POWER_DELAY, ht.TPositiveFloat,
705
     "Time in seconds to wait between powering on nodes"),
706
    ]
707

    
708

    
709
# node opcodes
710

    
711
class OpNodeRemove(OpCode):
712
  """Remove a node.
713

714
  @type node_name: C{str}
715
  @ivar node_name: The name of the node to remove. If the node still has
716
                   instances on it, the operation will fail.
717

718
  """
719
  OP_DSC_FIELD = "node_name"
720
  OP_PARAMS = [
721
    _PNodeName,
722
    ]
723

    
724

    
725
class OpNodeAdd(OpCode):
726
  """Add a node to the cluster.
727

728
  @type node_name: C{str}
729
  @ivar node_name: The name of the node to add. This can be a short name,
730
                   but it will be expanded to the FQDN.
731
  @type primary_ip: IP address
732
  @ivar primary_ip: The primary IP of the node. This will be ignored when the
733
                    opcode is submitted, but will be filled during the node
734
                    add (so it will be visible in the job query).
735
  @type secondary_ip: IP address
736
  @ivar secondary_ip: The secondary IP of the node. This needs to be passed
737
                      if the cluster has been initialized in 'dual-network'
738
                      mode, otherwise it must not be given.
739
  @type readd: C{bool}
740
  @ivar readd: Whether to re-add an existing node to the cluster. If
741
               this is not passed, then the operation will abort if the node
742
               name is already in the cluster; use this parameter to 'repair'
743
               a node that had its configuration broken, or was reinstalled
744
               without removal from the cluster.
745
  @type group: C{str}
746
  @ivar group: The node group to which this node will belong.
747
  @type vm_capable: C{bool}
748
  @ivar vm_capable: The vm_capable node attribute
749
  @type master_capable: C{bool}
750
  @ivar master_capable: The master_capable node attribute
751

752
  """
753
  OP_DSC_FIELD = "node_name"
754
  OP_PARAMS = [
755
    _PNodeName,
756
    ("primary_ip", None, ht.NoType, "Primary IP address"),
757
    ("secondary_ip", None, ht.TMaybeString, "Secondary IP address"),
758
    ("readd", False, ht.TBool, "Whether node is re-added to cluster"),
759
    ("group", None, ht.TMaybeString, "Initial node group"),
760
    ("master_capable", None, ht.TMaybeBool,
761
     "Whether node can become master or master candidate"),
762
    ("vm_capable", None, ht.TMaybeBool,
763
     "Whether node can host instances"),
764
    ("ndparams", None, ht.TMaybeDict, "Node parameters"),
765
    ]
766

    
767

    
768
class OpNodeQuery(OpCode):
769
  """Compute the list of nodes."""
770
  OP_PARAMS = [
771
    _POutputFields,
772
    _PUseLocking,
773
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
774
     "Empty list to query all nodes, node names otherwise"),
775
    ]
776

    
777

    
778
class OpNodeQueryvols(OpCode):
779
  """Get list of volumes on node."""
780
  OP_PARAMS = [
781
    _POutputFields,
782
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
783
     "Empty list to query all nodes, node names otherwise"),
784
    ]
785

    
786

    
787
class OpNodeQueryStorage(OpCode):
788
  """Get information on storage for node(s)."""
789
  OP_PARAMS = [
790
    _POutputFields,
791
    _PStorageType,
792
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "List of nodes"),
793
    ("name", None, ht.TMaybeString, "Storage name"),
794
    ]
795

    
796

    
797
class OpNodeModifyStorage(OpCode):
798
  """Modifies the properies of a storage unit"""
799
  OP_PARAMS = [
800
    _PNodeName,
801
    _PStorageType,
802
    _PStorageName,
803
    ("changes", ht.NoDefault, ht.TDict, "Requested changes"),
804
    ]
805

    
806

    
807
class OpRepairNodeStorage(OpCode):
808
  """Repairs the volume group on a node."""
809
  OP_DSC_FIELD = "node_name"
810
  OP_PARAMS = [
811
    _PNodeName,
812
    _PStorageType,
813
    _PStorageName,
814
    _PIgnoreConsistency,
815
    ]
816

    
817

    
818
class OpNodeSetParams(OpCode):
819
  """Change the parameters of a node."""
820
  OP_DSC_FIELD = "node_name"
821
  OP_PARAMS = [
822
    _PNodeName,
823
    _PForce,
824
    ("master_candidate", None, ht.TMaybeBool,
825
     "Whether the node should become a master candidate"),
826
    ("offline", None, ht.TMaybeBool,
827
     "Whether the node should be marked as offline"),
828
    ("drained", None, ht.TMaybeBool,
829
     "Whether the node should be marked as drained"),
830
    ("auto_promote", False, ht.TBool,
831
     "Whether node(s) should be promoted to master candidate if necessary"),
832
    ("master_capable", None, ht.TMaybeBool,
833
     "Denote whether node can become master or master candidate"),
834
    ("vm_capable", None, ht.TMaybeBool,
835
     "Denote whether node can host instances"),
836
    ("secondary_ip", None, ht.TMaybeString,
837
     "Change node's secondary IP address"),
838
    ("ndparams", None, ht.TMaybeDict, "Set node parameters"),
839
    ("powered", None, ht.TMaybeBool,
840
     "Whether the node should be marked as powered"),
841
    ]
842

    
843

    
844
class OpNodePowercycle(OpCode):
845
  """Tries to powercycle a node."""
846
  OP_DSC_FIELD = "node_name"
847
  OP_PARAMS = [
848
    _PNodeName,
849
    _PForce,
850
    ]
851

    
852

    
853
class OpNodeMigrate(OpCode):
854
  """Migrate all instances from a node."""
855
  OP_DSC_FIELD = "node_name"
856
  OP_PARAMS = [
857
    _PNodeName,
858
    _PMigrationMode,
859
    _PMigrationLive,
860
    ("iallocator", None, ht.TMaybeString,
861
     "Iallocator for deciding the target node for shared-storage instances"),
862
    ]
863

    
864

    
865
class OpNodeEvacStrategy(OpCode):
866
  """Compute the evacuation strategy for a list of nodes."""
867
  OP_DSC_FIELD = "nodes"
868
  OP_PARAMS = [
869
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), None),
870
    ("remote_node", None, ht.TMaybeString, None),
871
    ("iallocator", None, ht.TMaybeString, None),
872
    ]
873

    
874

    
875
# instance opcodes
876

    
877
class OpInstanceCreate(OpCode):
878
  """Create an instance.
879

880
  @ivar instance_name: Instance name
881
  @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES})
882
  @ivar source_handshake: Signed handshake from source (remote import only)
883
  @ivar source_x509_ca: Source X509 CA in PEM format (remote import only)
884
  @ivar source_instance_name: Previous name of instance (remote import only)
885
  @ivar source_shutdown_timeout: Shutdown timeout used for source instance
886
    (remote import only)
887

888
  """
889
  OP_DSC_FIELD = "instance_name"
890
  OP_PARAMS = [
891
    _PInstanceName,
892
    _PForceVariant,
893
    _PWaitForSync,
894
    _PNameCheck,
895
    ("beparams", ht.EmptyDict, ht.TDict, "Backend parameters for instance"),
896
    ("disks", ht.NoDefault,
897
     # TODO: Generate check from constants.IDISK_PARAMS_TYPES
898
     ht.TListOf(ht.TDictOf(ht.TElemOf(constants.IDISK_PARAMS),
899
                           ht.TOr(ht.TNonEmptyString, ht.TInt))),
900
     "Disk descriptions, for example ``[{\"%s\": 100}, {\"%s\": 5}]``;"
901
     " each disk definition must contain a ``%s`` value and"
902
     " can contain an optional ``%s`` value denoting the disk access mode"
903
     " (%s)" %
904
     (constants.IDISK_SIZE, constants.IDISK_SIZE, constants.IDISK_SIZE,
905
      constants.IDISK_MODE,
906
      " or ".join("``%s``" % i for i in sorted(constants.DISK_ACCESS_SET)))),
907
    ("disk_template", ht.NoDefault, _CheckDiskTemplate, "Disk template"),
908
    ("file_driver", None, ht.TOr(ht.TNone, ht.TElemOf(constants.FILE_DRIVER)),
909
     "Driver for file-backed disks"),
910
    ("file_storage_dir", None, ht.TMaybeString,
911
     "Directory for storing file-backed disks"),
912
    ("hvparams", ht.EmptyDict, ht.TDict,
913
     "Hypervisor parameters for instance, hypervisor-dependent"),
914
    ("hypervisor", None, ht.TMaybeString, "Hypervisor"),
915
    ("iallocator", None, ht.TMaybeString,
916
     "Iallocator for deciding which node(s) to use"),
917
    ("identify_defaults", False, ht.TBool,
918
     "Reset instance parameters to default if equal"),
919
    ("ip_check", True, ht.TBool, _PIpCheckDoc),
920
    ("mode", ht.NoDefault, ht.TElemOf(constants.INSTANCE_CREATE_MODES),
921
     "Instance creation mode"),
922
    ("nics", ht.NoDefault, ht.TListOf(_TestNicDef),
923
     "List of NIC (network interface) definitions, for example"
924
     " ``[{}, {}, {\"%s\": \"198.51.100.4\"}]``; each NIC definition can"
925
     " contain the optional values %s" %
926
     (constants.INIC_IP,
927
      ", ".join("``%s``" % i for i in sorted(constants.INIC_PARAMS)))),
928
    ("no_install", None, ht.TMaybeBool,
929
     "Do not install the OS (will disable automatic start)"),
930
    ("osparams", ht.EmptyDict, ht.TDict, "OS parameters for instance"),
931
    ("os_type", None, ht.TMaybeString, "Operating system"),
932
    ("pnode", None, ht.TMaybeString, "Primary node"),
933
    ("snode", None, ht.TMaybeString, "Secondary node"),
934
    ("source_handshake", None, ht.TOr(ht.TList, ht.TNone),
935
     "Signed handshake from source (remote import only)"),
936
    ("source_instance_name", None, ht.TMaybeString,
937
     "Source instance name (remote import only)"),
938
    ("source_shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT,
939
     ht.TPositiveInt,
940
     "How long source instance was given to shut down (remote import only)"),
941
    ("source_x509_ca", None, ht.TMaybeString,
942
     "Source X509 CA in PEM format (remote import only)"),
943
    ("src_node", None, ht.TMaybeString, "Source node for import"),
944
    ("src_path", None, ht.TMaybeString, "Source directory for import"),
945
    ("start", True, ht.TBool, "Whether to start instance after creation"),
946
    ]
947

    
948

    
949
class OpInstanceReinstall(OpCode):
950
  """Reinstall an instance's OS."""
951
  OP_DSC_FIELD = "instance_name"
952
  OP_PARAMS = [
953
    _PInstanceName,
954
    _PForceVariant,
955
    ("os_type", None, ht.TMaybeString, "Instance operating system"),
956
    ("osparams", None, ht.TMaybeDict, "Temporary OS parameters"),
957
    ]
958

    
959

    
960
class OpInstanceRemove(OpCode):
961
  """Remove an instance."""
962
  OP_DSC_FIELD = "instance_name"
963
  OP_PARAMS = [
964
    _PInstanceName,
965
    _PShutdownTimeout,
966
    ("ignore_failures", False, ht.TBool,
967
     "Whether to ignore failures during removal"),
968
    ]
969

    
970

    
971
class OpInstanceRename(OpCode):
972
  """Rename an instance."""
973
  OP_PARAMS = [
974
    _PInstanceName,
975
    _PNameCheck,
976
    ("new_name", ht.NoDefault, ht.TNonEmptyString, "New instance name"),
977
    ("ip_check", False, ht.TBool, _PIpCheckDoc),
978
    ]
979

    
980

    
981
class OpInstanceStartup(OpCode):
982
  """Startup an instance."""
983
  OP_DSC_FIELD = "instance_name"
984
  OP_PARAMS = [
985
    _PInstanceName,
986
    _PForce,
987
    _PIgnoreOfflineNodes,
988
    ("hvparams", ht.EmptyDict, ht.TDict,
989
     "Temporary hypervisor parameters, hypervisor-dependent"),
990
    ("beparams", ht.EmptyDict, ht.TDict, "Temporary backend parameters"),
991
    ]
992

    
993

    
994
class OpInstanceShutdown(OpCode):
995
  """Shutdown an instance."""
996
  OP_DSC_FIELD = "instance_name"
997
  OP_PARAMS = [
998
    _PInstanceName,
999
    _PIgnoreOfflineNodes,
1000
    ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TPositiveInt,
1001
     "How long to wait for instance to shut down"),
1002
    ]
1003

    
1004

    
1005
class OpInstanceReboot(OpCode):
1006
  """Reboot an instance."""
1007
  OP_DSC_FIELD = "instance_name"
1008
  OP_PARAMS = [
1009
    _PInstanceName,
1010
    _PShutdownTimeout,
1011
    ("ignore_secondaries", False, ht.TBool,
1012
     "Whether to start the instance even if secondary disks are failing"),
1013
    ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES),
1014
     "How to reboot instance"),
1015
    ]
1016

    
1017

    
1018
class OpInstanceReplaceDisks(OpCode):
1019
  """Replace the disks of an instance."""
1020
  OP_DSC_FIELD = "instance_name"
1021
  OP_PARAMS = [
1022
    _PInstanceName,
1023
    ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES),
1024
     "Replacement mode"),
1025
    ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt),
1026
     "Disk indexes"),
1027
    ("remote_node", None, ht.TMaybeString, "New secondary node"),
1028
    ("iallocator", None, ht.TMaybeString,
1029
     "Iallocator for deciding new secondary node"),
1030
    ("early_release", False, ht.TBool,
1031
     "Whether to release locks as soon as possible"),
1032
    ]
1033

    
1034

    
1035
class OpInstanceFailover(OpCode):
1036
  """Failover an instance."""
1037
  OP_DSC_FIELD = "instance_name"
1038
  OP_PARAMS = [
1039
    _PInstanceName,
1040
    _PShutdownTimeout,
1041
    _PIgnoreConsistency,
1042
    ("iallocator", None, ht.TMaybeString,
1043
     "Iallocator for deciding the target node for shared-storage instances"),
1044
    ("target_node", None, ht.TMaybeString,
1045
     "Target node for shared-storage instances"),
1046
    ]
1047

    
1048

    
1049
class OpInstanceMigrate(OpCode):
1050
  """Migrate an instance.
1051

1052
  This migrates (without shutting down an instance) to its secondary
1053
  node.
1054

1055
  @ivar instance_name: the name of the instance
1056
  @ivar mode: the migration mode (live, non-live or None for auto)
1057

1058
  """
1059
  OP_DSC_FIELD = "instance_name"
1060
  OP_PARAMS = [
1061
    _PInstanceName,
1062
    _PMigrationMode,
1063
    _PMigrationLive,
1064
    ("cleanup", False, ht.TBool,
1065
     "Whether a previously failed migration should be cleaned up"),
1066
    ("iallocator", None, ht.TMaybeString,
1067
     "Iallocator for deciding the target node for shared-storage instances"),
1068
    ("target_node", None, ht.TMaybeString,
1069
     "Target node for shared-storage instances"),
1070
    ("allow_failover", False, ht.TBool,
1071
     "Whether we can fallback to failover if migration is not possible"),
1072
    ]
1073

    
1074

    
1075
class OpInstanceMove(OpCode):
1076
  """Move an instance.
1077

1078
  This move (with shutting down an instance and data copying) to an
1079
  arbitrary node.
1080

1081
  @ivar instance_name: the name of the instance
1082
  @ivar target_node: the destination node
1083

1084
  """
1085
  OP_DSC_FIELD = "instance_name"
1086
  OP_PARAMS = [
1087
    _PInstanceName,
1088
    _PShutdownTimeout,
1089
    ("target_node", ht.NoDefault, ht.TNonEmptyString, "Target node"),
1090
    ]
1091

    
1092

    
1093
class OpInstanceConsole(OpCode):
1094
  """Connect to an instance's console."""
1095
  OP_DSC_FIELD = "instance_name"
1096
  OP_PARAMS = [
1097
    _PInstanceName
1098
    ]
1099

    
1100

    
1101
class OpInstanceActivateDisks(OpCode):
1102
  """Activate an instance's disks."""
1103
  OP_DSC_FIELD = "instance_name"
1104
  OP_PARAMS = [
1105
    _PInstanceName,
1106
    ("ignore_size", False, ht.TBool, "Whether to ignore recorded size"),
1107
    ]
1108

    
1109

    
1110
class OpInstanceDeactivateDisks(OpCode):
1111
  """Deactivate an instance's disks."""
1112
  OP_DSC_FIELD = "instance_name"
1113
  OP_PARAMS = [
1114
    _PInstanceName,
1115
    _PForce,
1116
    ]
1117

    
1118

    
1119
class OpInstanceRecreateDisks(OpCode):
1120
  """Deactivate an instance's disks."""
1121
  OP_DSC_FIELD = "instance_name"
1122
  OP_PARAMS = [
1123
    _PInstanceName,
1124
    ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt),
1125
     "List of disk indexes"),
1126
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1127
     "New instance nodes, if relocation is desired"),
1128
    ]
1129

    
1130

    
1131
class OpInstanceQuery(OpCode):
1132
  """Compute the list of instances."""
1133
  OP_PARAMS = [
1134
    _POutputFields,
1135
    _PUseLocking,
1136
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1137
     "Empty list to query all instances, instance names otherwise"),
1138
    ]
1139

    
1140

    
1141
class OpInstanceQueryData(OpCode):
1142
  """Compute the run-time status of instances."""
1143
  OP_PARAMS = [
1144
    _PUseLocking,
1145
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1146
     "Instance names"),
1147
    ("static", False, ht.TBool,
1148
     "Whether to only return configuration data without querying"
1149
     " nodes"),
1150
    ]
1151

    
1152

    
1153
class OpInstanceSetParams(OpCode):
1154
  """Change the parameters of an instance."""
1155
  OP_DSC_FIELD = "instance_name"
1156
  OP_PARAMS = [
1157
    _PInstanceName,
1158
    _PForce,
1159
    _PForceVariant,
1160
    # TODO: Use _TestNicDef
1161
    ("nics", ht.EmptyList, ht.TList,
1162
     "List of NIC changes. Each item is of the form ``(op, settings)``."
1163
     " ``op`` can be ``%s`` to add a new NIC with the specified settings,"
1164
     " ``%s`` to remove the last NIC or a number to modify the settings"
1165
     " of the NIC with that index." %
1166
     (constants.DDM_ADD, constants.DDM_REMOVE)),
1167
    ("disks", ht.EmptyList, ht.TList, "List of disk changes. See ``nics``."),
1168
    ("beparams", ht.EmptyDict, ht.TDict, "Per-instance backend parameters"),
1169
    ("hvparams", ht.EmptyDict, ht.TDict,
1170
     "Per-instance hypervisor parameters, hypervisor-dependent"),
1171
    ("disk_template", None, ht.TOr(ht.TNone, _CheckDiskTemplate),
1172
     "Disk template for instance"),
1173
    ("remote_node", None, ht.TMaybeString,
1174
     "Secondary node (used when changing disk template)"),
1175
    ("os_name", None, ht.TMaybeString,
1176
     "Change instance's OS name. Does not reinstall the instance."),
1177
    ("osparams", None, ht.TMaybeDict, "Per-instance OS parameters"),
1178
    ("wait_for_sync", True, ht.TBool,
1179
     "Whether to wait for the disk to synchronize, when changing template"),
1180
    ]
1181

    
1182

    
1183
class OpInstanceGrowDisk(OpCode):
1184
  """Grow a disk of an instance."""
1185
  OP_DSC_FIELD = "instance_name"
1186
  OP_PARAMS = [
1187
    _PInstanceName,
1188
    _PWaitForSync,
1189
    ("disk", ht.NoDefault, ht.TInt, "Disk index"),
1190
    ("amount", ht.NoDefault, ht.TInt,
1191
     "Amount of disk space to add (megabytes)"),
1192
    ]
1193

    
1194

    
1195
# Node group opcodes
1196

    
1197
class OpGroupAdd(OpCode):
1198
  """Add a node group to the cluster."""
1199
  OP_DSC_FIELD = "group_name"
1200
  OP_PARAMS = [
1201
    _PGroupName,
1202
    _PNodeGroupAllocPolicy,
1203
    _PGroupNodeParams,
1204
    ]
1205

    
1206

    
1207
class OpGroupAssignNodes(OpCode):
1208
  """Assign nodes to a node group."""
1209
  OP_DSC_FIELD = "group_name"
1210
  OP_PARAMS = [
1211
    _PGroupName,
1212
    _PForce,
1213
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
1214
     "List of nodes to assign"),
1215
    ]
1216

    
1217

    
1218
class OpGroupQuery(OpCode):
1219
  """Compute the list of node groups."""
1220
  OP_PARAMS = [
1221
    _POutputFields,
1222
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1223
     "Empty list to query all groups, group names otherwise"),
1224
    ]
1225

    
1226

    
1227
class OpGroupSetParams(OpCode):
1228
  """Change the parameters of a node group."""
1229
  OP_DSC_FIELD = "group_name"
1230
  OP_PARAMS = [
1231
    _PGroupName,
1232
    _PNodeGroupAllocPolicy,
1233
    _PGroupNodeParams,
1234
    ]
1235

    
1236

    
1237
class OpGroupRemove(OpCode):
1238
  """Remove a node group from the cluster."""
1239
  OP_DSC_FIELD = "group_name"
1240
  OP_PARAMS = [
1241
    _PGroupName,
1242
    ]
1243

    
1244

    
1245
class OpGroupRename(OpCode):
1246
  """Rename a node group in the cluster."""
1247
  OP_PARAMS = [
1248
    _PGroupName,
1249
    ("new_name", ht.NoDefault, ht.TNonEmptyString, "New group name"),
1250
    ]
1251

    
1252

    
1253
# OS opcodes
1254
class OpOsDiagnose(OpCode):
1255
  """Compute the list of guest operating systems."""
1256
  OP_PARAMS = [
1257
    _POutputFields,
1258
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1259
     "Which operating systems to diagnose"),
1260
    ]
1261

    
1262

    
1263
# Exports opcodes
1264
class OpBackupQuery(OpCode):
1265
  """Compute the list of exported images."""
1266
  OP_PARAMS = [
1267
    _PUseLocking,
1268
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1269
     "Empty list to query all nodes, node names otherwise"),
1270
    ]
1271

    
1272

    
1273
class OpBackupPrepare(OpCode):
1274
  """Prepares an instance export.
1275

1276
  @ivar instance_name: Instance name
1277
  @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1278

1279
  """
1280
  OP_DSC_FIELD = "instance_name"
1281
  OP_PARAMS = [
1282
    _PInstanceName,
1283
    ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES),
1284
     "Export mode"),
1285
    ]
1286

    
1287

    
1288
class OpBackupExport(OpCode):
1289
  """Export an instance.
1290

1291
  For local exports, the export destination is the node name. For remote
1292
  exports, the export destination is a list of tuples, each consisting of
1293
  hostname/IP address, port, HMAC and HMAC salt. The HMAC is calculated using
1294
  the cluster domain secret over the value "${index}:${hostname}:${port}". The
1295
  destination X509 CA must be a signed certificate.
1296

1297
  @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1298
  @ivar target_node: Export destination
1299
  @ivar x509_key_name: X509 key to use (remote export only)
1300
  @ivar destination_x509_ca: Destination X509 CA in PEM format (remote export
1301
                             only)
1302

1303
  """
1304
  OP_DSC_FIELD = "instance_name"
1305
  OP_PARAMS = [
1306
    _PInstanceName,
1307
    _PShutdownTimeout,
1308
    # TODO: Rename target_node as it changes meaning for different export modes
1309
    # (e.g. "destination")
1310
    ("target_node", ht.NoDefault, ht.TOr(ht.TNonEmptyString, ht.TList),
1311
     "Destination information, depends on export mode"),
1312
    ("shutdown", True, ht.TBool, "Whether to shutdown instance before export"),
1313
    ("remove_instance", False, ht.TBool,
1314
     "Whether to remove instance after export"),
1315
    ("ignore_remove_failures", False, ht.TBool,
1316
     "Whether to ignore failures while removing instances"),
1317
    ("mode", constants.EXPORT_MODE_LOCAL, ht.TElemOf(constants.EXPORT_MODES),
1318
     "Export mode"),
1319
    ("x509_key_name", None, ht.TOr(ht.TList, ht.TNone),
1320
     "Name of X509 key (remote export only)"),
1321
    ("destination_x509_ca", None, ht.TMaybeString,
1322
     "Destination X509 CA (remote export only)"),
1323
    ]
1324

    
1325

    
1326
class OpBackupRemove(OpCode):
1327
  """Remove an instance's export."""
1328
  OP_DSC_FIELD = "instance_name"
1329
  OP_PARAMS = [
1330
    _PInstanceName,
1331
    ]
1332

    
1333

    
1334
# Tags opcodes
1335
class OpTagsGet(OpCode):
1336
  """Returns the tags of the given object."""
1337
  OP_DSC_FIELD = "name"
1338
  OP_PARAMS = [
1339
    _PTagKind,
1340
    # Name is only meaningful for nodes and instances
1341
    ("name", ht.NoDefault, ht.TMaybeString, None),
1342
    ]
1343

    
1344

    
1345
class OpTagsSearch(OpCode):
1346
  """Searches the tags in the cluster for a given pattern."""
1347
  OP_DSC_FIELD = "pattern"
1348
  OP_PARAMS = [
1349
    ("pattern", ht.NoDefault, ht.TNonEmptyString, None),
1350
    ]
1351

    
1352

    
1353
class OpTagsSet(OpCode):
1354
  """Add a list of tags on a given object."""
1355
  OP_PARAMS = [
1356
    _PTagKind,
1357
    _PTags,
1358
    # Name is only meaningful for nodes and instances
1359
    ("name", ht.NoDefault, ht.TMaybeString, None),
1360
    ]
1361

    
1362

    
1363
class OpTagsDel(OpCode):
1364
  """Remove a list of tags from a given object."""
1365
  OP_PARAMS = [
1366
    _PTagKind,
1367
    _PTags,
1368
    # Name is only meaningful for nodes and instances
1369
    ("name", ht.NoDefault, ht.TMaybeString, None),
1370
    ]
1371

    
1372
# Test opcodes
1373
class OpTestDelay(OpCode):
1374
  """Sleeps for a configured amount of time.
1375

1376
  This is used just for debugging and testing.
1377

1378
  Parameters:
1379
    - duration: the time to sleep
1380
    - on_master: if true, sleep on the master
1381
    - on_nodes: list of nodes in which to sleep
1382

1383
  If the on_master parameter is true, it will execute a sleep on the
1384
  master (before any node sleep).
1385

1386
  If the on_nodes list is not empty, it will sleep on those nodes
1387
  (after the sleep on the master, if that is enabled).
1388

1389
  As an additional feature, the case of duration < 0 will be reported
1390
  as an execution error, so this opcode can be used as a failure
1391
  generator. The case of duration == 0 will not be treated specially.
1392

1393
  """
1394
  OP_DSC_FIELD = "duration"
1395
  OP_PARAMS = [
1396
    ("duration", ht.NoDefault, ht.TFloat, None),
1397
    ("on_master", True, ht.TBool, None),
1398
    ("on_nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
1399
    ("repeat", 0, ht.TPositiveInt, None),
1400
    ]
1401

    
1402

    
1403
class OpTestAllocator(OpCode):
1404
  """Allocator framework testing.
1405

1406
  This opcode has two modes:
1407
    - gather and return allocator input for a given mode (allocate new
1408
      or replace secondary) and a given instance definition (direction
1409
      'in')
1410
    - run a selected allocator for a given operation (as above) and
1411
      return the allocator output (direction 'out')
1412

1413
  """
1414
  OP_DSC_FIELD = "allocator"
1415
  OP_PARAMS = [
1416
    ("direction", ht.NoDefault,
1417
     ht.TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS), None),
1418
    ("mode", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_MODES), None),
1419
    ("name", ht.NoDefault, ht.TNonEmptyString, None),
1420
    ("nics", ht.NoDefault, ht.TOr(ht.TNone, ht.TListOf(
1421
     ht.TDictOf(ht.TElemOf([constants.INIC_MAC, constants.INIC_IP, "bridge"]),
1422
                ht.TOr(ht.TNone, ht.TNonEmptyString)))), None),
1423
    ("disks", ht.NoDefault, ht.TOr(ht.TNone, ht.TList), None),
1424
    ("hypervisor", None, ht.TMaybeString, None),
1425
    ("allocator", None, ht.TMaybeString, None),
1426
    ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
1427
    ("mem_size", None, ht.TOr(ht.TNone, ht.TPositiveInt), None),
1428
    ("vcpus", None, ht.TOr(ht.TNone, ht.TPositiveInt), None),
1429
    ("os", None, ht.TMaybeString, None),
1430
    ("disk_template", None, ht.TMaybeString, None),
1431
    ("evac_nodes", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString)),
1432
     None),
1433
    ("instances", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString)),
1434
     None),
1435
    ("reloc_mode", None,
1436
     ht.TOr(ht.TNone, ht.TElemOf(constants.IALLOCATOR_MRELOC_MODES)), None),
1437
    ("target_groups", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString)),
1438
     None),
1439
    ]
1440

    
1441

    
1442
class OpTestJqueue(OpCode):
1443
  """Utility opcode to test some aspects of the job queue.
1444

1445
  """
1446
  OP_PARAMS = [
1447
    ("notify_waitlock", False, ht.TBool, None),
1448
    ("notify_exec", False, ht.TBool, None),
1449
    ("log_messages", ht.EmptyList, ht.TListOf(ht.TString), None),
1450
    ("fail", False, ht.TBool, None),
1451
    ]
1452

    
1453

    
1454
class OpTestDummy(OpCode):
1455
  """Utility opcode used by unittests.
1456

1457
  """
1458
  OP_PARAMS = [
1459
    ("result", ht.NoDefault, ht.NoType, None),
1460
    ("messages", ht.NoDefault, ht.NoType, None),
1461
    ("fail", ht.NoDefault, ht.NoType, None),
1462
    ("submit_jobs", None, ht.NoType, None),
1463
    ]
1464
  WITH_LU = False
1465

    
1466

    
1467
def _GetOpList():
1468
  """Returns list of all defined opcodes.
1469

1470
  Does not eliminate duplicates by C{OP_ID}.
1471

1472
  """
1473
  return [v for v in globals().values()
1474
          if (isinstance(v, type) and issubclass(v, OpCode) and
1475
              hasattr(v, "OP_ID") and v is not OpCode)]
1476

    
1477

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