Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes.py @ d91d06e0

History | View | Annotate | Download (68.5 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""OpCodes module
23

24
This module implements the data structures which define the cluster
25
operations - the so-called opcodes.
26

27
Every operation which modifies the cluster state is expressed via
28
opcodes.
29

30
"""
31

    
32
# this are practically structures, so disable the message about too
33
# few public methods:
34
# pylint: disable=R0903
35

    
36
import logging
37
import re
38
import ipaddr
39

    
40
from ganeti import constants
41
from ganeti import errors
42
from ganeti import ht
43
from ganeti import objects
44
from ganeti import outils
45

    
46

    
47
# Common opcode attributes
48

    
49
#: output fields for a query operation
50
_POutputFields = ("output_fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
51
                  "Selected output fields")
52

    
53
#: the shutdown timeout
54
_PShutdownTimeout = \
55
  ("shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TNonNegativeInt,
56
   "How long to wait for instance to shut down")
57

    
58
#: the force parameter
59
_PForce = ("force", False, ht.TBool, "Whether to force the operation")
60

    
61
#: a required instance name (for single-instance LUs)
62
_PInstanceName = ("instance_name", ht.NoDefault, ht.TNonEmptyString,
63
                  "Instance name")
64

    
65
#: a instance UUID (for single-instance LUs)
66
_PInstanceUuid = ("instance_uuid", None, ht.TMaybeString,
67
                  "Instance UUID")
68

    
69
#: Whether to ignore offline nodes
70
_PIgnoreOfflineNodes = ("ignore_offline_nodes", False, ht.TBool,
71
                        "Whether to ignore offline nodes")
72

    
73
#: a required node name (for single-node LUs)
74
_PNodeName = ("node_name", ht.NoDefault, ht.TNonEmptyString, "Node name")
75

    
76
#: a node UUID (for use with _PNodeName)
77
_PNodeUuid = ("node_uuid", None, ht.TMaybeString, "Node UUID")
78

    
79
#: a required node group name (for single-group LUs)
80
_PGroupName = ("group_name", ht.NoDefault, ht.TNonEmptyString, "Group name")
81

    
82
#: Migration type (live/non-live)
83
_PMigrationMode = ("mode", None,
84
                   ht.TMaybe(ht.TElemOf(constants.HT_MIGRATION_MODES)),
85
                   "Migration mode")
86

    
87
#: Obsolete 'live' migration mode (boolean)
88
_PMigrationLive = ("live", None, ht.TMaybeBool,
89
                   "Legacy setting for live migration, do not use")
90

    
91
#: Tag type
92
_PTagKind = ("kind", ht.NoDefault, ht.TElemOf(constants.VALID_TAG_TYPES),
93
             "Tag kind")
94

    
95
#: List of tag strings
96
_PTags = ("tags", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
97
          "List of tag names")
98

    
99
_PForceVariant = ("force_variant", False, ht.TBool,
100
                  "Whether to force an unknown OS variant")
101

    
102
_PWaitForSync = ("wait_for_sync", True, ht.TBool,
103
                 "Whether to wait for the disk to synchronize")
104

    
105
_PWaitForSyncFalse = ("wait_for_sync", False, ht.TBool,
106
                      "Whether to wait for the disk to synchronize"
107
                      " (defaults to false)")
108

    
109
_PIgnoreConsistency = ("ignore_consistency", False, ht.TBool,
110
                       "Whether to ignore disk consistency")
111

    
112
_PStorageName = ("name", ht.NoDefault, ht.TMaybeString, "Storage name")
113

    
114
_PUseLocking = ("use_locking", False, ht.TBool,
115
                "Whether to use synchronization")
116

    
117
_PNameCheck = ("name_check", True, ht.TBool, "Whether to check name")
118

    
119
_PNodeGroupAllocPolicy = \
120
  ("alloc_policy", None,
121
   ht.TMaybe(ht.TElemOf(constants.VALID_ALLOC_POLICIES)),
122
   "Instance allocation policy")
123

    
124
_PGroupNodeParams = ("ndparams", None, ht.TMaybeDict,
125
                     "Default node parameters for group")
126

    
127
_PQueryWhat = ("what", ht.NoDefault, ht.TElemOf(constants.QR_VIA_OP),
128
               "Resource(s) to query for")
129

    
130
_PEarlyRelease = ("early_release", False, ht.TBool,
131
                  "Whether to release locks as soon as possible")
132

    
133
_PIpCheckDoc = "Whether to ensure instance's IP address is inactive"
134

    
135
#: Do not remember instance state changes
136
_PNoRemember = ("no_remember", False, ht.TBool,
137
                "Do not remember the state change")
138

    
139
#: Target node for instance migration/failover
140
_PMigrationTargetNode = ("target_node", None, ht.TMaybeString,
141
                         "Target node for shared-storage instances")
142

    
143
_PMigrationTargetNodeUuid = ("target_node_uuid", None, ht.TMaybeString,
144
                             "Target node UUID for shared-storage instances")
145

    
146
_PStartupPaused = ("startup_paused", False, ht.TBool,
147
                   "Pause instance at startup")
148

    
149
_PVerbose = ("verbose", False, ht.TBool, "Verbose mode")
150

    
151
# Parameters for cluster verification
152
_PDebugSimulateErrors = ("debug_simulate_errors", False, ht.TBool,
153
                         "Whether to simulate errors (useful for debugging)")
154
_PErrorCodes = ("error_codes", False, ht.TBool, "Error codes")
155
_PSkipChecks = ("skip_checks", ht.EmptyList,
156
                ht.TListOf(ht.TElemOf(constants.VERIFY_OPTIONAL_CHECKS)),
157
                "Which checks to skip")
158
_PIgnoreErrors = ("ignore_errors", ht.EmptyList,
159
                  ht.TListOf(ht.TElemOf(constants.CV_ALL_ECODES_STRINGS)),
160
                  "List of error codes that should be treated as warnings")
161

    
162
# Disk parameters
163
_PDiskParams = \
164
  ("diskparams", None,
165
   ht.TMaybe(ht.TDictOf(ht.TElemOf(constants.DISK_TEMPLATES), ht.TDict)),
166
   "Disk templates' parameter defaults")
167

    
168
# Parameters for node resource model
169
_PHvState = ("hv_state", None, ht.TMaybeDict, "Set hypervisor states")
170
_PDiskState = ("disk_state", None, ht.TMaybeDict, "Set disk states")
171

    
172
#: Opportunistic locking
173
_POpportunisticLocking = \
174
  ("opportunistic_locking", False, ht.TBool,
175
   ("Whether to employ opportunistic locking for nodes, meaning nodes"
176
    " already locked by another opcode won't be considered for instance"
177
    " allocation (only when an iallocator is used)"))
178

    
179
_PIgnoreIpolicy = ("ignore_ipolicy", False, ht.TBool,
180
                   "Whether to ignore ipolicy violations")
181

    
182
# Allow runtime changes while migrating
183
_PAllowRuntimeChgs = ("allow_runtime_changes", True, ht.TBool,
184
                      "Allow runtime changes (eg. memory ballooning)")
185

    
186
#: IAllocator field builder
187
_PIAllocFromDesc = lambda desc: ("iallocator", None, ht.TMaybeString, desc)
188

    
189
#: a required network name
190
_PNetworkName = ("network_name", ht.NoDefault, ht.TNonEmptyString,
191
                 "Set network name")
192

    
193
_PTargetGroups = \
194
  ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString),
195
   "Destination group names or UUIDs (defaults to \"all but current group\")")
196

    
197
#: OP_ID conversion regular expression
198
_OPID_RE = re.compile("([a-z])([A-Z])")
199

    
200
#: Utility function for L{OpClusterSetParams}
201
_TestClusterOsListItem = \
202
  ht.TAnd(ht.TIsLength(2), ht.TItems([
203
    ht.TElemOf(constants.DDMS_VALUES),
204
    ht.TNonEmptyString,
205
    ]))
206

    
207
_TestClusterOsList = ht.TMaybeListOf(_TestClusterOsListItem)
208

    
209
# TODO: Generate check from constants.INIC_PARAMS_TYPES
210
#: Utility function for testing NIC definitions
211
_TestNicDef = \
212
  ht.Comment("NIC parameters")(ht.TDictOf(ht.TElemOf(constants.INIC_PARAMS),
213
                                          ht.TMaybeString))
214

    
215
_TSetParamsResultItemItems = [
216
  ht.Comment("name of changed parameter")(ht.TNonEmptyString),
217
  ht.Comment("new value")(ht.TAny),
218
  ]
219

    
220
_TSetParamsResult = \
221
  ht.TListOf(ht.TAnd(ht.TIsLength(len(_TSetParamsResultItemItems)),
222
                     ht.TItems(_TSetParamsResultItemItems)))
223

    
224
# In the disks option we can provide arbitrary parameters too, which
225
# we may not be able to validate at this level, so we just check the
226
# format of the dict here and the checks concerning IDISK_PARAMS will
227
# happen at the LU level
228
_TDiskParams = \
229
  ht.Comment("Disk parameters")(ht.TDictOf(ht.TNonEmptyString,
230
                                           ht.TOr(ht.TNonEmptyString, ht.TInt)))
231

    
232
_TQueryRow = \
233
  ht.TListOf(ht.TAnd(ht.TIsLength(2),
234
                     ht.TItems([ht.TElemOf(constants.RS_ALL),
235
                                ht.TAny])))
236

    
237
_TQueryResult = ht.TListOf(_TQueryRow)
238

    
239
_TOldQueryRow = ht.TListOf(ht.TAny)
240

    
241
_TOldQueryResult = ht.TListOf(_TOldQueryRow)
242

    
243

    
244
_SUMMARY_PREFIX = {
245
  "CLUSTER_": "C_",
246
  "GROUP_": "G_",
247
  "NODE_": "N_",
248
  "INSTANCE_": "I_",
249
  }
250

    
251
#: Attribute name for dependencies
252
DEPEND_ATTR = "depends"
253

    
254
#: Attribute name for comment
255
COMMENT_ATTR = "comment"
256

    
257

    
258
def _NameComponents(name):
259
  """Split an opcode class name into its components
260

261
  @type name: string
262
  @param name: the class name, as OpXxxYyy
263
  @rtype: array of strings
264
  @return: the components of the name
265

266
  """
267
  assert name.startswith("Op")
268
  # Note: (?<=[a-z])(?=[A-Z]) would be ideal, since it wouldn't
269
  # consume any input, and hence we would just have all the elements
270
  # in the list, one by one; but it seems that split doesn't work on
271
  # non-consuming input, hence we have to process the input string a
272
  # bit
273
  name = _OPID_RE.sub(r"\1,\2", name)
274
  elems = name.split(",")
275
  return elems
276

    
277

    
278
def _NameToId(name):
279
  """Convert an opcode class name to an OP_ID.
280

281
  @type name: string
282
  @param name: the class name, as OpXxxYyy
283
  @rtype: string
284
  @return: the name in the OP_XXXX_YYYY format
285

286
  """
287
  if not name.startswith("Op"):
288
    return None
289
  return "_".join(n.upper() for n in _NameComponents(name))
290

    
291

    
292
def NameToReasonSrc(name):
293
  """Convert an opcode class name to a source string for the reason trail
294

295
  @type name: string
296
  @param name: the class name, as OpXxxYyy
297
  @rtype: string
298
  @return: the name in the OP_XXXX_YYYY format
299

300
  """
301
  if not name.startswith("Op"):
302
    return None
303
  return "%s:%s" % (constants.OPCODE_REASON_SRC_OPCODE,
304
                    "_".join(n.lower() for n in _NameComponents(name)))
305

    
306

    
307
def _GenerateObjectTypeCheck(obj, fields_types):
308
  """Helper to generate type checks for objects.
309

310
  @param obj: The object to generate type checks
311
  @param fields_types: The fields and their types as a dict
312
  @return: A ht type check function
313

314
  """
315
  assert set(obj.GetAllSlots()) == set(fields_types.keys()), \
316
    "%s != %s" % (set(obj.GetAllSlots()), set(fields_types.keys()))
317
  return ht.TStrictDict(True, True, fields_types)
318

    
319

    
320
_TQueryFieldDef = \
321
  _GenerateObjectTypeCheck(objects.QueryFieldDefinition, {
322
    "name": ht.TNonEmptyString,
323
    "title": ht.TNonEmptyString,
324
    "kind": ht.TElemOf(constants.QFT_ALL),
325
    "doc": ht.TNonEmptyString,
326
    })
327

    
328

    
329
def _BuildDiskTemplateCheck(accept_none):
330
  """Builds check for disk template.
331

332
  @type accept_none: bool
333
  @param accept_none: whether to accept None as a correct value
334
  @rtype: callable
335

336
  """
337
  template_check = ht.TElemOf(constants.DISK_TEMPLATES)
338

    
339
  if accept_none:
340
    template_check = ht.TMaybe(template_check)
341

    
342
  return template_check
343

    
344

    
345
def _CheckStorageType(storage_type):
346
  """Ensure a given storage type is valid.
347

348
  """
349
  if storage_type not in constants.STORAGE_TYPES:
350
    raise errors.OpPrereqError("Unknown storage type: %s" % storage_type,
351
                               errors.ECODE_INVAL)
352
  return True
353

    
354

    
355
#: Storage type parameter
356
_PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType,
357
                 "Storage type")
358

    
359

    
360
@ht.WithDesc("IPv4 network")
361
def _CheckCIDRNetNotation(value):
362
  """Ensure a given CIDR notation type is valid.
363

364
  """
365
  try:
366
    ipaddr.IPv4Network(value)
367
  except ipaddr.AddressValueError:
368
    return False
369
  return True
370

    
371

    
372
@ht.WithDesc("IPv4 address")
373
def _CheckCIDRAddrNotation(value):
374
  """Ensure a given CIDR notation type is valid.
375

376
  """
377
  try:
378
    ipaddr.IPv4Address(value)
379
  except ipaddr.AddressValueError:
380
    return False
381
  return True
382

    
383

    
384
@ht.WithDesc("IPv6 address")
385
def _CheckCIDR6AddrNotation(value):
386
  """Ensure a given CIDR notation type is valid.
387

388
  """
389
  try:
390
    ipaddr.IPv6Address(value)
391
  except ipaddr.AddressValueError:
392
    return False
393
  return True
394

    
395

    
396
@ht.WithDesc("IPv6 network")
397
def _CheckCIDR6NetNotation(value):
398
  """Ensure a given CIDR notation type is valid.
399

400
  """
401
  try:
402
    ipaddr.IPv6Network(value)
403
  except ipaddr.AddressValueError:
404
    return False
405
  return True
406

    
407

    
408
_TIpAddress4 = ht.TAnd(ht.TString, _CheckCIDRAddrNotation)
409
_TIpAddress6 = ht.TAnd(ht.TString, _CheckCIDR6AddrNotation)
410
_TIpNetwork4 = ht.TAnd(ht.TString, _CheckCIDRNetNotation)
411
_TIpNetwork6 = ht.TAnd(ht.TString, _CheckCIDR6NetNotation)
412
_TMaybeAddr4List = ht.TMaybe(ht.TListOf(_TIpAddress4))
413

    
414

    
415
class _AutoOpParamSlots(outils.AutoSlots):
416
  """Meta class for opcode definitions.
417

418
  """
419
  def __new__(mcs, name, bases, attrs):
420
    """Called when a class should be created.
421

422
    @param mcs: The meta class
423
    @param name: Name of created class
424
    @param bases: Base classes
425
    @type attrs: dict
426
    @param attrs: Class attributes
427

428
    """
429
    assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
430

    
431
    slots = mcs._GetSlots(attrs)
432
    assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
433
      "Class '%s' uses unknown field in OP_DSC_FIELD" % name
434
    assert ("OP_DSC_FORMATTER" not in attrs or
435
            callable(attrs["OP_DSC_FORMATTER"])), \
436
      ("Class '%s' uses non-callable in OP_DSC_FORMATTER (%s)" %
437
       (name, type(attrs["OP_DSC_FORMATTER"])))
438

    
439
    attrs["OP_ID"] = _NameToId(name)
440

    
441
    return outils.AutoSlots.__new__(mcs, name, bases, attrs)
442

    
443
  @classmethod
444
  def _GetSlots(mcs, attrs):
445
    """Build the slots out of OP_PARAMS.
446

447
    """
448
    # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams
449
    params = attrs.setdefault("OP_PARAMS", [])
450

    
451
    # Use parameter names as slots
452
    return [pname for (pname, _, _, _) in params]
453

    
454

    
455
class BaseOpCode(outils.ValidatedSlots):
456
  """A simple serializable object.
457

458
  This object serves as a parent class for OpCode without any custom
459
  field handling.
460

461
  """
462
  # pylint: disable=E1101
463
  # as OP_ID is dynamically defined
464
  __metaclass__ = _AutoOpParamSlots
465

    
466
  def __getstate__(self):
467
    """Generic serializer.
468

469
    This method just returns the contents of the instance as a
470
    dictionary.
471

472
    @rtype:  C{dict}
473
    @return: the instance attributes and their values
474

475
    """
476
    state = {}
477
    for name in self.GetAllSlots():
478
      if hasattr(self, name):
479
        state[name] = getattr(self, name)
480
    return state
481

    
482
  def __setstate__(self, state):
483
    """Generic unserializer.
484

485
    This method just restores from the serialized state the attributes
486
    of the current instance.
487

488
    @param state: the serialized opcode data
489
    @type state:  C{dict}
490

491
    """
492
    if not isinstance(state, dict):
493
      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
494
                       type(state))
495

    
496
    for name in self.GetAllSlots():
497
      if name not in state and hasattr(self, name):
498
        delattr(self, name)
499

    
500
    for name in state:
501
      setattr(self, name, state[name])
502

    
503
  @classmethod
504
  def GetAllParams(cls):
505
    """Compute list of all parameters for an opcode.
506

507
    """
508
    slots = []
509
    for parent in cls.__mro__:
510
      slots.extend(getattr(parent, "OP_PARAMS", []))
511
    return slots
512

    
513
  def Validate(self, set_defaults): # pylint: disable=W0221
514
    """Validate opcode parameters, optionally setting default values.
515

516
    @type set_defaults: bool
517
    @param set_defaults: Whether to set default values
518
    @raise errors.OpPrereqError: When a parameter value doesn't match
519
                                 requirements
520

521
    """
522
    for (attr_name, default, test, _) in self.GetAllParams():
523
      assert test == ht.NoType or callable(test)
524

    
525
      if not hasattr(self, attr_name):
526
        if default == ht.NoDefault:
527
          raise errors.OpPrereqError("Required parameter '%s.%s' missing" %
528
                                     (self.OP_ID, attr_name),
529
                                     errors.ECODE_INVAL)
530
        elif set_defaults:
531
          if callable(default):
532
            dval = default()
533
          else:
534
            dval = default
535
          setattr(self, attr_name, dval)
536

    
537
      if test == ht.NoType:
538
        # no tests here
539
        continue
540

    
541
      if set_defaults or hasattr(self, attr_name):
542
        attr_val = getattr(self, attr_name)
543
        if not test(attr_val):
544
          logging.error("OpCode %s, parameter %s, has invalid type %s/value"
545
                        " '%s' expecting type %s",
546
                        self.OP_ID, attr_name, type(attr_val), attr_val, test)
547
          raise errors.OpPrereqError("Parameter '%s.%s' fails validation" %
548
                                     (self.OP_ID, attr_name),
549
                                     errors.ECODE_INVAL)
550

    
551

    
552
def _BuildJobDepCheck(relative):
553
  """Builds check for job dependencies (L{DEPEND_ATTR}).
554

555
  @type relative: bool
556
  @param relative: Whether to accept relative job IDs (negative)
557
  @rtype: callable
558

559
  """
560
  if relative:
561
    job_id = ht.TOr(ht.TJobId, ht.TRelativeJobId)
562
  else:
563
    job_id = ht.TJobId
564

    
565
  job_dep = \
566
    ht.TAnd(ht.TOr(ht.TList, ht.TTuple),
567
            ht.TIsLength(2),
568
            ht.TItems([job_id,
569
                       ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))]))
570

    
571
  return ht.TMaybeListOf(job_dep)
572

    
573

    
574
TNoRelativeJobDependencies = _BuildJobDepCheck(False)
575

    
576
#: List of submission status and job ID as returned by C{SubmitManyJobs}
577
_TJobIdListItem = \
578
  ht.TAnd(ht.TIsLength(2),
579
          ht.TItems([ht.Comment("success")(ht.TBool),
580
                     ht.Comment("Job ID if successful, error message"
581
                                " otherwise")(ht.TOr(ht.TString,
582
                                                     ht.TJobId))]))
583
TJobIdList = ht.TListOf(_TJobIdListItem)
584

    
585
#: Result containing only list of submitted jobs
586
TJobIdListOnly = ht.TStrictDict(True, True, {
587
  constants.JOB_IDS_KEY: ht.Comment("List of submitted jobs")(TJobIdList),
588
  })
589

    
590

    
591
class OpCode(BaseOpCode):
592
  """Abstract OpCode.
593

594
  This is the root of the actual OpCode hierarchy. All clases derived
595
  from this class should override OP_ID.
596

597
  @cvar OP_ID: The ID of this opcode. This should be unique amongst all
598
               children of this class.
599
  @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
600
                      string returned by Summary(); see the docstring of that
601
                      method for details).
