Merge branch 'stable-2.16' into stable-2.17
[ganeti-github.git] / lib / opcodes.py.in_before
1 #
2 #
3
4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
9 # met:
10 #
11 # 1. Redistributions of source code must retain the above copyright notice,
12 # this list of conditions and the following disclaimer.
13 #
14 # 2. Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in the
16 # documentation and/or other materials provided with the distribution.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19 # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30
31 """OpCodes module
32
33 Note that this file is autogenerated using @src/hs2py@ with a header
34 from @lib/opcodes.py.in_before@ and a footer from @lib/opcodes.py.in_after@.
35
36 This module implements part of the data structures which define the
37 cluster operations - the so-called opcodes.
38
39 Every operation which modifies the cluster state is expressed via
40 opcodes.
41
42 """
43
44 # this are practically structures, so disable the message about too
45 # few public methods:
46 # pylint: disable=R0903
47 # pylint: disable=C0301
48
49 from ganeti import constants
50 from ganeti import ht
51
52 from ganeti import opcodes_base
53
54
55 class OpCode(opcodes_base.BaseOpCode):
56   """Abstract OpCode.
57
58   This is the root of the actual OpCode hierarchy. All clases derived
59   from this class should override OP_ID.
60
61   @cvar OP_ID: The ID of this opcode. This should be unique amongst all
62                children of this class.
63   @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
64                       string returned by Summary(); see the docstring of that
65                       method for details).
66   @cvar OP_DSC_FORMATTER: A callable that should format the OP_DSC_FIELD; if
67                           not present, then the field will be simply converted
68                           to string
69   @cvar OP_PARAMS: List of opcode attributes, the default values they should
70                    get if not already defined, and types they must match.
71   @cvar OP_RESULT: Callable to verify opcode result
72   @cvar WITH_LU: Boolean that specifies whether this should be included in
73       mcpu's dispatch table
74   @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
75                  the check steps
76   @ivar priority: Opcode priority for queue
77
78   """
79   # pylint: disable=E1101
80   # as OP_ID is dynamically defined
81   WITH_LU = True
82   OP_PARAMS = [
83     ("dry_run", None, ht.TMaybe(ht.TBool), "Run checks only, don't execute"),
84     ("debug_level", None, ht.TMaybe(ht.TNonNegative(ht.TInt)), "Debug level"),
85     ("priority", constants.OP_PRIO_DEFAULT,
86      ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID), "Opcode priority"),
87     (opcodes_base.DEPEND_ATTR, None, opcodes_base.BuildJobDepCheck(True),
88      "Job dependencies; if used through ``SubmitManyJobs`` relative (negative)"
89      " job IDs can be used; see :doc:`design document <design-chained-jobs>`"
90      " for details"),
91     (opcodes_base.COMMENT_ATTR, None, ht.TMaybe(ht.TString),
92      "Comment describing the purpose of the opcode"),
93     (constants.OPCODE_REASON, [], ht.TMaybe(ht.TListOf(ht.TAny)),
94      "The reason trail, describing why the OpCode is executed"),
95     ]
96   OP_RESULT = None
97
98   def __getstate__(self):
99     """Specialized getstate for opcodes.
100
101     This method adds to the state dictionary the OP_ID of the class,
102     so that on unload we can identify the correct class for
103     instantiating the opcode.
104
105     @rtype:   C{dict}
106     @return:  the state as a dictionary
107
108     """
109     data = opcodes_base.BaseOpCode.__getstate__(self)
110     data["OP_ID"] = self.OP_ID
111     return data
112
113   @classmethod
114   def LoadOpCode(cls, data):
115     """Generic load opcode method.
116
117     The method identifies the correct opcode class from the dict-form
118     by looking for a OP_ID key, if this is not found, or its value is
119     not available in this module as a child of this class, we fail.
120
121     @type data:  C{dict}
122     @param data: the serialized opcode
123
124     """
125     if not isinstance(data, dict):
126       raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
127     if "OP_ID" not in data:
128       raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
129     op_id = data["OP_ID"]
130     op_class = None
131     if op_id in OP_MAPPING:
132       op_class = OP_MAPPING[op_id]
133     else:
134       raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
135                        op_id)
136     op = op_class()
137     new_data = data.copy()
138     del new_data["OP_ID"]
139     op.__setstate__(new_data)
140     return op
141
142   def Summary(self):
143     """Generates a summary description of this opcode.
144
145     The summary is the value of the OP_ID attribute (without the "OP_"
146     prefix), plus the value of the OP_DSC_FIELD attribute, if one was
147     defined; this field should allow to easily identify the operation
148     (for an instance creation job, e.g., it would be the instance
149     name).
150
151     """
152     assert self.OP_ID is not None and len(self.OP_ID) > 3
153     # all OP_ID start with OP_, we remove that
154     txt = self.OP_ID[3:]
155     field_name = getattr(self, "OP_DSC_FIELD", None)
156     if field_name:
157       field_value = getattr(self, field_name, None)
158       field_formatter = getattr(self, "OP_DSC_FORMATTER", None)
159       if callable(field_formatter):
160         field_value = field_formatter(field_value)
161       elif isinstance(field_value, (list, tuple)):
162         field_value = ",".join(str(i) for i in field_value)
163       txt = "%s(%s)" % (txt, field_value)
164     return txt
165
166   def TinySummary(self):
167     """Generates a compact summary description of the opcode.
168
169     """
170     assert self.OP_ID.startswith("OP_")
171
172     text = self.OP_ID[3:]
173
174     for (prefix, supplement) in opcodes_base.SUMMARY_PREFIX.items():
175       if text.startswith(prefix):
176         return supplement + text[len(prefix):]
177
178     return text
179
180
181 class OpInstanceMultiAllocBase(OpCode):
182   """Allocates multiple instances.
183
184   """
185   def __getstate__(self):
186     """Generic serializer.
187
188     """
189     state = OpCode.__getstate__(self)
190     if hasattr(self, "instances"):
191       # pylint: disable=E1101
192       state["instances"] = [inst.__getstate__() for inst in self.instances]
193     return state
194
195   def __setstate__(self, state):
196     """Generic unserializer.
197
198     This method just restores from the serialized state the attributes
199     of the current instance.
200
201     @param state: the serialized opcode data
202     @type state: C{dict}
203
204     """
205     if not isinstance(state, dict):
206       raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
207                        type(state))
208
209     if "instances" in state:
210       state["instances"] = map(OpCode.LoadOpCode, state["instances"])
211
212     return OpCode.__setstate__(self, state)
213
214   def Validate(self, set_defaults):
215     """Validates this opcode.
216
217     We do this recursively.
218
219     @type set_defaults: bool
220     @param set_defaults: whether to set default values
221
222     @rtype: NoneType
223     @return: L{None}, if the validation succeeds
224
225     @raise errors.OpPrereqError: when a parameter value doesn't match
226                                  requirements
227
228     """
229     OpCode.Validate(self, set_defaults)
230
231     for inst in self.instances: # pylint: disable=E1101
232       inst.Validate(set_defaults)