Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes.py @ 65e183af

History | View | Annotate | Download (35 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010 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
from ganeti import constants
37
from ganeti import errors
38
from ganeti import ht
39

    
40

    
41
# Common opcode attributes
42

    
43
#: output fields for a query operation
44
_POutputFields = ("output_fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString))
45

    
46
#: the shutdown timeout
47
_PShutdownTimeout = ("shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT,
48
                     ht.TPositiveInt)
49

    
50
#: the force parameter
51
_PForce = ("force", False, ht.TBool)
52

    
53
#: a required instance name (for single-instance LUs)
54
_PInstanceName = ("instance_name", ht.NoDefault, ht.TNonEmptyString)
55

    
56
#: Whether to ignore offline nodes
57
_PIgnoreOfflineNodes = ("ignore_offline_nodes", False, ht.TBool)
58

    
59
#: a required node name (for single-node LUs)
60
_PNodeName = ("node_name", ht.NoDefault, ht.TNonEmptyString)
61

    
62
#: a required node group name (for single-group LUs)
63
_PGroupName = ("group_name", ht.NoDefault, ht.TNonEmptyString)
64

    
65
#: Migration type (live/non-live)
66
_PMigrationMode = ("mode", None,
67
                   ht.TOr(ht.TNone, ht.TElemOf(constants.HT_MIGRATION_MODES)))
68

    
69
#: Obsolete 'live' migration mode (boolean)
70
_PMigrationLive = ("live", None, ht.TMaybeBool)
71

    
72
#: Tag type
73
_PTagKind = ("kind", ht.NoDefault, ht.TElemOf(constants.VALID_TAG_TYPES))
74

    
75
#: List of tag strings
76
_PTags = ("tags", ht.NoDefault, ht.TListOf(ht.TNonEmptyString))
77

    
78

    
79
def RequireFileStorage():
80
  """Checks that file storage is enabled.
81

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

85
  @raise errors.OpPrereqError: when file storage is disabled
86

87
  """
88
  if not constants.ENABLE_FILE_STORAGE:
89
    raise errors.OpPrereqError("File storage disabled at configure time",
90
                               errors.ECODE_INVAL)
91

    
92

    
93
def _CheckDiskTemplate(template):
94
  """Ensure a given disk template is valid.
95

96
  """
97
  if template not in constants.DISK_TEMPLATES:
98
    # Using str.join directly to avoid importing utils for CommaJoin
99
    msg = ("Invalid disk template name '%s', valid templates are: %s" %
100
           (template, ", ".join(constants.DISK_TEMPLATES)))
101
    raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
102
  if template == constants.DT_FILE:
103
    RequireFileStorage()
104
  return True
105

    
106

    
107
def _CheckStorageType(storage_type):
108
  """Ensure a given storage type is valid.
109

110
  """
111
  if storage_type not in constants.VALID_STORAGE_TYPES:
112
    raise errors.OpPrereqError("Unknown storage type: %s" % storage_type,
113
                               errors.ECODE_INVAL)
114
  if storage_type == constants.ST_FILE:
115
    RequireFileStorage()
116
  return True
117

    
118

    
119
#: Storage type parameter
120
_PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType)
121

    
122

    
123
class _AutoOpParamSlots(type):
124
  """Meta class for opcode definitions.
125

126
  """
127
  def __new__(mcs, name, bases, attrs):
128
    """Called when a class should be created.
129

130
    @param mcs: The meta class
131
    @param name: Name of created class
132
    @param bases: Base classes
133
    @type attrs: dict
134
    @param attrs: Class attributes
135

136
    """
137
    assert "__slots__" not in attrs, \
138
      "Class '%s' defines __slots__ when it should use OP_PARAMS" % name
139
    assert "OP_ID" in attrs, "Class '%s' is missing OP_ID attribute" % name
140

    
141
    # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams
142
    params = attrs.setdefault("OP_PARAMS", [])
143

    
144
    # Use parameter names as slots
145
    slots = [pname for (pname, _, _) in params]
146

    
147
    assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
148
      "Class '%s' uses unknown field in OP_DSC_FIELD" % name
149

    
150
    attrs["__slots__"] = slots
151

    
152
    return type.__new__(mcs, name, bases, attrs)
153

    
154

    
155
class BaseOpCode(object):
156
  """A simple serializable object.
157

158
  This object serves as a parent class for OpCode without any custom
159
  field handling.
160

161
  """
162
  __metaclass__ = _AutoOpParamSlots
163

    
164
  OP_ID = None
165

    
166
  def __init__(self, **kwargs):
167
    """Constructor for BaseOpCode.
168

169
    The constructor takes only keyword arguments and will set
170
    attributes on this object based on the passed arguments. As such,
171
    it means that you should not pass arguments which are not in the
172
    __slots__ attribute for this class.
173

174
    """
175
    slots = self._all_slots()
176
    for key in kwargs:
177
      if key not in slots:
178
        raise TypeError("Object %s doesn't support the parameter '%s'" %
179
                        (self.__class__.__name__, key))
180
      setattr(self, key, kwargs[key])
181

    
182
  def __getstate__(self):
183
    """Generic serializer.
184

185
    This method just returns the contents of the instance as a
186
    dictionary.
187

188
    @rtype:  C{dict}
189
    @return: the instance attributes and their values
190

191
    """
192
    state = {}
193
    for name in self._all_slots():
194
      if hasattr(self, name):
195
        state[name] = getattr(self, name)
196
    return state
197

    
198
  def __setstate__(self, state):
199
    """Generic unserializer.
200

201
    This method just restores from the serialized state the attributes
202
    of the current instance.
203

204
    @param state: the serialized opcode data
205
    @type state:  C{dict}
206

207
    """
208
    if not isinstance(state, dict):
209
      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
210
                       type(state))
