Revision 65e183af

b/lib/opcodes.py
33 33
# few public methods:
34 34
# pylint: disable-msg=R0903
35 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

  
36 154

  
37 155
class BaseOpCode(object):
38 156
  """A simple serializable object.
......
41 159
  field handling.
42 160

  
43 161
  """
44
  __slots__ = []
162
  __metaclass__ = _AutoOpParamSlots
163

  
164
  OP_ID = None
45 165

  
46 166
  def __init__(self, **kwargs):
47 167
    """Constructor for BaseOpCode.
......
106 226
      slots.extend(getattr(parent, "__slots__", []))
107 227
    return slots
108 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

  
109 239

  
110 240
class OpCode(BaseOpCode):
111 241
  """Abstract OpCode.
......
118 248
  @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
119 249
                      string returned by Summary(); see the docstring of that
120 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.
121 253
  @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
122 254
                 the check steps
123 255
  @ivar priority: Opcode priority for queue
124 256

  
125 257
  """
126 258
  OP_ID = "OP_ABSTRACT"
127
  __slots__ = ["dry_run", "debug_level", "priority"]
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
    ]
128 265

  
129 266
  def __getstate__(self):
130 267
    """Specialized getstate for opcodes.
......
200 337

  
201 338
  """
202 339
  OP_ID = "OP_CLUSTER_POST_INIT"
203
  __slots__ = []
204 340

  
205 341

  
206 342
class OpDestroyCluster(OpCode):
......
211 347

  
212 348
  """
213 349
  OP_ID = "OP_CLUSTER_DESTROY"
214
  __slots__ = []
215 350

  
216 351

  
217 352
class OpQueryClusterInfo(OpCode):
218 353
  """Query cluster information."""
219 354
  OP_ID = "OP_CLUSTER_QUERY"
220
  __slots__ = []
221 355

  
222 356

  
223 357
class OpVerifyCluster(OpCode):
......
231 365

  
232 366
  """
233 367
  OP_ID = "OP_CLUSTER_VERIFY"
234
  __slots__ = ["skip_checks", "verbose", "error_codes",
235
               "debug_simulate_errors"]
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
    ]
236 375

  
237 376

  
238 377
class OpVerifyDisks(OpCode):
......
257 396

  
258 397
  """
259 398
  OP_ID = "OP_CLUSTER_VERIFY_DISKS"
260
  __slots__ = []
261 399

  
262 400

  
263 401
class OpRepairDiskSizes(OpCode):
......
277 415

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

  
282 422

  
283 423
class OpQueryConfigValues(OpCode):
284 424
  """Query cluster configuration values."""
285 425
  OP_ID = "OP_CLUSTER_CONFIG_QUERY"
286
  __slots__ = ["output_fields"]
426
  OP_PARAMS = [
427
    _POutputFields
428
    ]
287 429

  
288 430

  
289 431
class OpRenameCluster(OpCode):
......
297 439
  """
298 440
  OP_ID = "OP_CLUSTER_RENAME"
299 441
  OP_DSC_FIELD = "name"
300
  __slots__ = ["name"]
442
  OP_PARAMS = [
443
    ("name", ht.NoDefault, ht.TNonEmptyString),
444
    ]
301 445

  
302 446

  
303 447
class OpSetClusterParams(OpCode):
......
308 452

  
309 453
  """
310 454
  OP_ID = "OP_CLUSTER_SET_PARAMS"
311
  __slots__ = [
312
    "vg_name",
313
    "drbd_helper",
314
    "enabled_hypervisors",
315
    "hvparams",
316
    "os_hvp",
317
    "beparams",
318
    "osparams",
319
    "nicparams",
320
    "ndparams",
321
    "candidate_pool_size",
322
    "maintain_node_health",
323
    "uid_pool",
324
    "add_uids",
325
    "remove_uids",
326
    "default_iallocator",
327
    "reserved_lvs",
328
    "hidden_os",
329
    "blacklisted_os",
330
    "prealloc_wipe_disks",
331
    "master_netdev",
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)),
332 489
    ]
333 490

  
334 491

  
......
337 494

  
338 495
  """
339 496
  OP_ID = "OP_CLUSTER_REDIST_CONF"
340
  __slots__ = []
341 497

  
342 498

  
343 499
class OpQuery(OpCode):
......
349 505

  
350 506
  """