602
  @cvar OP_DSC_FORMATTER: A callable that should format the OP_DSC_FIELD; if
603
                          not present, then the field will be simply converted
604
                          to string
605
  @cvar OP_PARAMS: List of opcode attributes, the default values they should
606
                   get if not already defined, and types they must match.
607
  @cvar OP_RESULT: Callable to verify opcode result
608
  @cvar WITH_LU: Boolean that specifies whether this should be included in
609
      mcpu's dispatch table
610
  @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
611
                 the check steps
612
  @ivar priority: Opcode priority for queue
613

614
  """
615
  # pylint: disable=E1101
616
  # as OP_ID is dynamically defined
617
  WITH_LU = True
618
  OP_PARAMS = [
619
    ("dry_run", None, ht.TMaybeBool, "Run checks only, don't execute"),
620
    ("debug_level", None, ht.TMaybe(ht.TNonNegativeInt), "Debug level"),
621
    ("priority", constants.OP_PRIO_DEFAULT,
622
     ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID), "Opcode priority"),
623
    (DEPEND_ATTR, None, _BuildJobDepCheck(True),
624
     "Job dependencies; if used through ``SubmitManyJobs`` relative (negative)"
625
     " job IDs can be used; see :doc:`design document <design-chained-jobs>`"
626
     " for details"),
627
    (COMMENT_ATTR, None, ht.TMaybeString,
628
     "Comment describing the purpose of the opcode"),
629
    (constants.OPCODE_REASON, None, ht.TMaybeList,
630
     "The reason trail, describing why the OpCode is executed"),
631
    ]
632
  OP_RESULT = None
633

    
634
  def __getstate__(self):
635
    """Specialized getstate for opcodes.
