## root / lib / opcodes_base.py @ 178ad717

History | View | Annotate | Download (7.7 kB)

1 | 580b1fdd | Jose A. Lopes | ```
#
``` |
---|---|---|---|

2 | 580b1fdd | Jose A. Lopes | ```
#
``` |

3 | 580b1fdd | Jose A. Lopes | |

4 | 580b1fdd | Jose A. Lopes | ```
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
``` |

5 | 580b1fdd | Jose A. Lopes | ```
#
``` |

6 | 580b1fdd | Jose A. Lopes | ```
# This program is free software; you can redistribute it and/or modify
``` |

7 | 580b1fdd | Jose A. Lopes | ```
# it under the terms of the GNU General Public License as published by
``` |

8 | 580b1fdd | Jose A. Lopes | ```
# the Free Software Foundation; either version 2 of the License, or
``` |

9 | 580b1fdd | Jose A. Lopes | ```
# (at your option) any later version.
``` |

10 | 580b1fdd | Jose A. Lopes | ```
#
``` |

11 | 580b1fdd | Jose A. Lopes | ```
# This program is distributed in the hope that it will be useful, but
``` |

12 | 580b1fdd | Jose A. Lopes | ```
# WITHOUT ANY WARRANTY; without even the implied warranty of
``` |

13 | 580b1fdd | Jose A. Lopes | ```
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
``` |

14 | 580b1fdd | Jose A. Lopes | ```
# General Public License for more details.
``` |

15 | 580b1fdd | Jose A. Lopes | ```
#
``` |

16 | 580b1fdd | Jose A. Lopes | ```
# You should have received a copy of the GNU General Public License
``` |

17 | 580b1fdd | Jose A. Lopes | ```
# along with this program; if not, write to the Free Software
``` |

18 | 580b1fdd | Jose A. Lopes | ```
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
``` |

19 | 580b1fdd | Jose A. Lopes | ```
# 02110-1301, USA.
``` |

20 | 580b1fdd | Jose A. Lopes | |

21 | 580b1fdd | Jose A. Lopes | |

22 | 580b1fdd | Jose A. Lopes | ```
"""OpCodes base module
``` |

23 | 580b1fdd | Jose A. Lopes | ```
``` |

24 | 580b1fdd | Jose A. Lopes | ```
This module implements part of the data structures which define the
``` |

25 | 580b1fdd | Jose A. Lopes | ```
cluster operations - the so-called opcodes.
``` |

26 | 580b1fdd | Jose A. Lopes | ```
``` |

27 | 580b1fdd | Jose A. Lopes | ```
Every operation which modifies the cluster state is expressed via
``` |

28 | 580b1fdd | Jose A. Lopes | ```
opcodes.
``` |

29 | 580b1fdd | Jose A. Lopes | ```
``` |

30 | 580b1fdd | Jose A. Lopes | ```
"""
``` |

31 | 580b1fdd | Jose A. Lopes | |

32 | 580b1fdd | Jose A. Lopes | ```
# this are practically structures, so disable the message about too
``` |

33 | 580b1fdd | Jose A. Lopes | ```
# few public methods:
``` |

34 | 580b1fdd | Jose A. Lopes | ```
# pylint: disable=R0903
``` |

35 | 580b1fdd | Jose A. Lopes | |

36 | 580b1fdd | Jose A. Lopes | import copy |

37 | 580b1fdd | Jose A. Lopes | import logging |

38 | 580b1fdd | Jose A. Lopes | import re |

39 | 580b1fdd | Jose A. Lopes | |

40 | 580b1fdd | Jose A. Lopes | from ganeti import constants |

41 | 580b1fdd | Jose A. Lopes | from ganeti import errors |

42 | 580b1fdd | Jose A. Lopes | from ganeti import ht |

43 | 580b1fdd | Jose A. Lopes | from ganeti import outils |

44 | 580b1fdd | Jose A. Lopes | |

45 | 580b1fdd | Jose A. Lopes | |

46 | 580b1fdd | Jose A. Lopes | ```
#: OP_ID conversion regular expression
``` |

47 | 580b1fdd | Jose A. Lopes | ```
_OPID_RE = re.compile("([a-z])([A-Z])")
``` |

