Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes.py @ 427746af

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

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

    
43

    
44
# Common opcode attributes
45

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

    
49
#: the shutdown timeout
50
_PShutdownTimeout = ("shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT,
51
                     ht.TPositiveInt)
52

    
53
#: the force parameter
54
_PForce = ("force", False, ht.TBool)
55

    
56
#: a required instance name (for single-instance LUs)
57
_PInstanceName = ("instance_name", ht.NoDefault, ht.TNonEmptyString)
58

    
59
#: Whether to ignore offline nodes
60
_PIgnoreOfflineNodes = ("ignore_offline_nodes", False, ht.TBool)
61

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

    
65
#: a required node group name (for single-group LUs)
66
_PGroupName = ("group_name", ht.NoDefault, ht.TNonEmptyString)
67

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

    
72
#: Obsolete 'live' migration mode (boolean)
73
_PMigrationLive = ("live", None, ht.TMaybeBool)
74

    
75
#: Tag type
76
_PTagKind = ("kind", ht.NoDefault, ht.TElemOf(constants.VALID_TAG_TYPES))
77

    
78
#: List of tag strings
79
_PTags = ("tags", ht.NoDefault, ht.TListOf(ht.TNonEmptyString))
80

    
81
#: Ignore consistency
82
_PIgnoreConsistency = ("ignore_consistency", False, ht.TBool)
83

    
84
#: OP_ID conversion regular expression
85
_OPID_RE = re.compile("([a-z])([A-Z])")
86

    
87

    
88
def _NameToId(name):
89
  """Convert an opcode class name to an OP_ID.
90

91
  @type name: string
92
  @param name: the class name, as OpXxxYyy
93
  @rtype: string
94
  @return: the name in the OP_XXXX_YYYY format
95

96
  """
97
  if not name.startswith("Op"):
98
    return None
99
  # Note: (?<=[a-z])(?=[A-Z]) would be ideal, since it wouldn't
100
  # consume any input, and hence we would just have all the elements
101
  # in the list, one by one; but it seems that split doesn't work on
102
  # non-consuming input, hence we have to process the input string a
103
  # bit
104
  name = _OPID_RE.sub(r"\1,\2", name)
105
  elems = name.split(",")
106
  return "_".join(n.upper() for n in elems)
107

    
108

    
109
def RequireFileStorage():
110
  """Checks that file storage is enabled.
111

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

115
  @raise errors.OpPrereqError: when file storage is disabled
116

117
  """
118
  if not constants.ENABLE_FILE_STORAGE:
119
    raise errors.OpPrereqError("File storage disabled at configure time",
120
                               errors.ECODE_INVAL)
121

    
122

    
123
def RequireSharedFileStorage():
124
  """Checks that shared file storage is enabled.
125

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

129
  @raise errors.OpPrereqError: when shared file storage is disabled
130

131
  """
132
  if not constants.ENABLE_SHARED_FILE_STORAGE:
133
    raise errors.OpPrereqError("Shared file storage disabled at"
134
                               " configure time", errors.ECODE_INVAL)
135

    
136

    
137
def _CheckDiskTemplate(template):
138
  """Ensure a given disk template is valid.
139

140
  """
141
  if template not in constants.DISK_TEMPLATES:
142
    # Using str.join directly to avoid importing utils for CommaJoin
143
    msg = ("Invalid disk template name '%s', valid templates are: %s" %
144
           (template, ", ".join(constants.DISK_TEMPLATES)))
145
    raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
146
  if template == constants.DT_FILE:
147
    RequireFileStorage()
148
  elif template == constants.DT_SHARED_FILE:
149
    RequireSharedFileStorage()
150
  return True
151

    
152

    
153
def _CheckStorageType(storage_type):
154
  """Ensure a given storage type is valid.
155

156
  """
157
  if storage_type not in constants.VALID_STORAGE_TYPES:
158
    raise errors.OpPrereqError("Unknown storage type: %s" % storage_type,
159
                               errors.ECODE_INVAL)
160
  if storage_type == constants.ST_FILE:
161
    RequireFileStorage()
162
  return True
163

    
164

    
165
#: Storage type parameter
166
_PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType)
167

    
168

    
169
class _AutoOpParamSlots(type):
170
  """Meta class for opcode definitions.
171

172
  """
173
  def __new__(mcs, name, bases, attrs):
174
    """Called when a class should be created.
175

176
    @param mcs: The meta class
177
    @param name: Name of created class
178
    @param bases: Base classes
179
    @type attrs: dict
180
    @param attrs: Class attributes
181

182
    """
183
    assert "__slots__" not in attrs, \
184
      "Class '%s' defines __slots__ when it should use OP_PARAMS" % name
185
    assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
186

    
187
    attrs["OP_ID"] = _NameToId(name)
188

    
189
    # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams
190
    params = attrs.setdefault("OP_PARAMS", [])
191

    
192
    # Use parameter names as slots
193
    slots = [pname for (pname, _, _) in params]
194

    
195
    assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
196
      "Class '%s' uses unknown field in OP_DSC_FIELD" % name
197

    
198
    attrs["__slots__"] = slots
199

    
200
    return type.__new__(mcs, name, bases, attrs)
201

    
202

    
203
class BaseOpCode(object):
204
  """A simple serializable object.
205

206
  This object serves as a parent class for OpCode without any custom
207
  field handling.
208

209
  """
210
  # pylint: disable-msg=E1101
211
  # as OP_ID is dynamically defined
212
  __metaclass__ = _AutoOpParamSlots
213

    
214
  def __init__(self, **kwargs):