351 507
  OP_ID = "OP_QUERY"
352
  __slots__ = [
353
    "what",
354
    "fields",
355
    "filter",
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)))),
356 513
    ]
357 514

  
358 515

  
......
364 521

  
365 522
  """
366 523
  OP_ID = "OP_QUERY_FIELDS"
367
  __slots__ = [
368
    "what",
369
    "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))),
370 527
    ]
371 528

  
372 529

  
373 530
class OpOobCommand(OpCode):
374 531
  """Interact with OOB."""
375 532
  OP_ID = "OP_OOB_COMMAND"
376
  __slots__ = [
377
    "node_name",
378
    "command",
379
    "timeout",
533
  OP_PARAMS = [
534
    _PNodeName,
535
    ("command", None, ht.TElemOf(constants.OOB_COMMANDS)),
536
    ("timeout", constants.OOB_TIMEOUT, ht.TInt),
380 537
    ]
381 538

  
382 539

  
......
392 549
  """
393 550
  OP_ID = "OP_NODE_REMOVE"
394 551
  OP_DSC_FIELD = "node_name"
395
  __slots__ = ["node_name"]
552
  OP_PARAMS = [
553
    _PNodeName,
554
    ]
396 555

  
397 556

  
398 557
class OpAddNode(OpCode):
......
425 584
  """
426 585
  OP_ID = "OP_NODE_ADD"
427 586
  OP_DSC_FIELD = "node_name"
428
  __slots__ = ["node_name", "primary_ip", "secondary_ip", "readd", "group",
429
               "vm_capable", "master_capable", "ndparams"]
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
    ]
430 597

  
431 598

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

  
437 608

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

  
443 617

  
444 618
class OpQueryNodeStorage(OpCode):
445 619
  """Get information on storage for node(s)."""
446 620
  OP_ID = "OP_NODE_QUERY_STORAGE"
447
  __slots__ = [
448
    "nodes",
449
    "storage_type",
450
    "name",
451
    "output_fields",
621
  OP_PARAMS = [
622
    _POutputFields,
623
    _PStorageType,
624
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
625
    ("name", None, ht.TMaybeString),
452 626
    ]
453 627

  
454 628

  
455 629
class OpModifyNodeStorage(OpCode):
456 630
  """Modifies the properies of a storage unit"""
457 631
  OP_ID = "OP_NODE_MODIFY_STORAGE"
458
  __slots__ = [
459
    "node_name",
460
    "storage_type",
461
    "name",
462
    "changes",
632
  OP_PARAMS = [
633
    _PNodeName,
634
    _PStorageType,
635
    ("name", ht.NoDefault, ht.TNonEmptyString),
636
    ("changes", ht.NoDefault, ht.TDict),
463 637
    ]
464 638

  
465 639

  
......
467 641
  """Repairs the volume group on a node."""
468 642
  OP_ID = "OP_REPAIR_NODE_STORAGE"
469 643
  OP_DSC_FIELD = "node_name"
470
  __slots__ = [
471
    "node_name",
472
    "storage_type",
473
    "name",
474
    "ignore_consistency",
644
  OP_PARAMS = [
645
    _PNodeName,
646
    _PStorageType,
647
    ("name", ht.NoDefault, ht.TNonEmptyString),
648
    ("ignore_consistency", False, ht.TBool),
475 649
    ]
476 650

  
477 651

  
......
479 653
  """Change the parameters of a node."""
480 654
  OP_ID = "OP_NODE_SET_PARAMS"
481 655
  OP_DSC_FIELD = "node_name"
482
  __slots__ = [
483
    "node_name",
484
    "force",
485
    "master_candidate",
486
    "offline",
487
    "drained",
488
    "auto_promote",
489
    "master_capable",
490
    "vm_capable",
491
    "secondary_ip",
492
    "ndparams",
493
    "powered",
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),
494 668
    ]
495 669

  
496 670

  
......
498 672
  """Tries to powercycle a node."""
499 673
  OP_ID = "OP_NODE_POWERCYCLE"
500 674
  OP_DSC_FIELD = "node_name"
501
  __slots__ = [
502
    "node_name",
503
    "force",
675
  OP_PARAMS = [
676
    _PNodeName,
677
    _PForce,
504 678
    ]
505 679

  
506 680

  
......
508 682
  """Migrate all instances from a node."""
509 683
  OP_ID = "OP_NODE_MIGRATE"
510 684
  OP_DSC_FIELD = "node_name"
511
  __slots__ = [
512
    "node_name",
513
    "mode",
514
    "live",
685
  OP_PARAMS = [
686
    _PNodeName,
687
    _PMigrationMode,
688
    _PMigrationLive,
515 689
    ]
516 690

  
517 691

  
......
519 693
  """Compute the evacuation strategy for a list of nodes."""
520 694
  OP_ID = "OP_NODE_EVAC_STRATEGY"
521 695
  OP_DSC_FIELD = "nodes"
522
  __slots__ = ["nodes", "iallocator", "remote_node"]
696
  OP_PARAMS = [
697
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
698
    ("remote_node", None, ht.TMaybeString),
699
    ("iallocator", None, ht.TMaybeString),
700
    ]
523 701

  
524 702

  
525 703
# instance opcodes
......
538 716
  """