636

637
    This method adds to the state dictionary the OP_ID of the class,
638
    so that on unload we can identify the correct class for
639
    instantiating the opcode.
640

641
    @rtype:   C{dict}
642
    @return:  the state as a dictionary
643

644
    """
645
    data = BaseOpCode.__getstate__(self)
646
    data["OP_ID"] = self.OP_ID
647
    return data
648

    
649
  @classmethod
650
  def LoadOpCode(cls, data):
651
    """Generic load opcode method.
652

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

657
    @type data:  C{dict}
658
    @param data: the serialized opcode
659

660
    """
661
    if not isinstance(data, dict):
662
      raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
663
    if "OP_ID" not in data:
664
      raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
665
    op_id = data["OP_ID"]
666
    op_class = None
667
    if op_id in OP_MAPPING:
668
      op_class = OP_MAPPING[op_id]
669
    else:
670
      raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
671
                       op_id)
672
    op = op_class()
673
    new_data = data.copy()
674
    del new_data["OP_ID"]
675
    op.__setstate__(new_data)
676
    return op
677

    
678
  def Summary(self):
679
    """Generates a summary description of this opcode.
680

681
    The summary is the value of the OP_ID attribute (without the "OP_"
682
    prefix), plus the value of the OP_DSC_FIELD attribute, if one was
683
    defined; this field should allow to easily identify the operation
684
    (for an instance creation job, e.g., it would be the instance
685
    name).
686

687
    """
688
    assert self.OP_ID is not None and len(self.OP_ID) > 3
689
    # all OP_ID start with OP_, we remove that
690
    txt = self.OP_ID[3:]
691
    field_name = getattr(self, "OP_DSC_FIELD", None)
692
    if field_name:
693
      field_value = getattr(self, field_name, None)
694
      field_formatter = getattr(self, "OP_DSC_FORMATTER", None)
695
      if callable(field_formatter):
696
        field_value = field_formatter(field_value)
697
      elif isinstance(field_value, (list, tuple)):
698
        field_value = ",".join(str(i) for i in field_value)
699
      txt = "%s(%s)" % (txt, field_value)
700
    return txt
701

    
702
  def TinySummary(self):
703
    """Generates a compact summary description of the opcode.
704

705
    """
706
    assert self.OP_ID.startswith("OP_")
707

    
708
    text = self.OP_ID[3:]
709

    
710
    for (prefix, supplement) in _SUMMARY_PREFIX.items():
711
      if text.startswith(prefix):
712
        return supplement + text[len(prefix):]
713

    
714
    return text
715

    
716

    
717
# cluster opcodes
718

    
719
class OpClusterPostInit(OpCode):
720
  """Post cluster initialization.
721

722
  This opcode does not touch the cluster at all. Its purpose is to run hooks
723
  after the cluster has been initialized.
724

725
  """
726
  OP_RESULT = ht.TBool
727

    
728

    
729
class OpClusterDestroy(OpCode):
730
  """Destroy the cluster.
731

732
  This opcode has no other parameters. All the state is irreversibly
733
  lost after the execution of this opcode.
734

735
  """
736
  OP_RESULT = ht.TNonEmptyString
737

    
738

    
739
class OpClusterQuery(OpCode):
740
  """Query cluster information."""
741
  OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TAny)
742

    
743

    
744
class OpClusterVerify(OpCode):
745
  """Submits all jobs necessary to verify the cluster.
746

747
  """
748
  OP_PARAMS = [
749
    _PDebugSimulateErrors,
750
    _PErrorCodes,
751
    _PSkipChecks,
752
    _PIgnoreErrors,
753
    _PVerbose,
754
    ("group_name", None, ht.TMaybeString, "Group to verify"),
755
    ]
756
  OP_RESULT = TJobIdListOnly
757

    
758

    
759
class OpClusterVerifyConfig(OpCode):
760
  """Verify the cluster config.
761

762
  """
763
  OP_PARAMS = [
764
    _PDebugSimulateErrors,
765
    _PErrorCodes,
766
    _PIgnoreErrors,
767
    _PVerbose,
768
    ]
769
  OP_RESULT = ht.TBool
770

    
771

    
772
class OpClusterVerifyGroup(OpCode):
773
  """Run verify on a node group from the cluster.
774

775
  @type skip_checks: C{list}
776
  @ivar skip_checks: steps to be skipped from the verify process; this
777
                     needs to be a subset of
778
                     L{constants.VERIFY_OPTIONAL_CHECKS}; currently
779
                     only L{constants.VERIFY_NPLUSONE_MEM} can be passed
780

781
  """
782
  OP_DSC_FIELD = "group_name"
783
  OP_PARAMS = [
784
    _PGroupName,
785
    _PDebugSimulateErrors,
786
    _PErrorCodes,
787
    _PSkipChecks,
788
    _PIgnoreErrors,
789
    _PVerbose,
790
    ]
791
  OP_RESULT = ht.TBool
792

    
793

    
794
class OpClusterVerifyDisks(OpCode):
795
  """Verify the cluster disks.
796

797
  """
798
  OP_RESULT = TJobIdListOnly
799

    
800

    
801
class OpGroupVerifyDisks(OpCode):
802
  """Verifies the status of all disks in a node group.
803

804
  Result: a tuple of three elements:
805
    - dict of node names with issues (values: error msg)
806
    - list of instances with degraded disks (that should be activated)
807
    - dict of instances with missing logical volumes (values: (node, vol)
808
      pairs with details about the missing volumes)
809

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

815
  Note that only instances that are drbd-based are taken into
816
  consideration. This might need to be revisited in the future.
817

818
  """
819
  OP_DSC_FIELD = "group_name"
820
  OP_PARAMS = [
821
    _PGroupName,
822
    ]
823
  OP_RESULT = \
824
    ht.TAnd(ht.TIsLength(3),
825
            ht.TItems([ht.TDictOf(ht.TString, ht.TString),
826
                       ht.TListOf(ht.TString),
827
                       ht.TDictOf(ht.TString,
828
                                  ht.TListOf(ht.TListOf(ht.TString)))]))
829

    
830

    
831
class OpClusterRepairDiskSizes(OpCode):
832
  """Verify the disk sizes of the instances and fixes configuration
833
  mimatches.
834

835
  Parameters: optional instances list, in case we want to restrict the
836
  checks to only a subset of the instances.
837

838
  Result: a list of tuples, (instance, disk, parameter, new-size) for changed
839
  configurations.
840

841
  In normal operation, the list should be empty.
842

843
  @type instances: list
844
  @ivar instances: the list of instances to check, or empty for all instances
845

846
  """
847
  OP_PARAMS = [
848
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
849
    ]
850
  OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(4),
851
                                 ht.TItems([ht.TNonEmptyString,
852
                                            ht.TNonNegativeInt,
853
                                            ht.TNonEmptyString,
854
                                            ht.TNonNegativeInt])))
855

    
856

    
857
class OpClusterConfigQuery(OpCode):
858
  """Query cluster configuration values."""
859
  OP_PARAMS = [
860
    _POutputFields,
861
    ]
862
  OP_RESULT = ht.TListOf(ht.TAny)
863

    
864

    
865
class OpClusterRename(OpCode):
866
  """Rename the cluster.
867

868
  @type name: C{str}
869
  @ivar name: The new name of the cluster. The name and/or the master IP
870
              address will be changed to match the new name and its IP
871
              address.
872

873
  """
874
  OP_DSC_FIELD = "name"
875
  OP_PARAMS = [
876
    ("name", ht.NoDefault, ht.TNonEmptyString, None),
877
    ]
878
  OP_RESULT = ht.TNonEmptyString
879

    
880

    
881
class OpClusterSetParams(OpCode):
882
  """Change the parameters of the cluster.
883

884
  @type vg_name: C{str} or C{None}
885
  @ivar vg_name: The new volume group name or None to disable LVM usage.
886

887
  """
888
  OP_PARAMS = [
889
    _PForce,
890
    _PHvState,
891
    _PDiskState,
892
    ("vg_name", None, ht.TMaybe(ht.TString), "Volume group name"),
893
    ("enabled_hypervisors", None,
894
     ht.TMaybe(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)),
895
                       ht.TTrue)),
896
     "List of enabled hypervisors"),
897
    ("hvparams", None,
898
     ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)),
899
     "Cluster-wide hypervisor parameter defaults, hypervisor-dependent"),
900
    ("beparams", None, ht.TMaybeDict,
901
     "Cluster-wide backend parameter defaults"),
902
    ("os_hvp", None, ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)),
903
     "Cluster-wide per-OS hypervisor parameter defaults"),
904
    ("osparams", None,
905
     ht.TMaybe(ht.TDictOf(ht.TNonEmptyString, ht.TDict)),
906
     "Cluster-wide OS parameter defaults"),
907
    _PDiskParams,
908
    ("candidate_pool_size", None, ht.TMaybe(ht.TPositiveInt),
909
     "Master candidate pool size"),
910
    ("uid_pool", None, ht.NoType,
911
     "Set UID pool, must be list of lists describing UID ranges (two items,"
912
     " start and end inclusive)"),
913
    ("add_uids", None, ht.NoType,
914
     "Extend UID pool, must be list of lists describing UID ranges (two"
915
     " items, start and end inclusive) to be added"),
916
    ("remove_uids", None, ht.NoType,
917
     "Shrink UID pool, must be list of lists describing UID ranges (two"
918
     " items, start and end inclusive) to be removed"),
919
    ("maintain_node_health", None, ht.TMaybeBool,
920
     "Whether to automatically maintain node health"),
921
    ("prealloc_wipe_disks", None, ht.TMaybeBool,
922
     "Whether to wipe disks before allocating them to instances"),
923
    ("nicparams", None, ht.TMaybeDict, "Cluster-wide NIC parameter defaults"),
924
    ("ndparams", None, ht.TMaybeDict, "Cluster-wide node parameter defaults"),
925
    ("ipolicy", None, ht.TMaybeDict,
926
     "Cluster-wide :ref:`instance policy <rapi-ipolicy>` specs"),
927
    ("drbd_helper", None, ht.TMaybe(ht.TString), "DRBD helper program"),
928
    ("default_iallocator", None, ht.TMaybe(ht.TString),
929
     "Default iallocator for cluster"),
930
    ("master_netdev", None, ht.TMaybe(ht.TString),
931
     "Master network device"),
932
    ("master_netmask", None, ht.TMaybe(ht.TNonNegativeInt),
933
     "Netmask of the master IP"),
934
    ("reserved_lvs", None, ht.TMaybeListOf(ht.TNonEmptyString),
935
     "List of reserved LVs"),
936
    ("hidden_os", None, _TestClusterOsList,
937
     "Modify list of hidden operating systems: each modification must have"
938
     " two items, the operation and the OS name; the operation can be"
939
     " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)),
940
    ("blacklisted_os", None, _TestClusterOsList,
941
     "Modify list of blacklisted operating systems: each modification must"
942
     " have two items, the operation and the OS name; the operation can be"
943
     " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)),
944
    ("use_external_mip_script", None, ht.TMaybeBool,
945
     "Whether to use an external master IP address setup script"),
946
    ("enabled_disk_templates", None,
947
     ht.TMaybe(ht.TAnd(ht.TListOf(ht.TElemOf(constants.DISK_TEMPLATES)),
948
                       ht.TTrue)),
949
     "List of enabled disk templates"),
950
    ("modify_etc_hosts", None, ht.TMaybeBool,
951
     "Whether the cluster can modify and keep in sync the /etc/hosts files"),
952
    ("file_storage_dir", None, ht.TMaybeString,
953
     "Default directory for storing file-backed disks"),
954
    ]
955
  OP_RESULT = ht.TNone
956

    
957

    
958
class OpClusterRedistConf(OpCode):
959
  """Force a full push of the cluster configuration.