48 | 580b1fdd | Jose A. Lopes | |

49 | 580b1fdd | Jose A. Lopes | SUMMARY_PREFIX = { |

50 | 580b1fdd | Jose A. Lopes | "CLUSTER_": "C_", |

51 | 580b1fdd | Jose A. Lopes | "GROUP_": "G_", |

52 | 580b1fdd | Jose A. Lopes | "NODE_": "N_", |

53 | 580b1fdd | Jose A. Lopes | "INSTANCE_": "I_", |

54 | 580b1fdd | Jose A. Lopes | } |

55 | 580b1fdd | Jose A. Lopes | |

56 | 580b1fdd | Jose A. Lopes | ```
#: Attribute name for dependencies
``` |

57 | 580b1fdd | Jose A. Lopes | ```
DEPEND_ATTR = "depends"
``` |

58 | 580b1fdd | Jose A. Lopes | |

59 | 580b1fdd | Jose A. Lopes | ```
#: Attribute name for comment
``` |

60 | 580b1fdd | Jose A. Lopes | ```
COMMENT_ATTR = "comment"
``` |

61 | 580b1fdd | Jose A. Lopes | |

62 | 580b1fdd | Jose A. Lopes | |

63 | 580b1fdd | Jose A. Lopes | def _NameComponents(name): |

64 | 580b1fdd | Jose A. Lopes | ```
"""Split an opcode class name into its components
``` |

65 | 580b1fdd | Jose A. Lopes | ```
``` |

66 | 580b1fdd | Jose A. Lopes | ```
@type name: string
``` |

67 | 580b1fdd | Jose A. Lopes | ```
@param name: the class name, as OpXxxYyy
``` |

68 | 580b1fdd | Jose A. Lopes | ```
@rtype: array of strings
``` |

69 | 580b1fdd | Jose A. Lopes | ```
@return: the components of the name
``` |

70 | 580b1fdd | Jose A. Lopes | ```
``` |

71 | 580b1fdd | Jose A. Lopes | ```
"""
``` |

72 | 580b1fdd | Jose A. Lopes | assert name.startswith("Op") |

73 | 580b1fdd | Jose A. Lopes | ```
# Note: (?<=[a-z])(?=[A-Z]) would be ideal, since it wouldn't
``` |

74 | 580b1fdd | Jose A. Lopes | ```
# consume any input, and hence we would just have all the elements
``` |

75 | 580b1fdd | Jose A. Lopes | ```
# in the list, one by one; but it seems that split doesn't work on
``` |

76 | 580b1fdd | Jose A. Lopes | ```
# non-consuming input, hence we have to process the input string a
``` |

77 | 580b1fdd | Jose A. Lopes | ```
# bit
``` |

78 | 580b1fdd | Jose A. Lopes | ```
name = _OPID_RE.sub(r"\1,\2", name)
``` |

79 | 580b1fdd | Jose A. Lopes | ```
elems = name.split(",")
``` |

80 | 580b1fdd | Jose A. Lopes | ```
return elems
``` |

81 | 580b1fdd | Jose A. Lopes | |

82 | 580b1fdd | Jose A. Lopes | |

83 | 580b1fdd | Jose A. Lopes | def _NameToId(name): |

84 | 580b1fdd | Jose A. Lopes | ```
"""Convert an opcode class name to an OP_ID.
``` |

85 | 580b1fdd | Jose A. Lopes | ```
``` |

86 | 580b1fdd | Jose A. Lopes | ```
@type name: string
``` |

87 | 580b1fdd | Jose A. Lopes | ```
@param name: the class name, as OpXxxYyy
``` |

88 | 580b1fdd | Jose A. Lopes | ```
@rtype: string
``` |

89 | 580b1fdd | Jose A. Lopes | ```
@return: the name in the OP_XXXX_YYYY format
``` |

90 | 580b1fdd | Jose A. Lopes | ```
``` |

91 | 580b1fdd | Jose A. Lopes | ```
"""
``` |

92 | 580b1fdd | Jose A. Lopes | if not name.startswith("Op"): |

93 | 580b1fdd | Jose A. Lopes | return None |

