Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes.py @ a0c9f010

History | View | Annotate | Download (12 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 OpDestroyCluster(OpCode):
163
  """Destroy the cluster."""
164
  OP_ID = "OP_CLUSTER_DESTROY"
165
  __slots__ = []
166

    
167

    
168
class OpQueryClusterInfo(OpCode):
169
  """Query cluster information."""
170
  OP_ID = "OP_CLUSTER_QUERY"
171
  __slots__ = []
172

    
173

    
174
class OpClusterCopyFile(OpCode):
175
  """Copy a file to multiple nodes."""
176
  OP_ID = "OP_CLUSTER_COPYFILE"
177
  __slots__ = ["nodes", "filename"]
178

    
179

    
180
class OpRunClusterCommand(OpCode):
181
  """Run a command on multiple nodes."""
182
  OP_ID = "OP_CLUSTER_RUNCOMMAND"
183
  __slots__ = ["nodes", "command"]
184

    
185

    
186
class OpVerifyCluster(OpCode):
187
  """Verify the cluster state."""
188
  OP_ID = "OP_CLUSTER_VERIFY"
189
  __slots__ = ["skip_checks"]
190

    
191

    
192
class OpVerifyDisks(OpCode):
193
  """Verify the cluster disks.
194

195
  Parameters: none
196

197
  Result: two lists:
198
    - list of node names with bad data returned (unreachable, etc.)
199
    - dist of node names with broken volume groups (values: error msg)
200
    - list of instances with degraded disks (that should be activated)
201
    - dict of instances with missing logical volumes (values: (node, vol)
202
      pairs with details about the missing volumes)
203

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

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

212
  """
213
  OP_ID = "OP_CLUSTER_VERIFY_DISKS"
214
  __slots__ = []
215

    
216

    
217
class OpMasterFailover(OpCode):
218
  """Do a master failover."""
219
  OP_ID = "OP_CLUSTER_MASTERFAILOVER"
220
  __slots__ = []
221

    
222

    
223
class OpDumpClusterConfig(OpCode):
224
  """Dump the cluster configuration."""
225
  OP_ID = "OP_CLUSTER_DUMPCONFIG"
226
  __slots__ = []
227

    
228

    
229
class OpRenameCluster(OpCode):
230
  """Rename the cluster."""
231
  OP_ID = "OP_CLUSTER_RENAME"
232
  __slots__ = ["name"]
233

    
234

    
235
class OpSetClusterParams(OpCode):
236
  """Change the parameters of the cluster."""
237
  OP_ID = "OP_CLUSTER_SET_PARAMS"
238
  __slots__ = ["vg_name"]
239

    
240

    
241
# node opcodes
242

    
243
class OpRemoveNode(OpCode):
244
  """Remove a node."""
245
  OP_ID = "OP_NODE_REMOVE"
246
  __slots__ = ["node_name"]
247

    
248

    
249
class OpAddNode(OpCode):
250
  """Add a node."""
251
  OP_ID = "OP_NODE_ADD"
252
  __slots__ = ["node_name", "primary_ip", "secondary_ip", "readd"]
253

    
254

    
255
class OpQueryNodes(OpCode):
256
  """Compute the list of nodes."""
257
  OP_ID = "OP_NODE_QUERY"
258
  __slots__ = ["output_fields", "names"]
259

    
260

    
261
class OpQueryNodeVolumes(OpCode):
262
  """Get list of volumes on node."""
263
  OP_ID = "OP_NODE_QUERYVOLS"
264
  __slots__ = ["nodes", "output_fields"]
265

    
266

    
267
# instance opcodes
268

    
269
class OpCreateInstance(OpCode):
270
  """Create an instance."""
271
  OP_ID = "OP_INSTANCE_CREATE"
272
  __slots__ = [
273
    "instance_name", "mem_size", "disk_size", "os_type", "pnode",
274
    "disk_template", "snode", "swap_size", "mode",
275
    "vcpus", "ip", "bridge", "src_node", "src_path", "start",
276
    "wait_for_sync", "ip_check", "mac",
277
    "kernel_path", "initrd_path", "hvm_boot_order", "hvm_acpi",
278
    "hvm_pae", "hvm_cdrom_image_path", "vnc_bind_address",
279
    "file_storage_dir", "file_driver",
280
    "iallocator",
281
    ]
282

    
283

    
284
class OpReinstallInstance(OpCode):
285
  """Reinstall an instance's OS."""
286
  OP_ID = "OP_INSTANCE_REINSTALL"
287
  __slots__ = ["instance_name", "os_type"]
288

    
289

    
290
class OpRemoveInstance(OpCode):
291
  """Remove an instance."""
292
  OP_ID = "OP_INSTANCE_REMOVE"
293
  __slots__ = ["instance_name", "ignore_failures"]
294

    
295

    
296
class OpRenameInstance(OpCode):
297
  """Rename an instance."""
298
  OP_ID = "OP_INSTANCE_RENAME"
299
  __slots__ = ["instance_name", "ignore_ip", "new_name"]
300

    
301

    
302
class OpStartupInstance(OpCode):
303
  """Startup an instance."""
304
  OP_ID = "OP_INSTANCE_STARTUP"
305
  __slots__ = ["instance_name", "force", "extra_args"]
306

    
307

    
308
class OpShutdownInstance(OpCode):
309
  """Shutdown an instance."""
310
  OP_ID = "OP_INSTANCE_SHUTDOWN"
311
  __slots__ = ["instance_name"]
312

    
313

    
314
class OpRebootInstance(OpCode):
315
  """Reboot an instance."""
316
  OP_ID = "OP_INSTANCE_REBOOT"
317
  __slots__ = ["instance_name", "reboot_type", "extra_args",
318
               "ignore_secondaries" ]
319

    
320

    
321
class OpReplaceDisks(OpCode):
322
  """Replace the disks of an instance."""
323
  OP_ID = "OP_INSTANCE_REPLACE_DISKS"
324
  __slots__ = ["instance_name", "remote_node", "mode", "disks", "iallocator"]
325

    
326

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

    
332

    
333
class OpConnectConsole(OpCode):
334
  """Connect to an instance's console."""
335
  OP_ID = "OP_INSTANCE_CONSOLE"
336
  __slots__ = ["instance_name"]
337

    
338

    
339
class OpActivateInstanceDisks(OpCode):
340
  """Activate an instance's disks."""
341
  OP_ID = "OP_INSTANCE_ACTIVATE_DISKS"
342
  __slots__ = ["instance_name"]
343

    
344

    
345
class OpDeactivateInstanceDisks(OpCode):
346
  """Deactivate an instance's disks."""
347
  OP_ID = "OP_INSTANCE_DEACTIVATE_DISKS"
348
  __slots__ = ["instance_name"]
349

    
350

    
351
class OpQueryInstances(OpCode):
352
  """Compute the list of instances."""
353
  OP_ID = "OP_INSTANCE_QUERY"
354
  __slots__ = ["output_fields", "names"]
355

    
356

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

    
362

    
363
class OpSetInstanceParams(OpCode):
364
  """Change the parameters of an instance."""
365
  OP_ID = "OP_INSTANCE_SET_PARAMS"
366
  __slots__ = [
367
    "instance_name", "mem", "vcpus", "ip", "bridge", "mac",
368
    "kernel_path", "initrd_path", "hvm_boot_order", "hvm_acpi",
369
    "hvm_pae", "hvm_cdrom_image_path", "vnc_bind_address"
370
    ]
371

    
372

    
373
# OS opcodes
374
class OpDiagnoseOS(OpCode):
375
  """Compute the list of guest operating systems."""
376
  OP_ID = "OP_OS_DIAGNOSE"
377
  __slots__ = ["output_fields", "names"]
378

    
379

    
380
# Exports opcodes
381
class OpQueryExports(OpCode):
382
  """Compute the list of exported images."""
383
  OP_ID = "OP_BACKUP_QUERY"
384
  __slots__ = ["nodes"]
385

    
386

    
387
class OpExportInstance(OpCode):
388
  """Export an instance."""
389
  OP_ID = "OP_BACKUP_EXPORT"
390
  __slots__ = ["instance_name", "target_node", "shutdown"]
391

    
392
class OpRemoveExport(OpCode):
393
  """Remove an instance's export."""
394
  OP_ID = "OP_BACKUP_REMOVE"
395
  __slots__ = ["instance_name"]
396

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

    
403

    
404
class OpSearchTags(OpCode):
405
  """Searches the tags in the cluster for a given pattern."""
406
  OP_ID = "OP_TAGS_SEARCH"
407
  __slots__ = ["pattern"]
408

    
409

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

    
415

    
416
class OpDelTags(OpCode):
417
  """Remove a list of tags from a given object."""
418
  OP_ID = "OP_TAGS_DEL"
419
  __slots__ = ["kind", "name", "tags"]
420

    
421

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

426
  This is used just for debugging and testing.
427

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

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

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

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

443
  """
444
  OP_ID = "OP_TEST_DELAY"
445
  __slots__ = ["duration", "on_master", "on_nodes"]
446

    
447

    
448
class OpTestAllocator(OpCode):
449
  """Allocator framework testing.
450

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

458
  """
459
  OP_ID = "OP_TEST_ALLOCATOR"
460
  __slots__ = [
461
    "direction", "mode", "allocator", "name",
462
    "mem_size", "disks", "disk_template",
463
    "os", "tags", "nics", "vcpus",
464
    ]