960

961
  """
962
  OP_RESULT = ht.TNone
963

    
964

    
965
class OpClusterActivateMasterIp(OpCode):
966
  """Activate the master IP on the master node.
967

968
  """
969
  OP_RESULT = ht.TNone
970

    
971

    
972
class OpClusterDeactivateMasterIp(OpCode):
973
  """Deactivate the master IP on the master node.
974

975
  """
976
  OP_RESULT = ht.TNone
977

    
978

    
979
class OpQuery(OpCode):
980
  """Query for resources/items.
981

982
  @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
983
  @ivar fields: List of fields to retrieve
984
  @ivar qfilter: Query filter
985

986
  """
987
  OP_DSC_FIELD = "what"
988
  OP_PARAMS = [
989
    _PQueryWhat,
990
    _PUseLocking,
991
    ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
992
     "Requested fields"),
993
    ("qfilter", None, ht.TMaybe(ht.TList),
994
     "Query filter"),
995
    ]
996
  OP_RESULT = \
997
    _GenerateObjectTypeCheck(objects.QueryResponse, {
998
      "fields": ht.TListOf(_TQueryFieldDef),
999
      "data": _TQueryResult,
1000
      })
1001

    
1002

    
1003
class OpQueryFields(OpCode):
1004
  """Query for available resource/item fields.
1005

1006
  @ivar what: Resources to query for, must be one of L{constants.QR_VIA_OP}
1007
  @ivar fields: List of fields to retrieve
1008

1009
  """
1010
  OP_DSC_FIELD = "what"
1011
  OP_PARAMS = [
1012
    _PQueryWhat,
1013
    ("fields", None, ht.TMaybeListOf(ht.TNonEmptyString),
1014
     "Requested fields; if not given, all are returned"),
1015
    ]
1016
  OP_RESULT = \
1017
    _GenerateObjectTypeCheck(objects.QueryFieldsResponse, {
1018
      "fields": ht.TListOf(_TQueryFieldDef),
1019
      })
1020

    
1021

    
1022
class OpOobCommand(OpCode):
1023
  """Interact with OOB."""
1024
  OP_PARAMS = [
1025
    ("node_names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1026
     "List of node names to run the OOB command against"),
1027
    ("node_uuids", None, ht.TMaybeListOf(ht.TNonEmptyString),
1028
     "List of node UUIDs to run the OOB command against"),
1029
    ("command", ht.NoDefault, ht.TElemOf(constants.OOB_COMMANDS),
1030
     "OOB command to be run"),
1031
    ("timeout", constants.OOB_TIMEOUT, ht.TInt,
1032
     "Timeout before the OOB helper will be terminated"),
1033
    ("ignore_status", False, ht.TBool,
1034
     "Ignores the node offline status for power off"),
1035
    ("power_delay", constants.OOB_POWER_DELAY, ht.TNonNegativeFloat,
1036
     "Time in seconds to wait between powering on nodes"),
1037
    ]
1038
  # Fixme: Make it more specific with all the special cases in LUOobCommand
1039
  OP_RESULT = _TQueryResult
1040

    
1041

    
1042
class OpRestrictedCommand(OpCode):
1043
  """Runs a restricted command on node(s).
1044

1045
  """
1046
  OP_PARAMS = [
1047
    _PUseLocking,
1048
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
1049
     "Nodes on which the command should be run (at least one)"),
1050
    ("node_uuids", None, ht.TMaybeListOf(ht.TNonEmptyString),
1051
     "Node UUIDs on which the command should be run (at least one)"),
1052
    ("command", ht.NoDefault, ht.TNonEmptyString,
1053
     "Command name (no parameters)"),
1054
    ]
1055

    
1056
  _RESULT_ITEMS = [
1057
    ht.Comment("success")(ht.TBool),
1058
    ht.Comment("output or error message")(ht.TString),
1059
    ]
1060

    
1061
  OP_RESULT = \
1062
    ht.TListOf(ht.TAnd(ht.TIsLength(len(_RESULT_ITEMS)),
1063
                       ht.TItems(_RESULT_ITEMS)))
1064

    
1065

    
1066
# node opcodes
1067

    
1068
class OpNodeRemove(OpCode):
1069
  """Remove a node.
1070

1071
  @type node_name: C{str}
1072
  @ivar node_name: The name of the node to remove. If the node still has
1073
                   instances on it, the operation will fail.
1074

1075
  """
1076
  OP_DSC_FIELD = "node_name"
1077
  OP_PARAMS = [
1078
    _PNodeName,
1079
    _PNodeUuid
1080
    ]
1081
  OP_RESULT = ht.TNone
1082

    
1083

    
1084
class OpNodeAdd(OpCode):
1085
  """Add a node to the cluster.
1086

1087
  @type node_name: C{str}
1088
  @ivar node_name: The name of the node to add. This can be a short name,
1089
                   but it will be expanded to the FQDN.
1090
  @type primary_ip: IP address
1091
  @ivar primary_ip: The primary IP of the node. This will be ignored when the
1092
                    opcode is submitted, but will be filled during the node
1093
                    add (so it will be visible in the job query).
1094
  @type secondary_ip: IP address
1095
  @ivar secondary_ip: The secondary IP of the node. This needs to be passed
1096
                      if the cluster has been initialized in 'dual-network'
1097
                      mode, otherwise it must not be given.
1098
  @type readd: C{bool}
1099
  @ivar readd: Whether to re-add an existing node to the cluster. If
1100
               this is not passed, then the operation will abort if the node
1101
               name is already in the cluster; use this parameter to 'repair'
1102
               a node that had its configuration broken, or was reinstalled
1103
               without removal from the cluster.
1104
  @type group: C{str}
1105
  @ivar group: The node group to which this node will belong.
1106
  @type vm_capable: C{bool}
1107
  @ivar vm_capable: The vm_capable node attribute
1108
  @type master_capable: C{bool}
1109
  @ivar master_capable: The master_capable node attribute
1110

1111
  """
1112
  OP_DSC_FIELD = "node_name"
1113
  OP_PARAMS = [
1114
    _PNodeName,
1115
    _PHvState,
1116
    _PDiskState,
1117
    ("primary_ip", None, ht.NoType, "Primary IP address"),
1118
    ("secondary_ip", None, ht.TMaybeString, "Secondary IP address"),
1119
    ("readd", False, ht.TBool, "Whether node is re-added to cluster"),
1120
    ("group", None, ht.TMaybeString, "Initial node group"),
1121
    ("master_capable", None, ht.TMaybeBool,
1122
     "Whether node can become master or master candidate"),
1123
    ("vm_capable", None, ht.TMaybeBool,
1124
     "Whether node can host instances"),
1125
    ("ndparams", None, ht.TMaybeDict, "Node parameters"),
1126
    ]
1127
  OP_RESULT = ht.TNone
1128

    
1129

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

    
1140

    
1141
class OpNodeQueryvols(OpCode):
1142
  """Get list of volumes on node."""
1143
  OP_PARAMS = [
1144
    _POutputFields,
1145
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1146
     "Empty list to query all nodes, node names otherwise"),
1147
    ]
1148
  OP_RESULT = ht.TListOf(ht.TAny)
1149

    
1150

    
1151
class OpNodeQueryStorage(OpCode):
1152
  """Get information on storage for node(s)."""
1153
  OP_PARAMS = [
1154
    _POutputFields,
1155
    _PStorageType,
1156
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "List of nodes"),
1157
    ("name", None, ht.TMaybeString, "Storage name"),
1158
    ]
1159
  OP_RESULT = _TOldQueryResult
1160

    
1161

    
1162
class OpNodeModifyStorage(OpCode):
1163
  """Modifies the properies of a storage unit"""
1164
  OP_DSC_FIELD = "node_name"
1165
  OP_PARAMS = [
1166
    _PNodeName,
1167
    _PNodeUuid,
1168
    _PStorageType,
1169
    _PStorageName,
1170
    ("changes", ht.NoDefault, ht.TDict, "Requested changes"),
1171
    ]
1172
  OP_RESULT = ht.TNone
1173

    
1174

    
1175
class OpRepairNodeStorage(OpCode):
1176
  """Repairs the volume group on a node."""
1177
  OP_DSC_FIELD = "node_name"
1178
  OP_PARAMS = [
1179
    _PNodeName,
1180
    _PNodeUuid,
1181
    _PStorageType,
1182
    _PStorageName,
1183
    _PIgnoreConsistency,
1184
    ]
1185
  OP_RESULT = ht.TNone
1186

    
1187

    
1188
class OpNodeSetParams(OpCode):
1189
  """Change the parameters of a node."""
1190
  OP_DSC_FIELD = "node_name"
1191
  OP_PARAMS = [
1192
    _PNodeName,
1193
    _PNodeUuid,
1194
    _PForce,
1195
    _PHvState,
1196
    _PDiskState,
1197
    ("master_candidate", None, ht.TMaybeBool,
1198
     "Whether the node should become a master candidate"),
1199
    ("offline", None, ht.TMaybeBool,
1200
     "Whether the node should be marked as offline"),
1201
    ("drained", None, ht.TMaybeBool,
1202
     "Whether the node should be marked as drained"),
1203
    ("auto_promote", False, ht.TBool,
1204
     "Whether node(s) should be promoted to master candidate if necessary"),
1205
    ("master_capable", None, ht.TMaybeBool,
1206
     "Denote whether node can become master or master candidate"),
1207
    ("vm_capable", None, ht.TMaybeBool,
1208
     "Denote whether node can host instances"),
1209
    ("secondary_ip", None, ht.TMaybeString,
1210
     "Change node's secondary IP address"),
1211
    ("ndparams", None, ht.TMaybeDict, "Set node parameters"),
1212
    ("powered", None, ht.TMaybeBool,
1213
     "Whether the node should be marked as powered"),
1214
    ]
1215
  OP_RESULT = _TSetParamsResult
1216

    
1217

    
1218
class OpNodePowercycle(OpCode):
1219
  """Tries to powercycle a node."""
1220
  OP_DSC_FIELD = "node_name"
1221
  OP_PARAMS = [
1222
    _PNodeName,
1223
    _PNodeUuid,
1224
    _PForce,
1225
    ]
1226
  OP_RESULT = ht.TMaybeString
1227

    
1228

    
1229
class OpNodeMigrate(OpCode):
1230
  """Migrate all instances from a node."""
1231
  OP_DSC_FIELD = "node_name"
1232
  OP_PARAMS = [
1233
    _PNodeName,
1234
    _PNodeUuid,
1235
    _PMigrationMode,
1236
    _PMigrationLive,
1237
    _PMigrationTargetNode,
1238
    _PMigrationTargetNodeUuid,
1239
    _PAllowRuntimeChgs,
1240
    _PIgnoreIpolicy,
1241
    _PIAllocFromDesc("Iallocator for deciding the target node"
1242
                     " for shared-storage instances"),
1243
    ]
1244
  OP_RESULT = TJobIdListOnly
1245

    
1246

    
1247
class OpNodeEvacuate(OpCode):
1248
  """Evacuate instances off a number of nodes."""
1249
  OP_DSC_FIELD = "node_name"
1250
  OP_PARAMS = [
1251
    _PEarlyRelease,
1252
    _PNodeName,
1253
    _PNodeUuid,
1254
    ("remote_node", None, ht.TMaybeString, "New secondary node"),
1255
    ("remote_node_uuid", None, ht.TMaybeString, "New secondary node UUID"),
1256
    _PIAllocFromDesc("Iallocator for computing solution"),
1257
    ("mode", ht.NoDefault, ht.TElemOf(constants.NODE_EVAC_MODES),
1258
     "Node evacuation mode"),
1259
    ]
1260
  OP_RESULT = TJobIdListOnly
1261

    
1262

    
1263
# instance opcodes
1264

    
1265
class OpInstanceCreate(OpCode):
1266
  """Create an instance.
