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 @type prefix: string
112 @param prefix: the prefix that will be prepended to the opcode name
113 @rtype: string
114 @return: the name in the OP_XXXX_YYYY format
115
116 """
117 if not name.startswith("Op"):
118 return None
119 return "%s:%s" % (prefix,
120 "_".join(n.lower() for n in _NameComponents(name)))
121
124 """Meta class for opcode definitions.
125
126 """
127 - def __new__(mcs, name, bases, attrs):
128 """Called when a class should be created.
129
130 @param mcs: The meta class
131 @param name: Name of created class
132 @param bases: Base classes
133 @type attrs: dict
134 @param attrs: Class attributes
135
136 """
137 assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
138
139 slots = mcs._GetSlots(attrs)
140 assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
141 "Class '%s' uses unknown field in OP_DSC_FIELD" % name
142 assert ("OP_DSC_FORMATTER" not in attrs or
143 callable(attrs["OP_DSC_FORMATTER"])), \
144 ("Class '%s' uses non-callable in OP_DSC_FORMATTER (%s)" %
145 (name, type(attrs["OP_DSC_FORMATTER"])))
146
147 attrs["OP_ID"] = _NameToId(name)
148
149 return outils.AutoSlots.__new__(mcs, name, bases, attrs)
150
151 @classmethod
153 """Build the slots out of OP_PARAMS.
154
155 """
156
157 params = attrs.setdefault("OP_PARAMS", [])
158
159
160 return [pname for (pname, _, _, _) in params]
161
164 """A simple serializable object.
165
166 This object serves as a parent class for OpCode without any custom
167 field handling.
168
169 """
170
171
172 __metaclass__ = _AutoOpParamSlots
173
179
181 """Generic serializer.
182
183 This method just returns the contents of the instance as a
184 dictionary.
185
186 @rtype: C{dict}
187 @return: the instance attributes and their values
188
189 """
190 state = {}
191 for name in self.GetAllSlots():
192 if hasattr(self, name):
193 state[name] = getattr(self, name)
194 return state
195
197 """Generic unserializer.
198
199 This method just restores from the serialized state the attributes
200 of the current instance.
201
202 @param state: the serialized opcode data
203 @type state: C{dict}
204
205 """
206 if not isinstance(state, dict):
207 raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
208 type(state))
209
210 for name in self.GetAllSlots():
211 if name not in state and hasattr(self, name):
212 delattr(self, name)
213
214 for name in state:
215 setattr(self, name, state[name])
216
217 @classmethod
219 """Compute list of all parameters for an opcode.
220
221 """
222 slots = []
223 for parent in cls.__mro__:
224 slots.extend(getattr(parent, "OP_PARAMS", []))
225 return slots
226
228 """Validate opcode parameters, optionally setting default values.
229
230 @type set_defaults: bool
231 @param set_defaults: whether to set default values
232
233 @rtype: NoneType
234 @return: L{None}, if the validation succeeds
235 @raise errors.OpPrereqError: when a parameter value doesn't match
236 requirements
237
238 """
239 for (attr_name, default, test, _) in self.GetAllParams():
240 assert callable(test)
241
242 if hasattr(self, attr_name):
243 attr_val = getattr(self, attr_name)
244 else:
245 attr_val = copy.deepcopy(default)
246
247 if test(attr_val):
248 if set_defaults:
249 setattr(self, attr_name, attr_val)
250 elif ht.TInt(attr_val) and test(float(attr_val)):
251 if set_defaults:
252 setattr(self, attr_name, float(attr_val))
253 else:
254 logging.error("OpCode %s, parameter %s, has invalid type %s/value"
255 " '%s' expecting type %s",
256 self.OP_ID, attr_name, type(attr_val), attr_val, test)
257
258 if attr_val is None:
259 logging.error("OpCode %s, parameter %s, has default value None which"
260 " is does not check against the parameter's type: this"
261 " means this parameter is required but no value was"
262 " given",
263 self.OP_ID, attr_name)
264
265 raise errors.OpPrereqError("Parameter '%s.%s' fails validation" %
266 (self.OP_ID, attr_name),
267 errors.ECODE_INVAL)
268
290
291
292 TNoRelativeJobDependencies = BuildJobDepCheck(False)
293