opcodes.py 36 KB
Newer Older
Iustin Pop's avatar
Iustin Pop committed
1
#
Iustin Pop's avatar
Iustin Pop committed
2 3
#

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


"""OpCodes module

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

Iustin Pop's avatar
Iustin Pop committed
27 28
Every operation which modifies the cluster state is expressed via
opcodes.
Iustin Pop's avatar
Iustin Pop committed
29 30 31 32 33 34 35

"""

# this are practically structures, so disable the message about too
# few public methods:
# pylint: disable-msg=R0903

36
import logging
37
import re
38

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
from ganeti import constants
from ganeti import errors
from ganeti import ht


# Common opcode attributes

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

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

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

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

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

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

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

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

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

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

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

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
#: OP_ID conversion regular expression
_OPID_RE = re.compile("([a-z])([A-Z])")


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

  @type name: string
  @param name: the class name, as OpXxxYyy
  @rtype: string
  @return: the name in the OP_XXXX_YYYY format

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

105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165

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

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

  @raise errors.OpPrereqError: when file storage is disabled

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


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

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


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

  """
  if storage_type not in constants.VALID_STORAGE_TYPES:
    raise errors.OpPrereqError("Unknown storage type: %s" % storage_type,
                               errors.ECODE_INVAL)
  if storage_type == constants.ST_FILE:
    RequireFileStorage()
  return True


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


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

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

    @param mcs: The meta class
    @param name: Name of created class
    @param bases: Base classes
    @type attrs: dict
    @param attrs: Class attributes

    """
    assert "__slots__" not in attrs, \
      "Class '%s' defines __slots__ when it should use OP_PARAMS" % name
166
    assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
167

168
    attrs["OP_ID"] = _NameToId(name)
169 170 171 172 173 174 175 176 177 178 179 180 181 182

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

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

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

    attrs["__slots__"] = slots

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

183

Iustin Pop's avatar
Iustin Pop committed
184
class BaseOpCode(object):
185 186
  """A simple serializable object.

Iustin Pop's avatar
Iustin Pop committed
187 188 189
  This object serves as a parent class for OpCode without any custom
  field handling.

190
  """
191 192
  # pylint: disable-msg=E1101
  # as OP_ID is dynamically defined
193 194
  __metaclass__ = _AutoOpParamSlots

Iustin Pop's avatar
Iustin Pop committed
195
  def __init__(self, **kwargs):
Iustin Pop's avatar
Iustin Pop committed
196 197 198 199 200 201 202 203
    """Constructor for BaseOpCode.

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

    """
204
    slots = self._all_slots()
Iustin Pop's avatar
Iustin Pop committed
205
    for key in kwargs:
206
      if key not in slots:
207
        raise TypeError("Object %s doesn't support the parameter '%s'" %
208
                        (self.__class__.__name__, key))
Iustin Pop's avatar
Iustin Pop committed
209 210
      setattr(self, key, kwargs[key])

211
  def __getstate__(self):
Iustin Pop's avatar
Iustin Pop committed
212 213 214 215 216 217 218 219 220
    """Generic serializer.

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

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

    """
221
    state = {}
222
    for name in self._all_slots():
223 224 225 226 227
      if hasattr(self, name):
        state[name] = getattr(self, name)
    return state

  def __setstate__(self, state):
Iustin Pop's avatar
Iustin Pop committed
228 229 230 231 232 233 234 235 236
    """Generic unserializer.

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

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

    """
237 238 239 240
    if not isinstance(state, dict):
      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
                       type(state))

241
    for name in self._all_slots():
Iustin Pop's avatar
Iustin Pop committed
242
      if name not in state and hasattr(self, name):
243 244 245 246 247
        delattr(self, name)

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

248 249 250 251 252 253 254 255 256 257
  @classmethod
  def _all_slots(cls):
    """Compute the list of all declared slots for a class.

    """
    slots = []
    for parent in cls.__mro__:
      slots.extend(getattr(parent, "__slots__", []))
    return slots

258 259 260 261 262 263 264 265 266 267
  @classmethod
  def GetAllParams(cls):
    """Compute list of all parameters for an opcode.

    """
    slots = []
    for parent in cls.__mro__:
      slots.extend(getattr(parent, "OP_PARAMS", []))
    return slots

