Statistics
| Branch: | Tag: | Revision:

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)