Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes.py @ e3e66f02

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

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",
286
    "file_storage_dir", "file_driver",
287
    "iallocator",
288
    ]
289

    
290

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

    
296

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

    
302

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

    
308

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

    
314

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

    
320

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

    
327

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

    
333

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

    
339

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

    
345

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

    
351

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

    
357

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

    
363

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

    
369

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

    
378

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

    
385

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

    
392

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

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

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

    
409

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

    
415

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

    
421

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

    
427

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

432
  This is used just for debugging and testing.
433

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

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

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

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

449
  """
450
  OP_ID = "OP_TEST_DELAY"
451
  __slots__ = ["duration", "on_master", "on_nodes"]
452

    
453

    
454
class OpTestAllocator(OpCode):
455
  """Allocator framework testing.
456

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

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