268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
  def Validate(self, set_defaults):
    """Validate opcode parameters, optionally setting default values.

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

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

      if not hasattr(self, attr_name):
        if default == ht.NoDefault:
          raise errors.OpPrereqError("Required parameter '%s.%s' missing" %
                                     (self.OP_ID, attr_name),
                                     errors.ECODE_INVAL)
        elif set_defaults:
          if callable(default):
            dval = default()
          else:
            dval = default
          setattr(self, attr_name, dval)

      if test == ht.NoType:
        # no tests here
        continue

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

305

Iustin Pop's avatar
Iustin Pop committed
306
class OpCode(BaseOpCode):
Iustin Pop's avatar
Iustin Pop committed
307 308 309 310 311 312
  """Abstract OpCode.

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

  @cvar OP_ID: The ID of this opcode. This should be unique amongst all
313
               children of this class.
314 315 316
  @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
                      string returned by Summary(); see the docstring of that
                      method for details).
317 318
  @cvar OP_PARAMS: List of opcode attributes, the default values they should
                   get if not already defined, and types they must match.
319 320
  @cvar WITH_LU: Boolean that specifies whether this should be included in
      mcpu's dispatch table
321 322
  @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
                 the check steps
323
  @ivar priority: Opcode priority for queue
Iustin Pop's avatar
Iustin Pop committed
324 325

  """
326 327
  # pylint: disable-msg=E1101
  # as OP_ID is dynamically defined
328
  WITH_LU = True
329 330 331 332 333 334
  OP_PARAMS = [
    ("dry_run", None, ht.TMaybeBool),
    ("debug_level", None, ht.TOr(ht.TNone, ht.TPositiveInt)),
    ("priority", constants.OP_PRIO_DEFAULT,
     ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID)),
    ]
335 336 337 338

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

Iustin Pop's avatar
Iustin Pop committed
339 340 341 342 343 344 345
    This method adds to the state dictionary the OP_ID of the class,
    so that on unload we can identify the correct class for
    instantiating the opcode.

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

346
    """
Iustin Pop's avatar
Iustin Pop committed
347
    data = BaseOpCode.__getstate__(self)
348 349 350 351
    data["OP_ID"] = self.OP_ID
    return data

  @classmethod
352
  def LoadOpCode(cls, data):
353 354
    """Generic load opcode method.

Iustin Pop's avatar
Iustin Pop committed
355 356 357 358 359 360 361
    The method identifies the correct opcode class from the dict-form
    by looking for a OP_ID key, if this is not found, or its value is
    not available in this module as a child of this class, we fail.

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

362 363 364 365 366 367 368
    """
    if not isinstance(data, dict):
      raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
    if "OP_ID" not in data:
      raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
    op_id = data["OP_ID"]
    op_class = None
Iustin Pop's avatar
Iustin Pop committed
369 370 371
    if op_id in OP_MAPPING:
      op_class = OP_MAPPING[op_id]
    else:
372 373 374 375 376 377 378 379
      raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
                       op_id)
    op = op_class()
    new_data = data.copy()
    del new_data["OP_ID"]
    op.__setstate__(new_data)
    return op

380 381 382
  def Summary(self):
    """Generates a summary description of this opcode.

383 384 385 386 387
    The summary is the value of the OP_ID attribute (without the "OP_"
    prefix), plus the value of the OP_DSC_FIELD attribute, if one was
    defined; this field should allow to easily identify the operation
    (for an instance creation job, e.g., it would be the instance
    name).
388

389
    """
390
    assert self.OP_ID is not None and len(self.OP_ID) > 3
391 392 393 394 395
    # all OP_ID start with OP_, we remove that
    txt = self.OP_ID[3:]
    field_name = getattr(self, "OP_DSC_FIELD", None)
    if field_name:
      field_value = getattr(self, field_name, None)
396 397
      if isinstance(field_value, (list, tuple)):
        field_value = ",".join(str(i) for i in field_value)
398 399 400
      txt = "%s(%s)" % (txt, field_value)
    return txt

Iustin Pop's avatar
Iustin Pop committed
401

402 403
# cluster opcodes

404
class OpClusterPostInit(OpCode):
405 406 407 408 409 410 411 412
  """Post cluster initialization.

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

  """


413
class OpClusterDestroy(OpCode):
Iustin Pop's avatar
Iustin Pop committed
414 415 416 417 418 419
  """Destroy the cluster.

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

  """
Iustin Pop's avatar
Iustin Pop committed
420 421