94 | 580b1fdd | Jose A. Lopes | return "_".join(n.upper() for n in _NameComponents(name)) |

95 | 580b1fdd | Jose A. Lopes | |

96 | 580b1fdd | Jose A. Lopes | |

97 | 580b1fdd | Jose A. Lopes | def NameToReasonSrc(name): |

98 | 580b1fdd | Jose A. Lopes | ```
"""Convert an opcode class name to a source string for the reason trail
``` |

99 | 580b1fdd | Jose A. Lopes | ```
``` |

100 | 580b1fdd | Jose A. Lopes | ```
@type name: string
``` |

101 | 580b1fdd | Jose A. Lopes | ```
@param name: the class name, as OpXxxYyy
``` |

102 | 580b1fdd | Jose A. Lopes | ```
@rtype: string
``` |

103 | 580b1fdd | Jose A. Lopes | ```
@return: the name in the OP_XXXX_YYYY format
``` |

104 | 580b1fdd | Jose A. Lopes | ```
``` |

105 | 580b1fdd | Jose A. Lopes | ```
"""
``` |

106 | 580b1fdd | Jose A. Lopes | if not name.startswith("Op"): |

107 | 580b1fdd | Jose A. Lopes | return None |

108 | 580b1fdd | Jose A. Lopes | return "%s:%s" % (constants.OPCODE_REASON_SRC_OPCODE, |

109 | 580b1fdd | Jose A. Lopes | "_".join(n.lower() for n in _NameComponents(name))) |

110 | 580b1fdd | Jose A. Lopes | |

111 | 580b1fdd | Jose A. Lopes | |

112 | 580b1fdd | Jose A. Lopes | class _AutoOpParamSlots(outils.AutoSlots): |

113 | 580b1fdd | Jose A. Lopes | ```
"""Meta class for opcode definitions.
``` |

114 | 580b1fdd | Jose A. Lopes | ```
``` |

115 | 580b1fdd | Jose A. Lopes | ```
"""
``` |

116 | 580b1fdd | Jose A. Lopes | def __new__(mcs, name, bases, attrs): |

117 | 580b1fdd | Jose A. Lopes | ```
"""Called when a class should be created.
``` |

118 | 580b1fdd | Jose A. Lopes | ```
``` |

119 | 580b1fdd | Jose A. Lopes | ```
@param mcs: The meta class
``` |

120 | 580b1fdd | Jose A. Lopes | ```
@param name: Name of created class
``` |

121 | 580b1fdd | Jose A. Lopes | ```
@param bases: Base classes
``` |

122 | 580b1fdd | Jose A. Lopes | ```
@type attrs: dict
``` |

123 | 580b1fdd | Jose A. Lopes | ```
@param attrs: Class attributes
``` |

124 | 580b1fdd | Jose A. Lopes | ```
``` |

125 | 580b1fdd | Jose A. Lopes | ```
"""
``` |

126 | 580b1fdd | Jose A. Lopes | assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name |

127 | 580b1fdd | Jose A. Lopes | |

128 | 580b1fdd | Jose A. Lopes | slots = mcs._GetSlots(attrs) |

129 | 580b1fdd | Jose A. Lopes | assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \ |

130 | 580b1fdd | Jose A. Lopes | ```
"Class '%s' uses unknown field in OP_DSC_FIELD" % name
``` |

131 | 580b1fdd | Jose A. Lopes | assert ("OP_DSC_FORMATTER" not in attrs or |

132 | 580b1fdd | Jose A. Lopes | callable(attrs["OP_DSC_FORMATTER"])), \ |

133 | 580b1fdd | Jose A. Lopes | ```
("Class '%s' uses non-callable in OP_DSC_FORMATTER (%s)" %
``` |

134 | 580b1fdd | Jose A. Lopes | (name, type(attrs["OP_DSC_FORMATTER"]))) |

135 | 580b1fdd | Jose A. Lopes | |

136 | 580b1fdd | Jose A. Lopes | ```
attrs["OP_ID"] = _NameToId(name)
``` |

137 | 580b1fdd | Jose A. Lopes | |

138 | 580b1fdd | Jose A. Lopes | ```
return outils.AutoSlots.__new__(mcs, name, bases, attrs)
``` |

