Design document for hsqueeze
[ganeti-local] / lib / opcodes.py.in_before
1 #
2 #
3
4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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 Note that this file is autogenerated using @src/hs2py@ with a header
25 from @lib/opcodes.py.in_before@ and a footer from @lib/opcodes.py.in_after@.
26
27 This module implements part of the data structures which define the
28 cluster operations - the so-called opcodes.
29
30 Every operation which modifies the cluster state is expressed via
31 opcodes.
32
33 """
34
35 # this are practically structures, so disable the message about too
36 # few public methods:
37 # pylint: disable=R0903
38 # pylint: disable=C0301
39
40 from ganeti import constants
41 from ganeti import ht
42
43 from ganeti import opcodes_base
44
45
46 class OpCode(opcodes_base.BaseOpCode):
47   """Abstract OpCode.
48
49   This is the root of the actual OpCode hierarchy. All clases derived
50   from this class should override OP_ID.
51
52   @cvar OP_ID: The ID of this opcode. This should be unique amongst all
53                children of this class.
54   @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
55                       string returned by Summary(); see the docstring of that
56                       method for details).
57   @cvar OP_DSC_FORMATTER: A callable that should format the OP_DSC_FIELD; if
58                           not present, then the field will be simply converted
59                           to string
60   @cvar OP_PARAMS: List of opcode attributes, the default values they should
61                    get if not already defined, and types they must match.
62   @cvar OP_RESULT: Callable to verify opcode result
63   @cvar WITH_LU: Boolean that specifies whether this should be included in
64       mcpu's dispatch table
65   @ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
66                  the check steps
67   @ivar priority: Opcode priority for queue
68
69   """
70   # pylint: disable=E1101
71   # as OP_ID is dynamically defined
72   WITH_LU = True
73   OP_PARAMS = [
74     ("dry_run", None, ht.TMaybe(ht.TBool), "Run checks only, don't execute"),
75     ("debug_level", None, ht.TMaybe(ht.TNonNegative(ht.TInt)), "Debug level"),
76     ("priority", constants.OP_PRIO_DEFAULT,
77      ht.TElemOf(constants.OP_PRIO_SUBMIT_VALID), "Opcode priority"),
78     (opcodes_base.DEPEND_ATTR, None, opcodes_base.BuildJobDepCheck(True),
79      "Job dependencies; if used through ``SubmitManyJobs`` relative (negative)"
80      " job IDs can be used; see :doc:`design document <design-chained-jobs>`"
81      " for details"),
82     (opcodes_base.COMMENT_ATTR, None, ht.TMaybe(ht.TString),
83      "Comment describing the purpose of the opcode"),
84     (constants.OPCODE_REASON, None, ht.TMaybe(ht.TListOf(ht.TAny)),
85      "The reason trail, describing why the OpCode is executed"),
86     ]
87   OP_RESULT = None
88
89   def __getstate__(self):
90     """Specialized getstate for opcodes.
91
92     This method adds to the state dictionary the OP_ID of the class,
93     so that on unload we can identify the correct class for
94     instantiating the opcode.
95
96     @rtype:   C{dict}
97     @return:  the state as a dictionary
98
99     """
100     data = opcodes_base.BaseOpCode.__getstate__(self)
101     data["OP_ID"] = self.OP_ID
102     return data
103
104   @classmethod
105   def LoadOpCode(cls, data):
106     """Generic load opcode method.
107
108     The method identifies the correct opcode class from the dict-form
109     by looking for a OP_ID key, if this is not found, or its value is
110     not available in this module as a child of this class, we fail.
111
112     @type data:  C{dict}
113     @param data: the serialized opcode
114
115     """
116     if not isinstance(data, dict):
117       raise ValueError("Invalid data to LoadOpCode (%s)" % type(data))
118     if "OP_ID" not in data:
119       raise ValueError("Invalid data to LoadOpcode, missing OP_ID")
120     op_id = data["OP_ID"]
121     op_class = None
122     if op_id in OP_MAPPING:
123       op_class = OP_MAPPING[op_id]
124     else:
125       raise ValueError("Invalid data to LoadOpCode: OP_ID %s unsupported" %
126                        op_id)
127     op = op_class()
128     new_data = data.copy()
129     del new_data["OP_ID"]
130     op.__setstate__(new_data)
131     return op
132
133   def Summary(self):
134     """Generates a summary description of this opcode.
135
136     The summary is the value of the OP_ID attribute (without the "OP_"
137     prefix), plus the value of the OP_DSC_FIELD attribute, if one was
138     defined; this field should allow to easily identify the operation
139     (for an instance creation job, e.g., it would be the instance
140     name).
141
142     """
143     assert self.OP_ID is not None and len(self.OP_ID) > 3
144     # all OP_ID start with OP_, we remove that
145     txt = self.OP_ID[3:]
146     field_name = getattr(self, "OP_DSC_FIELD", None)
147     if field_name:
148       field_value = getattr(self, field_name, None)
149       field_formatter = getattr(self, "OP_DSC_FORMATTER", None)
150       if callable(field_formatter):
151         field_value = field_formatter(field_value)
152       elif isinstance(field_value, (list, tuple)):
153         field_value = ",".join(str(i) for i in field_value)
154       txt = "%s(%s)" % (txt, field_value)
155     return txt
156
157   def TinySummary(self):
158     """Generates a compact summary description of the opcode.
159
160     """
161     assert self.OP_ID.startswith("OP_")
162
163     text = self.OP_ID[3:]
164
165     for (prefix, supplement) in opcodes_base.SUMMARY_PREFIX.items():
166       if text.startswith(prefix):
167         return supplement + text[len(prefix):]
168
169     return text
170
171
172 class OpInstanceMultiAllocBase(OpCode):
173   """Allocates multiple instances.
174
175   """
176   def __getstate__(self):
177     """Generic serializer.
178
179     """
180     state = OpCode.__getstate__(self)
181     if hasattr(self, "instances"):
182       # pylint: disable=E1101
183       state["instances"] = [inst.__getstate__() for inst in self.instances]
184     return state
185
186   def __setstate__(self, state):
187     """Generic unserializer.
188
189     This method just restores from the serialized state the attributes
190     of the current instance.
191
192     @param state: the serialized opcode data
193     @type state: C{dict}
194
195     """
196     if not isinstance(state, dict):
197       raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
198                        type(state))
199
200     if "instances" in state:
201       state["instances"] = map(OpCode.LoadOpCode, state["instances"])
202
203     return OpCode.__setstate__(self, state)
204
205   def Validate(self, set_defaults):
206     """Validates this opcode.
207
208     We do this recursively.
209
210     """
211     OpCode.Validate(self, set_defaults)
212
213     for inst in self.instances: # pylint: disable=E1101
214       inst.Validate(set_defaults)