422
class OpClusterQuery(OpCode):
Iustin Pop's avatar
Iustin Pop committed
423
  """Query cluster information."""
Iustin Pop's avatar
Iustin Pop committed
424 425


426
class OpClusterVerify(OpCode):
Iustin Pop's avatar
Iustin Pop committed
427 428 429 430 431 432 433 434 435
  """Verify the cluster state.

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

  """
436 437 438 439 440 441 442
  OP_PARAMS = [
    ("skip_checks", ht.EmptyList,
     ht.TListOf(ht.TElemOf(constants.VERIFY_OPTIONAL_CHECKS))),
    ("verbose", False, ht.TBool),
    ("error_codes", False, ht.TBool),
    ("debug_simulate_errors", False, ht.TBool),
    ]
Iustin Pop's avatar
Iustin Pop committed
443 444


445
class OpClusterVerifyDisks(OpCode):
Iustin Pop's avatar
Iustin Pop committed
446 447 448 449
  """Verify the cluster disks.

  Parameters: none

450
  Result: a tuple of four elements:
Iustin Pop's avatar
Iustin Pop committed
451
    - list of node names with bad data returned (unreachable, etc.)
Iustin Pop's avatar
Iustin Pop committed
452
    - dict of node names with broken volume groups (values: error msg)
Iustin Pop's avatar
Iustin Pop committed
453
    - list of instances with degraded disks (that should be activated)
454 455
    - dict of instances with missing logical volumes (values: (node, vol)
      pairs with details about the missing volumes)
Iustin Pop's avatar
Iustin Pop committed
456

457 458 459 460
  In normal operation, all lists should be empty. A non-empty instance
  list (3rd element of the result) is still ok (errors were fixed) but
  non-empty node list means some node is down, and probably there are
  unfixable drbd errors.
Iustin Pop's avatar
Iustin Pop committed
461 462 463 464 465 466 467

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

  """


468
class OpClusterRepairDiskSizes(OpCode):
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
  """Verify the disk sizes of the instances and fixes configuration
  mimatches.

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

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

  In normal operation, the list should be empty.

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

  """
484 485 486
  OP_PARAMS = [
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
    ]
487 488


489
class OpClusterConfigQuery(OpCode):
490
  """Query cluster configuration values."""
491 492 493
  OP_PARAMS = [
    _POutputFields
    ]
Iustin Pop's avatar
Iustin Pop committed
494 495


496
class OpClusterRename(OpCode):
Iustin Pop's avatar
Iustin Pop committed
497 498 499 500 501 502 503 504
  """Rename the cluster.

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

  """
505
  OP_DSC_FIELD = "name"
506 507 508
  OP_PARAMS = [
    ("name", ht.NoDefault, ht.TNonEmptyString),
    ]
509 510


511
class OpClusterSetParams(OpCode):
Iustin Pop's avatar
Iustin Pop committed
512 513 514 515 516 517
  """Change the parameters of the cluster.

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

  """
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
  OP_PARAMS = [
    ("vg_name", None, ht.TMaybeString),
    ("enabled_hypervisors", None,
     ht.TOr(ht.TAnd(ht.TListOf(ht.TElemOf(constants.HYPER_TYPES)), ht.TTrue),
            ht.TNone)),
    ("hvparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
                              ht.TNone)),
    ("beparams", None, ht.TOr(ht.TDict, ht.TNone)),
    ("os_hvp", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
                            ht.TNone)),
    ("osparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
                              ht.TNone)),
    ("candidate_pool_size", None, ht.TOr(ht.TStrictPositiveInt, ht.TNone)),
    ("uid_pool", None, ht.NoType),
    ("add_uids", None, ht.NoType),
    ("remove_uids", None, ht.NoType),
    ("maintain_node_health", None, ht.TMaybeBool),
    ("prealloc_wipe_disks", None, ht.TMaybeBool),
    ("nicparams", None, ht.TOr(ht.TDict, ht.TNone)),
    ("ndparams", None, ht.TOr(ht.TDict, ht.TNone)),
    ("drbd_helper", None, ht.TOr(ht.TString, ht.TNone)),
    ("default_iallocator", None, ht.TOr(ht.TString, ht.TNone)),
    ("master_netdev", None, ht.TOr(ht.TString, ht.TNone)),
    ("reserved_lvs", None, ht.TOr(ht.TListOf(ht.TNonEmptyString), ht.TNone)),
    ("hidden_os", None, ht.TOr(ht.TListOf(
          ht.TAnd(ht.TList,
                ht.TIsLength(2),
                ht.TMap(lambda v: v[0], ht.TElemOf(constants.DDMS_VALUES)))),
          ht.TNone)),
    ("blacklisted_os", None, ht.TOr(ht.TListOf(
          ht.TAnd(ht.TList,
                ht.TIsLength(2),
                ht.TMap(lambda v: v[0], ht.TElemOf(constants.DDMS_VALUES)))),
          ht.TNone)),
