opcodes.py 12 KB
Newer Older
Iustin Pop's avatar
Iustin Pop committed
1
#
Iustin Pop's avatar
Iustin Pop committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
#

# Copyright (C) 2006, 2007 Google Inc.
#
# 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.


This module implements the logic for doing operations in the cluster. There
are two kinds of classes defined:
  - opcodes, which are small classes only holding data for the task at hand
  - logical units, which know how to deal with their specific opcode only

"""

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

39 40 41 42 43 44 45 46

class BaseJO(object):
  """A simple serializable object.

  This object serves as a parent class for both OpCode and Job since
  they are serialized in the same way.

  """
Iustin Pop's avatar
Iustin Pop committed
47 48 49 50 51
  __slots__ = []

  def __init__(self, **kwargs):
    for key in kwargs:
      if key not in self.__slots__:
52
        raise TypeError("Object %s doesn't support the parameter '%s'" %
53
                        (self.__class__.__name__, key))
Iustin Pop's avatar
Iustin Pop committed
54 55
      setattr(self, key, kwargs[key])

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
  def __getstate__(self):
    state = {}
    for name in self.__slots__:
      if hasattr(self, name):
        state[name] = getattr(self, name)
    return state

  def __setstate__(self, state):
    if not isinstance(state, dict):
      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
                       type(state))

    for name in self.__slots__:
      if name not in state:
        delattr(self, name)

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


class Job(BaseJO):
77 78 79 80 81 82 83 84 85 86 87 88
  """Job definition structure

  The Job definitions has two sets of parameters:
    - the parameters of the job itself (all filled by server):
      - job_id,
      - status: pending, running, successfull, failed, aborted
    - opcode parameters:
      - op_list, list of opcodes, clients creates this
      - op_status, status for each opcode, server fills in
      - op_result, result for each opcode, server fills in

  """
89 90
  STATUS_PENDING = 1
  STATUS_RUNNING = 2
91 92 93 94 95 96 97 98 99 100 101
  STATUS_SUCCESS = 3
  STATUS_FAIL = 4
  STATUS_ABORT = 5

  __slots__ = [
    "job_id",
    "status",
    "op_list",
    "op_status",
    "op_result",
    ]
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117

  def __getstate__(self):
    """Specialized getstate for jobs

    """
    data = BaseJO.__getstate__(self)
    if "op_list" in data:
      data["op_list"] = [op.__getstate__() for op in data["op_list"]]
    return data

  def __setstate__(self, state):
    """Specialized setstate for jobs

    """
    BaseJO.__setstate__(self, state)
    if "op_list" in state:
118
      self.op_list = [OpCode.LoadOpCode(op) for op in state["op_list"]]
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134


class OpCode(BaseJO):
  """Abstract OpCode"""
  OP_ID = "OP_ABSTRACT"
  __slots__ = []

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

    """
    data = BaseJO.__getstate__(self)
    data["OP_ID"] = self.OP_ID
    return data

  @classmethod
135
  def LoadOpCode(cls, data):
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
    """Generic load opcode method.

    """
    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
    for item in globals().values():
      if (isinstance(item, type) and
          issubclass(item, cls) and
          hasattr(item, "OP_ID") and
          getattr(item, "OP_ID") == op_id):
        op_class = item
        break
    if op_class is None:
      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

Iustin Pop's avatar
Iustin Pop committed
161 162 163 164 165

class OpInitCluster(OpCode):
  """Initialise the cluster."""
  OP_ID = "OP_CLUSTER_INIT"
  __slots__ = ["cluster_name", "secondary_ip", "hypervisor_type",
166 167
               "vg_name", "mac_prefix", "def_bridge", "master_netdev",
               "file_storage_dir"]
Iustin Pop's avatar
Iustin Pop committed
168 169 170 171 172 173 174 175 176


class OpDestroyCluster(OpCode):
  """Destroy the cluster."""
  OP_ID = "OP_CLUSTER_DESTROY"
  __slots__ = []


