Fix a misuse of exc_info in logging.info
[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 Every operation which modifies the cluster state is expressed via
28 opcodes.
29
30 """
31
32 # this are practically structures, so disable the message about too
33 # few public methods:
34 # pylint: disable-msg=R0903
35
36
37 class BaseOpCode(object):
38   """A simple serializable object.
39
40   This object serves as a parent class for OpCode without any custom
41   field handling.
42
43   """
44   __slots__ = []
45
46   def __init__(self, **kwargs):
47     """Constructor for BaseOpCode.
48
49     The constructor takes only keyword arguments and will set
50     attributes on this object based on the passed arguments. As such,
51     it means that you should not pass arguments which are not in the
52     __slots__ attribute for this class.
53
54     """
55     for key in kwargs:
56       if key not in self.__slots__:
57         raise TypeError("Object %s doesn't support the parameter '%s'" %
58                         (self.__class__.__name__, key))
59       setattr(self, key, kwargs[key])
60
61   def __getstate__(self):
62     """Generic serializer.
63
64     This method just returns the contents of the instance as a
65     dictionary.
66
67     @rtype:  C{dict}
68     @return: the instance attributes and their values
69
70     """
71     state = {}
72     for name in self.__slots__:
73       if hasattr(self, name):
74         state[name] = getattr(self, name)
75     return state
76
77   def __setstate__(self, state):
78     """Generic unserializer.
79
80     This method just restores from the serialized state the attributes
81     of the current instance.
82
83     @param state: the serialized opcode data
84     @type state:  C{dict}
85
86     """
87     if not isinstance(state, dict):
88       raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
89                        type(state))
90
91     for name in self.__slots__:
92       if name not in state:
93         delattr(self, name)
94
95     for name in state:
96       setattr(self, name, state[name])
97
98
99 class OpCode(BaseOpCode):
100   """Abstract OpCode.
101
102   This is the root of the actual OpCode hierarchy. All clases derived
103   from this class should override OP_ID.
104
105   @cvar OP_ID: The ID of this opcode. This should be unique amongst all
106                childre of this class.
107
108   """
109   OP_ID = "OP_ABSTRACT"
110   __slots__ = []
111
112   def __getstate__(self):
113     """Specialized getstate for opcodes.
114
115     This method adds to the state dictionary the OP_ID of the class,
116     so that on unload we can identify the correct class for
117     instantiating the opcode.
118
119     @rtype:   C{dict}
120     @return:  the state as a dictionary
121
122     """
123     data = BaseOpCode.__getstate__(self)
124     data["OP_ID"] = self.OP_ID
125     return data
126
127   @classmethod
128   def LoadOpCode(cls, data):
129     """Generic load opcode method.
130
131     The method identifies the correct opcode class from the dict-form
132     by looking for a OP_ID key, if this is not found, or its value is
133     not available in this module as a child of this class, we fail.
134
135     @type data:  C{dict}
136     @param data: the serialized opcode
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
165   This opcode has no other parameters. All the state is irreversibly
166   lost after the execution of this opcode.
167
168   """
169   OP_ID = "OP_CLUSTER_DESTROY"
170   __slots__ = []
171
172
173 class OpQueryClusterInfo(OpCode):
174   """Query cluster information."""
175   OP_ID = "OP_CLUSTER_QUERY"
176   __slots__ = []
177
178
179 class OpVerifyCluster(OpCode):
180   """Verify the cluster state.
181
182   @type skip_checks: C{list}
183   @ivar skip_checks: steps to be skipped from the verify process; this
184                      needs to be a subset of
185                      L{constants.VERIFY_OPTIONAL_CHECKS}; currently
186                      only L{constants.VERIFY_NPLUSONE_MEM} can be passed
187
188   """
189   OP_ID = "OP_CLUSTER_VERIFY"
190   __slots__ = ["skip_checks"]
191
192
193 class OpVerifyDisks(OpCode):
194   """Verify the cluster disks.
195
196   Parameters: none
197
198   Result: two lists:
199     - list of node names with bad data returned (unreachable, etc.)
200     - dict of node names with broken volume groups (values: error msg)
201     - list of instances with degraded disks (that should be activated)
202     - dict of instances with missing logical volumes (values: (node, vol)
203       pairs with details about the missing volumes)
204
205   In normal operation, all lists should be empty. A non-empty instance
206   list (3rd element of the result) is still ok (errors were fixed) but
207   non-empty node list means some node is down, and probably there are
208   unfixable drbd errors.
209
210   Note that only instances that are drbd-based are taken into
211   consideration. This might need to be revisited in the future.
212
213   """
214   OP_ID = "OP_CLUSTER_VERIFY_DISKS"
215   __slots__ = []
216
217
218 class OpDumpClusterConfig(OpCode):
219   """Dump the cluster configuration."""
220   OP_ID = "OP_CLUSTER_DUMPCONFIG"
221   __slots__ = []
222
223
224 class OpRenameCluster(OpCode):
225   """Rename the cluster.
226
227   @type name: C{str}
228   @ivar name: The new name of the cluster. The name and/or the master IP
229               address will be changed to match the new name and its IP
230               address.
231
232   """
233   OP_ID = "OP_CLUSTER_RENAME"
234   __slots__ = ["name"]
235
236
237 class OpSetClusterParams(OpCode):
238   """Change the parameters of the cluster.
239
240   @type vg_name: C{str} or C{None}
241   @ivar vg_name: The new volume group name or None to disable LVM usage.
242
243   """
244   OP_ID = "OP_CLUSTER_SET_PARAMS"
245   __slots__ = ["vg_name"]
246
247
248 # node opcodes
249
250 class OpRemoveNode(OpCode):
251   """Remove a node.
252
253   @type node_name: C{str}
254   @ivar node_name: The name of the node to remove. If the node still has
255                    instances on it, the operation will fail.
256
257   """
258   OP_ID = "OP_NODE_REMOVE"
259   __slots__ = ["node_name"]
260
261
262 class OpAddNode(OpCode):
263   """Add a node to the cluster.
264
265   @type node_name: C{str}
266   @ivar node_name: The name of the node to add. This can be a short name,
267                    but it will be expanded to the FQDN.
268   @type primary_ip: IP address
269   @ivar primary_ip: The primary IP of the node. This will be ignored when the
270                     opcode is submitted, but will be filled during the node
271                     add (so it will be visible in the job query).
272   @type secondary_ip: IP address
273   @ivar secondary_ip: The secondary IP of the node. This needs to be passed
274                       if the cluster has been initialized in 'dual-network'
275                       mode, otherwise it must not be given.
276   @type readd: C{bool}
277   @ivar readd: Whether to re-add an existing node to the cluster. If
278                this is not passed, then the operation will abort if the node
279                name is already in the cluster; use this parameter to 'repair'
280                a node that had its configuration broken, or was reinstalled
281                without removal from the cluster.
282
283   """
284   OP_ID = "OP_NODE_ADD"
285   __slots__ = ["node_name", "primary_ip", "secondary_ip", "readd"]
286
287
288 class OpQueryNodes(OpCode):
289   """Compute the list of nodes."""
290   OP_ID = "OP_NODE_QUERY"
291   __slots__ = ["output_fields", "names"]
292
293
294 class OpQueryNodeVolumes(OpCode):
295   """Get list of volumes on node."""
296   OP_ID = "OP_NODE_QUERYVOLS"
297   __slots__ = ["nodes", "output_fields"]
298
299
300 # instance opcodes
301
302 class OpCreateInstance(OpCode):
303   """Create an instance."""
304   OP_ID = "OP_INSTANCE_CREATE"
305   __slots__ = [
306     "instance_name", "mem_size", "disk_size", "os_type", "pnode",
307     "disk_template", "snode", "swap_size", "mode",
308     "vcpus", "ip", "bridge", "src_node", "src_path", "start",
309     "wait_for_sync", "ip_check", "mac",
310     "kernel_path", "initrd_path", "hvm_boot_order", "hvm_acpi",
311     "hvm_pae", "hvm_cdrom_image_path", "vnc_bind_address",
312     "file_storage_dir", "file_driver",
313     "iallocator",
314     ]
315
316
317 class OpReinstallInstance(OpCode):
318   """Reinstall an instance's OS."""
319   OP_ID = "OP_INSTANCE_REINSTALL"
320   __slots__ = ["instance_name", "os_type"]
321
322
323 class OpRemoveInstance(OpCode):
324   """Remove an instance."""
325   OP_ID = "OP_INSTANCE_REMOVE"
326   __slots__ = ["instance_name", "ignore_failures"]
327
328
329 class OpRenameInstance(OpCode):
330   """Rename an instance."""
331   OP_ID = "OP_INSTANCE_RENAME"
332   __slots__ = ["instance_name", "ignore_ip", "new_name"]
333
334
335 class OpStartupInstance(OpCode):
336   """Startup an instance."""
337   OP_ID = "OP_INSTANCE_STARTUP"
338   __slots__ = ["instance_name", "force", "extra_args"]
339
340
341 class OpShutdownInstance(OpCode):
342   """Shutdown an instance."""
343   OP_ID = "OP_INSTANCE_SHUTDOWN"
344   __slots__ = ["instance_name"]
345
346
347 class OpRebootInstance(OpCode):
348   """Reboot an instance."""
349   OP_ID = "OP_INSTANCE_REBOOT"
350   __slots__ = ["instance_name", "reboot_type", "extra_args",
351                "ignore_secondaries" ]
352
353
354 class OpReplaceDisks(OpCode):
355   """Replace the disks of an instance."""
356   OP_ID = "OP_INSTANCE_REPLACE_DISKS"
357   __slots__ = ["instance_name", "remote_node", "mode", "disks", "iallocator"]
358
359
360 class OpFailoverInstance(OpCode):
361   """Failover an instance."""
362   OP_ID = "OP_INSTANCE_FAILOVER"
363   __slots__ = ["instance_name", "ignore_consistency"]
364
365
366 class OpConnectConsole(OpCode):
367   """Connect to an instance's console."""
368   OP_ID = "OP_INSTANCE_CONSOLE"
369   __slots__ = ["instance_name"]
370
371
372 class OpActivateInstanceDisks(OpCode):
373   """Activate an instance's disks."""
374   OP_ID = "OP_INSTANCE_ACTIVATE_DISKS"
375   __slots__ = ["instance_name"]
376
377
378 class OpDeactivateInstanceDisks(OpCode):
379   """Deactivate an instance's disks."""
380   OP_ID = "OP_INSTANCE_DEACTIVATE_DISKS"
381   __slots__ = ["instance_name"]
382
383
384 class OpQueryInstances(OpCode):
385   """Compute the list of instances."""
386   OP_ID = "OP_INSTANCE_QUERY"
387   __slots__ = ["output_fields", "names"]
388
389
390 class OpQueryInstanceData(OpCode):
391   """Compute the run-time status of instances."""
392   OP_ID = "OP_INSTANCE_QUERY_DATA"
393   __slots__ = ["instances"]
394
395
396 class OpSetInstanceParams(OpCode):
397   """Change the parameters of an instance."""
398   OP_ID = "OP_INSTANCE_SET_PARAMS"
399   __slots__ = [
400     "instance_name", "mem", "vcpus", "ip", "bridge", "mac",
401     "kernel_path", "initrd_path", "hvm_boot_order", "hvm_acpi",
402     "hvm_pae", "hvm_cdrom_image_path", "vnc_bind_address"
403     ]
404
405
406 class OpGrowDisk(OpCode):
407   """Grow a disk of an instance."""
408   OP_ID = "OP_INSTANCE_GROW_DISK"
409   __slots__ = ["instance_name", "disk", "amount"]
410
411
412 # OS opcodes
413 class OpDiagnoseOS(OpCode):
414   """Compute the list of guest operating systems."""
415   OP_ID = "OP_OS_DIAGNOSE"
416   __slots__ = ["output_fields", "names"]
417
418
419 # Exports opcodes
420 class OpQueryExports(OpCode):
421   """Compute the list of exported images."""
422   OP_ID = "OP_BACKUP_QUERY"
423   __slots__ = ["nodes"]
424
425
426 class OpExportInstance(OpCode):
427   """Export an instance."""
428   OP_ID = "OP_BACKUP_EXPORT"
429   __slots__ = ["instance_name", "target_node", "shutdown"]
430
431 class OpRemoveExport(OpCode):
432   """Remove an instance's export."""
433   OP_ID = "OP_BACKUP_REMOVE"
434   __slots__ = ["instance_name"]
435
436 # Tags opcodes
437 class OpGetTags(OpCode):
438   """Returns the tags of the given object."""
439   OP_ID = "OP_TAGS_GET"
440   __slots__ = ["kind", "name"]
441
442
443 class OpSearchTags(OpCode):
444   """Searches the tags in the cluster for a given pattern."""
445   OP_ID = "OP_TAGS_SEARCH"
446   __slots__ = ["pattern"]
447
448
449 class OpAddTags(OpCode):
450   """Add a list of tags on a given object."""
451   OP_ID = "OP_TAGS_SET"
452   __slots__ = ["kind", "name", "tags"]
453
454
455 class OpDelTags(OpCode):
456   """Remove a list of tags from a given object."""
457   OP_ID = "OP_TAGS_DEL"
458   __slots__ = ["kind", "name", "tags"]
459
460
461 # Test opcodes
462 class OpTestDelay(OpCode):
463   """Sleeps for a configured amount of time.
464
465   This is used just for debugging and testing.
466
467   Parameters:
468     - duration: the time to sleep
469     - on_master: if true, sleep on the master
470     - on_nodes: list of nodes in which to sleep
471
472   If the on_master parameter is true, it will execute a sleep on the
473   master (before any node sleep).
474
475   If the on_nodes list is not empty, it will sleep on those nodes
476   (after the sleep on the master, if that is enabled).
477
478   As an additional feature, the case of duration < 0 will be reported
479   as an execution error, so this opcode can be used as a failure
480   generator. The case of duration == 0 will not be treated specially.
481
482   """
483   OP_ID = "OP_TEST_DELAY"
484   __slots__ = ["duration", "on_master", "on_nodes"]
485
486
487 class OpTestAllocator(OpCode):
488   """Allocator framework testing.
489
490   This opcode has two modes:
491     - gather and return allocator input for a given mode (allocate new
492       or replace secondary) and a given instance definition (direction
493       'in')
494     - run a selected allocator for a given operation (as above) and
495       return the allocator output (direction 'out')
496
497   """
498   OP_ID = "OP_TEST_ALLOCATOR"
499   __slots__ = [
500     "direction", "mode", "allocator", "name",
501     "mem_size", "disks", "disk_template",
502     "os", "tags", "nics", "vcpus",
503     ]