539 717
  OP_ID = "OP_INSTANCE_CREATE"
540 718
  OP_DSC_FIELD = "instance_name"
541
  __slots__ = [
542
    "instance_name",
543
    "os_type", "force_variant", "no_install",
544
    "pnode", "disk_template", "snode", "mode",
545
    "disks", "nics",
546
    "src_node", "src_path", "start", "identify_defaults",
547
    "wait_for_sync", "ip_check", "name_check",
548
    "file_storage_dir", "file_driver",
549
    "iallocator",
550
    "hypervisor", "hvparams", "beparams", "osparams",
551
    "source_handshake",
552
    "source_x509_ca",
553
    "source_instance_name",
554
    "source_shutdown_timeout",
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),
555 749
    ]
556 750

  
557 751

  
......
559 753
  """Reinstall an instance's OS."""
560 754
  OP_ID = "OP_INSTANCE_REINSTALL"
561 755
  OP_DSC_FIELD = "instance_name"
562
  __slots__ = ["instance_name", "os_type", "force_variant", "osparams"]
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
    ]
563 762

  
564 763

  
565 764
class OpRemoveInstance(OpCode):
566 765
  """Remove an instance."""
567 766
  OP_ID = "OP_INSTANCE_REMOVE"
568 767
  OP_DSC_FIELD = "instance_name"
569
  __slots__ = [
570
    "instance_name",
571
    "ignore_failures",
572
    "shutdown_timeout",
768
  OP_PARAMS = [
769
    _PInstanceName,
770
    _PShutdownTimeout,
771
    ("ignore_failures", False, ht.TBool),
573 772
    ]
574 773

  
575 774

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

  
583 785

  
......
585 787
  """Startup an instance."""
586 788
  OP_ID = "OP_INSTANCE_STARTUP"
587 789
  OP_DSC_FIELD = "instance_name"
588
  __slots__ = [
589
    "instance_name", "force", "hvparams", "beparams", "ignore_offline_nodes",
790
  OP_PARAMS = [
791
    _PInstanceName,
792
    _PForce,
793
    _PIgnoreOfflineNodes,
794
    ("hvparams", ht.EmptyDict, ht.TDict),
795
    ("beparams", ht.EmptyDict, ht.TDict),
590 796
    ]
591 797

  
592 798

  
......
594 800
  """Shutdown an instance."""
595 801
  OP_ID = "OP_INSTANCE_SHUTDOWN"
596 802
  OP_DSC_FIELD = "instance_name"
597
  __slots__ = [
598
    "instance_name", "timeout", "ignore_offline_nodes",
803
  OP_PARAMS = [
804
    _PInstanceName,
805
    _PIgnoreOfflineNodes,
806
    ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TPositiveInt),
599 807
    ]
600 808

  
601 809

  
......
603 811
  """Reboot an instance."""
604 812
  OP_ID = "OP_INSTANCE_REBOOT"
605 813
  OP_DSC_FIELD = "instance_name"
606
  __slots__ = [
607
    "instance_name", "reboot_type", "ignore_secondaries", "shutdown_timeout",
814
  OP_PARAMS = [
815
    _PInstanceName,
816
    _PShutdownTimeout,
817
    ("ignore_secondaries", False, ht.TBool),
818
    ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES)),
608 819
    ]
609 820

  
610 821

  
......
612 823
  """Replace the disks of an instance."""
613 824
  OP_ID = "OP_INSTANCE_REPLACE_DISKS"
614 825
  OP_DSC_FIELD = "instance_name"
615
  __slots__ = [
616
    "instance_name", "remote_node", "mode", "disks", "iallocator",
617
    "early_release",
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),
618 833
    ]