552
    ]
553 554


555
class OpClusterRedistConf(OpCode):
556 557 558 559
  """Force a full push of the cluster configuration.

  """

Michael Hanselmann's avatar
Michael Hanselmann committed
560 561 562 563 564 565 566 567 568

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

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

  """
569 570 571 572 573
  OP_PARAMS = [
    ("what", ht.NoDefault, ht.TElemOf(constants.QR_OP_QUERY)),
    ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
    ("filter", None, ht.TOr(ht.TNone,
                            ht.TListOf(ht.TOr(ht.TNonEmptyString, ht.TList)))),
Michael Hanselmann's avatar
Michael Hanselmann committed
574 575 576 577 578 579 580 581 582 583
    ]


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

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

  """
584 585 586
  OP_PARAMS = [
    ("what", ht.NoDefault, ht.TElemOf(constants.QR_OP_QUERY)),
    ("fields", None, ht.TOr(ht.TNone, ht.TListOf(ht.TNonEmptyString))),
Michael Hanselmann's avatar
Michael Hanselmann committed
587 588 589
    ]


590
class OpOobCommand(OpCode):
René Nussbaumer's avatar
René Nussbaumer committed
591
  """Interact with OOB."""
592
  OP_PARAMS = [
593
    ("node_names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
594 595
    ("command", None, ht.TElemOf(constants.OOB_COMMANDS)),
    ("timeout", constants.OOB_TIMEOUT, ht.TInt),
René Nussbaumer's avatar
René Nussbaumer committed
596 597 598
    ]


599 600
# node opcodes

601
class OpNodeRemove(OpCode):
Iustin Pop's avatar
Iustin Pop committed
602 603 604 605 606 607 608
  """Remove a node.

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

  """
609
  OP_DSC_FIELD = "node_name"
610 611 612
  OP_PARAMS = [
    _PNodeName,
    ]
Iustin Pop's avatar
Iustin Pop committed
613 614


Iustin Pop's avatar
Iustin Pop committed
615
class OpNodeAdd(OpCode):
Iustin Pop's avatar
Iustin Pop committed
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
  """Add a node to the cluster.

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

  """
643
  OP_DSC_FIELD = "node_name"
644 645 646 647 648 649 650 651 652 653
  OP_PARAMS = [
    _PNodeName,
    ("primary_ip", None, ht.NoType),
    ("secondary_ip", None, ht.TMaybeString),
    ("readd", False, ht.TBool),
    ("group", None, ht.TMaybeString),
    ("master_capable", None, ht.TMaybeBool),
    ("vm_capable", None, ht.TMaybeBool),
    ("ndparams", None, ht.TOr(ht.TDict, ht.TNone)),
    ]
Iustin Pop's avatar
Iustin Pop committed
654 655


656
class OpNodeQuery(OpCode):
Iustin Pop's avatar
Iustin Pop committed
657
  """Compute the list of nodes."""
658 659 660 661 662
  OP_PARAMS = [
    _POutputFields,
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
    ("use_locking", False, ht.TBool),
    ]
Iustin Pop's avatar
Iustin Pop committed
663 664


665
class OpNodeQueryvols(OpCode):
666
  """Get list of volumes on node."""
667 668 669 670
  OP_PARAMS = [
    _POutputFields,
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
    ]
671 672


673
class OpNodeQueryStorage(OpCode):
674
  """Get information on storage for node(s)."""
675 676 677 678 679
  OP_PARAMS = [
    _POutputFields,
    _PStorageType,
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
    ("name", None, ht.TMaybeString),
680 681 682
    ]


683
class OpNodeModifyStorage(OpCode):
684
  """Modifies the properies of a storage unit"""
685 686 687 688 689
  OP_PARAMS = [
    _PNodeName,
    _PStorageType,
    ("name", ht.NoDefault, ht.TNonEmptyString),
    ("changes", ht.NoDefault, ht.TDict),
690 691 692
    ]


693 694 695
class OpRepairNodeStorage(OpCode):
  """Repairs the volume group on a node."""
  OP_DSC_FIELD = "node_name"
696 697 698 699 700
  OP_PARAMS = [
    _PNodeName,
    _PStorageType,
    ("name", ht.NoDefault, ht.TNonEmptyString),
    ("ignore_consistency", False, ht.TBool),
701 702 703
    ]


704
class OpNodeSetParams(OpCode):
Iustin Pop's avatar
Iustin Pop committed
705 706
  """Change the parameters of a node."""
  OP_DSC_FIELD = "node_name"
707 708 709 710 711 712 713 714 715 716 717 718
  OP_PARAMS = [
    _PNodeName,
    _PForce,
    ("master_candidate", None, ht.TMaybeBool),
    ("offline", None, ht.TMaybeBool),
    ("drained", None, ht.TMaybeBool),
    ("auto_promote", False, ht.TBool),
    ("master_capable", None, ht.TMaybeBool),
    ("vm_capable", None, ht.TMaybeBool),
    ("secondary_ip", None, ht.TMaybeString),
    ("ndparams", None, ht.TOr(ht.TDict, ht.TNone)),
    ("powered", None, ht.TMaybeBool),
Iustin Pop's avatar
Iustin Pop committed
719 720
    ]

Iustin Pop's avatar
Iustin Pop committed
721

722
class OpNodePowercycle(OpCode):
Iustin Pop's avatar
Iustin Pop committed
723 724
  """Tries to powercycle a node."""
  OP_DSC_FIELD = "node_name"
725 726 727
  OP_PARAMS = [
    _PNodeName,
    _PForce,
Iustin Pop's avatar
Iustin Pop committed
728 729
    ]

730

731
class OpNodeMigrate(OpCode):
732 733
  """Migrate all instances from a node."""
  OP_DSC_FIELD = "node_name"
734 735 736 737
  OP_PARAMS = [
    _PNodeName,
    _PMigrationMode,
    _PMigrationLive,
738 739 740
    ]


741
class OpNodeEvacStrategy(OpCode):
742 743
  """Compute the evacuation strategy for a list of nodes."""
  OP_DSC_FIELD = "nodes"
744 745 746 747 748
  OP_PARAMS = [
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
    ("remote_node", None, ht.TMaybeString),
    ("iallocator", None, ht.TMaybeString),
    ]
749 750


Iustin Pop's avatar
Iustin Pop committed
751 752
# instance opcodes

753
class OpInstanceCreate(OpCode):
754 755 756 757 758 759 760
  """Create an instance.

  @ivar instance_name: Instance name
  @ivar mode: Instance creation mode (one of L{constants.INSTANCE_CREATE_MODES})
  @ivar source_handshake: Signed handshake from source (remote import only)
  @ivar source_x509_ca: Source X509 CA in PEM format (remote import only)
  @ivar source_instance_name: Previous name of instance (remote import only)
761 762
  @ivar source_shutdown_timeout: Shutdown timeout used for source instance
    (remote import only)
763 764

  """
765
  OP_DSC_FIELD = "instance_name"
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
  OP_PARAMS = [
    _PInstanceName,
    ("beparams", ht.EmptyDict, ht.TDict),
    ("disks", ht.NoDefault, ht.TListOf(ht.TDict)),
    ("disk_template", ht.NoDefault, _CheckDiskTemplate),
    ("file_driver", None, ht.TOr(ht.TNone, ht.TElemOf(constants.FILE_DRIVER))),
    ("file_storage_dir", None, ht.TMaybeString),
    ("force_variant", False, ht.TBool),
    ("hvparams", ht.EmptyDict, ht.TDict),
    ("hypervisor", None, ht.TMaybeString),
    ("iallocator", None, ht.TMaybeString),
    ("identify_defaults", False, ht.TBool),
    ("ip_check", True, ht.TBool),
    ("mode", ht.NoDefault, ht.TElemOf(constants.INSTANCE_CREATE_MODES)),
    ("name_check", True, ht.TBool),
    ("nics", ht.NoDefault, ht.TListOf(ht.TDict)),
    ("no_install", None, ht.TMaybeBool),
    ("osparams", ht.EmptyDict, ht.TDict),
    ("os_type", None, ht.TMaybeString),
    ("pnode", None, ht.TMaybeString),
    ("snode", None, ht.TMaybeString),
    ("source_handshake", None, ht.TOr(ht.TList, ht.TNone)),
    ("source_instance_name", None, ht.TMaybeString),
    ("source_shutdown_timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT,
     ht.TPositiveInt),
    ("source_x509_ca", None, ht.TMaybeString),
    ("src_node", None, ht.TMaybeString),
    ("src_path", None, ht.TMaybeString),
    ("start", True, ht.TBool),
    ("wait_for_sync", True, ht.TBool),
796
    ]
Iustin Pop's avatar
Iustin Pop committed
797 798


799
class OpInstanceReinstall(OpCode):
Iustin Pop's avatar
Iustin Pop committed
800
  """Reinstall an instance's OS."""
801
  OP_DSC_FIELD = "instance_name"
802 803 804 805 806 807
  OP_PARAMS = [
    _PInstanceName,
    ("os_type", None, ht.TMaybeString),
    ("force_variant", False, ht.TBool),
    ("osparams", None, ht.TOr(ht.TDict, ht.TNone)),
    ]
808 809


810
class OpInstanceRemove(OpCode):
Iustin Pop's avatar
Iustin Pop committed
811
  """Remove an instance."""
812
  OP_DSC_FIELD = "instance_name"
813 814 815 816
  OP_PARAMS = [
    _PInstanceName,
    _PShutdownTimeout,
    ("ignore_failures", False, ht.TBool),
817
    ]
Iustin Pop's avatar
Iustin Pop committed
818 819


820
class OpInstanceRename(OpCode):
821
  """Rename an instance."""
822 823 824 825 826
  OP_PARAMS = [
    _PInstanceName,
    ("new_name", ht.NoDefault, ht.TNonEmptyString),
    ("ip_check", False, ht.TBool),
    ("name_check", True, ht.TBool),
827
    ]
828 829


830
class OpInstanceStartup(OpCode):
Iustin Pop's avatar
Iustin Pop committed
831
  """Startup an instance."""
832
  OP_DSC_FIELD = "instance_name"
833 834 835 836 837 838
  OP_PARAMS = [
    _PInstanceName,
    _PForce,
    _PIgnoreOfflineNodes,
    ("hvparams", ht.EmptyDict, ht.TDict),
    ("beparams", ht.EmptyDict, ht.TDict),
839
    ]
Iustin Pop's avatar
Iustin Pop committed
840 841


842
class OpInstanceShutdown(OpCode):
Iustin Pop's avatar
Iustin Pop committed
843
  """Shutdown an instance."""
844
  OP_DSC_FIELD = "instance_name"
845 846 847 848
  OP_PARAMS = [
    _PInstanceName,
    _PIgnoreOfflineNodes,
    ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TPositiveInt),
849
    ]