139 | 580b1fdd | Jose A. Lopes | |

140 | 580b1fdd | Jose A. Lopes | ```
@classmethod
``` |

141 | 580b1fdd | Jose A. Lopes | def _GetSlots(mcs, attrs): |

142 | 580b1fdd | Jose A. Lopes | ```
"""Build the slots out of OP_PARAMS.
``` |

143 | 580b1fdd | Jose A. Lopes | ```
``` |

144 | 580b1fdd | Jose A. Lopes | ```
"""
``` |

145 | 580b1fdd | Jose A. Lopes | ```
# Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams
``` |

146 | 580b1fdd | Jose A. Lopes | ```
params = attrs.setdefault("OP_PARAMS", [])
``` |

147 | 580b1fdd | Jose A. Lopes | |

148 | 580b1fdd | Jose A. Lopes | ```
# Use parameter names as slots
``` |

149 | 580b1fdd | Jose A. Lopes | return [pname for (pname, _, _, _) in params] |

150 | 580b1fdd | Jose A. Lopes | |

151 | 580b1fdd | Jose A. Lopes | |

152 | 580b1fdd | Jose A. Lopes | class BaseOpCode(outils.ValidatedSlots): |

153 | 580b1fdd | Jose A. Lopes | ```
"""A simple serializable object.
``` |

154 | 580b1fdd | Jose A. Lopes | ```
``` |

155 | 580b1fdd | Jose A. Lopes | ```
This object serves as a parent class for OpCode without any custom
``` |

156 | 580b1fdd | Jose A. Lopes | ```
field handling.
``` |

157 | 580b1fdd | Jose A. Lopes | ```
``` |

158 | 580b1fdd | Jose A. Lopes | ```
"""
``` |

159 | 580b1fdd | Jose A. Lopes | ```
# pylint: disable=E1101
``` |

160 | 580b1fdd | Jose A. Lopes | ```
# as OP_ID is dynamically defined
``` |

161 | 580b1fdd | Jose A. Lopes | __metaclass__ = _AutoOpParamSlots |

162 | 580b1fdd | Jose A. Lopes | |

163 | a9e1819b | Klaus Aehlig | def __init__(self, **kwargs): |

164 | a9e1819b | Klaus Aehlig | ```
outils.ValidatedSlots.__init__(self, **kwargs)
``` |

165 | a9e1819b | Klaus Aehlig | for key, default, _, _ in self.__class__.GetAllParams(): |

166 | a9e1819b | Klaus Aehlig | if not hasattr(self, key): |

167 | a9e1819b | Klaus Aehlig | setattr(self, key, default) |

168 | a9e1819b | Klaus Aehlig | |

169 | 580b1fdd | Jose A. Lopes | def __getstate__(self): |

170 | 580b1fdd | Jose A. Lopes | ```
"""Generic serializer.
``` |

171 | 580b1fdd | Jose A. Lopes | ```
``` |

172 | 580b1fdd | Jose A. Lopes | ```
This method just returns the contents of the instance as a
``` |

173 | 580b1fdd | Jose A. Lopes | ```
dictionary.
``` |

174 | 580b1fdd | Jose A. Lopes | ```
``` |

175 | 580b1fdd | Jose A. Lopes | ```
@rtype: C{dict}
``` |

176 | 580b1fdd | Jose A. Lopes | ```
@return: the instance attributes and their values
``` |

177 | 580b1fdd | Jose A. Lopes | ```
``` |

178 | 580b1fdd | Jose A. Lopes | ```
"""
``` |

179 | 580b1fdd | Jose A. Lopes | state = {} |

180 | 580b1fdd | Jose A. Lopes | for name in self.GetAllSlots(): |

181 | 580b1fdd | Jose A. Lopes | if hasattr(self, name): |

182 | 580b1fdd | Jose A. Lopes | state[name] = getattr(self, name) |

183 | 580b1fdd | Jose A. Lopes | ```
return state
``` |

184 | 580b1fdd | Jose A. Lopes | |

185 | 580b1fdd | Jose A. Lopes | def __setstate__(self, state): |

186 | 580b1fdd | Jose A. Lopes | ```
"""Generic unserializer.
``` |