619 834

  
620 835

  
......
622 837
  """Failover an instance."""
623 838
  OP_ID = "OP_INSTANCE_FAILOVER"
624 839
  OP_DSC_FIELD = "instance_name"
625
  __slots__ = [
626
    "instance_name", "ignore_consistency", "shutdown_timeout",
840
  OP_PARAMS = [
841
    _PInstanceName,
842
    _PShutdownTimeout,
843
    ("ignore_consistency", False, ht.TBool),
627 844
    ]
628 845

  
629 846

  
......
639 856
  """
640 857
  OP_ID = "OP_INSTANCE_MIGRATE"
641 858
  OP_DSC_FIELD = "instance_name"
642
  __slots__ = ["instance_name", "mode", "cleanup", "live"]
859
  OP_PARAMS = [
860
    _PInstanceName,
861
    _PMigrationMode,
862
    _PMigrationLive,
863
    ("cleanup", False, ht.TBool),
864
    ]
643 865

  
644 866

  
645 867
class OpMoveInstance(OpCode):
......
654 876
  """
655 877
  OP_ID = "OP_INSTANCE_MOVE"
656 878
  OP_DSC_FIELD = "instance_name"
657
  __slots__ = [
658
    "instance_name", "target_node", "shutdown_timeout",
879
  OP_PARAMS = [
880
    _PInstanceName,
881
    _PShutdownTimeout,
882
    ("target_node", ht.NoDefault, ht.TNonEmptyString),
659 883
    ]
660 884

  
661 885

  
......
663 887
  """Connect to an instance's console."""
664 888
  OP_ID = "OP_INSTANCE_CONSOLE"
665 889
  OP_DSC_FIELD = "instance_name"
666
  __slots__ = ["instance_name"]
890
  OP_PARAMS = [
891
    _PInstanceName
892
    ]
667 893

  
668 894

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

  
675 904

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

  
682 913

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

  
689 923

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

  
695 933

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

  
701 942

  
702 943
class OpSetInstanceParams(OpCode):
703 944
  """Change the parameters of an instance."""
704 945
  OP_ID = "OP_INSTANCE_SET_PARAMS"
705 946
  OP_DSC_FIELD = "instance_name"
706
  __slots__ = [
707
    "instance_name",
708
    "hvparams", "beparams", "osparams", "force",
709
    "nics", "disks", "disk_template",
710
    "remote_node", "os_name", "force_variant",
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)),
711 959
    ]
712 960

  
713 961

  
......
715 963
  """Grow a disk of an instance."""
716 964
  OP_ID = "OP_INSTANCE_GROW_DISK"
717 965
  OP_DSC_FIELD = "instance_name"
718
  __slots__ = [
719
    "instance_name", "disk", "amount", "wait_for_sync",
966
  OP_PARAMS = [
967
    _PInstanceName,
968
    ("disk", ht.NoDefault, ht.TInt),
969
    ("amount", ht.NoDefault, ht.TInt),
970
    ("wait_for_sync", True, ht.TBool),
720 971
    ]
721 972

  
722 973

  
......
726 977
  """Add a node group to the cluster."""
727 978
  OP_ID = "OP_GROUP_ADD"
728 979
  OP_DSC_FIELD = "group_name"
729
  __slots__ = [
730
    "group_name",
731
    "ndparams",
732
    "alloc_policy",
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))),
733 985
    ]
734 986

  
735 987

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

  
741 996

  
742 997
class OpSetGroupParams(OpCode):
743 998
  """Change the parameters of a node group."""
744 999
  OP_ID = "OP_GROUP_SET_PARAMS"