215
    """Constructor for BaseOpCode.
216

217
    The constructor takes only keyword arguments and will set
218
    attributes on this object based on the passed arguments. As such,
219
    it means that you should not pass arguments which are not in the
220
    __slots__ attribute for this class.
221

222
    """
223
    slots = self._all_slots()
224
    for key in kwargs:
225
      if key not in slots:
226
        raise TypeError("Object %s doesn't support the parameter '%s'" %
227
                        (self.__class__.__name__, key))
228
      setattr(self, key, kwargs[key])
229

    
230
  def __getstate__(self):
231
    """Generic serializer.
232

233
    This method just returns the contents of the instance as a
234
    dictionary.
235

236
    @rtype:  C{dict}
237
    @return: the instance attributes and their values
238

239
    """
240
    state = {}
241
    for name in self._all_slots():
242
      if hasattr(self, name):
243
        state[name] = getattr(self, name)
244
    return state
245

    
246
  def __setstate__(self, state):
247
    """Generic unserializer.
248

249
    This method just restores from the serialized state the attributes
250
    of the current instance.
251

252
    @param state: the serialized opcode data
253
    @type state:  C{dict}
254

255
    """
256
    if not isinstance(state, dict):
257
      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
258
                       type(state))
259

    
260
    for name in self._all_slots():
261
      if name not in state and hasattr(self, name):
262
        delattr(self, name)
263

    
264
    for name in state:
265
      setattr(self, name, state[name])
266

    
267
  @classmethod
268
  def _all_slots(cls):
269
    """Compute the list of all declared slots for a class.
270

271
    """
272
    slots = []
273
    for parent in cls.__mro__:
274
      slots.extend(getattr(parent, "__slots__", []))
275
    return slots
276

    
277
  @classmethod
278
  def GetAllParams(cls):
279
    """Compute list of all parameters for an opcode.
280

281
    """
282
    slots = []
283
    for parent in cls.__mro__:
284
      slots.extend(getattr(parent, "OP_PARAMS", []))
285
    return slots
286

    
287
  def Validate(self, set_defaults):
288
    """Validate opcode parameters, optionally setting default values.
289

290
    @type set_defaults: bool
291
    @param set_defaults: Whether to set default values
292
    @raise errors.OpPrereqError: When a parameter value doesn't match
293
                                 requirements
294

295
    """
296
    for (attr_name, default, test) in self.GetAllParams():
297
      assert test == ht.NoType or callable(test)
298

    
299
      if not hasattr(self, attr_name):
300
        if default == ht.NoDefault:
301
          raise errors.OpPrereqError("Required parameter '%s.%s' missing" %
302
                                     (self.OP_ID, attr_name),
303
                                     errors.ECODE_INVAL)
304
        elif set_defaults:
305
          if callable(default):
306
            dval = default()
307
          else:
308
            dval = default
309
          setattr(self, attr_name, dval)
310

    
311
      if test == ht.NoType:
312
        # no tests here
313
        continue
314

    
315
      if set_defaults or hasattr(self, attr_name):
316
        attr_val = getattr(self, attr_name)
317
        if not test(attr_val):
318
          logging.error("OpCode %s, parameter %s, has invalid type %s/value %s",
319
                        self.OP_ID, attr_name, type(attr_val), attr_val)
320
          raise errors.OpPrereqError("Parameter '%s.%s' fails validation" %
321
                                     (self.OP_ID, attr_name),
322
                                     errors.ECODE_INVAL)
323

    
324

    
325
class OpCode(BaseOpCode):
326
  """Abstract OpCode.
327

328
  This is the root of the actual OpCode hierarchy. All clases derived
329
  from this class should override OP_ID.
330

331
  @cvar OP_ID: The ID of this opcode. This should be unique amongst all
332
               children of this class.
333
  @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
334
                      string returned by Summary(); see the docstring of that
335
                      method for details).
336
  @cvar OP_PARAMS: List of opcode attributes, the default values they should
337
                   get if not already defined, and types they must match.
338
  @cvar WITH_LU: Boolean that specifies whether this should be included in
339
      mcpu's dispatch table
340
  @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
341
                 the check steps
342
  @ivar priority: Opcode priority for queue
343

344
  """
345
  # pylint: disable-msg=E1101
346
  # as OP_ID is dynamically defined
347
  WITH_LU = True
348
  OP_PARAMS = [
349
    ("dry_run", None, ht.TMaybeBool),
350
    ("debug_level", None, ht.TOr(ht.TNone, ht.TPositiveInt)),
351
    ("priority", constants.OP_PRIO_DEFAULT,
352
     ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID)),
353
    ]
354

    
355
  def __getstate__(self):
356
    """Specialized getstate for opcodes.
357

358
    This method adds to the state dictionary the OP_ID of the class,
359
    so that on unload we can identify the correct class for
360
    instantiating the opcode.
361

362
    @rtype:   C{dict}
363
    @return:  the state as a dictionary
364

365
    """
366
    data = BaseOpCode.__getstate__(self)
367
    data["OP_ID"] = self.OP_ID
368
    return data
369

    
370
  @classmethod
371
  def LoadOpCode(cls, data):
372
    """Generic load opcode method.
373

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

378
    @type data:  C{dict}
379
    @param data: the serialized opcode
380

381
    """
382
    if not isinstance(data, dict):
383
      raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
384
    if "OP_ID" not in data:
385
      raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
386
    op_id = data["OP_ID"]
387
    op_class = None
388
    if op_id in OP_MAPPING:
389
      op_class = OP_MAPPING[op_id]
390
    else:
391
      raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
392
                       op_id)
393
    op = op_class()
394
    new_data = data.copy()
395
    del new_data["OP_ID"]