1267

1268
  @ivar instance_name: Instance name
1269
  @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES})
1270
  @ivar source_handshake: Signed handshake from source (remote import only)
1271
  @ivar source_x509_ca: Source X509 CA in PEM format (remote import only)
1272
  @ivar source_instance_name: Previous name of instance (remote import only)
1273
  @ivar source_shutdown_timeout: Shutdown timeout used for source instance
1274
    (remote import only)
1275

1276
  """
1277
  OP_DSC_FIELD = "instance_name"
1278
  OP_PARAMS = [
1279
    _PInstanceName,
1280
    _PForceVariant,
1281
    _PWaitForSync,
1282
    _PNameCheck,
1283
    _PIgnoreIpolicy,
1284
    _POpportunisticLocking,
1285
    ("beparams", ht.EmptyDict, ht.TDict, "Backend parameters for instance"),
1286
    ("disks", ht.NoDefault, ht.TListOf(_TDiskParams),
1287
     "Disk descriptions, for example ``[{\"%s\": 100}, {\"%s\": 5}]``;"
1288
     " each disk definition must contain a ``%s`` value and"
1289
     " can contain an optional ``%s`` value denoting the disk access mode"
1290
     " (%s)" %
1291
     (constants.IDISK_SIZE, constants.IDISK_SIZE, constants.IDISK_SIZE,
1292
      constants.IDISK_MODE,
1293
      " or ".join("``%s``" % i for i in sorted(constants.DISK_ACCESS_SET)))),
1294
    ("disk_template", ht.NoDefault, _BuildDiskTemplateCheck(True),
1295
     "Disk template"),
1296
    ("file_driver", None, ht.TMaybe(ht.TElemOf(constants.FILE_DRIVER)),
1297
     "Driver for file-backed disks"),
1298
    ("file_storage_dir", None, ht.TMaybeString,
1299
     "Directory for storing file-backed disks"),
1300
    ("hvparams", ht.EmptyDict, ht.TDict,
1301
     "Hypervisor parameters for instance, hypervisor-dependent"),
1302
    ("hypervisor", None, ht.TMaybeString, "Hypervisor"),
1303
    _PIAllocFromDesc("Iallocator for deciding which node(s) to use"),
1304
    ("identify_defaults", False, ht.TBool,
1305
     "Reset instance parameters to default if equal"),
1306
    ("ip_check", True, ht.TBool, _PIpCheckDoc),
1307
    ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"),
1308
    ("mode", ht.NoDefault, ht.TElemOf(constants.INSTANCE_CREATE_MODES),
1309
     "Instance creation mode"),
1310
    ("nics", ht.NoDefault, ht.TListOf(_TestNicDef),
1311
     "List of NIC (network interface) definitions, for example"
1312
     " ``[{}, {}, {\"%s\": \"198.51.100.4\"}]``; each NIC definition can"
1313
     " contain the optional values %s" %
1314
     (constants.INIC_IP,
1315
      ", ".join("``%s``" % i for i in sorted(constants.INIC_PARAMS)))),
1316
    ("no_install", None, ht.TMaybeBool,
1317
     "Do not install the OS (will disable automatic start)"),
1318
    ("osparams", ht.EmptyDict, ht.TDict, "OS parameters for instance"),
1319
    ("os_type", None, ht.TMaybeString, "Operating system"),
1320
    ("pnode", None, ht.TMaybeString, "Primary node"),
1321
    ("pnode_uuid", None, ht.TMaybeString, "Primary node UUID"),
1322
    ("snode", None, ht.TMaybeString, "Secondary node"),
1323
    ("snode_uuid", None, ht.TMaybeString, "Secondary node UUID"),
1324
    ("source_handshake", None, ht.TMaybe(ht.TList),
1325
     "Signed handshake from source (remote import only)"),
1326
    ("source_instance_name", None, ht.TMaybeString,
1327
     "Source instance name (remote import only)"),
1328
    ("source_shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT,
1329
     ht.TNonNegativeInt,
1330
     "How long source instance was given to shut down (remote import only)"),
1331
    ("source_x509_ca", None, ht.TMaybeString,
1332
     "Source X509 CA in PEM format (remote import only)"),
1333
    ("src_node", None, ht.TMaybeString, "Source node for import"),
1334
    ("src_node_uuid", None, ht.TMaybeString, "Source node UUID for import"),
1335
    ("src_path", None, ht.TMaybeString, "Source directory for import"),
1336
    ("start", True, ht.TBool, "Whether to start instance after creation"),
1337
    ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Instance tags"),
1338
    ]
1339
  OP_RESULT = ht.Comment("instance nodes")(ht.TListOf(ht.TNonEmptyString))
1340

    
1341

    
1342
class OpInstanceMultiAlloc(OpCode):
1343
  """Allocates multiple instances.
1344

1345
  """
1346
  OP_PARAMS = [
1347
    _POpportunisticLocking,
1348
    _PIAllocFromDesc("Iallocator used to allocate all the instances"),
1349
    ("instances", ht.EmptyList, ht.TListOf(ht.TInstanceOf(OpInstanceCreate)),
1350
     "List of instance create opcodes describing the instances to allocate"),
1351
    ]
1352
  _JOB_LIST = ht.Comment("List of submitted jobs")(TJobIdList)
1353
  ALLOCATABLE_KEY = "allocatable"
1354
  FAILED_KEY = "allocatable"
1355
  OP_RESULT = ht.TStrictDict(True, True, {
1356
    constants.JOB_IDS_KEY: _JOB_LIST,
1357
    ALLOCATABLE_KEY: ht.TListOf(ht.TNonEmptyString),
1358
    FAILED_KEY: ht.TListOf(ht.TNonEmptyString),
1359
    })
1360

    
1361
  def __getstate__(self):
1362
    """Generic serializer.
1363

1364
    """
1365
    state = OpCode.__getstate__(self)
1366
    if hasattr(self, "instances"):
1367
      # pylint: disable=E1101
1368
      state["instances"] = [inst.__getstate__() for inst in self.instances]
1369
    return state
1370

    
1371
  def __setstate__(self, state):
1372
    """Generic unserializer.
1373

1374
    This method just restores from the serialized state the attributes
1375
    of the current instance.
1376

1377
    @param state: the serialized opcode data
1378
    @type state: C{dict}
1379

1380
    """
1381
    if not isinstance(state, dict):
1382
      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
1383
                       type(state))
1384

    
1385
    if "instances" in state:
1386
      state["instances"] = map(OpCode.LoadOpCode, state["instances"])
1387

    
1388
    return OpCode.__setstate__(self, state)
1389

    
1390
  def Validate(self, set_defaults):
1391
    """Validates this opcode.
1392

1393
    We do this recursively.
1394

1395
    """
1396
    OpCode.Validate(self, set_defaults)
1397

    
1398
    for inst in self.instances: # pylint: disable=E1101
1399
      inst.Validate(set_defaults)
1400

    
1401

    
1402
class OpInstanceReinstall(OpCode):
1403
  """Reinstall an instance's OS."""
1404
  OP_DSC_FIELD = "instance_name"
1405
  OP_PARAMS = [
1406
    _PInstanceName,
1407
    _PInstanceUuid,
1408
    _PForceVariant,
1409
    ("os_type", None, ht.TMaybeString, "Instance operating system"),
1410
    ("osparams", None, ht.TMaybeDict, "Temporary OS parameters"),
1411
    ]
1412
  OP_RESULT = ht.TNone
1413

    
1414

    
1415
class OpInstanceRemove(OpCode):
1416
  """Remove an instance."""
1417
  OP_DSC_FIELD = "instance_name"
1418
  OP_PARAMS = [
1419
    _PInstanceName,
1420
    _PInstanceUuid,
1421
    _PShutdownTimeout,
1422
    ("ignore_failures", False, ht.TBool,
1423
     "Whether to ignore failures during removal"),
1424
    ]
1425
  OP_RESULT = ht.TNone
1426

    
1427

    
1428
class OpInstanceRename(OpCode):
1429
  """Rename an instance."""
1430
  OP_PARAMS = [
1431
    _PInstanceName,
1432
    _PInstanceUuid,
1433
    _PNameCheck,
1434
    ("new_name", ht.NoDefault, ht.TNonEmptyString, "New instance name"),
1435
    ("ip_check", False, ht.TBool, _PIpCheckDoc),
1436
    ]
1437
  OP_RESULT = ht.Comment("New instance name")(ht.TNonEmptyString)