class OpQueryClusterInfo(OpCode):
Iustin Pop's avatar
Iustin Pop committed
177
  """Query cluster information."""
Iustin Pop's avatar
Iustin Pop committed
178 179 180 181 182
  OP_ID = "OP_CLUSTER_QUERY"
  __slots__ = []


class OpClusterCopyFile(OpCode):
Iustin Pop's avatar
Iustin Pop committed
183
  """Copy a file to multiple nodes."""
Iustin Pop's avatar
Iustin Pop committed
184 185 186 187 188
  OP_ID = "OP_CLUSTER_COPYFILE"
  __slots__ = ["nodes", "filename"]


class OpRunClusterCommand(OpCode):
Iustin Pop's avatar
Iustin Pop committed
189
  """Run a command on multiple nodes."""
Iustin Pop's avatar
Iustin Pop committed
190 191 192 193 194
  OP_ID = "OP_CLUSTER_RUNCOMMAND"
  __slots__ = ["nodes", "command"]


class OpVerifyCluster(OpCode):
Iustin Pop's avatar
Iustin Pop committed
195
  """Verify the cluster state."""
Iustin Pop's avatar
Iustin Pop committed
196
  OP_ID = "OP_CLUSTER_VERIFY"
197
  __slots__ = ["skip_checks"]
Iustin Pop's avatar
Iustin Pop committed
198 199


Iustin Pop's avatar
Iustin Pop committed
200 201 202 203 204 205 206
class OpVerifyDisks(OpCode):
  """Verify the cluster disks.

  Parameters: none

  Result: two lists:
    - list of node names with bad data returned (unreachable, etc.)
207
    - dist of node names with broken volume groups (values: error msg)
Iustin Pop's avatar
Iustin Pop committed
208
    - list of instances with degraded disks (that should be activated)
209 210
    - dict of instances with missing logical volumes (values: (node, vol)
      pairs with details about the missing volumes)
Iustin Pop's avatar
Iustin Pop committed
211

212 213 214 215
  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
216 217 218 219 220 221 222 223 224

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

  """
  OP_ID = "OP_CLUSTER_VERIFY_DISKS"
  __slots__ = []


Iustin Pop's avatar
Iustin Pop committed
225
class OpMasterFailover(OpCode):
Iustin Pop's avatar
Iustin Pop committed
226
  """Do a master failover."""
Iustin Pop's avatar
Iustin Pop committed
227 228 229 230 231
  OP_ID = "OP_CLUSTER_MASTERFAILOVER"
  __slots__ = []


class OpDumpClusterConfig(OpCode):
Iustin Pop's avatar
Iustin Pop committed
232
  """Dump the cluster configuration."""
Iustin Pop's avatar
Iustin Pop committed
233 234 235 236
  OP_ID = "OP_CLUSTER_DUMPCONFIG"
  __slots__ = []


237 238 239 240 241 242
class OpRenameCluster(OpCode):
  """Rename the cluster."""
  OP_ID = "OP_CLUSTER_RENAME"
  __slots__ = ["name"]


243 244 245 246 247 248
class OpSetClusterParams(OpCode):
  """Change the parameters of the cluster."""
  OP_ID = "OP_CLUSTER_SET_PARAMS"
  __slots__ = ["vg_name"]


249 250
# node opcodes

Iustin Pop's avatar
Iustin Pop committed
251 252 253 254 255 256 257 258 259
class OpRemoveNode(OpCode):
  """Remove a node."""
  OP_ID = "OP_NODE_REMOVE"
  __slots__ = ["node_name"]


class OpAddNode(OpCode):
  """Add a node."""
  OP_ID = "OP_NODE_ADD"
260
  __slots__ = ["node_name", "primary_ip", "secondary_ip", "readd"]
Iustin Pop's avatar
Iustin Pop committed
261 262 263 264 265


class OpQueryNodes(OpCode):
  """Compute the list of nodes."""
  OP_ID = "OP_NODE_QUERY"
266
  __slots__ = ["output_fields", "names"]
Iustin Pop's avatar
Iustin Pop committed
267 268