396
    op.__setstate__(new_data)
397
    return op
398

    
399
  def Summary(self):
400
    """Generates a summary description of this opcode.
401

402
    The summary is the value of the OP_ID attribute (without the "OP_"
403
    prefix), plus the value of the OP_DSC_FIELD attribute, if one was
404
    defined; this field should allow to easily identify the operation
405
    (for an instance creation job, e.g., it would be the instance
406
    name).
407

408
    """
409
    assert self.OP_ID is not None and len(self.OP_ID) > 3
410
    # all OP_ID start with OP_, we remove that
411
    txt = self.OP_ID[3:]
412
    field_name = getattr(self, "OP_DSC_FIELD", None)
413
    if field_name:
414
      field_value = getattr(self, field_name, None)
415
      if isinstance(field_value, (list, tuple)):
416
        field_value = ",".join(str(i) for i in field_value)
417
      txt = "%s(%s)" % (txt, field_value)
418
    return txt
419

    
420

    
421
# cluster opcodes
422

    
423
class OpClusterPostInit(OpCode):
424
  """Post cluster initialization.
425

426
  This opcode does not touch the cluster at all. Its purpose is to run hooks
427
  after the cluster has been initialized.
428

429
  """
430

    
431

    
432
class OpClusterDestroy(OpCode):
433
  """Destroy the cluster.
434

435
  This opcode has no other parameters. All the state is irreversibly
436
  lost after the execution of this opcode.
437

438
  """
439

    
440

    
441
class OpClusterQuery(OpCode):
442
  """Query cluster information."""
443

    
444

    
445
class OpClusterVerify(OpCode):
446
  """Verify the cluster state.
447

448
  @type skip_checks: C{list}
449
  @ivar skip_checks: steps to be skipped from the verify process; this
450
                     needs to be a subset of
451
                     L{constants.VERIFY_OPTIONAL_CHECKS}; currently
452
                     only L{constants.VERIFY_NPLUSONE_MEM} can be passed
453

454
  """
455
  OP_PARAMS = [
456
    ("skip_checks", ht.EmptyList,
457
     ht.TListOf(ht.TElemOf(constants.VERIFY_OPTIONAL_CHECKS))),
458
    ("verbose", False, ht.TBool),
459
    ("error_codes", False, ht.TBool),
460
    ("debug_simulate_errors", False, ht.TBool),
461
    ]
462

    
463

    
464
class OpClusterVerifyDisks(OpCode):
465
  """Verify the cluster disks.
466

467
  Parameters: none
468

469
  Result: a tuple of four elements:
470
    - list of node names with bad data returned (unreachable, etc.)
471
    - dict of node names with broken volume groups (values: error msg)
472
    - list of instances with degraded disks (that should be activated)
473
    - dict of instances with missing logical volumes (values: (node, vol)
474
      pairs with details about the missing volumes)
475

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

481
  Note that only instances that are drbd-based are taken into
482
  consideration. This might need to be revisited in the future.
483

484
  """
485

    
486

    
487
class OpClusterRepairDiskSizes(OpCode):
488
  """Verify the disk sizes of the instances and fixes configuration
489
  mimatches.
490

491
  Parameters: optional instances list, in case we want to restrict the
492
  checks to only a subset of the instances.
493

494
  Result: a list of tuples, (instance, disk, new-size) for changed
495
  configurations.
496

497
  In normal operation, the list should be empty.
498

499
  @type instances: list
500
  @ivar instances: the list of instances to check, or empty for all instances
501

502
  """
503
  OP_PARAMS = [
504
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
505
    ]
506

    
507

    
508
class OpClusterConfigQuery(OpCode):
509
  """Query cluster configuration values."""
510
  OP_PARAMS = [
511
    _POutputFields
512
    ]
513

    
514

    
515
class OpClusterRename(OpCode):
516
  """Rename the cluster.
517

518
  @type name: C{str}
519
  @ivar name: The new name of the cluster. The name and/or the master IP
520
              address will be changed to match the new name and its IP
521
              address.
522

523
  """
524
  OP_DSC_FIELD = "name"
525
  OP_PARAMS = [
526
    ("name", ht.NoDefault, ht.TNonEmptyString),
527
    ]
528

    
529

    
530
class OpClusterSetParams(OpCode):
531
  """Change the parameters of the cluster.
532

533
  @type vg_name: C{str} or C{None}
534
  @ivar vg_name: The new volume group name or None to disable LVM usage.
535

536
  """
537
  OP_PARAMS = [
538
    ("vg_name", None, ht.TMaybeString),
539
    ("enabled_hypervisors", None,
540
     ht.TOr(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)), ht.TTrue),
541
            ht.TNone)),
542
    ("hvparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
543
                              ht.TNone)),
544
    ("beparams", None, ht.TOr(ht.TDict, ht.TNone)),
545
    ("os_hvp", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
546
                            ht.TNone)),
547
    ("osparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
548
                              ht.TNone)),
549
    ("candidate_pool_size", None, ht.TOr(ht.TStrictPositiveInt, ht.TNone)),
550
    ("uid_pool", None, ht.NoType),
551
    ("add_uids", None, ht.NoType),
552
    ("remove_uids", None, ht.NoType),
553
    ("maintain_node_health", None, ht.TMaybeBool),
554
    ("prealloc_wipe_disks", None, ht.TMaybeBool),
555
    ("nicparams", None, ht.TMaybeDict),
556
    ("ndparams", None, ht.TMaybeDict),
557
    ("drbd_helper", None, ht.TOr(ht.TString, ht.TNone)),
558
    ("default_iallocator", None, ht.TOr(ht.TString, ht.TNone)),
