Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes.py @ a21dda8b

History | View | Annotate | Download (12.2 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

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

88
  """
89
  STATUS_PENDING = 1
90
  STATUS_RUNNING = 2
91
  STATUS_SUCCESS = 3
92
  STATUS_FAIL = 4
93
  STATUS_ABORT = 5
94

    
95
  __slots__ = [
96
    "job_id",
97
    "status",
98
    "op_list",
99
    "op_status",
100
    "op_result",
101
    ]
102

    
103
  def __getstate__(self):
104
    """Specialized getstate for jobs
105

106
    """
107
    data = BaseJO.__getstate__(self)
108
    if "op_list" in data:
109
      data["op_list"] = [op.__getstate__() for op in data["op_list"]]
110
    return data
111

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

115
    """
116
    BaseJO.__setstate__(self, state)
117
    if "op_list" in state:
118
      self.op_list = [OpCode.LoadOpCode(op) for op in state["op_list"]]
119

    
120

    
121
class OpCode(BaseJO):
122
  """Abstract OpCode"""
123
  OP_ID = "OP_ABSTRACT"
124
  __slots__ = []
125

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

129
    """
130
    data = BaseJO.__getstate__(self)
131
    data["OP_ID"] = self.OP_ID
132
    return data
133

    
134
  @classmethod
135
  def LoadOpCode(cls, data):
136
    """Generic load opcode method.
137

138
    """
139
    if not isinstance(data, dict):
140
      raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
141
    if "OP_ID" not in data:
142
      raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
143
    op_id = data["OP_ID"]
144
    op_class = None
145
    for item in globals().values():
146
      if (isinstance(item, type) and
147
          issubclass(item, cls) and
148
          hasattr(item, "OP_ID") and
149
          getattr(item, "OP_ID") == op_id):
150
        op_class = item
151
        break
152
    if op_class is None:
153
      raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
154
                       op_id)
155
    op = op_class()
156
    new_data = data.copy()
157
    del new_data["OP_ID"]
158
    op.__setstate__(new_data)
159
    return op
160

    
161

    
162
class OpInitCluster(OpCode):
163
  """Initialise the cluster."""
164
  OP_ID = "OP_CLUSTER_INIT"
165
  __slots__ = ["cluster_name", "secondary_ip", "hypervisor_type",
166
               "vg_name", "mac_prefix", "def_bridge", "master_netdev",
167
               "file_storage_dir"]
168

    
169

    
170
class OpDestroyCluster(OpCode):
171
  """Destroy the cluster."""
172
  OP_ID = "OP_CLUSTER_DESTROY"
173
  __slots__ = []
174

    
175

    
176
class OpQueryClusterInfo(OpCode):
177
  """Query cluster information."""
178
  OP_ID = "OP_CLUSTER_QUERY"
179
  __slots__ = []
180

    
181

    
182
class OpClusterCopyFile(OpCode):
183
  """Copy a file to multiple nodes."""
184
  OP_ID = "OP_CLUSTER_COPYFILE"
185
  __slots__ = ["nodes", "filename"]
186

    
187

    
188
class OpRunClusterCommand(OpCode):
189
  """Run a command on multiple nodes."""
190
  OP_ID = "OP_CLUSTER_RUNCOMMAND"
191
  __slots__ = ["nodes", "command"]
192

    
193

    
194
class OpVerifyCluster(OpCode):
195
  """Verify the cluster state."""
196
  OP_ID = "OP_CLUSTER_VERIFY"
197
  __slots__ = ["skip_checks"]
198

    
199

    
200
class OpVerifyDisks(OpCode):
201
  """Verify the cluster disks.
202

203
  Parameters: none
204

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

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

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

220
  """
221
  OP_ID = "OP_CLUSTER_VERIFY_DISKS"
222
  __slots__ = []
223

    
224

    
225
class OpMasterFailover(OpCode):
226
  """Do a master failover."""
227
  OP_ID = "OP_CLUSTER_MASTERFAILOVER"
228
  __slots__ = []
229

    
230

    
231
class OpDumpClusterConfig(OpCode):
232
  """Dump the cluster configuration."""
233
  OP_ID = "OP_CLUSTER_DUMPCONFIG"
234
  __slots__ = []
235

    
236

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

    
242

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

    
248

    
249
# node opcodes
250

    
251
class OpRemoveNode(OpCode):
252
  """Remove a node."""
253
  OP_ID = "OP_NODE_REMOVE"
254
  __slots__ = ["node_name"]
255

    
256

    
257
class OpAddNode(OpCode):
258
  """Add a node."""
259
  OP_ID = "OP_NODE_ADD"
260
  __slots__ = ["node_name", "primary_ip", "secondary_ip", "readd"]
261

    
262

    
263
class OpQueryNodes(OpCode):
264
  """Compute the list of nodes."""
265
  OP_ID = "OP_NODE_QUERY"
266
  __slots__ = ["output_fields", "names"]
267

    
268

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

    
274

    
275
# instance opcodes
276

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

    
291

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

    
297

    
298
class OpRemoveInstance(OpCode):
299
  """Remove an instance."""
300
  OP_ID = "OP_INSTANCE_REMOVE"
301
  __slots__ = ["instance_name", "ignore_failures"]
302

    
303

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

    
309

    
310
class OpStartupInstance(OpCode):
311
  """Startup an instance."""
312
  OP_ID = "OP_INSTANCE_STARTUP"
313
  __slots__ = ["instance_name", "force", "extra_args"]
314

    
315

    
316
class OpShutdownInstance(OpCode):
317
  """Shutdown an instance."""
318
  OP_ID = "OP_INSTANCE_SHUTDOWN"
319
  __slots__ = ["instance_name"]
320

    
321

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

    
328

    
329
class OpReplaceDisks(OpCode):
330
  """Replace the disks of an instance."""
331
  OP_ID = "OP_INSTANCE_REPLACE_DISKS"
332
  __slots__ = ["instance_name", "remote_node", "mode", "disks", "iallocator"]
333

    
334

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

    
340

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

    
346

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

    
352

    
353
class OpDeactivateInstanceDisks(OpCode):
354
  """Deactivate an instance's disks."""
355
  OP_ID = "OP_INSTANCE_DEACTIVATE_DISKS"
356
  __slots__ = ["instance_name"]
357

    
358

    
359
class OpQueryInstances(OpCode):
360
  """Compute the list of instances."""
361
  OP_ID = "OP_INSTANCE_QUERY"
362
  __slots__ = ["output_fields", "names"]
363

    
364

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

    
370

    
371
class OpSetInstanceParams(OpCode):
372
  """Change the parameters of an instance."""
373
  OP_ID = "OP_INSTANCE_SET_PARAMS"
374
  __slots__ = [
375
    "instance_name", "mem", "vcpus", "ip", "bridge", "mac",
376
    "kernel_path", "initrd_path", "hvm_boot_order", "hvm_acpi",
377
    "hvm_pae", "hvm_cdrom_image_path", "vnc_bind_address"
378
    ]
379

    
380

    
381
# OS opcodes
382
class OpDiagnoseOS(OpCode):
383
  """Compute the list of guest operating systems."""
384
  OP_ID = "OP_OS_DIAGNOSE"
385
  __slots__ = ["output_fields", "names"]
386

    
387

    
388
# Exports opcodes
389
class OpQueryExports(OpCode):
390
  """Compute the list of exported images."""
391
  OP_ID = "OP_BACKUP_QUERY"
392
  __slots__ = ["nodes"]
393

    
394

    
395
class OpExportInstance(OpCode):
396
  """Export an instance."""
397
  OP_ID = "OP_BACKUP_EXPORT"
398
  __slots__ = ["instance_name", "target_node", "shutdown"]
399

    
400
class OpRemoveExport(OpCode):
401
  """Remove an instance's export."""
402
  OP_ID = "OP_BACKUP_REMOVE"
403
  __slots__ = ["instance_name"]
404

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

    
411

    
412
class OpSearchTags(OpCode):
413
  """Searches the tags in the cluster for a given pattern."""
414
  OP_ID = "OP_TAGS_SEARCH"
415
  __slots__ = ["pattern"]
416

    
417

    
418
class OpAddTags(OpCode):
419
  """Add a list of tags on a given object."""
420
  OP_ID = "OP_TAGS_SET"
421
  __slots__ = ["kind", "name", "tags"]
422

    
423

    
424
class OpDelTags(OpCode):
425
  """Remove a list of tags from a given object."""
426
  OP_ID = "OP_TAGS_DEL"
427
  __slots__ = ["kind", "name", "tags"]
428

    
429

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

434
  This is used just for debugging and testing.
435

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

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

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

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

451
  """
452
  OP_ID = "OP_TEST_DELAY"
453
  __slots__ = ["duration", "on_master", "on_nodes"]
454

    
455

    
456
class OpTestAllocator(OpCode):
457
  """Allocator framework testing.
458

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

466
  """
467
  OP_ID = "OP_TEST_ALLOCATOR"
468
  __slots__ = [
469
    "direction", "mode", "allocator", "name",
470
    "mem_size", "disks", "disk_template",
471
    "os", "tags", "nics", "vcpus",
472
    ]