211

    
212
    for name in self._all_slots():
213
      if name not in state and hasattr(self, name):
214
        delattr(self, name)
215

    
216
    for name in state:
217
      setattr(self, name, state[name])
218

    
219
  @classmethod
220
  def _all_slots(cls):
221
    """Compute the list of all declared slots for a class.
222

223
    """
224
    slots = []
225
    for parent in cls.__mro__:
226
      slots.extend(getattr(parent, "__slots__", []))
227
    return slots
228

    
229
  @classmethod
230
  def GetAllParams(cls):
231
    """Compute list of all parameters for an opcode.
232

233
    """
234
    slots = []
235
    for parent in cls.__mro__:
236
      slots.extend(getattr(parent, "OP_PARAMS", []))
237
    return slots
238

    
239

    
240
class OpCode(BaseOpCode):
241
  """Abstract OpCode.
242

243
  This is the root of the actual OpCode hierarchy. All clases derived
244
  from this class should override OP_ID.
245

246
  @cvar OP_ID: The ID of this opcode. This should be unique amongst all
247
               children of this class.
248
  @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
249
                      string returned by Summary(); see the docstring of that
250
                      method for details).
251
  @cvar OP_PARAMS: List of opcode attributes, the default values they should
252
                   get if not already defined, and types they must match.
253
  @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
254
                 the check steps
255
  @ivar priority: Opcode priority for queue
256

257
  """
258
  OP_ID = "OP_ABSTRACT"
259
  OP_PARAMS = [
260
    ("dry_run", None, ht.TMaybeBool),
261
    ("debug_level", None, ht.TOr(ht.TNone, ht.TPositiveInt)),
262
    ("priority", constants.OP_PRIO_DEFAULT,
263
     ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID)),
264
    ]
265

    
266
  def __getstate__(self):
267
    """Specialized getstate for opcodes.
268

269
    This method adds to the state dictionary the OP_ID of the class,
270
    so that on unload we can identify the correct class for
271
    instantiating the opcode.
272

273
    @rtype:   C{dict}
274
    @return:  the state as a dictionary
275

276
    """
277
    data = BaseOpCode.__getstate__(self)
278
    data["OP_ID"] = self.OP_ID
279
    return data
280

    
281
  @classmethod
282
  def LoadOpCode(cls, data):
283
    """Generic load opcode method.
284

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

289
    @type data:  C{dict}
290
    @param data: the serialized opcode
291

292
    """
293
    if not isinstance(data, dict):
294
      raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
295
    if "OP_ID" not in data:
296
      raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
297
    op_id = data["OP_ID"]
298
    op_class = None
299
    if op_id in OP_MAPPING:
300
      op_class = OP_MAPPING[op_id]
301
    else:
302
      raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
303
                       op_id)
304
    op = op_class()
305
    new_data = data.copy()
306
    del new_data["OP_ID"]
307
    op.__setstate__(new_data)
308
    return op
309

    
310
  def Summary(self):
311
    """Generates a summary description of this opcode.
312

313
    The summary is the value of the OP_ID attribute (without the "OP_" prefix),
314
    plus the value of the OP_DSC_FIELD attribute, if one was defined; this field
315
    should allow to easily identify the operation (for an instance creation job,
316
    e.g., it would be the instance name).
317

318
    """
319
    # all OP_ID start with OP_, we remove that
320
    txt = self.OP_ID[3:]
321
    field_name = getattr(self, "OP_DSC_FIELD", None)
322
    if field_name:
323
      field_value = getattr(self, field_name, None)
324
      if isinstance(field_value, (list, tuple)):
325
        field_value = ",".join(str(i) for i in field_value)
326
      txt = "%s(%s)" % (txt, field_value)
327
    return txt
328

    
329

    
330
# cluster opcodes
331

    
332
class OpPostInitCluster(OpCode):
333
  """Post cluster initialization.
334

335
  This opcode does not touch the cluster at all. Its purpose is to run hooks
336
  after the cluster has been initialized.
337

338
  """
339
  OP_ID = "OP_CLUSTER_POST_INIT"
340

    
341

    
342
class OpDestroyCluster(OpCode):
343
  """Destroy the cluster.
344

345
  This opcode has no other parameters. All the state is irreversibly
346
  lost after the execution of this opcode.
347

348
  """
349
  OP_ID = "OP_CLUSTER_DESTROY"
350

    
351

    
352
class OpQueryClusterInfo(OpCode):
353
  """Query cluster information."""
354
  OP_ID = "OP_CLUSTER_QUERY"
355

    
356

    
357
class OpVerifyCluster(OpCode):
358
  """Verify the cluster state.
359

360
  @type skip_checks: C{list}
361
  @ivar skip_checks: steps to be skipped from the verify process; this
362
                     needs to be a subset of
363
                     L{constants.VERIFY_OPTIONAL_CHECKS}; currently
364
                     only L{constants.VERIFY_NPLUSONE_MEM} can be passed
365

366
  """
367
  OP_ID = "OP_CLUSTER_VERIFY"
368
  OP_PARAMS = [
369
    ("skip_checks", ht.EmptyList,
370
     ht.TListOf(ht.TElemOf(constants.VERIFY_OPTIONAL_CHECKS))),
371
    ("verbose", False, ht.TBool),
372
    ("error_codes", False, ht.TBool),
373
    ("debug_simulate_errors", False, ht.TBool),
374
    ]