559
    ("master_netdev", None, ht.TOr(ht.TString, ht.TNone)),
560
    ("reserved_lvs", None, ht.TOr(ht.TListOf(ht.TNonEmptyString), ht.TNone)),
561
    ("hidden_os", None, ht.TOr(ht.TListOf(
562
          ht.TAnd(ht.TList,
563
                ht.TIsLength(2),
564
                ht.TMap(lambda v: v[0], ht.TElemOf(constants.DDMS_VALUES)))),
565
          ht.TNone)),
566
    ("blacklisted_os", None, ht.TOr(ht.TListOf(
567
          ht.TAnd(ht.TList,
568
                ht.TIsLength(2),
569
                ht.TMap(lambda v: v[0], ht.TElemOf(constants.DDMS_VALUES)))),
570
          ht.TNone)),
571
    ]
572

    
573

    
574
class OpClusterRedistConf(OpCode):
575
  """Force a full push of the cluster configuration.
576

577
  """
578

    
579

    
580
class OpQuery(OpCode):
581
  """Query for resources/items.
582

583
  @ivar what: Resources to query for, must be one of L{constants.QR_OP_QUERY}
584
  @ivar fields: List of fields to retrieve
585
  @ivar filter: Query filter
586

587
  """
588
  OP_PARAMS = [
589
    ("what", ht.NoDefault, ht.TElemOf(constants.QR_OP_QUERY)),
590
    ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
591
    ("filter", None, ht.TOr(ht.TNone,
592
                            ht.TListOf(ht.TOr(ht.TNonEmptyString, ht.TList)))),
593
    ]
594

    
595

    
596
class OpQueryFields(OpCode):
597
  """Query for available resource/item fields.
598

599
  @ivar what: Resources to query for, must be one of L{constants.QR_OP_QUERY}
600
  @ivar fields: List of fields to retrieve
601

602
  """
603
  OP_PARAMS = [
604
    ("what", ht.NoDefault, ht.TElemOf(constants.QR_OP_QUERY)),
605
    ("fields", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString))),
606
    ]
607

    
608

    
609
class OpOobCommand(OpCode):
610
  """Interact with OOB."""