Iustin Pop's avatar
Iustin Pop committed
850 851


852
class OpInstanceReboot(OpCode):
853
  """Reboot an instance."""
854
  OP_DSC_FIELD = "instance_name"
855 856 857 858 859
  OP_PARAMS = [
    _PInstanceName,
    _PShutdownTimeout,
    ("ignore_secondaries", False, ht.TBool),
    ("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES)),
860
    ]
861 862


863
class OpInstanceReplaceDisks(OpCode):
Iustin Pop's avatar
Iustin Pop committed
864
  """Replace the disks of an instance."""
865
  OP_DSC_FIELD = "instance_name"
866 867 868 869 870 871 872
  OP_PARAMS = [
    _PInstanceName,
    ("mode", ht.NoDefault, ht.TElemOf(constants.REPLACE_MODES)),
    ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt)),
    ("remote_node", None, ht.TMaybeString),
    ("iallocator", None, ht.TMaybeString),
    ("early_release", False, ht.TBool),
873
    ]
Iustin Pop's avatar
Iustin Pop committed
874 875


876
class OpInstanceFailover(OpCode):
Iustin Pop's avatar
Iustin Pop committed
877
  """Failover an instance."""
878
  OP_DSC_FIELD = "instance_name"
879 880 881 882
  OP_PARAMS = [
    _PInstanceName,
    _PShutdownTimeout,
    ("ignore_consistency", False, ht.TBool),
883
    ]