375

    
376

    
377
class OpVerifyDisks(OpCode):
378
  """Verify the cluster disks.
379

380
  Parameters: none
381

382
  Result: a tuple of four elements:
383
    - list of node names with bad data returned (unreachable, etc.)
384
    - dict of node names with broken volume groups (values: error msg)
385
    - list of instances with degraded disks (that should be activated)
386
    - dict of instances with missing logical volumes (values: (node, vol)
387
      pairs with details about the missing volumes)
388

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

394
  Note that only instances that are drbd-based are taken into
395
  consideration. This might need to be revisited in the future.
396

397
  """
398
  OP_ID = "OP_CLUSTER_VERIFY_DISKS"
399

    
400

    
401
class OpRepairDiskSizes(OpCode):
402
  """Verify the disk sizes of the instances and fixes configuration
403
  mimatches.
404

405
  Parameters: optional instances list, in case we want to restrict the
406
  checks to only a subset of the instances.
407

408
  Result: a list of tuples, (instance, disk, new-size) for changed
409
  configurations.
410

411
  In normal operation, the list should be empty.
412

413
  @type instances: list
414
  @ivar instances: the list of instances to check, or empty for all instances
415

416
  """
417
  OP_ID = "OP_CLUSTER_REPAIR_DISK_SIZES"
418
  OP_PARAMS = [
419
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
420
    ]
421

    
422

    
423
class OpQueryConfigValues(OpCode):
424
  """Query cluster configuration values."""
425
  OP_ID = "OP_CLUSTER_CONFIG_QUERY"
426
  OP_PARAMS = [
427
    _POutputFields
428
    ]
429

    
430

    
431
class OpRenameCluster(OpCode):
432
  """Rename the cluster.
433

434
  @type name: C{str}
435
  @ivar name: The new name of the cluster. The name and/or the master IP
436
              address will be changed to match the new name and its IP
437
              address.
438

439
  """
440
  OP_ID = "OP_CLUSTER_RENAME"
441
  OP_DSC_FIELD = "name"
442
  OP_PARAMS = [
443
    ("name", ht.NoDefault, ht.TNonEmptyString),
444
    ]
445

    
446

    
447
class OpSetClusterParams(OpCode):
448
  """Change the parameters of the cluster.
449

450
  @type vg_name: C{str} or C{None}
451
  @ivar vg_name: The new volume group name or None to disable LVM usage.
452

453
  """
454
  OP_ID = "OP_CLUSTER_SET_PARAMS"
455
  OP_PARAMS = [
456
    ("vg_name", None, ht.TMaybeString),
457
    ("enabled_hypervisors", None,
458
     ht.TOr(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)), ht.TTrue),
459
            ht.TNone)),
460
    ("hvparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
461
                              ht.TNone)),
462
    ("beparams", None, ht.TOr(ht.TDict, ht.TNone)),
463
    ("os_hvp", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
464
                            ht.TNone)),
465
    ("osparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
466
                              ht.TNone)),
467
    ("candidate_pool_size", None, ht.TOr(ht.TStrictPositiveInt, ht.TNone)),
468
    ("uid_pool", None, ht.NoType),
469
    ("add_uids", None, ht.NoType),
470
    ("remove_uids", None, ht.NoType),
471
    ("maintain_node_health", None, ht.TMaybeBool),
472
    ("prealloc_wipe_disks", None, ht.TMaybeBool),
473
    ("nicparams", None, ht.TOr(ht.TDict, ht.TNone)),
474
    ("ndparams", None, ht.TOr(ht.TDict, ht.TNone)),
475
    ("drbd_helper", None, ht.TOr(ht.TString, ht.TNone)),
476
    ("default_iallocator", None, ht.TOr(ht.TString, ht.TNone)),
477
    ("master_netdev", None, ht.TOr(ht.TString, ht.TNone)),
478
    ("reserved_lvs", None, ht.TOr(ht.TListOf(ht.TNonEmptyString), ht.TNone)),
479
    ("hidden_os", None, ht.TOr(ht.TListOf(
480
          ht.TAnd(ht.TList,
481
                ht.TIsLength(2),
482
                ht.TMap(lambda v: v[0], ht.TElemOf(constants.DDMS_VALUES)))),
483
          ht.TNone)),
484
    ("blacklisted_os", None, ht.TOr(ht.TListOf(
485
          ht.TAnd(ht.TList,
486
                ht.TIsLength(2),
487
                ht.TMap(lambda v: v[0], ht.TElemOf(constants.DDMS_VALUES)))),
488
          ht.TNone)),
489
    ]
490

    
491

    
492
class OpRedistributeConfig(OpCode):
493
  """Force a full push of the cluster configuration.
494

495
  """
496
  OP_ID = "OP_CLUSTER_REDIST_CONF"
497

    
498

    
499
class OpQuery(OpCode):
500
  """Query for resources/items.
501

502
  @ivar what: Resources to query for, must be one of L{constants.QR_OP_QUERY}
503
  @ivar fields: List of fields to retrieve
504
  @ivar filter: Query filter
505

506
  """
507
  OP_ID = "OP_QUERY"
508
  OP_PARAMS = [
509
    ("what", ht.NoDefault, ht.TElemOf(constants.QR_OP_QUERY)),
510
    ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
511
    ("filter", None, ht.TOr(ht.TNone,
512
                            ht.TListOf(ht.TOr(ht.TNonEmptyString, ht.TList)))),
513
    ]
514

    
515

    
516
class OpQueryFields(OpCode):
517
  """Query for available resource/item fields.
518

519
  @ivar what: Resources to query for, must be one of L{constants.QR_OP_QUERY}
520
  @ivar fields: List of fields to retrieve
521

522
  """
523
  OP_ID = "OP_QUERY_FIELDS"
524
  OP_PARAMS = [
525
    ("what", ht.NoDefault, ht.TElemOf(constants.QR_OP_QUERY)),
526
    ("fields", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString))),