187 | 580b1fdd | Jose A. Lopes | ```
``` |

188 | 580b1fdd | Jose A. Lopes | ```
This method just restores from the serialized state the attributes
``` |

189 | 580b1fdd | Jose A. Lopes | ```
of the current instance.
``` |

190 | 580b1fdd | Jose A. Lopes | ```
``` |

191 | 580b1fdd | Jose A. Lopes | ```
@param state: the serialized opcode data
``` |

192 | 580b1fdd | Jose A. Lopes | ```
@type state: C{dict}
``` |

193 | 580b1fdd | Jose A. Lopes | ```
``` |

194 | 580b1fdd | Jose A. Lopes | ```
"""
``` |

195 | 580b1fdd | Jose A. Lopes | if not isinstance(state, dict): |

196 | 580b1fdd | Jose A. Lopes | raise ValueError("Invalid data to __setstate__: expected dict, got %s" % |

197 | 580b1fdd | Jose A. Lopes | ```
type(state))
``` |

198 | 580b1fdd | Jose A. Lopes | |

199 | 580b1fdd | Jose A. Lopes | for name in self.GetAllSlots(): |

200 | 580b1fdd | Jose A. Lopes | if name not in state and hasattr(self, name): |

201 | 580b1fdd | Jose A. Lopes | delattr(self, name) |

202 | 580b1fdd | Jose A. Lopes | |

203 | 580b1fdd | Jose A. Lopes | for name in state: |

204 | 580b1fdd | Jose A. Lopes | setattr(self, name, state[name]) |

205 | 580b1fdd | Jose A. Lopes | |

206 | 580b1fdd | Jose A. Lopes | ```
@classmethod
``` |

207 | 580b1fdd | Jose A. Lopes | def GetAllParams(cls): |

208 | 580b1fdd | Jose A. Lopes | ```
"""Compute list of all parameters for an opcode.
``` |

209 | 580b1fdd | Jose A. Lopes | ```
``` |

210 | 580b1fdd | Jose A. Lopes | ```
"""
``` |

211 | 580b1fdd | Jose A. Lopes | slots = [] |

212 | 580b1fdd | Jose A. Lopes | for parent in cls.__mro__: |

213 | 580b1fdd | Jose A. Lopes | slots.extend(getattr(parent, "OP_PARAMS", [])) |

214 | 580b1fdd | Jose A. Lopes | ```
return slots
``` |

215 | 580b1fdd | Jose A. Lopes | |

216 | 580b1fdd | Jose A. Lopes | def Validate(self, set_defaults): # pylint: disable=W0221 |

217 | 580b1fdd | Jose A. Lopes | ```
"""Validate opcode parameters, optionally setting default values.
``` |

218 | 580b1fdd | Jose A. Lopes | ```
``` |

219 | 580b1fdd | Jose A. Lopes | ```
@type set_defaults: bool
``` |

220 | 580b1fdd | Jose A. Lopes | ```
@param set_defaults: Whether to set default values
``` |

221 | 580b1fdd | Jose A. Lopes | ```
@raise errors.OpPrereqError: When a parameter value doesn't match
``` |

222 | 580b1fdd | Jose A. Lopes | ```
requirements
``` |

223 | 580b1fdd | Jose A. Lopes | ```
``` |

224 | 580b1fdd | Jose A. Lopes | ```
"""
``` |

225 | 580b1fdd | Jose A. Lopes | for (attr_name, default, test, _) in self.GetAllParams(): |

226 | 580b1fdd | Jose A. Lopes | assert callable(test) |

227 | 580b1fdd | Jose A. Lopes | |

228 | 580b1fdd | Jose A. Lopes | if hasattr(self, attr_name): |

229 | 580b1fdd | Jose A. Lopes | attr_val = getattr(self, attr_name) |

230 | 580b1fdd | Jose A. Lopes | ```
else:
``` |

231 | 580b1fdd | Jose A. Lopes | attr_val = copy.deepcopy(default) |

232 | 580b1fdd | Jose A. Lopes | |

233 | 580b1fdd | Jose A. Lopes | ```
if test(attr_val):
``` |

234 | 580b1fdd | Jose A. Lopes | ```
if set_defaults:
``` |

235 | 580b1fdd | Jose A. Lopes | setattr(self, attr_name, attr_val) |

236 | 580b1fdd | Jose A. Lopes | elif ht.TInt(attr_val) and test(float(attr_val)): |

237 | 580b1fdd | Jose A. Lopes | ```
if set_defaults:
``` |

238 | 580b1fdd | Jose A. Lopes | setattr(self, attr_name, float(attr_val)) |

239 | 580b1fdd | Jose A. Lopes | ```
else:
``` |

240 | 580b1fdd | Jose A. Lopes | ```
logging.error("OpCode %s, parameter %s, has invalid type %s/value"
``` |

241 | 580b1fdd | Jose A. Lopes | ```
" '%s' expecting type %s",
``` |

242 | 580b1fdd | Jose A. Lopes | self.OP_ID, attr_name, type(attr_val), attr_val, test) |

