symlinks: Add DISK_LINKS_DIR constant
[ganeti-local] / lib / opcodes.py
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 OpVerifyCluster(OpCode):
175   """Verify the cluster state."""
176   OP_ID = "OP_CLUSTER_VERIFY"
177   __slots__ = ["skip_checks"]
178
179
180 class OpVerifyDisks(OpCode):
181   """Verify the cluster disks.
182
183   Parameters: none
184
185   Result: two lists:
186     - list of node names with bad data returned (unreachable, etc.)
187     - dist of node names with broken volume groups (values: error msg)
188     - list of instances with degraded disks (that should be activated)
189     - dict of instances with missing logical volumes (values: (node, vol)
190       pairs with details about the missing volumes)
191
192   In normal operation, all lists should be empty. A non-empty instance
193   list (3rd element of the result) is still ok (errors were fixed) but
194   non-empty node list means some node is down, and probably there are
195   unfixable drbd errors.
196
197   Note that only instances that are drbd-based are taken into
198   consideration. This might need to be revisited in the future.
199
200   """
201   OP_ID = "OP_CLUSTER_VERIFY_DISKS"
202   __slots__ = []
203
204
205 class OpMasterFailover(OpCode):
206   """Do a master failover."""
207   OP_ID = "OP_CLUSTER_MASTERFAILOVER"
208   __slots__ = []
209
210
211 class OpDumpClusterConfig(OpCode):
212   """Dump the cluster configuration."""
213   OP_ID = "OP_CLUSTER_DUMPCONFIG"
214   __slots__ = []
215
216
217 class OpRenameCluster(OpCode):
218   """Rename the cluster."""
219   OP_ID = "OP_CLUSTER_RENAME"
220   __slots__ = ["name"]
221
222
223 class OpSetClusterParams(OpCode):
224   """Change the parameters of the cluster."""
225   OP_ID = "OP_CLUSTER_SET_PARAMS"
226   __slots__ = ["vg_name"]
227
228
229 # node opcodes
230
231 class OpRemoveNode(OpCode):
232   """Remove a node."""
233   OP_ID = "OP_NODE_REMOVE"
234   __slots__ = ["node_name"]
235
236
237 class OpAddNode(OpCode):
238   """Add a node."""
239   OP_ID = "OP_NODE_ADD"
240   __slots__ = ["node_name", "primary_ip", "secondary_ip", "readd"]
241
242
243 class OpQueryNodes(OpCode):
244   """Compute the list of nodes."""
245   OP_ID = "OP_NODE_QUERY"
246   __slots__ = ["output_fields", "names"]
247
248
249 class OpQueryNodeVolumes(OpCode):
250   """Get list of volumes on node."""
251   OP_ID = "OP_NODE_QUERYVOLS"
252   __slots__ = ["nodes", "output_fields"]
253
254
255 # instance opcodes
256
257 class OpCreateInstance(OpCode):
258   """Create an instance."""
259   OP_ID = "OP_INSTANCE_CREATE"
260   __slots__ = [
261     "instance_name", "mem_size", "disk_size", "os_type", "pnode",
262     "disk_template", "snode", "swap_size", "mode",
263     "vcpus", "ip", "bridge", "src_node", "src_path", "start",
264     "wait_for_sync", "ip_check", "mac",
265     "kernel_path", "initrd_path", "hvm_boot_order", "hvm_acpi",
266     "hvm_pae", "hvm_cdrom_image_path", "vnc_bind_address",
267     "file_storage_dir", "file_driver",
268     "iallocator",
269     ]
270
271
272 class OpReinstallInstance(OpCode):
273   """Reinstall an instance's OS."""
274   OP_ID = "OP_INSTANCE_REINSTALL"
275   __slots__ = ["instance_name", "os_type"]
276
277
278 class OpRemoveInstance(OpCode):
279   """Remove an instance."""
280   OP_ID = "OP_INSTANCE_REMOVE"
281   __slots__ = ["instance_name", "ignore_failures"]
282
283
284 class OpRenameInstance(OpCode):
285   """Rename an instance."""
286   OP_ID = "OP_INSTANCE_RENAME"
287   __slots__ = ["instance_name", "ignore_ip", "new_name"]
288
289
290 class OpStartupInstance(OpCode):
291   """Startup an instance."""
292   OP_ID = "OP_INSTANCE_STARTUP"
293   __slots__ = ["instance_name", "force", "extra_args"]
294
295
296 class OpShutdownInstance(OpCode):
297   """Shutdown an instance."""
298   OP_ID = "OP_INSTANCE_SHUTDOWN"
299   __slots__ = ["instance_name"]
300
301
302 class OpRebootInstance(OpCode):
303   """Reboot an instance."""
304   OP_ID = "OP_INSTANCE_REBOOT"
305   __slots__ = ["instance_name", "reboot_type", "extra_args",
306                "ignore_secondaries" ]
307
308
309 class OpReplaceDisks(OpCode):
310   """Replace the disks of an instance."""
311   OP_ID = "OP_INSTANCE_REPLACE_DISKS"
312   __slots__ = ["instance_name", "remote_node", "mode", "disks", "iallocator"]
313
314
315 class OpFailoverInstance(OpCode):
316   """Failover an instance."""
317   OP_ID = "OP_INSTANCE_FAILOVER"
318   __slots__ = ["instance_name", "ignore_consistency"]
319
320
321 class OpConnectConsole(OpCode):
322   """Connect to an instance's console."""
323   OP_ID = "OP_INSTANCE_CONSOLE"
324   __slots__ = ["instance_name"]
325
326
327 class OpActivateInstanceDisks(OpCode):
328   """Activate an instance's disks."""
329   OP_ID = "OP_INSTANCE_ACTIVATE_DISKS"
330   __slots__ = ["instance_name"]
331
332
333 class OpDeactivateInstanceDisks(OpCode):
334   """Deactivate an instance's disks."""
335   OP_ID = "OP_INSTANCE_DEACTIVATE_DISKS"
336   __slots__ = ["instance_name"]
337
338
339 class OpQueryInstances(OpCode):
340   """Compute the list of instances."""
341   OP_ID = "OP_INSTANCE_QUERY"
342   __slots__ = ["output_fields", "names"]
343
344
345 class OpQueryInstanceData(OpCode):
346   """Compute the run-time status of instances."""
347   OP_ID = "OP_INSTANCE_QUERY_DATA"
348   __slots__ = ["instances"]
349
350
351 class OpSetInstanceParams(OpCode):
352   """Change the parameters of an instance."""
353   OP_ID = "OP_INSTANCE_SET_PARAMS"
354   __slots__ = [
355     "instance_name", "mem", "vcpus", "ip", "bridge", "mac",
356     "kernel_path", "initrd_path", "hvm_boot_order", "hvm_acpi",
357     "hvm_pae", "hvm_cdrom_image_path", "vnc_bind_address"
358     ]
359
360
361 class OpGrowDisk(OpCode):
362   """Grow a disk of an instance."""
363   OP_ID = "OP_INSTANCE_GROW_DISK"
364   __slots__ = ["instance_name", "disk", "amount"]
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__ = ["output_fields", "names"]
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 class OpRemoveExport(OpCode):
387   """Remove an instance's export."""
388   OP_ID = "OP_BACKUP_REMOVE"
389   __slots__ = ["instance_name"]
390
391 # Tags opcodes
392 class OpGetTags(OpCode):
393   """Returns the tags of the given object."""
394   OP_ID = "OP_TAGS_GET"
395   __slots__ = ["kind", "name"]
396
397
398 class OpSearchTags(OpCode):
399   """Searches the tags in the cluster for a given pattern."""
400   OP_ID = "OP_TAGS_SEARCH"
401   __slots__ = ["pattern"]
402
403
404 class OpAddTags(OpCode):
405   """Add a list of tags on a given object."""
406   OP_ID = "OP_TAGS_SET"
407   __slots__ = ["kind", "name", "tags"]
408
409
410 class OpDelTags(OpCode):
411   """Remove a list of tags from a given object."""
412   OP_ID = "OP_TAGS_DEL"
413   __slots__ = ["kind", "name", "tags"]
414
415
416 # Test opcodes
417 class OpTestDelay(OpCode):
418   """Sleeps for a configured amount of time.
419
420   This is used just for debugging and testing.
421
422   Parameters:
423     - duration: the time to sleep
424     - on_master: if true, sleep on the master
425     - on_nodes: list of nodes in which to sleep
426
427   If the on_master parameter is true, it will execute a sleep on the
428   master (before any node sleep).
429
430   If the on_nodes list is not empty, it will sleep on those nodes
431   (after the sleep on the master, if that is enabled).
432
433   As an additional feature, the case of duration < 0 will be reported
434   as an execution error, so this opcode can be used as a failure
435   generator. The case of duration == 0 will not be treated specially.
436
437   """
438   OP_ID = "OP_TEST_DELAY"
439   __slots__ = ["duration", "on_master", "on_nodes"]
440
441
442 class OpTestAllocator(OpCode):
443   """Allocator framework testing.
444
445   This opcode has two modes:
446     - gather and return allocator input for a given mode (allocate new
447       or replace secondary) and a given instance definition (direction
448       'in')
449     - run a selected allocator for a given operation (as above) and
450       return the allocator output (direction 'out')
451
452   """
453   OP_ID = "OP_TEST_ALLOCATOR"
454   __slots__ = [
455     "direction", "mode", "allocator", "name",
456     "mem_size", "disks", "disk_template",
457     "os", "tags", "nics", "vcpus",
458     ]