527
    ]
528

    
529

    
530
class OpOobCommand(OpCode):
531
  """Interact with OOB."""
532
  OP_ID = "OP_OOB_COMMAND"
533
  OP_PARAMS = [
534
    _PNodeName,
535
    ("command", None, ht.TElemOf(constants.OOB_COMMANDS)),
536
    ("timeout", constants.OOB_TIMEOUT, ht.TInt),
537
    ]
538

    
539

    
540
# node opcodes
541

    
542
class OpRemoveNode(OpCode):
543
  """Remove a node.
544

545
  @type node_name: C{str}
546
  @ivar node_name: The name of the node to remove. If the node still has
547
                   instances on it, the operation will fail.
548

549
  """
550
  OP_ID = "OP_NODE_REMOVE"
551
  OP_DSC_FIELD = "node_name"
552
  OP_PARAMS = [
553
    _PNodeName,
554
    ]
555

    
556

    
557
class OpAddNode(OpCode):
558
  """Add a node to the cluster.
559

560
  @type node_name: C{str}
561
  @ivar node_name: The name of the node to add. This can be a short name,
562
                   but it will be expanded to the FQDN.
563
  @type primary_ip: IP address
564
  @ivar primary_ip: The primary IP of the node. This will be ignored when the
565
                    opcode is submitted, but will be filled during the node
566
                    add (so it will be visible in the job query).
567
  @type secondary_ip: IP address
568
  @ivar secondary_ip: The secondary IP of the node. This needs to be passed
569
                      if the cluster has been initialized in 'dual-network'
570
                      mode, otherwise it must not be given.
571
  @type readd: C{bool}
572
  @ivar readd: Whether to re-add an existing node to the cluster. If
573
               this is not passed, then the operation will abort if the node
574
               name is already in the cluster; use this parameter to 'repair'
575
               a node that had its configuration broken, or was reinstalled
576
               without removal from the cluster.
577
  @type group: C{str}
578
  @ivar group: The node group to which this node will belong.
579
  @type vm_capable: C{bool}
580
  @ivar vm_capable: The vm_capable node attribute
581
  @type master_capable: C{bool}
582
  @ivar master_capable: The master_capable node attribute
583

584
  """
585
  OP_ID = "OP_NODE_ADD"
586
  OP_DSC_FIELD = "node_name"
587
  OP_PARAMS = [
588
    _PNodeName,
589
    ("primary_ip", None, ht.NoType),
590
    ("secondary_ip", None, ht.TMaybeString),
591
    ("readd", False, ht.TBool),
592
    ("group", None, ht.TMaybeString),
593
    ("master_capable", None, ht.TMaybeBool),
594
    ("vm_capable", None, ht.TMaybeBool),
595
    ("ndparams", None, ht.TOr(ht.TDict, ht.TNone)),
596
    ]
597

    
598

    
599
class OpQueryNodes(OpCode):
600
  """Compute the list of nodes."""
601
  OP_ID = "OP_NODE_QUERY"
602
  OP_PARAMS = [
603
    _POutputFields,
604
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
605
    ("use_locking", False, ht.TBool),
606
    ]
607

    
608

    
609
class OpQueryNodeVolumes(OpCode):
610
  """Get list of volumes on node."""
611
  OP_ID = "OP_NODE_QUERYVOLS"
612
  OP_PARAMS = [
613
    _POutputFields,
614
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
615
    ]
616

    
617

    
618
class OpQueryNodeStorage(OpCode):
619
  """Get information on storage for node(s)."""
620
  OP_ID = "OP_NODE_QUERY_STORAGE"
621
  OP_PARAMS = [
622
    _POutputFields,
623
    _PStorageType,
624
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
625
    ("name", None, ht.TMaybeString),
626
    ]
627

    
628

    
629
class OpModifyNodeStorage(OpCode):
630
  """Modifies the properies of a storage unit"""
631
  OP_ID = "OP_NODE_MODIFY_STORAGE"
632
  OP_PARAMS = [
633
    _PNodeName,
634
    _PStorageType,
635
    ("name", ht.NoDefault, ht.TNonEmptyString),
636
    ("changes", ht.NoDefault, ht.TDict),
637
    ]
638

    
639

    
640
class OpRepairNodeStorage(OpCode):
641
  """Repairs the volume group on a node."""
642
  OP_ID = "OP_REPAIR_NODE_STORAGE"
643
  OP_DSC_FIELD = "node_name"
644
  OP_PARAMS = [
645
    _PNodeName,
646
    _PStorageType,
647
    ("name", ht.NoDefault, ht.TNonEmptyString),
648
    ("ignore_consistency", False, ht.TBool),
649
    ]
650

    
651

    
652
class OpSetNodeParams(OpCode):
653
  """Change the parameters of a node."""
654
  OP_ID = "OP_NODE_SET_PARAMS"
655
  OP_DSC_FIELD = "node_name"
656
  OP_PARAMS = [
657
    _PNodeName,
658
    _PForce,
659
    ("master_candidate", None, ht.TMaybeBool),
660
    ("offline", None, ht.TMaybeBool),
661
    ("drained", None, ht.TMaybeBool),
662
    ("auto_promote", False, ht.TBool),
663
    ("master_capable", None, ht.TMaybeBool),
664
    ("vm_capable", None, ht.TMaybeBool),
665
    ("secondary_ip", None, ht.TMaybeString),
666
    ("ndparams", None, ht.TOr(ht.TDict, ht.TNone)),
667
    ("powered", None, ht.TMaybeBool),
668
    ]
669

    
670

    
671
class OpPowercycleNode(OpCode):
672
  """Tries to powercycle a node."""