745 1000
  OP_DSC_FIELD = "group_name"
746
  __slots__ = [
747
    "group_name",
748
    "ndparams",
749
    "alloc_policy",
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))),
750 1006
    ]
751 1007

  
752 1008

  
......
754 1010
  """Remove a node group from the cluster."""
755 1011
  OP_ID = "OP_GROUP_REMOVE"
756 1012
  OP_DSC_FIELD = "group_name"
757
  __slots__ = ["group_name"]
1013
  OP_PARAMS = [
1014
    _PGroupName,
1015
    ]
758 1016

  
759 1017

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

  
766 1027

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

  
773 1037

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

  
780 1047

  
781 1048
class OpPrepareExport(OpCode):
......
787 1054
  """
788 1055
  OP_ID = "OP_BACKUP_PREPARE"
789 1056
  OP_DSC_FIELD = "instance_name"
790
  __slots__ = [
791
    "instance_name", "mode",
1057
  OP_PARAMS = [
1058
    _PInstanceName,
1059
    ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES)),
792 1060
    ]
793 1061

  
794 1062

  
......
810 1078
  """
811 1079
  OP_ID = "OP_BACKUP_EXPORT"
812 1080
  OP_DSC_FIELD = "instance_name"
813
  __slots__ = [
1081
  OP_PARAMS = [
1082
    _PInstanceName,
1083
    _PShutdownTimeout,
814 1084
    # TODO: Rename target_node as it changes meaning for different export modes
815 1085
    # (e.g. "destination")
816
    "instance_name", "target_node", "shutdown", "shutdown_timeout",
817
    "remove_instance",
818
    "ignore_remove_failures",
819
    "mode",
820
    "x509_key_name",
821
    "destination_x509_ca",
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),
822 1093
    ]
823 1094

  
824 1095

  
......
826 1097
  """Remove an instance's export."""
827 1098
  OP_ID = "OP_BACKUP_REMOVE"
828 1099
  OP_DSC_FIELD = "instance_name"
829
  __slots__ = ["instance_name"]
1100
  OP_PARAMS = [
1101
    _PInstanceName,
1102
    ]
830 1103

  
831 1104

  
832 1105
# Tags opcodes
......
834 1107
  """Returns the tags of the given object."""
835 1108
  OP_ID = "OP_TAGS_GET"
836 1109
  OP_DSC_FIELD = "name"
837
  __slots__ = ["kind", "name"]
1110
  OP_PARAMS = [
1111
    _PTagKind,
1112
    # Name is only meaningful for nodes and instances
1113
    ("name", ht.NoDefault, ht.TMaybeString),
1114
    ]
838 1115

  
839 1116

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

  
846 1125

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

  
852 1136

  
853 1137
class OpDelTags(OpCode):
854 1138
  """Remove a list of tags from a given object."""
855 1139
  OP_ID = "OP_TAGS_DEL"
856
  __slots__ = ["kind", "name", "tags"]
857

  
1140
  OP_PARAMS = [
1141
    _PTagKind,
1142
    _PTags,
1143
    # Name is only meaningful for nodes and instances
1144
    ("name", ht.NoDefault, ht.TMaybeString),
1145
    ]
858 1146

  
859 1147
# Test opcodes
860 1148
class OpTestDelay(OpCode):
......
880 1168
  """
881 1169
  OP_ID = "OP_TEST_DELAY"
882 1170
  OP_DSC_FIELD = "duration"
883
  __slots__ = ["duration", "on_master", "on_nodes", "repeat"]
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
    ]
884 1177

  
885 1178

  
886 1179
class OpTestAllocator(OpCode):
......
896 1189
  """
897 1190
  OP_ID = "OP_TEST_ALLOCATOR"
898 1191
  OP_DSC_FIELD = "allocator"
899
  __slots__ = [
900
    "direction", "mode", "allocator", "name",
901
    "mem_size", "disks", "disk_template",
902
    "os", "tags", "nics", "vcpus", "hypervisor",
903
    "evac_nodes",
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))),
904 1209
    ]