1438

    
1439

    
1440
class OpInstanceStartup(OpCode):
1441
  """Startup an instance."""
1442
  OP_DSC_FIELD = "instance_name"
1443
  OP_PARAMS = [
1444
    _PInstanceName,
1445
    _PInstanceUuid,
1446
    _PForce,
1447
    _PIgnoreOfflineNodes,
1448
    ("hvparams", ht.EmptyDict, ht.TDict,
1449
     "Temporary hypervisor parameters, hypervisor-dependent"),
1450
    ("beparams", ht.EmptyDict, ht.TDict, "Temporary backend parameters"),
1451
    _PNoRemember,
1452
    _PStartupPaused,
1453
    ]
1454
  OP_RESULT = ht.TNone
1455

    
1456

    
1457
class OpInstanceShutdown(OpCode):
1458
  """Shutdown an instance."""
1459
  OP_DSC_FIELD = "instance_name"
1460
  OP_PARAMS = [
1461
    _PInstanceName,
1462
    _PInstanceUuid,
1463
    _PForce,
1464
    _PIgnoreOfflineNodes,
1465
    ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TNonNegativeInt,
1466
     "How long to wait for instance to shut down"),
1467
    _PNoRemember,
1468
    ]
1469
  OP_RESULT = ht.TNone
1470

    
1471

    
1472
class OpInstanceReboot(OpCode):
1473
  """Reboot an instance."""
1474
  OP_DSC_FIELD = "instance_name"
1475
  OP_PARAMS = [
1476
    _PInstanceName,
1477
    _PInstanceUuid,
1478
    _PShutdownTimeout,
1479
    ("ignore_secondaries", False, ht.TBool,
1480
     "Whether to start the instance even if secondary disks are failing"),
1481
    ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES),
1482
     "How to reboot instance"),
1483
    ]
1484
  OP_RESULT = ht.TNone
1485

    
1486

    
1487
class OpInstanceReplaceDisks(OpCode):
1488
  """Replace the disks of an instance."""
1489
  OP_DSC_FIELD = "instance_name"
1490
  OP_PARAMS = [
1491
    _PInstanceName,
1492
    _PInstanceUuid,
1493
    _PEarlyRelease,
1494
    _PIgnoreIpolicy,
1495
    ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES),
1496
     "Replacement mode"),
1497
    ("disks", ht.EmptyList, ht.TListOf(ht.TNonNegativeInt),
1498
     "Disk indexes"),
1499
    ("remote_node", None, ht.TMaybeString, "New secondary node"),
1500
    ("remote_node_uuid", None, ht.TMaybeString, "New secondary node UUID"),
1501
    _PIAllocFromDesc("Iallocator for deciding new secondary node"),
1502
    ]
1503
  OP_RESULT = ht.TNone
1504

    
1505

    
1506
class OpInstanceFailover(OpCode):
1507
  """Failover an instance."""
1508
  OP_DSC_FIELD = "instance_name"
1509
  OP_PARAMS = [
1510
    _PInstanceName,
1511
    _PInstanceUuid,
1512
    _PShutdownTimeout,
1513
    _PIgnoreConsistency,
1514
    _PMigrationTargetNode,
1515
    _PMigrationTargetNodeUuid,
1516
    _PIgnoreIpolicy,
1517
    _PIAllocFromDesc("Iallocator for deciding the target node for"
1518
                     " shared-storage instances"),
1519
    ]
1520
  OP_RESULT = ht.TNone
1521

    
1522

    
1523
class OpInstanceMigrate(OpCode):
1524
  """Migrate an instance.
1525

1526
  This migrates (without shutting down an instance) to its secondary
1527
  node.
1528

1529
  @ivar instance_name: the name of the instance
1530
  @ivar mode: the migration mode (live, non-live or None for auto)
1531

1532
  """
1533
  OP_DSC_FIELD = "instance_name"
1534
  OP_PARAMS = [
1535
    _PInstanceName,
1536
    _PInstanceUuid,
1537
    _PMigrationMode,
1538
    _PMigrationLive,
1539
    _PMigrationTargetNode,
1540
    _PMigrationTargetNodeUuid,
1541
    _PAllowRuntimeChgs,
1542
    _PIgnoreIpolicy,
1543
    ("cleanup", False, ht.TBool,
1544
     "Whether a previously failed migration should be cleaned up"),
1545
    _PIAllocFromDesc("Iallocator for deciding the target node for"
1546
                     " shared-storage instances"),
1547
    ("allow_failover", False, ht.TBool,
1548
     "Whether we can fallback to failover if migration is not possible"),
1549
    ]
1550
  OP_RESULT = ht.TNone
1551

    
1552

    
1553
class OpInstanceMove(OpCode):
1554
  """Move an instance.
1555

1556
  This move (with shutting down an instance and data copying) to an
1557
  arbitrary node.
1558

1559
  @ivar instance_name: the name of the instance
1560
  @ivar target_node: the destination node
1561

1562
  """
1563
  OP_DSC_FIELD = "instance_name"
1564
  OP_PARAMS = [
1565
    _PInstanceName,
1566
    _PInstanceUuid,
1567
    _PShutdownTimeout,
1568
    _PIgnoreIpolicy,
1569
    ("target_node", ht.NoDefault, ht.TNonEmptyString, "Target node"),
1570
    ("target_node_uuid", None, ht.TMaybeString, "Target node UUID"),
1571
    _PIgnoreConsistency,
1572
    ]
1573
  OP_RESULT = ht.TNone
1574

    
1575

    
1576
class OpInstanceConsole(OpCode):
1577
  """Connect to an instance's console."""
1578
  OP_DSC_FIELD = "instance_name"
1579
  OP_PARAMS = [
1580
    _PInstanceName,
1581
    _PInstanceUuid,
1582
    ]
1583
  OP_RESULT = ht.TDict
1584

    
1585

    
1586
class OpInstanceActivateDisks(OpCode):
1587
  """Activate an instance's disks."""
1588
  OP_DSC_FIELD = "instance_name"
1589
  OP_PARAMS = [
1590
    _PInstanceName,
1591
    _PInstanceUuid,
1592
    ("ignore_size", False, ht.TBool, "Whether to ignore recorded size"),
1593
    _PWaitForSyncFalse,
1594
    ]
1595
  OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3),
1596
                                 ht.TItems([ht.TNonEmptyString,
1597
                                            ht.TNonEmptyString,
1598
                                            ht.TNonEmptyString])))
1599

    
1600

    
1601
class OpInstanceDeactivateDisks(OpCode):
1602
  """Deactivate an instance's disks."""
1603
  OP_DSC_FIELD = "instance_name"
1604
  OP_PARAMS = [
1605
    _PInstanceName,
1606
    _PInstanceUuid,
1607
    _PForce,
1608
    ]
1609
  OP_RESULT = ht.TNone
1610

    
1611

    
1612
class OpInstanceRecreateDisks(OpCode):
1613
  """Recreate an instance's disks."""
1614
  _TDiskChanges = \
1615
    ht.TAnd(ht.TIsLength(2),
1616
            ht.TItems([ht.Comment("Disk index")(ht.TNonNegativeInt),
1617
                       ht.Comment("Parameters")(_TDiskParams)]))
1618

    
1619
  OP_DSC_FIELD = "instance_name"
1620
  OP_PARAMS = [
1621
    _PInstanceName,
1622
    _PInstanceUuid,
1623
    ("disks", ht.EmptyList,
1624
     ht.TOr(ht.TListOf(ht.TNonNegativeInt), ht.TListOf(_TDiskChanges)),
1625
     "List of disk indexes (deprecated) or a list of tuples containing a disk"
1626
     " index and a possibly empty dictionary with disk parameter changes"),
1627
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1628
     "New instance nodes, if relocation is desired"),
1629
    ("node_uuids", None, ht.TMaybeListOf(ht.TNonEmptyString),
1630
     "New instance node UUIDs, if relocation is desired"),
1631
    _PIAllocFromDesc("Iallocator for deciding new nodes"),
1632
    ]
1633
  OP_RESULT = ht.TNone
1634

    
1635

    
1636
class OpInstanceQuery(OpCode):
1637
  """Compute the list of instances."""
1638
  OP_PARAMS = [
1639
    _POutputFields,
1640
    _PUseLocking,
1641
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1642
     "Empty list to query all instances, instance names otherwise"),
1643
    ]
1644
  OP_RESULT = _TOldQueryResult
1645

    
1646

    
1647
class OpInstanceQueryData(OpCode):
1648
  """Compute the run-time status of instances."""
1649
  OP_PARAMS = [
1650
    _PUseLocking,
1651
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1652
     "Instance names"),
1653
    ("static", False, ht.TBool,
1654
     "Whether to only return configuration data without querying"
1655
     " nodes"),
1656
    ]
1657
  OP_RESULT = ht.TDictOf(ht.TNonEmptyString, ht.TDict)
1658

    
1659

    
1660
def _TestInstSetParamsModList(fn):
1661
  """Generates a check for modification lists.
1662

1663
  """
1664
  # Old format
1665
  # TODO: Remove in version 2.8 including support in LUInstanceSetParams
1666
  old_mod_item_fn = \
1667
    ht.TAnd(ht.TIsLength(2), ht.TItems([
1668
      ht.TOr(ht.TElemOf(constants.DDMS_VALUES), ht.TNonNegativeInt),
1669
      fn,
1670
      ]))
1671

    
1672
  # New format, supporting adding/removing disks/NICs at arbitrary indices
1673
  mod_item_fn = \
1674
    ht.TAnd(ht.TIsLength(3), ht.TItems([
1675
      ht.TElemOf(constants.DDMS_VALUES_WITH_MODIFY),
1676
      ht.Comment("Device index, can be negative, e.g. -1 for last disk")
1677
                 (ht.TOr(ht.TInt, ht.TString)),
1678
      fn,
1679
      ]))
1680

    
1681
  return ht.TOr(ht.Comment("Recommended")(ht.TListOf(mod_item_fn)),
1682
                ht.Comment("Deprecated")(ht.TListOf(old_mod_item_fn)))
1683

    
1684

    
1685
class OpInstanceSetParams(OpCode):
1686
  """Change the parameters of an instance.
1687

1688
  """
1689
  TestNicModifications = _TestInstSetParamsModList(_TestNicDef)
1690
  TestDiskModifications = _TestInstSetParamsModList(_TDiskParams)
1691

    
1692
  OP_DSC_FIELD = "instance_name"