673
  OP_ID = "OP_NODE_POWERCYCLE"
674
  OP_DSC_FIELD = "node_name"
675
  OP_PARAMS = [
676
    _PNodeName,
677
    _PForce,
678
    ]
679

    
680

    
681
class OpMigrateNode(OpCode):
682
  """Migrate all instances from a node."""
683
  OP_ID = "OP_NODE_MIGRATE"
684
  OP_DSC_FIELD = "node_name"
685
  OP_PARAMS = [
686
    _PNodeName,
687
    _PMigrationMode,
688
    _PMigrationLive,
689
    ]
690

    
691

    
692
class OpNodeEvacuationStrategy(OpCode):
693
  """Compute the evacuation strategy for a list of nodes."""
694
  OP_ID = "OP_NODE_EVAC_STRATEGY"
695
  OP_DSC_FIELD = "nodes"
696
  OP_PARAMS = [
697
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
698
    ("remote_node", None, ht.TMaybeString),
699
    ("iallocator", None, ht.TMaybeString),
700
    ]
701

    
702

    
703
# instance opcodes
704

    
705
class OpCreateInstance(OpCode):
706
  """Create an instance.
707

708
  @ivar instance_name: Instance name
709
  @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES})
710
  @ivar source_handshake: Signed handshake from source (remote import only)
711
  @ivar source_x509_ca: Source X509 CA in PEM format (remote import only)
712
  @ivar source_instance_name: Previous name of instance (remote import only)
713
  @ivar source_shutdown_timeout: Shutdown timeout used for source instance
714
    (remote import only)
715

716
  """
717
  OP_ID = "OP_INSTANCE_CREATE"
718
  OP_DSC_FIELD = "instance_name"
719
  OP_PARAMS = [
720
    _PInstanceName,
721
    ("beparams", ht.EmptyDict, ht.TDict),
722
    ("disks", ht.NoDefault, ht.TListOf(ht.TDict)),
723
    ("disk_template", ht.NoDefault, _CheckDiskTemplate),
724
    ("file_driver", None, ht.TOr(ht.TNone, ht.TElemOf(constants.FILE_DRIVER))),
725
    ("file_storage_dir", None, ht.TMaybeString),
726
    ("force_variant", False, ht.TBool),
727
    ("hvparams", ht.EmptyDict, ht.TDict),
728
    ("hypervisor", None, ht.TMaybeString),
729
    ("iallocator", None, ht.TMaybeString),
730
    ("identify_defaults", False, ht.TBool),
731
    ("ip_check", True, ht.TBool),
732
    ("mode", ht.NoDefault, ht.TElemOf(constants.INSTANCE_CREATE_MODES)),
733
    ("name_check", True, ht.TBool),
734
    ("nics", ht.NoDefault, ht.TListOf(ht.TDict)),
735
    ("no_install", None, ht.TMaybeBool),
736
    ("osparams", ht.EmptyDict, ht.TDict),
737
    ("os_type", None, ht.TMaybeString),
738
    ("pnode", None, ht.TMaybeString),
739
    ("snode", None, ht.TMaybeString),
740
    ("source_handshake", None, ht.TOr(ht.TList, ht.TNone)),
741
    ("source_instance_name", None, ht.TMaybeString),
742
    ("source_shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT,
743
     ht.TPositiveInt),
744
    ("source_x509_ca", None, ht.TMaybeString),
745
    ("src_node", None, ht.TMaybeString),
746
    ("src_path", None, ht.TMaybeString),
747
    ("start", True, ht.TBool),
748
    ("wait_for_sync", True, ht.TBool),
749
    ]
750

    
751

    
752
class OpReinstallInstance(OpCode):
753
  """Reinstall an instance's OS."""
754
  OP_ID = "OP_INSTANCE_REINSTALL"
755
  OP_DSC_FIELD = "instance_name"
756
  OP_PARAMS = [
757
    _PInstanceName,
758
    ("os_type", None, ht.TMaybeString),
759
    ("force_variant", False, ht.TBool),
760
    ("osparams", None, ht.TOr(ht.TDict, ht.TNone)),
761
    ]
762

    
763

    
764
class OpRemoveInstance(OpCode):
765
  """Remove an instance."""
766
  OP_ID = "OP_INSTANCE_REMOVE"
767
  OP_DSC_FIELD = "instance_name"
768
  OP_PARAMS = [
769
    _PInstanceName,
770
    _PShutdownTimeout,
771
    ("ignore_failures", False, ht.TBool),
772
    ]
773

    
774

    
775
class OpRenameInstance(OpCode):
776
  """Rename an instance."""
777
  OP_ID = "OP_INSTANCE_RENAME"
778
  OP_PARAMS = [
779
    _PInstanceName,
780
    ("new_name", ht.NoDefault, ht.TNonEmptyString),
781
    ("ip_check", False, ht.TBool),
782
    ("name_check", True, ht.TBool),
783
    ]
784

    
785

    
786
class OpStartupInstance(OpCode):
787
  """Startup an instance."""
788
  OP_ID = "OP_INSTANCE_STARTUP"
789
  OP_DSC_FIELD = "instance_name"
790
  OP_PARAMS = [
791
    _PInstanceName,
792
    _PForce,
793
    _PIgnoreOfflineNodes,
794
    ("hvparams", ht.EmptyDict, ht.TDict),
795
    ("beparams", ht.EmptyDict, ht.TDict),
796
    ]
797

    
798

    
799
class OpShutdownInstance(OpCode):
800
  """Shutdown an instance."""
801
  OP_ID = "OP_INSTANCE_SHUTDOWN"
802
  OP_DSC_FIELD = "instance_name"
803
  OP_PARAMS = [
804
    _PInstanceName,
805
    _PIgnoreOfflineNodes,
806
    ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TPositiveInt),
