Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes_base.py @ 355d1f32

History | View | Annotate | Download (7.5 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 580b1fdd Jose A. Lopes
  def __getstate__(self):
164 580b1fdd Jose A. Lopes
    """Generic serializer.
165 580b1fdd Jose A. Lopes

166 580b1fdd Jose A. Lopes
    This method just returns the contents of the instance as a
167 580b1fdd Jose A. Lopes
    dictionary.
168 580b1fdd Jose A. Lopes

169 580b1fdd Jose A. Lopes
    @rtype:  C{dict}
170 580b1fdd Jose A. Lopes
    @return: the instance attributes and their values
171 580b1fdd Jose A. Lopes

172 580b1fdd Jose A. Lopes
    """
173 580b1fdd Jose A. Lopes
    state = {}
174 580b1fdd Jose A. Lopes
    for name in self.GetAllSlots():
175 580b1fdd Jose A. Lopes
      if hasattr(self, name):
176 580b1fdd Jose A. Lopes
        state[name] = getattr(self, name)
177 580b1fdd Jose A. Lopes
    return state
178 580b1fdd Jose A. Lopes
179 580b1fdd Jose A. Lopes
  def __setstate__(self, state):
180 580b1fdd Jose A. Lopes
    """Generic unserializer.
181 580b1fdd Jose A. Lopes

182 580b1fdd Jose A. Lopes
    This method just restores from the serialized state the attributes
183 580b1fdd Jose A. Lopes
    of the current instance.
184 580b1fdd Jose A. Lopes

185 580b1fdd Jose A. Lopes
    @param state: the serialized opcode data
186 580b1fdd Jose A. Lopes
    @type state:  C{dict}
187 580b1fdd Jose A. Lopes

188 580b1fdd Jose A. Lopes
    """
189 580b1fdd Jose A. Lopes
    if not isinstance(state, dict):
190 580b1fdd Jose A. Lopes
      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
191 580b1fdd Jose A. Lopes
                       type(state))
192 580b1fdd Jose A. Lopes
193 580b1fdd Jose A. Lopes
    for name in self.GetAllSlots():
194 580b1fdd Jose A. Lopes
      if name not in state and hasattr(self, name):
195 580b1fdd Jose A. Lopes
        delattr(self, name)
196 580b1fdd Jose A. Lopes
197 580b1fdd Jose A. Lopes
    for name in state:
198 580b1fdd Jose A. Lopes
      setattr(self, name, state[name])
199 580b1fdd Jose A. Lopes
200 580b1fdd Jose A. Lopes
  @classmethod
201 580b1fdd Jose A. Lopes
  def GetAllParams(cls):
202 580b1fdd Jose A. Lopes
    """Compute list of all parameters for an opcode.
203 580b1fdd Jose A. Lopes

204 580b1fdd Jose A. Lopes
    """
205 580b1fdd Jose A. Lopes
    slots = []
206 580b1fdd Jose A. Lopes
    for parent in cls.__mro__:
207 580b1fdd Jose A. Lopes
      slots.extend(getattr(parent, "OP_PARAMS", []))
208 580b1fdd Jose A. Lopes
    return slots
209 580b1fdd Jose A. Lopes
210 580b1fdd Jose A. Lopes
  def Validate(self, set_defaults): # pylint: disable=W0221
211 580b1fdd Jose A. Lopes
    """Validate opcode parameters, optionally setting default values.
212 580b1fdd Jose A. Lopes

213 580b1fdd Jose A. Lopes
    @type set_defaults: bool
214 580b1fdd Jose A. Lopes
    @param set_defaults: Whether to set default values
215 580b1fdd Jose A. Lopes
    @raise errors.OpPrereqError: When a parameter value doesn't match
216 580b1fdd Jose A. Lopes
                                 requirements
217 580b1fdd Jose A. Lopes

218 580b1fdd Jose A. Lopes
    """
219 580b1fdd Jose A. Lopes
    for (attr_name, default, test, _) in self.GetAllParams():
220 580b1fdd Jose A. Lopes
      assert callable(test)