269 270 271 272 273 274
class OpQueryNodeVolumes(OpCode):
  """Get list of volumes on node."""
  OP_ID = "OP_NODE_QUERYVOLS"
  __slots__ = ["nodes", "output_fields"]


Iustin Pop's avatar
Iustin Pop committed
275 276 277
# instance opcodes

class OpCreateInstance(OpCode):
Iustin Pop's avatar
Iustin Pop committed
278
  """Create an instance."""
Iustin Pop's avatar
Iustin Pop committed
279
  OP_ID = "OP_INSTANCE_CREATE"
280 281 282 283 284
  __slots__ = [
    "instance_name", "mem_size", "disk_size", "os_type", "pnode",
    "disk_template", "snode", "swap_size", "mode",
    "vcpus", "ip", "bridge", "src_node", "src_path", "start",
    "wait_for_sync", "ip_check", "mac",
285
    "kernel_path", "initrd_path", "hvm_boot_order",
286
    "file_storage_dir", "file_driver",
287
    "iallocator",
288
    ]
Iustin Pop's avatar
Iustin Pop committed
289 290


291
class OpReinstallInstance(OpCode):
Iustin Pop's avatar
Iustin Pop committed
292
  """Reinstall an instance's OS."""
293
  OP_ID = "OP_INSTANCE_REINSTALL"
294
  __slots__ = ["instance_name", "os_type"]
295 296


Iustin Pop's avatar
Iustin Pop committed
297 298 299
class OpRemoveInstance(OpCode):
  """Remove an instance."""
  OP_ID = "OP_INSTANCE_REMOVE"
Iustin Pop's avatar
Iustin Pop committed
300
  __slots__ = ["instance_name", "ignore_failures"]
Iustin Pop's avatar
Iustin Pop committed
301 302


303 304 305 306 307 308
class OpRenameInstance(OpCode):
  """Rename an instance."""
  OP_ID = "OP_INSTANCE_RENAME"
  __slots__ = ["instance_name", "ignore_ip", "new_name"]


Iustin Pop's avatar
Iustin Pop committed
309
class OpStartupInstance(OpCode):
Iustin Pop's avatar
Iustin Pop committed
310
  """Startup an instance."""
Iustin Pop's avatar
Iustin Pop committed
311 312 313 314 315
  OP_ID = "OP_INSTANCE_STARTUP"
  __slots__ = ["instance_name", "force", "extra_args"]


class OpShutdownInstance(OpCode):
Iustin Pop's avatar
Iustin Pop committed
316
  """Shutdown an instance."""
Iustin Pop's avatar
Iustin Pop committed
317 318 319 320
  OP_ID = "OP_INSTANCE_SHUTDOWN"
  __slots__ = ["instance_name"]


321 322
class OpRebootInstance(OpCode):
  """Reboot an instance."""
Iustin Pop's avatar
Iustin Pop committed
323
  OP_ID = "OP_INSTANCE_REBOOT"
324 325 326 327
  __slots__ = ["instance_name", "reboot_type", "extra_args",
               "ignore_secondaries" ]


Iustin Pop's avatar
Iustin Pop committed
328
class OpReplaceDisks(OpCode):
Iustin Pop's avatar
Iustin Pop committed
329
  """Replace the disks of an instance."""
Iustin Pop's avatar
Iustin Pop committed
330
  OP_ID = "OP_INSTANCE_REPLACE_DISKS"
331
  __slots__ = ["instance_name", "remote_node", "mode", "disks"]
Iustin Pop's avatar
Iustin Pop committed
332 333 334 335 336 337 338 339 340


class OpFailoverInstance(OpCode):
  """Failover an instance."""
  OP_ID = "OP_INSTANCE_FAILOVER"
  __slots__ = ["instance_name", "ignore_consistency"]


class OpConnectConsole(OpCode):
Iustin Pop's avatar
Iustin Pop committed
341
  """Connect to an instance's console."""
Iustin Pop's avatar
Iustin Pop committed
342 343 344 345 346
  OP_ID = "OP_INSTANCE_CONSOLE"
  __slots__ = ["instance_name"]