807
    ]
808

    
809

    
810
class OpRebootInstance(OpCode):
811
  """Reboot an instance."""
812
  OP_ID = "OP_INSTANCE_REBOOT"
813
  OP_DSC_FIELD = "instance_name"
814
  OP_PARAMS = [
815
    _PInstanceName,
816
    _PShutdownTimeout,
817
    ("ignore_secondaries", False, ht.TBool),
818
    ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES)),
819
    ]
820

    
821

    
822
class OpReplaceDisks(OpCode):
823
  """Replace the disks of an instance."""
824
  OP_ID = "OP_INSTANCE_REPLACE_DISKS"
825
  OP_DSC_FIELD = "instance_name"
826
  OP_PARAMS = [
827
    _PInstanceName,
828
    ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES)),
829
    ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt)),
830
    ("remote_node", None, ht.TMaybeString),
831
    ("iallocator", None, ht.TMaybeString),
832
    ("early_release", False, ht.TBool),
833
    ]
834

    
835

    
836
class OpFailoverInstance(OpCode):
837
  """Failover an instance."""
838
  OP_ID = "OP_INSTANCE_FAILOVER"
839
  OP_DSC_FIELD = "instance_name"
840
  OP_PARAMS = [
841
    _PInstanceName,
842
    _PShutdownTimeout,
843
    ("ignore_consistency", False, ht.TBool),
844
    ]
845

    
846

    
847
class OpMigrateInstance(OpCode):
848
  """Migrate an instance.
849

850
  This migrates (without shutting down an instance) to its secondary
851
  node.
852

853
  @ivar instance_name: the name of the instance
854
  @ivar mode: the migration mode (live, non-live or None for auto)
855

856
  """
857
  OP_ID = "OP_INSTANCE_MIGRATE"
858
  OP_DSC_FIELD = "instance_name"
859
  OP_PARAMS = [
860
    _PInstanceName,
861
    _PMigrationMode,
862
    _PMigrationLive,
863
    ("cleanup", False, ht.TBool),
864
    ]
865

    
866

    
867
class OpMoveInstance(OpCode):
868
  """Move an instance.
869

870
  This move (with shutting down an instance and data copying) to an
871
  arbitrary node.
872

873
  @ivar instance_name: the name of the instance
874
  @ivar target_node: the destination node
875

876
  """
877
  OP_ID = "OP_INSTANCE_MOVE"
878
  OP_DSC_FIELD = "instance_name"
879
  OP_PARAMS = [
880
    _PInstanceName,
881
    _PShutdownTimeout,
882
    ("target_node", ht.NoDefault, ht.TNonEmptyString),
883
    ]
884

    
885

    
886
class OpConnectConsole(OpCode):
887
  """Connect to an instance's console."""
888
  OP_ID = "OP_INSTANCE_CONSOLE"
889
  OP_DSC_FIELD = "instance_name"
890
  OP_PARAMS = [
891
    _PInstanceName
892
    ]
893

    
894

    
895
class OpActivateInstanceDisks(OpCode):
896
  """Activate an instance's disks."""
897
  OP_ID = "OP_INSTANCE_ACTIVATE_DISKS"
898
  OP_DSC_FIELD = "instance_name"
899
  OP_PARAMS = [
900
    _PInstanceName,
901
    ("ignore_size", False, ht.TBool),
902
    ]
903

    
904

    
905
class OpDeactivateInstanceDisks(OpCode):
906
  """Deactivate an instance's disks."""
907
  OP_ID = "OP_INSTANCE_DEACTIVATE_DISKS"
908
  OP_DSC_FIELD = "instance_name"
909
  OP_PARAMS = [
910
    _PInstanceName
911
    ]
912

    
913

    
914
class OpRecreateInstanceDisks(OpCode):
915
  """Deactivate an instance's disks."""
916
  OP_ID = "OP_INSTANCE_RECREATE_DISKS"
917
  OP_DSC_FIELD = "instance_name"
918
  OP_PARAMS = [
919
    _PInstanceName,
920
    ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt)),
921
    ]
922

    
923

    
924
class OpQueryInstances(OpCode):
925
  """Compute the list of instances."""
926
  OP_ID = "OP_INSTANCE_QUERY"
927
  OP_PARAMS = [
928
    _POutputFields,
929
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
930
    ("use_locking", False, ht.TBool),
931
    ]
932

    
933

    
934
class OpQueryInstanceData(OpCode):
935
  """Compute the run-time status of instances."""
936
  OP_ID = "OP_INSTANCE_QUERY_DATA"
937
  OP_PARAMS = [
938
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
939
    ("static", False, ht.TBool),
940
    ]
941

    
942

    
943
class OpSetInstanceParams(OpCode):
944
  """Change the parameters of an instance."""
945
  OP_ID = "OP_INSTANCE_SET_PARAMS"
946
  OP_DSC_FIELD = "instance_name"
947
  OP_PARAMS = [
948
    _PInstanceName,
949
    _PForce,
950
    ("nics", ht.EmptyList, ht.TList),
951
    ("disks", ht.EmptyList, ht.TList),
952
    ("beparams", ht.EmptyDict, ht.TDict),
953
    ("hvparams", ht.EmptyDict, ht.TDict),
954
    ("disk_template", None, _CheckDiskTemplate),
955
    ("remote_node", None, ht.TMaybeString),
956
    ("os_name", None, ht.TMaybeString),
957
    ("force_variant", False, ht.TBool),
958
    ("osparams", None, ht.TOr(ht.TDict, ht.TNone)),
959
    ]
960

    
961

    
962
class OpGrowDisk(OpCode):
963
  """Grow a disk of an instance."""
