Newer
Older
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
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.
"""
__slots__ = []
def __init__(self, **kwargs):
for key in kwargs:
if key not in self.__slots__:
raise TypeError("Object %s doesn't support the parameter '%s'" %
(self.__class__.__name__, key))
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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
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):
"""Job definition structure"""
STATUS_PENDING = 1
STATUS_RUNNING = 2
STATUS_FINISHED = 3
RESULT_OK = 1
RESULT_FAIL = 2
RESULT_ABORT = 3
__slots__ = ["job_id", "op_list", "status", "result"]
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:
self.op_list = [OpCode.LoadOpcode(op) for op in state["op_list"]]
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
def LoadOpcode(cls, data):
"""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
class OpInitCluster(OpCode):
"""Initialise the cluster."""
OP_ID = "OP_CLUSTER_INIT"
__slots__ = ["cluster_name", "secondary_ip", "hypervisor_type",
"vg_name", "mac_prefix", "def_bridge", "master_netdev",
"file_storage_dir"]
class OpDestroyCluster(OpCode):
"""Destroy the cluster."""
OP_ID = "OP_CLUSTER_DESTROY"
__slots__ = []
class OpQueryClusterInfo(OpCode):
OP_ID = "OP_CLUSTER_QUERY"
__slots__ = []
class OpClusterCopyFile(OpCode):
OP_ID = "OP_CLUSTER_COPYFILE"
__slots__ = ["nodes", "filename"]
class OpRunClusterCommand(OpCode):
OP_ID = "OP_CLUSTER_RUNCOMMAND"
__slots__ = ["nodes", "command"]
class OpVerifyCluster(OpCode):
class OpVerifyDisks(OpCode):
"""Verify the cluster disks.
Parameters: none
Result: two lists:
- list of node names with bad data returned (unreachable, etc.)
- dist of node names with broken volume groups (values: error msg)
- list of instances with degraded disks (that should be activated)
- dict of instances with missing logical volumes (values: (node, vol)
pairs with details about the missing volumes)
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.
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__ = []
OP_ID = "OP_CLUSTER_MASTERFAILOVER"
__slots__ = []
class OpDumpClusterConfig(OpCode):
class OpRenameCluster(OpCode):
"""Rename the cluster."""
OP_ID = "OP_CLUSTER_RENAME"
__slots__ = ["name"]
# node opcodes
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"
__slots__ = ["node_name", "primary_ip", "secondary_ip"]
class OpQueryNodes(OpCode):
"""Compute the list of nodes."""
OP_ID = "OP_NODE_QUERY"
__slots__ = ["output_fields", "names"]
class OpQueryNodeVolumes(OpCode):
"""Get list of volumes on node."""
OP_ID = "OP_NODE_QUERYVOLS"
__slots__ = ["nodes", "output_fields"]
__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",
"kernel_path", "initrd_path", "hvm_boot_order",
__slots__ = ["instance_name", "os_type"]
class OpRemoveInstance(OpCode):
"""Remove an instance."""
OP_ID = "OP_INSTANCE_REMOVE"
__slots__ = ["instance_name", "ignore_failures"]
class OpRenameInstance(OpCode):
"""Rename an instance."""
OP_ID = "OP_INSTANCE_RENAME"
__slots__ = ["instance_name", "ignore_ip", "new_name"]
OP_ID = "OP_INSTANCE_STARTUP"
__slots__ = ["instance_name", "force", "extra_args"]
class OpShutdownInstance(OpCode):
OP_ID = "OP_INSTANCE_SHUTDOWN"
__slots__ = ["instance_name"]
class OpRebootInstance(OpCode):
"""Reboot an instance."""
__slots__ = ["instance_name", "reboot_type", "extra_args",
"ignore_secondaries" ]
__slots__ = ["instance_name", "remote_node", "mode", "disks"]
class OpFailoverInstance(OpCode):
"""Failover an instance."""
OP_ID = "OP_INSTANCE_FAILOVER"
__slots__ = ["instance_name", "ignore_consistency"]
class OpConnectConsole(OpCode):
OP_ID = "OP_INSTANCE_CONSOLE"
__slots__ = ["instance_name"]
class OpActivateInstanceDisks(OpCode):
OP_ID = "OP_INSTANCE_ACTIVATE_DISKS"
__slots__ = ["instance_name"]
class OpDeactivateInstanceDisks(OpCode):
OP_ID = "OP_INSTANCE_DEACTIVATE_DISKS"
__slots__ = ["instance_name"]
class OpQueryInstances(OpCode):
"""Compute the list of instances."""
OP_ID = "OP_INSTANCE_QUERY"
__slots__ = ["output_fields", "names"]
class OpQueryInstanceData(OpCode):
"""Compute the run-time status of instances."""
OP_ID = "OP_INSTANCE_QUERY_DATA"
__slots__ = ["instances"]
class OpSetInstanceParms(OpCode):
"""Change the parameters of an instance."""
OP_ID = "OP_INSTANCE_SET_PARMS"
__slots__ = [
"instance_name", "mem", "vcpus", "ip", "bridge", "mac",
"kernel_path", "initrd_path", "hvm_boot_order",
# OS opcodes
class OpDiagnoseOS(OpCode):
"""Compute the list of guest operating systems."""
OP_ID = "OP_OS_DIAGNOSE"
__slots__ = []
# Exports opcodes
class OpQueryExports(OpCode):
"""Compute the list of exported images."""
OP_ID = "OP_BACKUP_QUERY"
__slots__ = ["nodes"]
class OpExportInstance(OpCode):
"""Export an instance."""
OP_ID = "OP_BACKUP_EXPORT"
__slots__ = ["instance_name", "target_node", "shutdown"]
# Tags opcodes
class OpGetTags(OpCode):
"""Returns the tags of the given object."""
OP_ID = "OP_TAGS_GET"
__slots__ = ["kind", "name"]
class OpSearchTags(OpCode):
"""Searches the tags in the cluster for a given pattern."""
OP_ID = "OP_TAGS_SEARCH"
__slots__ = ["pattern"]
class OpAddTags(OpCode):
"""Add a list of tags on a given object."""
OP_ID = "OP_TAGS_SET"
__slots__ = ["kind", "name", "tags"]
class OpDelTags(OpCode):
"""Remove a list of tags from a given object."""
OP_ID = "OP_TAGS_DEL"
__slots__ = ["kind", "name", "tags"]
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
# 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"]