611
  OP_PARAMS = [
612
    ("node_names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
613
    ("command", None, ht.TElemOf(constants.OOB_COMMANDS)),
614
    ("timeout", constants.OOB_TIMEOUT, ht.TInt),
615
    ]
616

    
617

    
618
# node opcodes
619

    
620
class OpNodeRemove(OpCode):
621
  """Remove a node.
622

623
  @type node_name: C{str}
624
  @ivar node_name: The name of the node to remove. If the node still has
625
                   instances on it, the operation will fail.
626

627
  """
628
  OP_DSC_FIELD = "node_name"
629
  OP_PARAMS = [
630
    _PNodeName,
631
    ]
632

    
633

    
634
class OpNodeAdd(OpCode):
635
  """Add a node to the cluster.
636

637
  @type node_name: C{str}
638
  @ivar node_name: The name of the node to add. This can be a short name,
639
                   but it will be expanded to the FQDN.
640
  @type primary_ip: IP address
641
  @ivar primary_ip: The primary IP of the node. This will be ignored when the
642
                    opcode is submitted, but will be filled during the node
643
                    add (so it will be visible in the job query).
644
  @type secondary_ip: IP address
645
  @ivar secondary_ip: The secondary IP of the node. This needs to be passed
646
                      if the cluster has been initialized in 'dual-network'
647
                      mode, otherwise it must not be given.
648
  @type readd: C{bool}
649
  @ivar readd: Whether to re-add an existing node to the cluster. If
650
               this is not passed, then the operation will abort if the node
651
               name is already in the cluster; use this parameter to 'repair'
652
               a node that had its configuration broken, or was reinstalled
653
               without removal from the cluster.
654
  @type group: C{str}
655
  @ivar group: The node group to which this node will belong.
656
  @type vm_capable: C{bool}
657
  @ivar vm_capable: The vm_capable node attribute
658
  @type master_capable: C{bool}
659
  @ivar master_capable: The master_capable node attribute
660

661
  """
662
  OP_DSC_FIELD = "node_name"
663
  OP_PARAMS = [
664
    _PNodeName,
665
    ("primary_ip", None, ht.NoType),
666
    ("secondary_ip", None, ht.TMaybeString),
667
    ("readd", False, ht.TBool),
668
    ("group", None, ht.TMaybeString),
669
    ("master_capable", None, ht.TMaybeBool),
670
    ("vm_capable", None, ht.TMaybeBool),
671
    ("ndparams", None, ht.TMaybeDict),
672
    ]
673

    
674

    
675
class OpNodeQuery(OpCode):
676
  """Compute the list of nodes."""
677
  OP_PARAMS = [
678
    _POutputFields,
679
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
680
    ("use_locking", False, ht.TBool),
681
    ]
682

    
683

    
684
class OpNodeQueryvols(OpCode):
685
  """Get list of volumes on node."""
686
  OP_PARAMS = [
687
    _POutputFields,
688
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
689
    ]
690

    
691

    
692
class OpNodeQueryStorage(OpCode):
693
  """Get information on storage for node(s)."""
694
  OP_PARAMS = [
695
    _POutputFields,
696
    _PStorageType,
697
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
698
    ("name", None, ht.TMaybeString),
699
    ]
700

    
701

    
702
class OpNodeModifyStorage(OpCode):
703
  """Modifies the properies of a storage unit"""
704
  OP_PARAMS = [
705
    _PNodeName,
706
    _PStorageType,
707
    ("name", ht.NoDefault, ht.TNonEmptyString),
708
    ("changes", ht.NoDefault, ht.TDict),
709
    ]
710

    
711

    
712
class OpRepairNodeStorage(OpCode):
713
  """Repairs the volume group on a node."""
714
  OP_DSC_FIELD = "node_name"
715
  OP_PARAMS = [
716
    _PNodeName,
717
    _PStorageType,
718
    _PIgnoreConsistency,
719
    ("name", ht.NoDefault, ht.TNonEmptyString),
720
    ]
721

    
722

    
723
class OpNodeSetParams(OpCode):
724
  """Change the parameters of a node."""
725
  OP_DSC_FIELD = "node_name"
726
  OP_PARAMS = [
727
    _PNodeName,
728
    _PForce,
729
    ("master_candidate", None, ht.TMaybeBool),
730
    ("offline", None, ht.TMaybeBool),
731
    ("drained", None, ht.TMaybeBool),
732
    ("auto_promote", False, ht.TBool),
733
    ("master_capable", None, ht.TMaybeBool),
734
    ("vm_capable", None, ht.TMaybeBool),
735
    ("secondary_ip", None, ht.TMaybeString),
736
    ("ndparams", None, ht.TMaybeDict),
737
    ("powered", None, ht.TMaybeBool),
738
    ]
739

    
740

    
741
class OpNodePowercycle(OpCode):
742
  """Tries to powercycle a node."""
743
  OP_DSC_FIELD = "node_name"
744
  OP_PARAMS = [
745
    _PNodeName,
746
    _PForce,
747
    ]
748

    
749

    
750
class OpNodeMigrate(OpCode):
751
  """Migrate all instances from a node."""
752
  OP_DSC_FIELD = "node_name"
753
  OP_PARAMS = [
754
    _PNodeName,
755
    _PMigrationMode,
756
    _PMigrationLive,
757
    ("iallocator", None, ht.TMaybeString),
758
    ]
759

    
760

    
761
class OpNodeEvacStrategy(OpCode):
762
  """Compute the evacuation strategy for a list of nodes."""
763
  OP_DSC_FIELD = "nodes"
764
  OP_PARAMS = [
765
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
766
    ("remote_node", None, ht.TMaybeString),
767
    ("iallocator", None, ht.TMaybeString),
768
    ]
769

    
770

    
771
# instance opcodes
772

    
773
class OpInstanceCreate(OpCode):
774
  """Create an instance.
775

776
  @ivar instance_name: Instance name
777
  @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES})
778
  @ivar source_handshake: Signed handshake from source (remote import only)
779
  @ivar source_x509_ca: Source X509 CA in PEM format (remote import only)
780
  @ivar source_instance_name: Previous name of instance (remote import only)
781
  @ivar source_shutdown_timeout: Shutdown timeout used for source instance
782
    (remote import only)
783

784
  """
785
  OP_DSC_FIELD = "instance_name"
786
  OP_PARAMS = [
787
    _PInstanceName,
788
    ("beparams", ht.EmptyDict, ht.TDict),
789
    ("disks", ht.NoDefault, ht.TListOf(ht.TDict)),
790
    ("disk_template", ht.NoDefault, _CheckDiskTemplate),
791
    ("file_driver", None, ht.TOr(ht.TNone, ht.TElemOf(constants.FILE_DRIVER))),
792
    ("file_storage_dir", None, ht.TMaybeString),
793
    ("force_variant", False, ht.TBool),
794
    ("hvparams", ht.EmptyDict, ht.TDict),
795
    ("hypervisor", None, ht.TMaybeString),
796
    ("iallocator", None, ht.TMaybeString),
797
    ("identify_defaults", False, ht.TBool),
798
    ("ip_check", True, ht.TBool),
799
    ("mode", ht.NoDefault, ht.TElemOf(constants.INSTANCE_CREATE_MODES)),
800
    ("name_check", True, ht.TBool),
801
    ("nics", ht.NoDefault, ht.TListOf(ht.TDict)),
802
    ("no_install", None, ht.TMaybeBool),
803
    ("osparams", ht.EmptyDict, ht.TDict),
804
    ("os_type", None, ht.TMaybeString),
805
    ("pnode", None, ht.TMaybeString),
806
    ("snode", None, ht.TMaybeString),
807
    ("source_handshake", None, ht.TOr(ht.TList, ht.TNone)),
808
    ("source_instance_name", None, ht.TMaybeString),
809
    ("source_shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT,
810
     ht.TPositiveInt),
811
    ("source_x509_ca", None, ht.TMaybeString),
812
    ("src_node", None, ht.TMaybeString),
813
    ("src_path", None, ht.TMaybeString),
814
    ("start", True, ht.TBool),
815
    ("wait_for_sync", True, ht.TBool),
816
    ]
817

    
818

    
819
class OpInstanceReinstall(OpCode):
820
  """Reinstall an instance's OS."""
821
  OP_DSC_FIELD = "instance_name"
822
  OP_PARAMS = [
823
    _PInstanceName,
824
    ("os_type", None, ht.TMaybeString),
825
    ("force_variant", False, ht.TBool),
826
    ("osparams", None, ht.TMaybeDict),
827
    ]
828

    
829

    
830
class OpInstanceRemove(OpCode):
831
  """Remove an instance."""
832
  OP_DSC_FIELD = "instance_name"
833
  OP_PARAMS = [
834
    _PInstanceName,
835
    _PShutdownTimeout,
836
    ("ignore_failures", False, ht.TBool),
837
    ]
838

    
839

    
840
class OpInstanceRename(OpCode):
841
  """Rename an instance."""
842
  OP_PARAMS = [
843
    _PInstanceName,
844
    ("new_name", ht.NoDefault, ht.TNonEmptyString),
845
    ("ip_check", False, ht.TBool),
846
    ("name_check", True, ht.TBool),
847
    ]
848

    
849

    
850
class OpInstanceStartup(OpCode):
851
  """Startup an instance."""
852
  OP_DSC_FIELD = "instance_name"
853
  OP_PARAMS = [
854
    _PInstanceName,
855
    _PForce,
856
    _PIgnoreOfflineNodes,
857
    ("hvparams", ht.EmptyDict, ht.TDict),
858
    ("beparams", ht.EmptyDict, ht.TDict),
859
    ]
860

    
861

    
862
class OpInstanceShutdown(OpCode):
863
  """Shutdown an instance."""
864
  OP_DSC_FIELD = "instance_name"
865
  OP_PARAMS = [
866
    _PInstanceName,
867
    _PIgnoreOfflineNodes,
868
    ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TPositiveInt),
869
    ]
