Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes.py @ df458e0b

History | View | Annotate | Download (11.1 kB)

1
#
2
#
3

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

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

33
"""
34

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

    
39

    
40
class BaseJO(object):
41
  """A simple serializable object.
42

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

46
  """
47
  __slots__ = []
48

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

    
56
  def __getstate__(self):
57
    state = {}
58
    for name in self.__slots__:
59
      if hasattr(self, name):
60
        state[name] = getattr(self, name)
61
    return state
62

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

    
68
    for name in self.__slots__:
69
      if name not in state:
70
        delattr(self, name)
71

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

    
75

    
76
class Job(BaseJO):
77
  """Job definition structure"""
78
  STATUS_PENDING = 1
79
  STATUS_RUNNING = 2
80
  STATUS_FINISHED = 3
81
  RESULT_OK = 1
82
  RESULT_FAIL = 2
83
  RESULT_ABORT = 3
84

    
85
  __slots__ = ["job_id", "op_list", "status", "result"]
86

    
87
  def __getstate__(self):
88
    """Specialized getstate for jobs
89

90
    """
91
    data = BaseJO.__getstate__(self)
92
    if "op_list" in data:
93
      data["op_list"] = [op.__getstate__() for op in data["op_list"]]
94
    return data
95

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

99
    """
100
    BaseJO.__setstate__(self, state)
101
    if "op_list" in state:
102
      self.op_list = [OpCode.LoadOpcode(op) for op in state["op_list"]]
103

    
104

    
105
class OpCode(BaseJO):
106
  """Abstract OpCode"""
107
  OP_ID = "OP_ABSTRACT"
108
  __slots__ = []
109

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

113
    """
114
    data = BaseJO.__getstate__(self)
115
    data["OP_ID"] = self.OP_ID
116
    return data
117

    
118
  @classmethod
119
  def LoadOpcode(cls, data):
120
    """Generic load opcode method.
121

122
    """
123
    if not isinstance(data, dict):
124
      raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
125
    if "OP_ID" not in data:
126
      raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
127
    op_id = data["OP_ID"]
128
    op_class = None
129
    for item in globals().values():
130
      if (isinstance(item, type) and
131
          issubclass(item, cls) and
132
          hasattr(item, "OP_ID") and
133
          getattr(item, "OP_ID") == op_id):
134
        op_class = item
135
        break
136
    if op_class is None:
137
      raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
138
                       op_id)
139
    op = op_class()
140
    new_data = data.copy()
141
    del new_data["OP_ID"]
142
    op.__setstate__(new_data)
143
    return op
144

    
145

    
146
class OpInitCluster(OpCode):
147
  """Initialise the cluster."""
148
  OP_ID = "OP_CLUSTER_INIT"
149
  __slots__ = ["cluster_name", "secondary_ip", "hypervisor_type",
150
               "vg_name", "mac_prefix", "def_bridge", "master_netdev",
151
               "file_storage_dir"]
152

    
153

    
154
class OpDestroyCluster(OpCode):
155
  """Destroy the cluster."""
156
  OP_ID = "OP_CLUSTER_DESTROY"
157
  __slots__ = []
158

    
159

    
160
class OpQueryClusterInfo(OpCode):
161
  """Query cluster information."""
162
  OP_ID = "OP_CLUSTER_QUERY"
163
  __slots__ = []
164

    
165

    
166
class OpClusterCopyFile(OpCode):
167
  """Copy a file to multiple nodes."""
168
  OP_ID = "OP_CLUSTER_COPYFILE"
169
  __slots__ = ["nodes", "filename"]
170

    
171

    
172
class OpRunClusterCommand(OpCode):
173
  """Run a command on multiple nodes."""
174
  OP_ID = "OP_CLUSTER_RUNCOMMAND"
175
  __slots__ = ["nodes", "command"]
176

    
177

    
178
class OpVerifyCluster(OpCode):
179
  """Verify the cluster state."""
180
  OP_ID = "OP_CLUSTER_VERIFY"
181
  __slots__ = []
182

    
183

    
184
class OpVerifyDisks(OpCode):
185
  """Verify the cluster disks.