243 | 580b1fdd | Jose A. Lopes | |

244 | 580b1fdd | Jose A. Lopes | if attr_val is None: |

245 | 580b1fdd | Jose A. Lopes | ```
logging.error("OpCode %s, parameter %s, has default value None which"
``` |

246 | 580b1fdd | Jose A. Lopes | ```
" is does not check against the parameter's type: this"
``` |

247 | 580b1fdd | Jose A. Lopes | ```
" means this parameter is required but no value was"
``` |

248 | 580b1fdd | Jose A. Lopes | ```
" given",
``` |

249 | 580b1fdd | Jose A. Lopes | ```
self.OP_ID, attr_name)
``` |

250 | 580b1fdd | Jose A. Lopes | |

251 | 580b1fdd | Jose A. Lopes | raise errors.OpPrereqError("Parameter '%s.%s' fails validation" % |

252 | 580b1fdd | Jose A. Lopes | ```
(self.OP_ID, attr_name),
``` |

253 | 580b1fdd | Jose A. Lopes | errors.ECODE_INVAL) |

254 | 580b1fdd | Jose A. Lopes | |

255 | 580b1fdd | Jose A. Lopes | |

256 | 580b1fdd | Jose A. Lopes | def BuildJobDepCheck(relative): |

257 | 580b1fdd | Jose A. Lopes | ```
"""Builds check for job dependencies (L{DEPEND_ATTR}).
``` |

258 | 580b1fdd | Jose A. Lopes | ```
``` |

259 | 580b1fdd | Jose A. Lopes | ```
@type relative: bool
``` |

260 | 580b1fdd | Jose A. Lopes | ```
@param relative: Whether to accept relative job IDs (negative)
``` |

261 | 580b1fdd | Jose A. Lopes | ```
@rtype: callable
``` |

262 | 580b1fdd | Jose A. Lopes | ```
``` |

263 | 580b1fdd | Jose A. Lopes | ```
"""
``` |

264 | 580b1fdd | Jose A. Lopes | ```
if relative:
``` |

265 | 580b1fdd | Jose A. Lopes | job_id = ht.TOr(ht.TJobId, ht.TRelativeJobId) |

266 | 580b1fdd | Jose A. Lopes | ```
else:
``` |

267 | 580b1fdd | Jose A. Lopes | job_id = ht.TJobId |

268 | 580b1fdd | Jose A. Lopes | |

269 | 580b1fdd | Jose A. Lopes | job_dep = \ |

270 | 580b1fdd | Jose A. Lopes | ht.TAnd(ht.TOr(ht.TListOf(ht.TAny), ht.TTuple), |

271 | 580b1fdd | Jose A. Lopes | ```
ht.TIsLength(2),
``` |

272 | 580b1fdd | Jose A. Lopes | ht.TItems([job_id, |

273 | 580b1fdd | Jose A. Lopes | ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))])) |

274 | 580b1fdd | Jose A. Lopes | |

275 | 580b1fdd | Jose A. Lopes | ```
return ht.TMaybe(ht.TListOf(job_dep))
``` |

276 | 580b1fdd | Jose A. Lopes | |

277 | 580b1fdd | Jose A. Lopes | |

278 | 580b1fdd | Jose A. Lopes | `TNoRelativeJobDependencies = BuildJobDepCheck(False)` |