Iustin Pop's avatar
Iustin Pop committed
884 885


886
class OpInstanceMigrate(OpCode):
887 888 889 890 891
  """Migrate an instance.

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

Iustin Pop's avatar
Iustin Pop committed
892
  @ivar instance_name: the name of the instance
893
  @ivar mode: the migration mode (live, non-live or None for auto)
894 895

  """
Iustin Pop's avatar
Iustin Pop committed
896
  OP_DSC_FIELD = "instance_name"
897 898 899 900 901 902
  OP_PARAMS = [
    _PInstanceName,
    _PMigrationMode,
    _PMigrationLive,
    ("cleanup", False, ht.TBool),
    ]
903 904


905
class OpInstanceMove(OpCode):
906 907 908 909 910 911 912 913 914 915
  """Move an instance.

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

  @ivar instance_name: the name of the instance
  @ivar target_node: the destination node

  """
  OP_DSC_FIELD = "instance_name"
916 917 918 919
  OP_PARAMS = [
    _PInstanceName,
    _PShutdownTimeout,
    ("target_node", ht.NoDefault, ht.TNonEmptyString),
Balazs Lecz's avatar
Balazs Lecz committed
920
    ]
921 922


923
class OpInstanceConsole(OpCode):
Iustin Pop's avatar
Iustin Pop committed
924
  """Connect to an instance's console."""