870

    
871

    
872
class OpInstanceReboot(OpCode):
873
  """Reboot an instance."""
874
  OP_DSC_FIELD = "instance_name"
875
  OP_PARAMS = [
876
    _PInstanceName,
877
    _PShutdownTimeout,
878
    ("ignore_secondaries", False, ht.TBool),
879
    ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES)),
880
    ]
881

    
882

    
883
class OpInstanceReplaceDisks(OpCode):
884
  """Replace the disks of an instance."""
885
  OP_DSC_FIELD = "instance_name"
886
  OP_PARAMS = [
887
    _PInstanceName,
888
    ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES)),
889
    ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt)),
890
    ("remote_node", None, ht.TMaybeString),
891
    ("iallocator", None, ht.TMaybeString),
892
    ("early_release", False, ht.TBool),
893
    ]
894

    
895

    
896
class OpInstanceFailover(OpCode):
897
  """Failover an instance."""
898
  OP_DSC_FIELD = "instance_name"
899
  OP_PARAMS = [
900
    _PInstanceName,
901
    _PShutdownTimeout,
902
    _PIgnoreConsistency,
903
    ("iallocator", None, ht.TMaybeString),
904
    ("target_node", None, ht.TMaybeString),
905
    ]
906

    
907

    
908
class OpInstanceMigrate(OpCode):
909
  """Migrate an instance.
910

911
  This migrates (without shutting down an instance) to its secondary
912
  node.
913

914
  @ivar instance_name: the name of the instance
915
  @ivar mode: the migration mode (live, non-live or None for auto)
916

917
  """
918
  OP_DSC_FIELD = "instance_name"
919
  OP_PARAMS = [
920
    _PInstanceName,
921
    _PMigrationMode,
922
    _PMigrationLive,
923
    ("cleanup", False, ht.TBool),
924
    ("iallocator", None, ht.TMaybeString),
925
    ("target_node", None, ht.TMaybeString),
926
    ]
927

    
928

    
929
class OpInstanceMove(OpCode):
930
  """Move an instance.
931

932
  This move (with shutting down an instance and data copying) to an
933
  arbitrary node.
934

935
  @ivar instance_name: the name of the instance
936
  @ivar target_node: the destination node
937

938
  """
939
  OP_DSC_FIELD = "instance_name"
940
  OP_PARAMS = [
941
    _PInstanceName,
942
    _PShutdownTimeout,
943
    _PIgnoreConsistency,
944
    ("target_node", ht.NoDefault, ht.TNonEmptyString),
945
    ]
946

    
947

    
948
class OpInstanceConsole(OpCode):
949
  """Connect to an instance's console."""
950
  OP_DSC_FIELD = "instance_name"
951
  OP_PARAMS = [
952
    _PInstanceName
953
    ]
954

    
955

    
956
class OpInstanceActivateDisks(OpCode):
957
  """Activate an instance's disks."""
958
  OP_DSC_FIELD = "instance_name"
959
  OP_PARAMS = [
960
    _PInstanceName,
961
    ("ignore_size", False, ht.TBool),
962
    ]
963

    
964

    
965
class OpInstanceDeactivateDisks(OpCode):
966
  """Deactivate an instance's disks."""
967
  OP_DSC_FIELD = "instance_name"
968
  OP_PARAMS = [
969
    _PInstanceName,
970
    _PForce,
971
    ]
972

    
973

    
974
class OpInstanceRecreateDisks(OpCode):
975
  """Deactivate an instance's disks."""
976
  OP_DSC_FIELD = "instance_name"
977
  OP_PARAMS = [
978
    _PInstanceName,
979
    ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt)),
980
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
981
    ]
982

    
983

    
984
class OpInstanceQuery(OpCode):
985
  """Compute the list of instances."""
986
  OP_PARAMS = [
987
    _POutputFields,
988
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
989
    ("use_locking", False, ht.TBool),
990
    ]
991

    
992

    
993
class OpInstanceQueryData(OpCode):
994
  """Compute the run-time status of instances."""
995
  OP_PARAMS = [
996
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
997
    ("static", False, ht.TBool),
998
    ("use_locking", False, ht.TBool),
999
    ]
1000

    
1001

    
1002
class OpInstanceSetParams(OpCode):
1003
  """Change the parameters of an instance."""
1004
  OP_DSC_FIELD = "instance_name"
1005
  OP_PARAMS = [
1006
    _PInstanceName,
1007
    _PForce,
1008
    ("nics", ht.EmptyList, ht.TList),
1009
    ("disks", ht.EmptyList, ht.TList),
1010
    ("beparams", ht.EmptyDict, ht.TDict),
1011
    ("hvparams", ht.EmptyDict, ht.TDict),
1012
    ("disk_template", None, ht.TOr(ht.TNone, _CheckDiskTemplate)),
1013
    ("remote_node", None, ht.TMaybeString),
1014
    ("os_name", None, ht.TMaybeString),
1015
    ("force_variant", False, ht.TBool),
1016
    ("osparams", None, ht.TMaybeDict),
1017
    ("wait_for_sync", True, ht.TBool),
1018
    ]
