Change JobStorage to work with ids not filenames
[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 OpCode(BaseJO):
77   """Abstract OpCode"""
78   OP_ID = "OP_ABSTRACT"
79   __slots__ = []
80
81   def __getstate__(self):
82     """Specialized getstate for opcodes.
83
84     """
85     data = BaseJO.__getstate__(self)
86     data["OP_ID"] = self.OP_ID
87     return data
88
89   @classmethod
90   def LoadOpCode(cls, data):
91     """Generic load opcode method.
92
93     """
94     if not isinstance(data, dict):
95       raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
96     if "OP_ID" not in data:
97       raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
98     op_id = data["OP_ID"]
99     op_class = None
100     for item in globals().values():
101       if (isinstance(item, type) and
102           issubclass(item, cls) and
103           hasattr(item, "OP_ID") and
104           getattr(item, "OP_ID") == op_id):
105         op_class = item
106         break
107     if op_class is None:
108       raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
109                        op_id)
110     op = op_class()
111     new_data = data.copy()
112     del new_data["OP_ID"]
113     op.__setstate__(new_data)
114     return op
115
116
117 class OpDestroyCluster(OpCode):
118   """Destroy the cluster."""
119   OP_ID = "OP_CLUSTER_DESTROY"
120   __slots__ = []
121
122
123 class OpQueryClusterInfo(OpCode):
124   """Query cluster information."""
125   OP_ID = "OP_CLUSTER_QUERY"
126   __slots__ = []
127
128
129 class OpVerifyCluster(OpCode):
130   """Verify the cluster state."""
131   OP_ID = "OP_CLUSTER_VERIFY"
132   __slots__ = ["skip_checks"]
133
134
135 class OpVerifyDisks(OpCode):
136   """Verify the cluster disks.
137
138   Parameters: none
139
140   Result: two lists:
141     - list of node names with bad data returned (unreachable, etc.)
142     - dist of node names with broken volume groups (values: error msg)
143     - list of instances with degraded disks (that should be activated)
144     - dict of instances with missing logical volumes (values: (node, vol)
145       pairs with details about the missing volumes)
146
147   In normal operation, all lists should be empty. A non-empty instance
148   list (3rd element of the result) is still ok (errors were fixed) but
149   non-empty node list means some node is down, and probably there are
150   unfixable drbd errors.
151
152   Note that only instances that are drbd-based are taken into
153   consideration. This might need to be revisited in the future.
154
155   """
156   OP_ID = "OP_CLUSTER_VERIFY_DISKS"
157   __slots__ = []
158
159
160 class OpMasterFailover(OpCode):
161   """Do a master failover."""
162   OP_ID = "OP_CLUSTER_MASTERFAILOVER"
163   __slots__ = []
164
165
166 class OpDumpClusterConfig(OpCode):
167   """Dump the cluster configuration."""
168   OP_ID = "OP_CLUSTER_DUMPCONFIG"
169   __slots__ = []
170
171
172 class OpRenameCluster(OpCode):
173   """Rename the cluster."""
174   OP_ID = "OP_CLUSTER_RENAME"
175   __slots__ = ["name"]
176
177
178 class OpSetClusterParams(OpCode):
179   """Change the parameters of the cluster."""
180   OP_ID = "OP_CLUSTER_SET_PARAMS"
181   __slots__ = ["vg_name"]
182
183
184 # node opcodes
185
186 class OpRemoveNode(OpCode):
187   """Remove a node."""
188   OP_ID = "OP_NODE_REMOVE"
189   __slots__ = ["node_name"]
190
191
192 class OpAddNode(OpCode):
193   """Add a node."""
194   OP_ID = "OP_NODE_ADD"
195   __slots__ = ["node_name", "primary_ip", "secondary_ip", "readd"]
196
197
198 class OpQueryNodes(OpCode):
199   """Compute the list of nodes."""
200   OP_ID = "OP_NODE_QUERY"
201   __slots__ = ["output_fields", "names"]
202
203
204 class OpQueryNodeVolumes(OpCode):
205   """Get list of volumes on node."""
206   OP_ID = "OP_NODE_QUERYVOLS"
207   __slots__ = ["nodes", "output_fields"]
208
209
210 # instance opcodes
211
212 class OpCreateInstance(OpCode):
213   """Create an instance."""
214   OP_ID = "OP_INSTANCE_CREATE"
215   __slots__ = [
216     "instance_name", "mem_size", "disk_size", "os_type", "pnode",
217     "disk_template", "snode", "swap_size", "mode",
218     "vcpus", "ip", "bridge", "src_node", "src_path", "start",
219     "wait_for_sync", "ip_check", "mac",
220     "kernel_path", "initrd_path", "hvm_boot_order", "hvm_acpi",
221     "hvm_pae", "hvm_cdrom_image_path", "vnc_bind_address",
222     "file_storage_dir", "file_driver",
223     "iallocator",
224     ]
225
226
227 class OpReinstallInstance(OpCode):
228   """Reinstall an instance's OS."""
229   OP_ID = "OP_INSTANCE_REINSTALL"
230   __slots__ = ["instance_name", "os_type"]
231
232
233 class OpRemoveInstance(OpCode):
234   """Remove an instance."""
235   OP_ID = "OP_INSTANCE_REMOVE"
236   __slots__ = ["instance_name", "ignore_failures"]
237
238
239 class OpRenameInstance(OpCode):
240   """Rename an instance."""
241   OP_ID = "OP_INSTANCE_RENAME"
242   __slots__ = ["instance_name", "ignore_ip", "new_name"]
243
244
245 class OpStartupInstance(OpCode):
246   """Startup an instance."""
247   OP_ID = "OP_INSTANCE_STARTUP"
248   __slots__ = ["instance_name", "force", "extra_args"]
249
250
251 class OpShutdownInstance(OpCode):
252   """Shutdown an instance."""
253   OP_ID = "OP_INSTANCE_SHUTDOWN"
254   __slots__ = ["instance_name"]
255
256
257 class OpRebootInstance(OpCode):
258   """Reboot an instance."""
259   OP_ID = "OP_INSTANCE_REBOOT"
260   __slots__ = ["instance_name", "reboot_type", "extra_args",
261                "ignore_secondaries" ]
262
263
264 class OpReplaceDisks(OpCode):
265   """Replace the disks of an instance."""
266   OP_ID = "OP_INSTANCE_REPLACE_DISKS"
267   __slots__ = ["instance_name", "remote_node", "mode", "disks", "iallocator"]
268
269
270 class OpFailoverInstance(OpCode):
271   """Failover an instance."""
272   OP_ID = "OP_INSTANCE_FAILOVER"
273   __slots__ = ["instance_name", "ignore_consistency"]
274
275
276 class OpConnectConsole(OpCode):
277   """Connect to an instance's console."""
278   OP_ID = "OP_INSTANCE_CONSOLE"
279   __slots__ = ["instance_name"]
280
281
282 class OpActivateInstanceDisks(OpCode):
283   """Activate an instance's disks."""
284   OP_ID = "OP_INSTANCE_ACTIVATE_DISKS"
285   __slots__ = ["instance_name"]
286
287
288 class OpDeactivateInstanceDisks(OpCode):
289   """Deactivate an instance's disks."""
290   OP_ID = "OP_INSTANCE_DEACTIVATE_DISKS"
291   __slots__ = ["instance_name"]
292
293
294 class OpQueryInstances(OpCode):
295   """Compute the list of instances."""
296   OP_ID = "OP_INSTANCE_QUERY"
297   __slots__ = ["output_fields", "names"]
298
299
300 class OpQueryInstanceData(OpCode):
301   """Compute the run-time status of instances."""
302   OP_ID = "OP_INSTANCE_QUERY_DATA"
303   __slots__ = ["instances"]
304
305
306 class OpSetInstanceParams(OpCode):
307   """Change the parameters of an instance."""
308   OP_ID = "OP_INSTANCE_SET_PARAMS"
309   __slots__ = [
310     "instance_name", "mem", "vcpus", "ip", "bridge", "mac",
311     "kernel_path", "initrd_path", "hvm_boot_order", "hvm_acpi",
312     "hvm_pae", "hvm_cdrom_image_path", "vnc_bind_address"
313     ]
314
315
316 class OpGrowDisk(OpCode):
317   """Grow a disk of an instance."""
318   OP_ID = "OP_INSTANCE_GROW_DISK"
319   __slots__ = ["instance_name", "disk", "amount"]
320
321
322 # OS opcodes
323 class OpDiagnoseOS(OpCode):
324   """Compute the list of guest operating systems."""
325   OP_ID = "OP_OS_DIAGNOSE"
326   __slots__ = ["output_fields", "names"]
327
328
329 # Exports opcodes
330 class OpQueryExports(OpCode):
331   """Compute the list of exported images."""
332   OP_ID = "OP_BACKUP_QUERY"
333   __slots__ = ["nodes"]
334
335
336 class OpExportInstance(OpCode):
337   """Export an instance."""
338   OP_ID = "OP_BACKUP_EXPORT"
339   __slots__ = ["instance_name", "target_node", "shutdown"]
340
341 class OpRemoveExport(OpCode):
342   """Remove an instance's export."""
343   OP_ID = "OP_BACKUP_REMOVE"
344   __slots__ = ["instance_name"]
345
346 # Tags opcodes
347 class OpGetTags(OpCode):
348   """Returns the tags of the given object."""
349   OP_ID = "OP_TAGS_GET"
350   __slots__ = ["kind", "name"]
351
352
353 class OpSearchTags(OpCode):
354   """Searches the tags in the cluster for a given pattern."""
355   OP_ID = "OP_TAGS_SEARCH"
356   __slots__ = ["pattern"]
357
358
359 class OpAddTags(OpCode):
360   """Add a list of tags on a given object."""
361   OP_ID = "OP_TAGS_SET"
362   __slots__ = ["kind", "name", "tags"]
363
364
365 class OpDelTags(OpCode):
366   """Remove a list of tags from a given object."""
367   OP_ID = "OP_TAGS_DEL"
368   __slots__ = ["kind", "name", "tags"]
369
370
371 # Test opcodes
372 class OpTestDelay(OpCode):
373   """Sleeps for a configured amount of time.
374
375   This is used just for debugging and testing.
376
377   Parameters:
378     - duration: the time to sleep
379     - on_master: if true, sleep on the master
380     - on_nodes: list of nodes in which to sleep
381
382   If the on_master parameter is true, it will execute a sleep on the
383   master (before any node sleep).
384
385   If the on_nodes list is not empty, it will sleep on those nodes
386   (after the sleep on the master, if that is enabled).
387
388   As an additional feature, the case of duration < 0 will be reported
389   as an execution error, so this opcode can be used as a failure
390   generator. The case of duration == 0 will not be treated specially.
391
392   """
393   OP_ID = "OP_TEST_DELAY"
394   __slots__ = ["duration", "on_master", "on_nodes"]
395
396
397 class OpTestAllocator(OpCode):
398   """Allocator framework testing.
399
400   This opcode has two modes:
401     - gather and return allocator input for a given mode (allocate new
402       or replace secondary) and a given instance definition (direction
403       'in')
404     - run a selected allocator for a given operation (as above) and
405       return the allocator output (direction 'out')
406
407   """
408   OP_ID = "OP_TEST_ALLOCATOR"
409   __slots__ = [
410     "direction", "mode", "allocator", "name",
411     "mem_size", "disks", "disk_template",
412     "os", "tags", "nics", "vcpus",
413     ]