925
  OP_DSC_FIELD = "instance_name"
926 927 928
  OP_PARAMS = [
    _PInstanceName
    ]
Iustin Pop's avatar
Iustin Pop committed
929 930


931
class OpInstanceActivateDisks(OpCode):
Iustin Pop's avatar
Iustin Pop committed
932
  """Activate an instance's disks."""
933
  OP_DSC_FIELD = "instance_name"
934 935 936 937
  OP_PARAMS = [
    _PInstanceName,
    ("ignore_size", False, ht.TBool),
    ]
Iustin Pop's avatar
Iustin Pop committed
938 939


940
class OpInstanceDeactivateDisks(OpCode):
Iustin Pop's avatar
Iustin Pop committed
941
  """Deactivate an instance's disks."""
942
  OP_DSC_FIELD = "instance_name"
943 944 945
  OP_PARAMS = [
    _PInstanceName
    ]
Iustin Pop's avatar
Iustin Pop committed
946 947


948
class OpInstanceRecreateDisks(OpCode):
Iustin Pop's avatar
Iustin Pop committed
949 950
  """Deactivate an instance's disks."""
  OP_DSC_FIELD = "instance_name"
951 952 953 954
  OP_PARAMS = [
    _PInstanceName,
    ("disks", ht.EmptyList, ht.TListOf(ht.TPositiveInt)),
    ]
Iustin Pop's avatar
Iustin Pop committed
955 956


957
class OpInstanceQuery(OpCode):
Iustin Pop's avatar
Iustin Pop committed
958
  """Compute the list of instances."""
959 960 961 962 963
  OP_PARAMS = [
    _POutputFields,
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
    ("use_locking", False, ht.TBool),
    ]
Iustin Pop's avatar
Iustin Pop committed
964 965


966
class OpInstanceQueryData(OpCode):
Iustin Pop's avatar
Iustin Pop committed
967
  """Compute the run-time status of instances."""