class OpActivateInstanceDisks(OpCode):
Iustin Pop's avatar
Iustin Pop committed
347
  """Activate an instance's disks."""
Iustin Pop's avatar
Iustin Pop committed
348 349 350 351 352
  OP_ID = "OP_INSTANCE_ACTIVATE_DISKS"
  __slots__ = ["instance_name"]


class OpDeactivateInstanceDisks(OpCode):
Iustin Pop's avatar
Iustin Pop committed
353
  """Deactivate an instance's disks."""
Iustin Pop's avatar
Iustin Pop committed
354 355 356 357 358 359 360
  OP_ID = "OP_INSTANCE_DEACTIVATE_DISKS"
  __slots__ = ["instance_name"]


class OpQueryInstances(OpCode):
  """Compute the list of instances."""
  OP_ID = "OP_INSTANCE_QUERY"
361
  __slots__ = ["output_fields", "names"]
Iustin Pop's avatar
Iustin Pop committed
362 363 364 365 366 367 368 369


class OpQueryInstanceData(OpCode):
  """Compute the run-time status of instances."""
  OP_ID = "OP_INSTANCE_QUERY_DATA"
  __slots__ = ["instances"]


370
class OpSetInstanceParams(OpCode):
Iustin Pop's avatar
Iustin Pop committed
371
  """Change the parameters of an instance."""
372
  OP_ID = "OP_INSTANCE_SET_PARAMS"
373 374
  __slots__ = [
    "instance_name", "mem", "vcpus", "ip", "bridge", "mac",
375
    "kernel_path", "initrd_path", "hvm_boot_order",
376
    ]
Iustin Pop's avatar
Iustin Pop committed
377 378 379 380 381 382


# OS opcodes
class OpDiagnoseOS(OpCode):
  """Compute the list of guest operating systems."""
  OP_ID = "OP_OS_DIAGNOSE"
383
  __slots__ = ["output_fields", "names"]
Iustin Pop's avatar
Iustin Pop committed
384

385

Iustin Pop's avatar
Iustin Pop committed
386 387 388 389 390 391
# Exports opcodes
class OpQueryExports(OpCode):
  """Compute the list of exported images."""
  OP_ID = "OP_BACKUP_QUERY"
  __slots__ = ["nodes"]

392

Iustin Pop's avatar
Iustin Pop committed
393 394 395 396
class OpExportInstance(OpCode):
  """Export an instance."""
  OP_ID = "OP_BACKUP_EXPORT"
  __slots__ = ["instance_name", "target_node", "shutdown"]
397 398 399 400 401 402 403 404 405


# Tags opcodes
class OpGetTags(OpCode):
  """Returns the tags of the given object."""
  OP_ID = "OP_TAGS_GET"
  __slots__ = ["kind", "name"]


Iustin Pop's avatar
Iustin Pop committed
406 407 408 409 410 411
class OpSearchTags(OpCode):
  """Searches the tags in the cluster for a given pattern."""
  OP_ID = "OP_TAGS_SEARCH"
  __slots__ = ["pattern"]


412 413
class OpAddTags(OpCode):
  """Add a list of tags on a given object."""
414
  OP_ID = "OP_TAGS_SET"
415
  __slots__ = ["kind", "name", "tags"]
416 417


418 419
class OpDelTags(OpCode):
  """Remove a list of tags from a given object."""
420
  OP_ID = "OP_TAGS_DEL"
421
  __slots__ = ["kind", "name", "tags"]
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447


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

  This is used just for debugging and testing.

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

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

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

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

  """
  OP_ID = "OP_TEST_DELAY"
  __slots__ = ["duration", "on_master", "on_nodes"]
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466


class OpTestAllocator(OpCode):
  """Allocator framework testing.

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

  """
  OP_ID = "OP_TEST_ALLOCATOR"
  __slots__ = [
    "direction", "mode", "allocator", "name",
    "mem_size", "disks", "disk_template",
    "os", "tags", "nics", "vcpus",
    ]