Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes.py @ 18397489

History | View | Annotate | Download (68.7 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, ht.EmptyList, 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.TMaybe(ht.TString),
953
     "Default directory for storing file-backed disks"),
954
    ("shared_file_storage_dir", None, ht.TMaybe(ht.TString),
955
     "Default directory for storing shared-file-backed disks"),
956
    ]
957
  OP_RESULT = ht.TNone
958

    
959

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

963
  """
964
  OP_RESULT = ht.TNone
965

    
966

    
967
class OpClusterActivateMasterIp(OpCode):
968
  """Activate the master IP on the master node.
969

970
  """
971
  OP_RESULT = ht.TNone
972

    
973

    
974
class OpClusterDeactivateMasterIp(OpCode):
975
  """Deactivate the master IP on the master node.
976

977
  """
978
  OP_RESULT = ht.TNone
979

    
980

    
981
class OpQuery(OpCode):
982
  """Query for resources/items.
983

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

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

    
1004

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

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

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

    
1023

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

    
1043

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

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

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

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

    
1067

    
1068
# node opcodes
1069

    
1070
class OpNodeRemove(OpCode):
1071
  """Remove a node.
1072

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

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

    
1085

    
1086
class OpNodeAdd(OpCode):
1087
  """Add a node to the cluster.
1088

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

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

    
1131

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

    
1142

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

    
1152

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

    
1163

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

    
1176

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

    
1189

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

    
1219

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

    
1230

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

    
1248

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

    
1264

    
1265
# instance opcodes
1266

    
1267
class OpInstanceCreate(OpCode):
1268
  """Create an instance.
1269

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

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

    
1343

    
1344
class OpInstanceMultiAlloc(OpCode):
1345
  """Allocates multiple instances.
1346

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

    
1363
  def __getstate__(self):
1364
    """Generic serializer.
1365

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

    
1373
  def __setstate__(self, state):
1374
    """Generic unserializer.
1375

1376
    This method just restores from the serialized state the attributes
1377
    of the current instance.
1378

1379
    @param state: the serialized opcode data
1380
    @type state: C{dict}
1381

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

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

    
1390
    return OpCode.__setstate__(self, state)
1391

    
1392
  def Validate(self, set_defaults):
1393
    """Validates this opcode.
1394

1395
    We do this recursively.
1396

1397
    """
1398
    OpCode.Validate(self, set_defaults)
1399

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

    
1403

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

    
1416

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

    
1429

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

    
1441

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

    
1458

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

    
1473

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

    
1488

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

    
1507

    
1508
class OpInstanceFailover(OpCode):
1509
  """Failover an instance."""
1510
  OP_DSC_FIELD = "instance_name"
1511
  OP_PARAMS = [
1512
    _PInstanceName,
1513
    _PInstanceUuid,
1514
    _PShutdownTimeout,
1515
    _PIgnoreConsistency,
1516
    _PMigrationTargetNode,
1517
    _PMigrationTargetNodeUuid,
1518
    _PIgnoreIpolicy,
1519
    _PIAllocFromDesc("Iallocator for deciding the target node for"
1520
                     " shared-storage instances"),
1521
    ("cleanup", False, ht.TBool,
1522
     "Whether a previously failed failover should be cleaned up"),
1523
    ]
1524
  OP_RESULT = ht.TNone
1525

    
1526

    
1527
class OpInstanceMigrate(OpCode):
1528
  """Migrate an instance.
1529

1530
  This migrates (without shutting down an instance) to its secondary
1531
  node.
1532

1533
  @ivar instance_name: the name of the instance
1534
  @ivar mode: the migration mode (live, non-live or None for auto)
1535

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

    
1556

    
1557
class OpInstanceMove(OpCode):
1558
  """Move an instance.
1559

1560
  This move (with shutting down an instance and data copying) to an
1561
  arbitrary node.
1562

1563
  @ivar instance_name: the name of the instance
1564
  @ivar target_node: the destination node
1565

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

    
1579

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

    
1589

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

    
1604

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

    
1615

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

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

    
1639

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

    
1650

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

    
1663

    
1664
def _TestInstSetParamsModList(fn):
1665
  """Generates a check for modification lists.
1666

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

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

    
1685
  return ht.TOr(ht.Comment("Recommended")(ht.TListOf(mod_item_fn)),
1686
                ht.Comment("Deprecated")(ht.TListOf(old_mod_item_fn)))
1687

    
1688

    
1689
class OpInstanceSetParams(OpCode):
1690
  """Change the parameters of an instance.
1691

1692
  """
1693
  TestNicModifications = _TestInstSetParamsModList(_TestNicDef)
1694
  TestDiskModifications = _TestInstSetParamsModList(_TDiskParams)
1695

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

    
1738

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

    
1754

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

    
1767

    
1768
# Node group opcodes
1769

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

    
1785

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

    
1799

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

    
1809

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

    
1824

    
1825
class OpGroupRemove(OpCode):
1826
  """Remove a node group from the cluster."""
1827
  OP_DSC_FIELD = "group_name"
1828
  OP_PARAMS = [
1829
    _PGroupName,
1830
    ]
1831
  OP_RESULT = ht.TNone
1832

    
1833

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

    
1842

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

    
1854

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

    
1865

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

    
1876

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

    
1889

    
1890
class OpBackupPrepare(OpCode):
1891
  """Prepares an instance export.