968 969 970 971
  OP_PARAMS = [
    ("instances", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
    ("static", False, ht.TBool),
    ]
Iustin Pop's avatar
Iustin Pop committed
972 973


974
class OpInstanceSetParams(OpCode):
Iustin Pop's avatar
Iustin Pop committed
975
  """Change the parameters of an instance."""
976
  OP_DSC_FIELD = "instance_name"
977 978 979 980 981 982 983
  OP_PARAMS = [
    _PInstanceName,
    _PForce,
    ("nics", ht.EmptyList, ht.TList),
    ("disks", ht.EmptyList, ht.TList),
    ("beparams", ht.EmptyDict, ht.TDict),
    ("hvparams", ht.EmptyDict, ht.TDict),
984
    ("disk_template", None, ht.TOr(ht.TNone, _CheckDiskTemplate)),
985 986 987 988
    ("remote_node", None, ht.TMaybeString),
    ("os_name", None, ht.TMaybeString),
    ("force_variant", False, ht.TBool),
    ("osparams", None, ht.TOr(ht.TDict, ht.TNone)),
989
    ]
Iustin Pop's avatar
Iustin Pop committed
990 991


Iustin Pop's avatar
Iustin Pop committed
992
class OpInstanceGrowDisk(OpCode):
Iustin Pop's avatar
Iustin Pop committed
993
  """Grow a disk of an instance."""
994
  OP_DSC_FIELD = "instance_name"
995 996 997 998 999
  OP_PARAMS = [
    _PInstanceName,
    ("disk", ht.NoDefault, ht.TInt),
    ("amount", ht.NoDefault, ht.TInt),
    ("wait_for_sync", True, ht.TBool),
1000
    ]
Iustin Pop's avatar
Iustin Pop committed
1001 1002


1003 1004
# Node group opcodes

Iustin Pop's avatar
Iustin Pop committed
1005
class OpGroupAdd(OpCode):
1006 1007
  """Add a node group to the cluster."""
  OP_DSC_FIELD = "group_name"
1008 1009 1010 1011 1012
  OP_PARAMS = [
    _PGroupName,
    ("ndparams", None, ht.TOr(ht.TDict, ht.TNone)),
    ("alloc_policy", None,
     ht.TOr(ht.TNone, ht.TElemOf(constants.VALID_ALLOC_POLICIES))),
1013
    ]
1014 1015


1016
class OpGroupAssignNodes(OpCode):
1017 1018 1019 1020 1021 1022 1023 1024 1025
  """Assign nodes to a node group."""
  OP_DSC_FIELD = "group_name"
  OP_PARAMS = [
    _PGroupName,
    _PForce,
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
    ]


1026
class OpGroupQuery(OpCode):
1027
  """Compute the list of node groups."""
1028 1029 1030 1031
  OP_PARAMS = [
    _POutputFields,
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
    ]
1032 1033


1034
class OpGroupSetParams(OpCode):
1035 1036
  """Change the parameters of a node group."""
  OP_DSC_FIELD = "group_name"
1037 1038 1039 1040 1041
  OP_PARAMS = [
    _PGroupName,
    ("ndparams", None, ht.TOr(ht.TDict, ht.TNone)),
    ("alloc_policy", None, ht.TOr(ht.TNone,
                                  ht.TElemOf(constants.VALID_ALLOC_POLICIES))),
1042 1043 1044
    ]


1045
class OpGroupRemove(OpCode):
1046 1047
  """Remove a node group from the cluster."""
  OP_DSC_FIELD = "group_name"
1048 1049 1050
  OP_PARAMS = [
    _PGroupName,
    ]
1051 1052


1053
class OpGroupRename(OpCode):
1054 1055
  """Rename a node group in the cluster."""
  OP_DSC_FIELD = "old_name"
1056 1057 1058 1059
  OP_PARAMS = [
    ("old_name", ht.NoDefault, ht.TNonEmptyString),
    ("new_name", ht.NoDefault, ht.TNonEmptyString),
    ]
1060 1061


Iustin Pop's avatar
Iustin Pop committed
1062
# OS opcodes
1063
class OpOsDiagnose(OpCode):
Iustin Pop's avatar
Iustin Pop committed
1064
  """Compute the list of guest operating systems."""
1065 1066 1067 1068
  OP_PARAMS = [
    _POutputFields,
    ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
    ]
Iustin Pop's avatar
Iustin Pop committed
1069

1070

Iustin Pop's avatar
Iustin Pop committed
1071
# Exports opcodes
1072
class OpBackupQuery(OpCode):
Iustin Pop's avatar
Iustin Pop committed
1073
  """Compute the list of exported images."""
1074 1075 1076 1077
  OP_PARAMS = [
    ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
    ("use_locking", False, ht.TBool),
    ]
Iustin Pop's avatar
Iustin Pop committed
1078

1079

1080
class OpBackupPrepare(OpCode):
1081 1082 1083 1084 1085 1086 1087
  """Prepares an instance export.

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

  """
  OP_DSC_FIELD = "instance_name"
1088 1089 1090
  OP_PARAMS = [
    _PInstanceName,
    ("mode", ht.NoDefault, ht.TElemOf(constants.EXPORT_MODES)),
1091 1092 1093
    ]


1094
class OpBackupExport(OpCode):
1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109
  """Export an instance.

  For local exports, the export destination is the node name. For remote
  exports, the export destination is a list of tuples, each consisting of
  hostname/IP address, port, HMAC and HMAC salt. The HMAC is calcul