1693
  OP_PARAMS = [
1694
    _PInstanceName,
1695
    _PInstanceUuid,
1696
    _PForce,
1697
    _PForceVariant,
1698
    _PIgnoreIpolicy,
1699
    ("nics", ht.EmptyList, TestNicModifications,
1700
     "List of NIC changes: each item is of the form"
1701
     " ``(op, identifier, settings)``, ``op`` is one of ``%s``, ``%s`` or"
1702
     " ``%s``, ``identifier`` can be a zero-based index number (or -1 to refer"
1703
     " to the last position), the NIC's UUID of the NIC's name; a"
1704
     " deprecated version of this parameter used the form ``(op, settings)``,"
1705
     " where ``op`` can be ``%s`` to add a new NIC with the specified"
1706
     " settings, ``%s`` to remove the last NIC or a number to modify the"
1707
     " settings of the NIC with that index" %
1708
     (constants.DDM_ADD, constants.DDM_MODIFY, constants.DDM_REMOVE,
1709
      constants.DDM_ADD, constants.DDM_REMOVE)),
1710
    ("disks", ht.EmptyList, TestDiskModifications,
1711
     "List of disk changes; see ``nics``"),
1712
    ("beparams", ht.EmptyDict, ht.TDict, "Per-instance backend parameters"),
1713
    ("runtime_mem", None, ht.TMaybePositiveInt, "New runtime memory"),
1714
    ("hvparams", ht.EmptyDict, ht.TDict,
1715
     "Per-instance hypervisor parameters, hypervisor-dependent"),
1716
    ("disk_template", None, ht.TMaybe(_BuildDiskTemplateCheck(False)),
1717
     "Disk template for instance"),
1718
    ("pnode", None, ht.TMaybeString, "New primary node"),
1719
    ("pnode_uuid", None, ht.TMaybeString, "New primary node UUID"),
1720
    ("remote_node", None, ht.TMaybeString,
1721
     "Secondary node (used when changing disk template)"),
1722
    ("remote_node_uuid", None, ht.TMaybeString,
1723
     "Secondary node UUID (used when changing disk template)"),
1724
    ("os_name", None, ht.TMaybeString,
1725
     "Change the instance's OS without reinstalling the instance"),
1726
    ("osparams", None, ht.TMaybeDict, "Per-instance OS parameters"),
1727
    ("wait_for_sync", True, ht.TBool,
1728
     "Whether to wait for the disk to synchronize, when changing template"),
1729
    ("offline", None, ht.TMaybeBool, "Whether to mark instance as offline"),
1730
    ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"),
1731
    ]
1732
  OP_RESULT = _TSetParamsResult
1733

    
1734

    
1735
class OpInstanceGrowDisk(OpCode):
1736
  """Grow a disk of an instance."""
1737
  OP_DSC_FIELD = "instance_name"
1738
  OP_PARAMS = [
1739
    _PInstanceName,
1740
    _PInstanceUuid,
1741
    _PWaitForSync,
1742
    ("disk", ht.NoDefault, ht.TInt, "Disk index"),
1743
    ("amount", ht.NoDefault, ht.TNonNegativeInt,
1744
     "Amount of disk space to add (megabytes)"),
1745
    ("absolute", False, ht.TBool,
1746
     "Whether the amount parameter is an absolute target or a relative one"),
1747
    ]
1748
  OP_RESULT = ht.TNone
1749

    
1750

    
1751
class OpInstanceChangeGroup(OpCode):
1752
  """Moves an instance to another node group."""
1753
  OP_DSC_FIELD = "instance_name"
1754
  OP_PARAMS = [
1755
    _PInstanceName,
1756
    _PInstanceUuid,
1757
    _PEarlyRelease,
1758
    _PIAllocFromDesc("Iallocator for computing solution"),
1759
    _PTargetGroups,
1760
    ]
1761
  OP_RESULT = TJobIdListOnly
1762

    
1763

    
1764
# Node group opcodes
1765

    
1766
class OpGroupAdd(OpCode):
1767
  """Add a node group to the cluster."""
1768
  OP_DSC_FIELD = "group_name"
1769
  OP_PARAMS = [
1770
    _PGroupName,
1771
    _PNodeGroupAllocPolicy,
1772
    _PGroupNodeParams,
1773
    _PDiskParams,
1774
    _PHvState,
1775
    _PDiskState,
1776
    ("ipolicy", None, ht.TMaybeDict,
1777
     "Group-wide :ref:`instance policy <rapi-ipolicy>` specs"),
1778
    ]
1779
  OP_RESULT = ht.TNone
1780

    
1781

    
1782
class OpGroupAssignNodes(OpCode):
1783
  """Assign nodes to a node group."""
1784
  OP_DSC_FIELD = "group_name"
1785
  OP_PARAMS = [
1786
    _PGroupName,
1787
    _PForce,
1788
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString),
1789
     "List of nodes to assign"),
1790
    ("node_uuids", None, ht.TMaybeListOf(ht.TNonEmptyString),
1791
     "List of node UUIDs to assign"),
1792
    ]
1793
  OP_RESULT = ht.TNone
1794

    
1795

    
1796
class OpGroupQuery(OpCode):
1797
  """Compute the list of node groups."""
1798
  OP_PARAMS = [
1799
    _POutputFields,
1800
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1801
     "Empty list to query all groups, group names otherwise"),
1802
    ]
1803
  OP_RESULT = _TOldQueryResult
1804

    
1805

    
1806
class OpGroupSetParams(OpCode):
1807
  """Change the parameters of a node group."""
1808
  OP_DSC_FIELD = "group_name"
1809
  OP_PARAMS = [
1810
    _PGroupName,
1811
    _PNodeGroupAllocPolicy,
1812
    _PGroupNodeParams,
1813
    _PDiskParams,
1814
    _PHvState,
1815
    _PDiskState,
1816
    ("ipolicy", None, ht.TMaybeDict, "Group-wide instance policy specs"),
1817
    ]
1818
  OP_RESULT = _TSetParamsResult
1819

    
1820

    
1821
class OpGroupRemove(OpCode):
1822
  """Remove a node group from the cluster."""
1823
  OP_DSC_FIELD = "group_name"
1824
  OP_PARAMS = [
1825
    _PGroupName,
1826
    ]
1827
  OP_RESULT = ht.TNone
1828

    
1829

    
1830
class OpGroupRename(OpCode):
1831
  """Rename a node group in the cluster."""
1832
  OP_PARAMS = [
1833
    _PGroupName,
1834
    ("new_name", ht.NoDefault, ht.TNonEmptyString, "New group name"),
1835
    ]
1836
  OP_RESULT = ht.Comment("New group name")(ht.TNonEmptyString)
1837

    
1838

    
1839
class OpGroupEvacuate(OpCode):
1840
  """Evacuate a node group in the cluster."""
1841
  OP_DSC_FIELD = "group_name"
1842
  OP_PARAMS = [
1843
    _PGroupName,
1844
    _PEarlyRelease,
1845
    _PIAllocFromDesc("Iallocator for computing solution"),
1846
    _PTargetGroups,
1847
    ]
1848
  OP_RESULT = TJobIdListOnly
1849

    
1850

    
1851
# OS opcodes
1852
class OpOsDiagnose(OpCode):
1853
  """Compute the list of guest operating systems."""
1854
  OP_PARAMS = [
1855
    _POutputFields,
1856
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1857
     "Which operating systems to diagnose"),
1858
    ]
1859
  OP_RESULT = _TOldQueryResult
1860

    
1861

    
1862
# ExtStorage opcodes
1863
class OpExtStorageDiagnose(OpCode):
1864
  """Compute the list of external storage providers."""
1865
  OP_PARAMS = [
1866
    _POutputFields,
1867
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1868
     "Which ExtStorage Provider to diagnose"),
1869
    ]
1870
  OP_RESULT = _TOldQueryResult
1871

    
1872

    
1873
# Exports opcodes
1874
class OpBackupQuery(OpCode):
1875
  """Compute the list of exported images."""
1876
  OP_PARAMS = [
1877
    _PUseLocking,
1878
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
1879
     "Empty list to query all nodes, node names otherwise"),
1880
    ]
1881
  OP_RESULT = ht.TDictOf(ht.TNonEmptyString,
1882
                         ht.TOr(ht.Comment("False on error")(ht.TBool),
1883
                                ht.TListOf(ht.TNonEmptyString)))
1884

    
1885

    
1886
class OpBackupPrepare(OpCode):
1887
  """Prepares an instance export.
1888

1889
  @ivar instance_name: Instance name
1890
  @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1891

1892
  """
1893
  OP_DSC_FIELD = "instance_name"
1894
  OP_PARAMS = [
1895
    _PInstanceName,
1896
    _PInstanceUuid,
1897
    ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES),
1898
     "Export mode"),
1899
    ]
1900
  OP_RESULT = ht.TMaybeDict
1901

    
1902

    
1903
class OpBackupExport(OpCode):
1904
  """Export an instance.
1905

1906
  For local exports, the export destination is the node name. For
1907
  remote exports, the export destination is a list of tuples, each
1908
  consisting of hostname/IP address, port, magic, HMAC and HMAC
1909
  salt. The HMAC is calculated using the cluster domain secret over
1910
  the value "${index}:${hostname}:${port}". The destination X509 CA
1911
  must be a signed certificate.
1912

1913
  @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1914
  @ivar target_node: Export destination
1915
  @ivar x509_key_name: X509 key to use (remote export only)
1916
  @ivar destination_x509_ca: Destination X509 CA in PEM format (remote export
1917
                             only)
1918

1919
  """
1920
  OP_DSC_FIELD = "instance_name"
1921
  OP_PARAMS = [
1922
    _PInstanceName,
1923
    _PInstanceUuid,
1924
    _PShutdownTimeout,
1925
    # TODO: Rename target_node as it changes meaning for different export modes
1926
    # (e.g. "destination")
1927
    ("target_node", ht.NoDefault, ht.TOr(ht.TNonEmptyString, ht.TList),
1928
     "Destination information, depends on export mode"),
1929
    ("target_node_uuid", None, ht.TMaybeString,
1930
     "Target node UUID (if local export)"),
1931
    ("shutdown", True, ht.TBool, "Whether to shutdown instance before export"),
1932
    ("remove_instance", False, ht.TBool,
1933
     "Whether to remove instance after export"),
1934
    ("ignore_remove_failures", False, ht.TBool,
1935
     "Whether to ignore failures while removing instances"),
1936
    ("mode", constants.EXPORT_MODE_LOCAL, ht.TElemOf(constants.EXPORT_MODES),
1937
     "Export mode"),
1938
    ("x509_key_name", None, ht.TMaybe(ht.TList),
1939
     "Name of X509 key (remote export only)"),
1940
    ("destination_x509_ca", None, ht.TMaybeString,
1941
     "Destination X509 CA (remote export only)"),
1942
    ]
1943
  OP_RESULT = \
1944
    ht.TAnd(ht.TIsLength(2), ht.TItems([
1945
      ht.Comment("Finalizing status")(ht.TBool),
1946
      ht.Comment("Status for every exported disk")(ht.TListOf(ht.TBool)),
1947
      ]))
1948

    
1949

    
1950
class OpBackupRemove(OpCode):
1951
  """Remove an instance's export."""
1952
  OP_DSC_FIELD = "instance_name"
1953
  OP_PARAMS = [
1954
    _PInstanceName,
1955
    _PInstanceUuid,
1956
    ]
1957
  OP_RESULT = ht.TNone
1958

    
1959

    
1960
# Tags opcodes
1961
class OpTagsGet(OpCode):
1962
  """Returns the tags of the given object."""
1963
  OP_DSC_FIELD = "name"
