1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 """OpCodes base module
32
33 This module implements part of the data structures which define the
34 cluster operations - the so-called opcodes.
35
36 Every operation which modifies the cluster state is expressed via
37 opcodes.
38
39 """
40
41
42
43
44
45 import copy
46 import logging
47 import re
48
49 from ganeti import constants
50 from ganeti import errors
51 from ganeti import ht
52 from ganeti import outils
53
54
55
56 _OPID_RE = re.compile("([a-z])([A-Z])")
57
58 SUMMARY_PREFIX = {
59 "CLUSTER_": "C_",
60 "GROUP_": "G_",
61 "NODE_": "N_",
62 "INSTANCE_": "I_",
63 }
64
65
66 DEPEND_ATTR = "depends"
67
68
69 COMMENT_ATTR = "comment"
73 """Split an opcode class name into its components
74
75 @type name: string
76 @param name: the class name, as OpXxxYyy
77 @rtype: array of strings
78 @return: the components of the name
79
80 """
81 assert name.startswith("Op")
82
83
84
85
86
87 name = _OPID_RE.sub(r"\1,\2", name)
88 elems = name.split(",")
89 return elems
90
93 """Convert an opcode class name to an OP_ID.
94
95 @type name: string
96 @param name: the class name, as OpXxxYyy
97 @rtype: string
98 @return: the name in the OP_XXXX_YYYY format
99
100 """
101 if not name.startswith("Op"):
102 return None
103 return "_".join(n.upper() for n in _NameComponents(name))
104
107 """Convert an opcode class name to a source string for the reason trail
108
109 @type name: string
110 @param name: the class name, as OpXxxYyy
111 @rtype: string
112 @return: the name in the OP_XXXX_YYYY format
113
114 """
115 if not name.startswith("Op"):
116 return None
117 return "%s:%s" % (constants.OPCODE_REASON_SRC_OPCODE,
118 "_".join(n.lower() for n in _NameComponents(name)))
119
122 """Meta class for opcode definitions.
123
124 """
125 - def __new__(mcs, name, bases, attrs):
126 """Called when a class should be created.
127
128 @param mcs: The meta class
129 @param name: Name of created class
130 @param bases: Base classes
131 @type attrs: dict
132 @param attrs: Class attributes
133
134 """
135 assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
136
137 slots = mcs._GetSlots(attrs)
138 assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
139 "Class '%s' uses unknown field in OP_DSC_FIELD" % name
140 assert ("OP_DSC_FORMATTER" not in attrs or
141 callable(attrs["OP_DSC_FORMATTER"])), \
142 ("Class '%s' uses non-callable in OP_DSC_FORMATTER (%s)" %
143 (name, type(attrs["OP_DSC_FORMATTER"])))
144
145 attrs["OP_ID"] = _NameToId(name)
146
147 return outils.AutoSlots.__new__(mcs, name, bases, attrs)
148
149 @classmethod
151 """Build the slots out of OP_PARAMS.
152
153 """
154
155 params = attrs.setdefault("OP_PARAMS", [])
156
157
158 return [pname for (pname, _, _, _) in params]
159
162 """A simple serializable object.
163
164 This object serves as a parent class for OpCode without any custom
165 field handling.
166
167 """
168
169
170 __metaclass__ = _AutoOpParamSlots
171
173 """Generic serializer.
174
175 This method just returns the contents of the instance as a
176 dictionary.
177
178 @rtype: C{dict}
179 @return: the instance attributes and their values
180
181 """
182 state = {}
183 for name in self.GetAllSlots():
184 if hasattr(self, name):
185 state[name] = getattr(self, name)
186 return state
187
189 """Generic unserializer.
190
191 This method just restores from the serialized state the attributes
192 of the current instance.
193
194 @param state: the serialized opcode data
195 @type state: C{dict}
196
197 """
198 if not isinstance(state, dict):
199 raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
200 type(state))
201
202 for name in self.GetAllSlots():
203 if name not in state and hasattr(self, name):
204 delattr(self, name)
205
206 for name in state:
207 setattr(self, name, state[name])
208
209 @classmethod
211 """Compute list of all parameters for an opcode.
212
213 """
214 slots = []
215 for parent in cls.__mro__:
216 slots.extend(getattr(parent, "OP_PARAMS", []))
217 return slots
218
220 """Validate opcode parameters, optionally setting default values.
221
222 @type set_defaults: bool
223 @param set_defaults: Whether to set default values
224 @raise errors.OpPrereqError: When a parameter value doesn't match
225 requirements
226
227 """
228 for (attr_name, default, test, _) in self.GetAllParams():
229 assert callable(test)
230
231 if hasattr(self, attr_name):
232 attr_val = getattr(self, attr_name)
233 else:
234 attr_val = copy.deepcopy(default)
235
236 if test(attr_val):
237 if set_defaults:
238 setattr(self, attr_name, attr_val)
239 elif ht.TInt(attr_val) and test(float(attr_val)):
240 if set_defaults:
241 setattr(self, attr_name, float(attr_val))
242 else:
243 logging.error("OpCode %s, parameter %s, has invalid type %s/value"
244 " '%s' expecting type %s",
245 self.OP_ID, attr_name, type(attr_val), attr_val, test)
246
247 if attr_val is None:
248 logging.error("OpCode %s, parameter %s, has default value None which"
249 " is does not check against the parameter's type: this"
250 " means this parameter is required but no value was"
251 " given",
252 self.OP_ID, attr_name)
253
254 raise errors.OpPrereqError("Parameter '%s.%s' fails validation" %
255 (self.OP_ID, attr_name),
256 errors.ECODE_INVAL)
257
279
280
281 TNoRelativeJobDependencies = BuildJobDepCheck(False)
282