221 580b1fdd Jose A. Lopes
222 580b1fdd Jose A. Lopes
      if hasattr(self, attr_name):
223 580b1fdd Jose A. Lopes
        attr_val = getattr(self, attr_name)
224 580b1fdd Jose A. Lopes
      else:
225 580b1fdd Jose A. Lopes
        attr_val = copy.deepcopy(default)
226 580b1fdd Jose A. Lopes
227 580b1fdd Jose A. Lopes
      if test(attr_val):
228 580b1fdd Jose A. Lopes
        if set_defaults:
229 580b1fdd Jose A. Lopes
          setattr(self, attr_name, attr_val)
230 580b1fdd Jose A. Lopes
      elif ht.TInt(attr_val) and test(float(attr_val)):
231 580b1fdd Jose A. Lopes
        if set_defaults:
232 580b1fdd Jose A. Lopes
          setattr(self, attr_name, float(attr_val))
233 580b1fdd Jose A. Lopes
      else:
234 580b1fdd Jose A. Lopes
        logging.error("OpCode %s, parameter %s, has invalid type %s/value"
235 580b1fdd Jose A. Lopes
                      " '%s' expecting type %s",
236 580b1fdd Jose A. Lopes
                      self.OP_ID, attr_name, type(attr_val), attr_val, test)
237 580b1fdd Jose A. Lopes
238 580b1fdd Jose A. Lopes
        if attr_val is None:
239 580b1fdd Jose A. Lopes
          logging.error("OpCode %s, parameter %s, has default value None which"
240 580b1fdd Jose A. Lopes
                        " is does not check against the parameter's type: this"
241 580b1fdd Jose A. Lopes
                        " means this parameter is required but no value was"
242 580b1fdd Jose A. Lopes
                        " given",
243 580b1fdd Jose A. Lopes
                        self.OP_ID, attr_name)
244 580b1fdd Jose A. Lopes
245 580b1fdd Jose A. Lopes
        raise errors.OpPrereqError("Parameter '%s.%s' fails validation" %
246 580b1fdd Jose A. Lopes
                                   (self.OP_ID, attr_name),
247 580b1fdd Jose A. Lopes
                                   errors.ECODE_INVAL)
248 580b1fdd Jose A. Lopes
249 580b1fdd Jose A. Lopes
250 580b1fdd Jose A. Lopes
def BuildJobDepCheck(relative):
251 580b1fdd Jose A. Lopes
  """Builds check for job dependencies (L{DEPEND_ATTR}).
252 580b1fdd Jose A. Lopes

253 580b1fdd Jose A. Lopes
  @type relative: bool
254 580b1fdd Jose A. Lopes
  @param relative: Whether to accept relative job IDs (negative)
255 580b1fdd Jose A. Lopes
  @rtype: callable
256 580b1fdd Jose A. Lopes

257 580b1fdd Jose A. Lopes
  """
258 580b1fdd Jose A. Lopes
  if relative:
259 580b1fdd Jose A. Lopes
    job_id = ht.TOr(ht.TJobId, ht.TRelativeJobId)
260 580b1fdd Jose A. Lopes
  else:
261 580b1fdd Jose A. Lopes
    job_id = ht.TJobId
262 580b1fdd Jose A. Lopes
263 580b1fdd Jose A. Lopes
  job_dep = \
264 580b1fdd Jose A. Lopes
    ht.TAnd(ht.TOr(ht.TListOf(ht.TAny), ht.TTuple),
265 580b1fdd Jose A. Lopes
            ht.TIsLength(2),
266 580b1fdd Jose A. Lopes
            ht.TItems([job_id,
267 580b1fdd Jose A. Lopes
                       ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))]))
268 580b1fdd Jose A. Lopes
269 580b1fdd Jose A. Lopes
  return ht.TMaybe(ht.TListOf(job_dep))
270 580b1fdd Jose A. Lopes
271 580b1fdd Jose A. Lopes
272 580b1fdd Jose A. Lopes
TNoRelativeJobDependencies = BuildJobDepCheck(False)