1019

    
1020

    
1021
class OpInstanceGrowDisk(OpCode):
1022
  """Grow a disk of an instance."""
1023
  OP_DSC_FIELD = "instance_name"
1024
  OP_PARAMS = [
1025
    _PInstanceName,
1026
    ("disk", ht.NoDefault, ht.TInt),
1027
    ("amount", ht.NoDefault, ht.TInt),
1028
    ("wait_for_sync", True, ht.TBool),
1029
    ]
1030

    
1031

    
1032
# Node group opcodes
1033

    
1034
class OpGroupAdd(OpCode):
1035
  """Add a node group to the cluster."""
1036
  OP_DSC_FIELD = "group_name"
1037
  OP_PARAMS = [
1038
    _PGroupName,
1039
    ("ndparams", None, ht.TMaybeDict),
1040
    ("alloc_policy", None,
1041
     ht.TOr(ht.TNone, ht.TElemOf(constants.VALID_ALLOC_POLICIES))),
1042
    ]
1043

    
1044

    
1045
class OpGroupAssignNodes(OpCode):
1046
  """Assign nodes to a node group."""
1047
  OP_DSC_FIELD = "group_name"
1048
  OP_PARAMS = [
1049
    _PGroupName,
1050
    _PForce,
1051
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
1052
    ]
1053

    
1054

    
1055
class OpGroupQuery(OpCode):
1056
  """Compute the list of node groups."""
1057
  OP_PARAMS = [
1058
    _POutputFields,
1059
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
1060
    ]
1061

    
1062

    
1063
class OpGroupSetParams(OpCode):
1064
  """Change the parameters of a node group."""
1065
  OP_DSC_FIELD = "group_name"
1066
  OP_PARAMS = [
1067
    _PGroupName,
1068
    ("ndparams", None, ht.TMaybeDict),
1069
    ("alloc_policy", None, ht.TOr(ht.TNone,
1070
                                  ht.TElemOf(constants.VALID_ALLOC_POLICIES))),
1071
    ("network", None, ht.TOr(ht.TNone, ht.TList)),
1072
    ]
1073

    
1074

    
1075
class OpGroupRemove(OpCode):
1076
  """Remove a node group from the cluster."""
1077
  OP_DSC_FIELD = "group_name"
1078
  OP_PARAMS = [
1079
    _PGroupName,
1080
    ]
1081

    
1082

    
1083
class OpGroupRename(OpCode):
1084
  """Rename a node group in the cluster."""
1085
  OP_DSC_FIELD = "old_name"
1086
  OP_PARAMS = [
1087
    ("old_name", ht.NoDefault, ht.TNonEmptyString),
1088
    ("new_name", ht.NoDefault, ht.TNonEmptyString),
1089
    ]
1090

    
1091

    
1092
# OS opcodes
1093
class OpOsDiagnose(OpCode):
1094
  """Compute the list of guest operating systems."""
1095
  OP_PARAMS = [
1096
    _POutputFields,
1097
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
1098
    ]
1099

    
1100

    
1101
# Exports opcodes
1102
class OpBackupQuery(OpCode):
1103
  """Compute the list of exported images."""
1104
  OP_PARAMS = [
1105
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
1106
    ("use_locking", False, ht.TBool),
1107
    ]
1108

    
1109

    
1110
class OpBackupPrepare(OpCode):
1111
  """Prepares an instance export.
1112

1113
  @ivar instance_name: Instance name
1114
  @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1115

1116
  """
1117
  OP_DSC_FIELD = "instance_name"
1118
  OP_PARAMS = [
1119
    _PInstanceName,
1120
    ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES)),
1121
    ]
1122

    
1123

    
1124
class OpBackupExport(OpCode):
1125
  """Export an instance.
1126

1127
  For local exports, the export destination is the node name. For remote
1128
  exports, the export destination is a list of tuples, each consisting of
1129
  hostname/IP address, port, HMAC and HMAC salt. The HMAC is calculated using
1130
  the cluster domain secret over the value "${index}:${hostname}:${port}". The
1131
  destination X509 CA must be a signed certificate.
1132

1133
  @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1134
  @ivar target_node: Export destination
1135
  @ivar x509_key_name: X509 key to use (remote export only)
1136
  @ivar destination_x509_ca: Destination X509 CA in PEM format (remote export
1137
                             only)
1138

1139
  """
1140
  OP_DSC_FIELD = "instance_name"
1141
  OP_PARAMS = [
1142
    _PInstanceName,
1143
    _PShutdownTimeout,
1144
    # TODO: Rename target_node as it changes meaning for different export modes
1145
    # (e.g. "destination")
1146
    ("target_node", ht.NoDefault, ht.TOr(ht.TNonEmptyString, ht.TList)),
1147
    ("shutdown", True, ht.TBool),
1148
    ("remove_instance", False, ht.TBool),
1149
    ("ignore_remove_failures", False, ht.TBool),
1150
    ("mode", constants.EXPORT_MODE_LOCAL, ht.TElemOf(constants.EXPORT_MODES)),
1151
    ("x509_key_name", None, ht.TOr(ht.TList, ht.TNone)),
1152
    ("destination_x509_ca", None, ht.TMaybeString),
1153
    ]
1154

    
1155

    
1156
class OpBackupRemove(OpCode):
1157
  """Remove an instance's export."""