186

187
  Parameters: none
188

189
  Result: two lists:
190
    - list of node names with bad data returned (unreachable, etc.)
191
    - dist of node names with broken volume groups (values: error msg)
192
    - list of instances with degraded disks (that should be activated)
193
    - dict of instances with missing logical volumes (values: (node, vol)
194
      pairs with details about the missing volumes)
195

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

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

204
  """
205
  OP_ID = "OP_CLUSTER_VERIFY_DISKS"
206
  __slots__ = []
207

    
208

    
209
class OpMasterFailover(OpCode):
210
  """Do a master failover."""
211
  OP_ID = "OP_CLUSTER_MASTERFAILOVER"
212
  __slots__ = []
213

    
214

    
215
class OpDumpClusterConfig(OpCode):
216
  """Dump the cluster configuration."""
217
  OP_ID = "OP_CLUSTER_DUMPCONFIG"
218
  __slots__ = []
219

    
220

    
221
class OpRenameCluster(OpCode):
222
  """Rename the cluster."""
223
  OP_ID = "OP_CLUSTER_RENAME"
224
  __slots__ = ["name"]
225

    
226

    
227
# node opcodes
228

    
229
class OpRemoveNode(OpCode):
230
  """Remove a node."""
231
  OP_ID = "OP_NODE_REMOVE"
232
  __slots__ = ["node_name"]
233

    
234

    
235
class OpAddNode(OpCode):
236
  """Add a node."""
237
  OP_ID = "OP_NODE_ADD"
238
  __slots__ = ["node_name", "primary_ip", "secondary_ip"]
239

    
240

    
241
class OpQueryNodes(OpCode):
242
  """Compute the list of nodes."""
243
  OP_ID = "OP_NODE_QUERY"
244
  __slots__ = ["output_fields", "names"]
245

    
246

    
247
class OpQueryNodeVolumes(OpCode):
248
  """Get list of volumes on node."""
249
  OP_ID = "OP_NODE_QUERYVOLS"
250
  __slots__ = ["nodes", "output_fields"]
251

    
252

    
253
# instance opcodes
254

    
255
class OpCreateInstance(OpCode):
256
  """Create an instance."""
257
  OP_ID = "OP_INSTANCE_CREATE"
258
  __slots__ = [
259
    "instance_name", "mem_size", "disk_size", "os_type", "pnode",
260
    "disk_template", "snode", "swap_size", "mode",
261
    "vcpus", "ip", "bridge", "src_node", "src_path", "start",
262
    "wait_for_sync", "ip_check", "mac",
263
    "kernel_path", "initrd_path", "hvm_boot_order",
264
    ]
265

    
266

    
267
class OpReinstallInstance(OpCode):
268
  """Reinstall an instance's OS."""
269
  OP_ID = "OP_INSTANCE_REINSTALL"
270
  __slots__ = ["instance_name", "os_type"]
271

    
272

    
273
class OpRemoveInstance(OpCode):
274
  """Remove an instance."""
275
  OP_ID = "OP_INSTANCE_REMOVE"
276
  __slots__ = ["instance_name", "ignore_failures"]
277

    
278

    
279
class OpRenameInstance(OpCode):
280
  """Rename an instance."""
281
  OP_ID = "OP_INSTANCE_RENAME"
282
  __slots__ = ["instance_name", "ignore_ip", "new_name"]
283

    
284

    
285
class OpStartupInstance(OpCode):
286
  """Startup an instance."""
287
  OP_ID = "OP_INSTANCE_STARTUP"
288
  __slots__ = ["instance_name", "force", "extra_args"]
289

    
290

    
291
class OpShutdownInstance(OpCode):
292
  """Shutdown an instance."""
293
  OP_ID = "OP_INSTANCE_SHUTDOWN"
294
  __slots__ = ["instance_name"]
295

    
296

    
297
class OpRebootInstance(OpCode):
298
  """Reboot an instance."""