964
  OP_ID = "OP_INSTANCE_GROW_DISK"
965
  OP_DSC_FIELD = "instance_name"
966
  OP_PARAMS = [
967
    _PInstanceName,
968
    ("disk", ht.NoDefault, ht.TInt),
969
    ("amount", ht.NoDefault, ht.TInt),
970
    ("wait_for_sync", True, ht.TBool),
971
    ]
972

    
973

    
974
# Node group opcodes
975

    
976
class OpAddGroup(OpCode):
977
  """Add a node group to the cluster."""
978
  OP_ID = "OP_GROUP_ADD"
979
  OP_DSC_FIELD = "group_name"
980
  OP_PARAMS = [
981
    _PGroupName,
982
    ("ndparams", None, ht.TOr(ht.TDict, ht.TNone)),
983
    ("alloc_policy", None,
984
     ht.TOr(ht.TNone, ht.TElemOf(constants.VALID_ALLOC_POLICIES))),
985
    ]
986

    
987

    
988
class OpQueryGroups(OpCode):
989
  """Compute the list of node groups."""
990
  OP_ID = "OP_GROUP_QUERY"
991
  OP_PARAMS = [
992
    _POutputFields,
993
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
994
    ]
995

    
996

    
997
class OpSetGroupParams(OpCode):
998
  """Change the parameters of a node group."""
999
  OP_ID = "OP_GROUP_SET_PARAMS"
1000
  OP_DSC_FIELD = "group_name"
1001
  OP_PARAMS = [
1002
    _PGroupName,
1003
    ("ndparams", None, ht.TOr(ht.TDict, ht.TNone)),
1004
    ("alloc_policy", None, ht.TOr(ht.TNone,
1005
                                  ht.TElemOf(constants.VALID_ALLOC_POLICIES))),
1006
    ]
1007

    
1008

    
1009
class OpRemoveGroup(OpCode):
1010
  """Remove a node group from the cluster."""
1011
  OP_ID = "OP_GROUP_REMOVE"
1012
  OP_DSC_FIELD = "group_name"
1013
  OP_PARAMS = [
1014
    _PGroupName,
1015
    ]
1016

    
1017

    
1018
class OpRenameGroup(OpCode):
1019
  """Rename a node group in the cluster."""
1020
  OP_ID = "OP_GROUP_RENAME"
1021
  OP_DSC_FIELD = "old_name"
1022
  OP_PARAMS = [
1023
    ("old_name", ht.NoDefault, ht.TNonEmptyString),
1024
    ("new_name", ht.NoDefault, ht.TNonEmptyString),
1025
    ]
1026

    
1027

    
1028
# OS opcodes
1029
class OpDiagnoseOS(OpCode):
1030
  """Compute the list of guest operating systems."""
1031
  OP_ID = "OP_OS_DIAGNOSE"
1032
  OP_PARAMS = [
1033
    _POutputFields,
1034
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
1035
    ]
1036

    
1037

    
1038
# Exports opcodes
1039
class OpQueryExports(OpCode):
1040
  """Compute the list of exported images."""
1041
  OP_ID = "OP_BACKUP_QUERY"
1042
  OP_PARAMS = [
1043
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
1044
    ("use_locking", False, ht.TBool),
1045
    ]
1046

    
1047

    
1048
class OpPrepareExport(OpCode):
1049
  """Prepares an instance export.
1050

1051
  @ivar instance_name: Instance name
1052
  @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1053

1054
  """
1055
  OP_ID = "OP_BACKUP_PREPARE"
1056
  OP_DSC_FIELD = "instance_name"
1057
  OP_PARAMS = [
1058
    _PInstanceName,
1059
    ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES)),
1060
    ]
1061

    
1062

    
1063
class OpExportInstance(OpCode):
1064
  """Export an instance.
1065

1066
  For local exports, the export destination is the node name. For remote
1067
  exports, the export destination is a list of tuples, each consisting of
1068
  hostname/IP address, port, HMAC and HMAC salt. The HMAC is calculated using
1069
  the cluster domain secret over the value "${index}:${hostname}:${port}". The
1070
  destination X509 CA must be a signed certificate.
1071

1072
  @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1073
  @ivar target_node: Export destination
1074
  @ivar x509_key_name: X509 key to use (remote export only)
1075
  @ivar destination_x509_ca: Destination X509 CA in PEM format (remote export
1076
                             only)
1077

1078
  """
1079
  OP_ID = "OP_BACKUP_EXPORT"
1080
  OP_DSC_FIELD = "instance_name"
1081
  OP_PARAMS = [
1082
    _PInstanceName,
1083
    _PShutdownTimeout,
1084
    # TODO: Rename target_node as it changes meaning for different export modes
1085
    # (e.g. "destination")
1086
    ("target_node", ht.NoDefault, ht.TOr(ht.TNonEmptyString, ht.TList)),
1087
    ("shutdown", True, ht.TBool),
1088
    ("remove_instance", False, ht.TBool),
1089
    ("ignore_remove_failures", False, ht.TBool),
1090
    ("mode", constants.EXPORT_MODE_LOCAL, ht.TElemOf(constants.EXPORT_MODES)),
1091
    ("x509_key_name", None, ht.TOr(ht.TList, ht.TNone)),
1092
    ("destination_x509_ca", None, ht.TMaybeString),
1093
    ]
1094

    
1095

    
1096
class OpRemoveExport(OpCode):
1097
  """Remove an instance's export."""
1098
  OP_ID = "OP_BACKUP_REMOVE"
1099
  OP_DSC_FIELD = "instance_name"
1100
  OP_PARAMS = [
1101
    _PInstanceName,
1102
    ]