1158
  OP_DSC_FIELD = "instance_name"
1159
  OP_PARAMS = [
1160
    _PInstanceName,
1161
    ]
1162

    
1163

    
1164
# Tags opcodes
1165
class OpTagsGet(OpCode):
1166
  """Returns the tags of the given object."""
1167
  OP_DSC_FIELD = "name"
1168
  OP_PARAMS = [
1169
    _PTagKind,
1170
    # Name is only meaningful for nodes and instances
1171
    ("name", ht.NoDefault, ht.TMaybeString),
1172
    ]
1173

    
1174

    
1175
class OpTagsSearch(OpCode):
1176
  """Searches the tags in the cluster for a given pattern."""
1177
  OP_DSC_FIELD = "pattern"
1178
  OP_PARAMS = [
1179
    ("pattern", ht.NoDefault, ht.TNonEmptyString),
1180
    ]
1181

    
1182

    
1183
class OpTagsSet(OpCode):
1184
  """Add a list of tags on a given object."""
1185
  OP_PARAMS = [
1186
    _PTagKind,
1187
    _PTags,
1188
    # Name is only meaningful for nodes and instances
1189
    ("name", ht.NoDefault, ht.TMaybeString),
1190
    ]
1191

    
1192

    
1193
class OpTagsDel(OpCode):
1194
  """Remove a list of tags from a given object."""
1195
  OP_PARAMS = [
1196
    _PTagKind,
1197
    _PTags,
1198
    # Name is only meaningful for nodes and instances
1199
    ("name", ht.NoDefault, ht.TMaybeString),
1200
    ]
1201

    
1202
# Test opcodes
1203
class OpTestDelay(OpCode):
1204
  """Sleeps for a configured amount of time.
1205

1206
  This is used just for debugging and testing.
1207

1208
  Parameters:
1209
    - duration: the time to sleep
1210
    - on_master: if true, sleep on the master
1211
    - on_nodes: list of nodes in which to sleep
1212

1213
  If the on_master parameter is true, it will execute a sleep on the
1214
  master (before any node sleep).
1215

1216
  If the on_nodes list is not empty, it will sleep on those nodes
1217
  (after the sleep on the master, if that is enabled).
1218

1219
  As an additional feature, the case of duration < 0 will be reported
1220
  as an execution error, so this opcode can be used as a failure
1221
  generator. The case of duration == 0 will not be treated specially.
1222

1223
  """
1224
  OP_DSC_FIELD = "duration"
1225
  OP_PARAMS = [
1226
    ("duration", ht.NoDefault, ht.TFloat),
1227
    ("on_master", True, ht.TBool),
1228
    ("on_nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
1229
    ("repeat", 0, ht.TPositiveInt)
1230
    ]
1231

    
1232

    
1233
class OpTestAllocator(OpCode):
1234
  """Allocator framework testing.
1235

1236
  This opcode has two modes:
1237
    - gather and return allocator input for a given mode (allocate new
1238
      or replace secondary) and a given instance definition (direction
1239
      'in')
1240
    - run a selected allocator for a given operation (as above) and
1241
      return the allocator output (direction 'out')
1242

1243
  """
1244
  OP_DSC_FIELD = "allocator"
1245
  OP_PARAMS = [
1246
    ("direction", ht.NoDefault,
1247
     ht.TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS)),
1248
    ("mode", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_MODES)),
1249
    ("name", ht.NoDefault, ht.TNonEmptyString),
1250
    ("nics", ht.NoDefault, ht.TOr(ht.TNone, ht.TListOf(
1251
      ht.TDictOf(ht.TElemOf(["mac", "ip", "bridge"]),
1252
               ht.TOr(ht.TNone, ht.TNonEmptyString))))),
1253
    ("disks", ht.NoDefault, ht.TOr(ht.TNone, ht.TList)),
1254
    ("hypervisor", None, ht.TMaybeString),
1255
    ("allocator", None, ht.TMaybeString),
1256
    ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
1257
    ("mem_size", None, ht.TOr(ht.TNone, ht.TPositiveInt)),
1258
    ("vcpus", None, ht.TOr(ht.TNone, ht.TPositiveInt)),
1259
    ("os", None, ht.TMaybeString),
1260
    ("disk_template", None, ht.TMaybeString),
1261
    ("evac_nodes", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString))),
1262
    ]
1263

    
1264

    
1265
class OpTestJqueue(OpCode):
1266
  """Utility opcode to test some aspects of the job queue.
1267

1268
  """
1269
  OP_PARAMS = [
1270
    ("notify_waitlock", False, ht.TBool),
1271
    ("notify_exec", False, ht.TBool),
1272
    ("log_messages", ht.EmptyList, ht.TListOf(ht.TString)),
1273
    ("fail", False, ht.TBool),
1274
    ]
1275

    
1276

    
1277
class OpTestDummy(OpCode):
1278
  """Utility opcode used by unittests.
1279

1280
  """
1281
  OP_PARAMS = [
1282
    ("result", ht.NoDefault, ht.NoType),
1283
    ("messages", ht.NoDefault, ht.NoType),
1284
    ("fail", ht.NoDefault, ht.NoType),
1285
    ]
1286
  WITH_LU = False
1287

    
1288

    
1289
def _GetOpList():
1290
  """Returns list of all defined opcodes.
1291

1292
  Does not eliminate duplicates by C{OP_ID}.
1293

1294
  """
1295
  return [v for v in globals().values()
1296
          if (isinstance(v, type) and issubclass(v, OpCode) and
1297
              hasattr(v, "OP_ID") and v is not OpCode)]
1298

    
1299

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