1892

1893
  @ivar instance_name: Instance name
1894
  @ivar mode: Export mode (one of L{constants.EXPORT_MODES})
1895

1896
  """
1897
  OP_DSC_FIELD = "instance_name"
1898
  OP_PARAMS = [
1899
    _PInstanceName,
1900
    _PInstanceUuid,
1901
    ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES),
1902
     "Export mode"),
1903
    ]
1904
  OP_RESULT = ht.TMaybeDict
1905

    
1906

    
1907
class OpBackupExport(OpCode):
1908
  """Export an instance.
1909

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

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

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

    
1953

    
1954
class OpBackupRemove(OpCode):
1955
  """Remove an instance's export."""
1956
  OP_DSC_FIELD = "instance_name"
1957
  OP_PARAMS = [
1958
    _PInstanceName,
1959
    _PInstanceUuid,
1960
    ]
1961
  OP_RESULT = ht.TNone
1962

    
1963

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

    
1978

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

    
1991

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

    
2003

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

    
2015

    
2016
# Test opcodes
2017
class OpTestDelay(OpCode):
2018
  """Sleeps for a configured amount of time.
2019

2020
  This is used just for debugging and testing.
2021

2022
  Parameters:
2023
    - duration: the time to sleep, in seconds
2024
    - on_master: if true, sleep on the master
2025
    - on_nodes: list of nodes in which to sleep
2026

2027
  If the on_master parameter is true, it will execute a sleep on the
2028
  master (before any node sleep).
2029

2030
  If the on_nodes list is not empty, it will sleep on those nodes
2031
  (after the sleep on the master, if that is enabled).
2032

2033
  As an additional feature, the case of duration < 0 will be reported
2034
  as an execution error, so this opcode can be used as a failure
2035
  generator. The case of duration == 0 will not be treated specially.
2036

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

    
2047
  def OP_DSC_FORMATTER(self, value): # pylint: disable=C0103,R0201
2048
    """Custom formatter for duration.
2049

2050
    """
2051
    try:
2052
      v = float(value)
2053
    except TypeError:
2054
      v = value
2055
    return str(v)
2056

    
2057

    
2058
class OpTestAllocator(OpCode):
2059
  """Allocator framework testing.
2060

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

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

    
2097

    
2098
class OpTestJqueue(OpCode):
2099
  """Utility opcode to test some aspects of the job queue.
2100

2101
  """
2102
  OP_PARAMS = [
2103
    ("notify_waitlock", False, ht.TBool, None),
2104
    ("notify_exec", False, ht.TBool, None),
2105
    ("log_messages", ht.EmptyList, ht.TListOf(ht.TString), None),
2106
    ("fail", False, ht.TBool, None),
2107
    ]
2108

    
2109

    
2110
class OpTestDummy(OpCode):
2111
  """Utility opcode used by unittests.
2112

2113
  """
2114
  OP_PARAMS = [
2115
    ("result", ht.NoDefault, ht.NoType, None),
2116
    ("messages", ht.NoDefault, ht.NoType, None),
2117
    ("fail", ht.NoDefault, ht.NoType, None),
2118
    ("submit_jobs", None, ht.NoType, None),
2119
    ]
2120
  WITH_LU = False
2121

    
2122

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

    
2144

    
2145
class OpNetworkRemove(OpCode):
2146
  """Remove an existing network from the cluster.
2147
     Must not be connected to any nodegroup.
2148

2149
  """
2150
  OP_DSC_FIELD = "network_name"
2151
  OP_PARAMS = [
2152
    _PNetworkName,
2153
    _PForce,
2154
    ]
2155
  OP_RESULT = ht.TNone
2156

    
2157

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

    
2175

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

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

    
2195

    
2196
class OpNetworkDisconnect(OpCode):
2197
  """Disconnect a Network from a Nodegroup. Produce errors if NICs are
2198
     present in the Network unless --no-conficts-check option is passed.
2199

2200
  """
2201
  OP_DSC_FIELD = "network_name"
2202
  OP_PARAMS = [
2203
    _PGroupName,
2204
    _PNetworkName,
2205
    ]
2206
  OP_RESULT = ht.TNone
2207

    
2208

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

    
2219

    
2220
def _GetOpList():
2221
  """Returns list of all defined opcodes.
2222

2223
  Does not eliminate duplicates by C{OP_ID}.
2224

2225
  """
2226
  return [v for v in globals().values()
2227
          if (isinstance(v, type) and issubclass(v, OpCode) and
2228
              hasattr(v, "OP_ID") and v is not OpCode)]
2229

    
2230

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