299
  OP_ID = "OP_INSTANCE_REBOOT"
300
  __slots__ = ["instance_name", "reboot_type", "extra_args",
301
               "ignore_secondaries" ]
302

    
303

    
304
class OpAddMDDRBDComponent(OpCode):
305
  """Add a MD-DRBD component."""
306
  OP_ID = "OP_INSTANCE_ADD_MDDRBD"
307
  __slots__ = ["instance_name", "remote_node", "disk_name"]
308

    
309

    
310
class OpRemoveMDDRBDComponent(OpCode):
311
  """Remove a MD-DRBD component."""
312
  OP_ID = "OP_INSTANCE_REMOVE_MDDRBD"
313
  __slots__ = ["instance_name", "disk_name", "disk_id"]
314

    
315

    
316
class OpReplaceDisks(OpCode):
317
  """Replace the disks of an instance."""
318
  OP_ID = "OP_INSTANCE_REPLACE_DISKS"
319
  __slots__ = ["instance_name", "remote_node", "mode", "disks"]
320

    
321

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

    
327

    
328
class OpConnectConsole(OpCode):
329
  """Connect to an instance's console."""
330
  OP_ID = "OP_INSTANCE_CONSOLE"
331
  __slots__ = ["instance_name"]
332

    
333

    
334
class OpActivateInstanceDisks(OpCode):
335
  """Activate an instance's disks."""
336
  OP_ID = "OP_INSTANCE_ACTIVATE_DISKS"
337
  __slots__ = ["instance_name"]
338

    
339

    
340
class OpDeactivateInstanceDisks(OpCode):
341
  """Deactivate an instance's disks."""
342
  OP_ID = "OP_INSTANCE_DEACTIVATE_DISKS"
343
  __slots__ = ["instance_name"]
344

    
345

    
346
class OpQueryInstances(OpCode):
347
  """Compute the list of instances."""
348
  OP_ID = "OP_INSTANCE_QUERY"
349
  __slots__ = ["output_fields", "names"]
350

    
351

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

    
357

    
358
class OpSetInstanceParms(OpCode):
359
  """Change the parameters of an instance."""
360
  OP_ID = "OP_INSTANCE_SET_PARMS"
361
  __slots__ = [
362
    "instance_name", "mem", "vcpus", "ip", "bridge", "mac",
363
    "kernel_path", "initrd_path", "hvm_boot_order",
364
    ]
365

    
366

    
367
# OS opcodes
368
class OpDiagnoseOS(OpCode):
369
  """Compute the list of guest operating systems."""
370
  OP_ID = "OP_OS_DIAGNOSE"
371
  __slots__ = []
372

    
373

    
374
# Exports opcodes
375
class OpQueryExports(OpCode):
376
  """Compute the list of exported images."""
377
  OP_ID = "OP_BACKUP_QUERY"
378
  __slots__ = ["nodes"]
379

    
380

    
381
class OpExportInstance(OpCode):
382
  """Export an instance."""
383
  OP_ID = "OP_BACKUP_EXPORT"
384
  __slots__ = ["instance_name", "target_node", "shutdown"]
385

    
386

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

    
393

    
394
class OpSearchTags(OpCode):
395
  """Searches the tags in the cluster for a given pattern."""
396
  OP_ID = "OP_TAGS_SEARCH"
397
  __slots__ = ["pattern"]
398

    
399

    
400
class OpAddTags(OpCode):
401
  """Add a list of tags on a given object."""
402
  OP_ID = "OP_TAGS_SET"
403
  __slots__ = ["kind", "name", "tags"]
404

    
405

    
406
class OpDelTags(OpCode):
407
  """Remove a list of tags from a given object."""
408
  OP_ID = "OP_TAGS_DEL"
409
  __slots__ = ["kind", "name", "tags"]
410

    
411

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

416
  This is used just for debugging and testing.
417

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

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

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

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

433
  """
434
  OP_ID = "OP_TEST_DELAY"
435
  __slots__ = ["duration", "on_master", "on_nodes"]