Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes.py @ d61df03e

History | View | Annotate | Download (11.9 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"]
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
    ]
288

    
289

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

    
295

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

    
301

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

    
307

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

    
313

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

    
319

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

    
326

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

    
332

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

    
338

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

    
344

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

    
350

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

    
356

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

    
362

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

    
368

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

    
377

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

    
384

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

    
391

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

    
397

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

    
404

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

    
410

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

    
416

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

    
422

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

427
  This is used just for debugging and testing.
428

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

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

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

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

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

    
448

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

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

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