905 1210

  
906 1211

  
......
909 1214

  
910 1215
  """
911 1216
  OP_ID = "OP_TEST_JQUEUE"
912
  __slots__ = [
913
    "notify_waitlock",
914
    "notify_exec",
915
    "log_messages",
916
    "fail",
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),
917 1222
    ]
918 1223

  
919 1224

  
......
922 1227

  
923 1228
  """
924 1229
  OP_ID = "OP_TEST_DUMMY"
925
  __slots__ = [
926
    "result",
927
    "messages",
928
    "fail",
1230
  OP_PARAMS = [
1231
    ("result", ht.NoDefault, ht.NoType),
1232
    ("messages", ht.NoDefault, ht.NoType),
1233
    ("fail", ht.NoDefault, ht.NoType),
929 1234
    ]
930 1235

  
931 1236

  
b/test/ganeti.cmdlib_unittest.py
35 35
from ganeti import errors
36 36
from ganeti import utils
37 37
from ganeti import luxi
38
from ganeti import ht
38 39

  
39 40
import testutils
40 41
import mocks
......
106 107

  
107 108
    class TestOpcode(opcodes.OpCode):
108 109
      OP_ID = "OP_TEST"
109
      __slots__ = ["iallocator", "node"]
110
      OP_PARAMS = [
111
        ("iallocator", None, ht.NoType),
112
        ("node", None, ht.NoType),
113
        ]
110 114

  
111 115
    default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
112 116
    other_iallocator = default_iallocator + "_not"
b/test/ganeti.opcodes_unittest.py
27 27

  
28 28
from ganeti import utils
29 29
from ganeti import opcodes
30
from ganeti import ht
30 31

  
31 32
import testutils
32 33

  
......
69 70
        self.assert_(isinstance(restored, cls))
70 71
        self._checkSummary(restored)
71 72

  
73
        for name in ["x_y_z", "hello_world"]:
74
          assert name not in cls._all_slots()
75
          for value in [None, True, False, [], "Hello World"]:
76
            self.assertRaises(AttributeError, setattr, op, name, value)
77

  
72 78
  def _checkSummary(self, op):
73 79
    summary = op.Summary()
74 80

  
......
78 84
    else:
79 85
      self.assertEqual("OP_%s" % summary, op.OP_ID)
80 86

  
87
  def testParams(self):
88
    supported_by_all = set(["debug_level", "dry_run", "priority"])
89

  
90
    self.assert_(opcodes.BaseOpCode not in opcodes.OP_MAPPING.values())
91
    self.assert_(opcodes.OpCode in opcodes.OP_MAPPING.values())
92

  
93
    for cls in opcodes.OP_MAPPING.values():
94
      all_slots = cls._all_slots()
95

  
96
      self.assertEqual(len(set(all_slots) & supported_by_all), 3,
97
                       msg=("Opcode %s doesn't support all base"
98
                            " parameters (%r)" % (cls.OP_ID, supported_by_all)))
99

  
100
      # All opcodes must have OP_PARAMS
101
      self.assert_(hasattr(cls, "OP_PARAMS"),
102
                   msg="%s doesn't have OP_PARAMS" % cls.OP_ID)
103

  
104
      param_names = [name for (name, _, _) in cls.GetAllParams()]
105

  
106
      self.assertEqual(all_slots, param_names)
107

  
108
      # Without inheritance
109
      self.assertEqual(cls.__slots__, [name for (name, _, _) in cls.OP_PARAMS])
110

  
111
      # This won't work if parameters are converted to a dictionary
112
      duplicates = utils.FindDuplicates(param_names)
113
      self.assertFalse(duplicates,
114
                       msg=("Found duplicate parameters %r in %s" %
115
                            (duplicates, cls.OP_ID)))
116

  
117
      # Check parameter definitions
118
      for attr_name, aval, test in cls.GetAllParams():
119
        self.assert_(attr_name)
120
        self.assert_(test is None or test is ht.NoType or callable(test),
121
                     msg=("Invalid type check for %s.%s" %
122
                          (cls.OP_ID, attr_name)))
123

  
124
        if callable(aval):
125
          self.assertFalse(callable(aval()),
126
                           msg="Default value returned by function is callable")
127

  
81 128

  
82 129
if __name__ == "__main__":
83 130
  testutils.GanetiTestProgram()

Also available in: Unified diff