1103

    
1104

    
1105
# Tags opcodes
1106
class OpGetTags(OpCode):
1107
  """Returns the tags of the given object."""
1108
  OP_ID = "OP_TAGS_GET"
1109
  OP_DSC_FIELD = "name"
1110
  OP_PARAMS = [
1111
    _PTagKind,
1112
    # Name is only meaningful for nodes and instances
1113
    ("name", ht.NoDefault, ht.TMaybeString),
1114
    ]
1115

    
1116

    
1117
class OpSearchTags(OpCode):
1118
  """Searches the tags in the cluster for a given pattern."""
1119
  OP_ID = "OP_TAGS_SEARCH"
1120
  OP_DSC_FIELD = "pattern"
1121
  OP_PARAMS = [
1122
    ("pattern", ht.NoDefault, ht.TNonEmptyString),
1123
    ]
1124

    
1125

    
1126
class OpAddTags(OpCode):
1127
  """Add a list of tags on a given object."""
1128
  OP_ID = "OP_TAGS_SET"
1129
  OP_PARAMS = [
1130
    _PTagKind,
1131
    _PTags,
1132
    # Name is only meaningful for nodes and instances
1133
    ("name", ht.NoDefault, ht.TMaybeString),
1134
    ]
1135

    
1136

    
1137
class OpDelTags(OpCode):
1138
  """Remove a list of tags from a given object."""
1139
  OP_ID = "OP_TAGS_DEL"
1140
  OP_PARAMS = [
1141
    _PTagKind,
1142
    _PTags,
1143
    # Name is only meaningful for nodes and instances
1144
    ("name", ht.NoDefault, ht.TMaybeString),
1145
    ]
1146

    
1147
# Test opcodes
1148
class OpTestDelay(OpCode):
1149
  """Sleeps for a configured amount of time.
1150

1151
  This is used just for debugging and testing.
1152

1153
  Parameters:
1154
    - duration: the time to sleep
1155
    - on_master: if true, sleep on the master
1156
    - on_nodes: list of nodes in which to sleep
1157

1158
  If the on_master parameter is true, it will execute a sleep on the
1159
  master (before any node sleep).
1160

1161
  If the on_nodes list is not empty, it will sleep on those nodes
1162
  (after the sleep on the master, if that is enabled).
1163

1164
  As an additional feature, the case of duration < 0 will be reported
1165
  as an execution error, so this opcode can be used as a failure
1166
  generator. The case of duration == 0 will not be treated specially.
1167

1168
  """
1169
  OP_ID = "OP_TEST_DELAY"
1170
  OP_DSC_FIELD = "duration"
1171
  OP_PARAMS = [
1172
    ("duration", ht.NoDefault, ht.TFloat),
1173
    ("on_master", True, ht.TBool),
1174
    ("on_nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
1175
    ("repeat", 0, ht.TPositiveInt)
1176
    ]
1177

    
1178

    
1179
class OpTestAllocator(OpCode):
1180
  """Allocator framework testing.
1181

1182
  This opcode has two modes:
1183
    - gather and return allocator input for a given mode (allocate new
1184
      or replace secondary) and a given instance definition (direction
1185
      'in')
1186
    - run a selected allocator for a given operation (as above) and
1187
      return the allocator output (direction 'out')
1188

1189
  """
1190
  OP_ID = "OP_TEST_ALLOCATOR"
1191
  OP_DSC_FIELD = "allocator"
1192
  OP_PARAMS = [
1193
    ("direction", ht.NoDefault,
1194
     ht.TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS)),
1195
    ("mode", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_MODES)),
1196
    ("name", ht.NoDefault, ht.TNonEmptyString),
1197
    ("nics", ht.NoDefault, ht.TOr(ht.TNone, ht.TListOf(
1198
      ht.TDictOf(ht.TElemOf(["mac", "ip", "bridge"]),
1199
               ht.TOr(ht.TNone, ht.TNonEmptyString))))),
1200
    ("disks", ht.NoDefault, ht.TOr(ht.TNone, ht.TList)),
1201
    ("hypervisor", None, ht.TMaybeString),
1202
    ("allocator", None, ht.TMaybeString),
1203
    ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
1204
    ("mem_size", None, ht.TOr(ht.TNone, ht.TPositiveInt)),
1205
    ("vcpus", None, ht.TOr(ht.TNone, ht.TPositiveInt)),
1206
    ("os", None, ht.TMaybeString),
1207
    ("disk_template", None, ht.TMaybeString),
1208
    ("evac_nodes", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString))),
1209
    ]
1210

    
1211

    
1212
class OpTestJobqueue(OpCode):
1213
  """Utility opcode to test some aspects of the job queue.
1214

1215
  """
1216
  OP_ID = "OP_TEST_JQUEUE"
1217
  OP_PARAMS = [
1218
    ("notify_waitlock", False, ht.TBool),
1219
    ("notify_exec", False, ht.TBool),
1220
    ("log_messages", ht.EmptyList, ht.TListOf(ht.TString)),
1221
    ("fail", False, ht.TBool),
1222
    ]
1223

    
1224

    
1225
class OpTestDummy(OpCode):
1226
  """Utility opcode used by unittests.
1227

1228
  """
1229
  OP_ID = "OP_TEST_DUMMY"
1230
  OP_PARAMS = [
1231
    ("result", ht.NoDefault, ht.NoType),
1232
    ("messages", ht.NoDefault, ht.NoType),
1233
    ("fail", ht.NoDefault, ht.NoType),
1234
    ]
1235

    
1236

    
1237
OP_MAPPING = dict([(v.OP_ID, v) for v in globals().values()
1238
                   if (isinstance(v, type) and issubclass(v, OpCode) and
1239
                       hasattr(v, "OP_ID"))])