1964
  OP_PARAMS = [
1965
    _PTagKind,
1966
    # Not using _PUseLocking as the default is different for historical reasons
1967
    ("use_locking", True, ht.TBool, "Whether to use synchronization"),
1968
    # Name is only meaningful for nodes and instances
1969
    ("name", ht.NoDefault, ht.TMaybeString,
1970
     "Name of object to retrieve tags from"),
1971
    ]
1972
  OP_RESULT = ht.TListOf(ht.TNonEmptyString)
1973

    
1974

    
1975
class OpTagsSearch(OpCode):
1976
  """Searches the tags in the cluster for a given pattern."""
1977
  OP_DSC_FIELD = "pattern"
1978
  OP_PARAMS = [
1979
    ("pattern", ht.NoDefault, ht.TNonEmptyString,
1980
     "Search pattern (regular expression)"),
1981
    ]
1982
  OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(2), ht.TItems([
1983
    ht.TNonEmptyString,
1984
    ht.TNonEmptyString,
1985
    ])))
1986

    
1987

    
1988
class OpTagsSet(OpCode):
1989
  """Add a list of tags on a given object."""
1990
  OP_PARAMS = [
1991
    _PTagKind,
1992
    _PTags,
1993
    # Name is only meaningful for groups, nodes and instances
1994
    ("name", ht.NoDefault, ht.TMaybeString,
1995
     "Name of object where tag(s) should be added"),
1996
    ]
1997
  OP_RESULT = ht.TNone
1998

    
1999

    
2000
class OpTagsDel(OpCode):
2001
  """Remove a list of tags from a given object."""
2002
  OP_PARAMS = [
2003
    _PTagKind,
2004
    _PTags,
2005
    # Name is only meaningful for groups, nodes and instances
2006
    ("name", ht.NoDefault, ht.TMaybeString,
2007
     "Name of object where tag(s) should be deleted"),
2008
    ]
2009
  OP_RESULT = ht.TNone
2010

    
2011

    
2012
# Test opcodes
2013
class OpTestDelay(OpCode):
2014
  """Sleeps for a configured amount of time.
2015

2016
  This is used just for debugging and testing.
2017

2018
  Parameters:
2019
    - duration: the time to sleep, in seconds
2020
    - on_master: if true, sleep on the master
2021
    - on_nodes: list of nodes in which to sleep
2022

2023
  If the on_master parameter is true, it will execute a sleep on the
2024
  master (before any node sleep).
2025

2026
  If the on_nodes list is not empty, it will sleep on those nodes
2027
  (after the sleep on the master, if that is enabled).
2028

2029
  As an additional feature, the case of duration < 0 will be reported
2030
  as an execution error, so this opcode can be used as a failure
2031
  generator. The case of duration == 0 will not be treated specially.
2032

2033
  """
2034
  OP_DSC_FIELD = "duration"
2035
  OP_PARAMS = [
2036
    ("duration", ht.NoDefault, ht.TNumber, None),
2037
    ("on_master", True, ht.TBool, None),
2038
    ("on_nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
2039
    ("on_node_uuids", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
2040
    ("repeat", 0, ht.TNonNegativeInt, None),
2041
    ]
2042

    
2043
  def OP_DSC_FORMATTER(self, value): # pylint: disable=C0103,R0201
2044
    """Custom formatter for duration.
2045

2046
    """
2047
    try:
2048
      v = float(value)
2049
    except TypeError:
2050
      v = value
2051
    return str(v)
2052

    
2053

    
2054
class OpTestAllocator(OpCode):
2055
  """Allocator framework testing.
2056

2057
  This opcode has two modes:
2058
    - gather and return allocator input for a given mode (allocate new
2059
      or replace secondary) and a given instance definition (direction
2060
      'in')
2061
    - run a selected allocator for a given operation (as above) and
2062
      return the allocator output (direction 'out')
2063

2064
  """
2065
  OP_DSC_FIELD = "iallocator"
2066
  OP_PARAMS = [
2067
    ("direction", ht.NoDefault,
2068
     ht.TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS), None),
2069
    ("mode", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_MODES), None),
2070
    ("name", ht.NoDefault, ht.TNonEmptyString, None),
2071
    ("nics", ht.NoDefault,
2072
     ht.TMaybeListOf(ht.TDictOf(ht.TElemOf([constants.INIC_MAC,
2073
                                            constants.INIC_IP,
2074
                                            "bridge"]),
2075
                                ht.TMaybeString)),
2076
     None),
2077
    ("disks", ht.NoDefault, ht.TMaybe(ht.TList), None),
2078
    ("hypervisor", None, ht.TMaybeString, None),
2079
    _PIAllocFromDesc(None),
2080
    ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None),
2081
    ("memory", None, ht.TMaybe(ht.TNonNegativeInt), None),
2082
    ("vcpus", None, ht.TMaybe(ht.TNonNegativeInt), None),
2083
    ("os", None, ht.TMaybeString, None),
2084
    ("disk_template", None, ht.TMaybeString, None),
2085
    ("instances", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
2086
    ("evac_mode", None,
2087
     ht.TMaybe(ht.TElemOf(constants.IALLOCATOR_NEVAC_MODES)), None),
2088
    ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString), None),
2089
    ("spindle_use", 1, ht.TNonNegativeInt, None),
2090
    ("count", 1, ht.TNonNegativeInt, None),
2091
    ]
2092

    
2093

    
2094
class OpTestJqueue(OpCode):
2095
  """Utility opcode to test some aspects of the job queue.
2096

2097
  """
2098
  OP_PARAMS = [
2099
    ("notify_waitlock", False, ht.TBool, None),
2100
    ("notify_exec", False, ht.TBool, None),
2101
    ("log_messages", ht.EmptyList, ht.TListOf(ht.TString), None),
2102
    ("fail", False, ht.TBool, None),
2103
    ]
2104

    
2105

    
2106
class OpTestDummy(OpCode):
2107
  """Utility opcode used by unittests.
2108

2109
  """
2110
  OP_PARAMS = [
2111
    ("result", ht.NoDefault, ht.NoType, None),
2112
    ("messages", ht.NoDefault, ht.NoType, None),
2113
    ("fail", ht.NoDefault, ht.NoType, None),
2114
    ("submit_jobs", None, ht.NoType, None),
2115
    ]
2116
  WITH_LU = False
2117

    
2118

    
2119
# Network opcodes
2120
# Add a new network in the cluster
2121
class OpNetworkAdd(OpCode):
2122
  """Add an IP network to the cluster."""
2123
  OP_DSC_FIELD = "network_name"
2124
  OP_PARAMS = [
2125
    _PNetworkName,
2126
    ("network", ht.NoDefault, _TIpNetwork4, "IPv4 subnet"),
2127
    ("gateway", None, ht.TMaybe(_TIpAddress4), "IPv4 gateway"),
2128
    ("network6", None, ht.TMaybe(_TIpNetwork6), "IPv6 subnet"),
2129
    ("gateway6", None, ht.TMaybe(_TIpAddress6), "IPv6 gateway"),
2130
    ("mac_prefix", None, ht.TMaybeString,
2131
     "MAC address prefix that overrides cluster one"),
2132
    ("add_reserved_ips", None, _TMaybeAddr4List,
2133
     "Which IP addresses to reserve"),
2134
    ("conflicts_check", True, ht.TBool,
2135
     "Whether to check for conflicting IP addresses"),
2136
    ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Network tags"),
2137
    ]
2138
  OP_RESULT = ht.TNone
2139

    
2140

    
2141
class OpNetworkRemove(OpCode):
2142
  """Remove an existing network from the cluster.
2143
     Must not be connected to any nodegroup.
2144

2145
  """
2146
  OP_DSC_FIELD = "network_name"
2147
  OP_PARAMS = [
2148
    _PNetworkName,
2149
    _PForce,
2150
    ]
2151
  OP_RESULT = ht.TNone
2152

    
2153

    
2154
class OpNetworkSetParams(OpCode):
2155
  """Modify Network's parameters except for IPv4 subnet"""
2156
  OP_DSC_FIELD = "network_name"
2157
  OP_PARAMS = [
2158
    _PNetworkName,
2159
    ("gateway", None, ht.TMaybeValueNone(_TIpAddress4), "IPv4 gateway"),
2160
    ("network6", None, ht.TMaybeValueNone(_TIpNetwork6), "IPv6 subnet"),
2161
    ("gateway6", None, ht.TMaybeValueNone(_TIpAddress6), "IPv6 gateway"),
2162
    ("mac_prefix", None, ht.TMaybeValueNone(ht.TString),
2163
     "MAC address prefix that overrides cluster one"),
2164
    ("add_reserved_ips", None, _TMaybeAddr4List,
2165
     "Which external IP addresses to reserve"),
2166
    ("remove_reserved_ips", None, _TMaybeAddr4List,
2167
     "Which external IP addresses to release"),
2168
    ]
2169
  OP_RESULT = ht.TNone
2170

    
2171

    
2172
class OpNetworkConnect(OpCode):
2173
  """Connect a Network to a specific Nodegroup with the defined netparams
2174
     (mode, link). Nics in this Network will inherit those params.
2175
     Produce errors if a NIC (that its not already assigned to a network)
2176
     has an IP that is contained in the Network this will produce error unless
2177
     --no-conflicts-check is passed.
2178

2179
  """
2180
  OP_DSC_FIELD = "network_name"
2181
  OP_PARAMS = [
2182
    _PGroupName,
2183
    _PNetworkName,
2184
    ("network_mode", ht.NoDefault, ht.TElemOf(constants.NIC_VALID_MODES),
2185
     "Connectivity mode"),
2186
    ("network_link", ht.NoDefault, ht.TString, "Connectivity link"),
2187
    ("conflicts_check", True, ht.TBool, "Whether to check for conflicting IPs"),
2188
    ]
2189
  OP_RESULT = ht.TNone
2190

    
2191

    
2192
class OpNetworkDisconnect(OpCode):
2193
  """Disconnect a Network from a Nodegroup. Produce errors if NICs are
2194
     present in the Network unless --no-conficts-check option is passed.
2195

2196
  """
2197
  OP_DSC_FIELD = "network_name"
2198
  OP_PARAMS = [
2199
    _PGroupName,
2200
    _PNetworkName,
2201
    ]
2202
  OP_RESULT = ht.TNone
2203

    
2204

    
2205
class OpNetworkQuery(OpCode):
2206
  """Compute the list of networks."""
2207
  OP_PARAMS = [
2208
    _POutputFields,
2209
    _PUseLocking,
2210
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString),
2211
     "Empty list to query all groups, group names otherwise"),
2212
    ]
2213
  OP_RESULT = _TOldQueryResult
2214

    
2215

    
2216
def _GetOpList():
2217
  """Returns list of all defined opcodes.
2218

2219
  Does not eliminate duplicates by C{OP_ID}.
2220

2221
  """
2222
  return [v for v in globals().values()
2223
          if (isinstance(v, type) and issubclass(v, OpCode) and
2224
              hasattr(v, "OP_ID") and v is not OpCode)]
2225

    
2226

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