Statistics
| Branch: | Tag: | Revision:

root / lib / opcodes_base.py @ be6cdf67

History | View | Annotate | Download (7.8 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 be6cdf67 Michele Tartara
def NameToReasonSrc(name, prefix):
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 be6cdf67 Michele Tartara
  @type prefix: string
103 be6cdf67 Michele Tartara
  @param prefix: the prefix that will be prepended to the opcode name
104 580b1fdd Jose A. Lopes
  @rtype: string
105 580b1fdd Jose A. Lopes
  @return: the name in the OP_XXXX_YYYY format
106 580b1fdd Jose A. Lopes

107 580b1fdd Jose A. Lopes
  """
108 580b1fdd Jose A. Lopes
  if not name.startswith("Op"):
109 580b1fdd Jose A. Lopes
    return None
110 be6cdf67 Michele Tartara
  return "%s:%s" % (prefix,
111 580b1fdd Jose A. Lopes
                    "_".join(n.lower() for n in _NameComponents(name)))
112 580b1fdd Jose A. Lopes
113 580b1fdd Jose A. Lopes
114 580b1fdd Jose A. Lopes
class _AutoOpParamSlots(outils.AutoSlots):
115 580b1fdd Jose A. Lopes
  """Meta class for opcode definitions.
116 580b1fdd Jose A. Lopes

117 580b1fdd Jose A. Lopes
  """
118 580b1fdd Jose A. Lopes
  def __new__(mcs, name, bases, attrs):
119 580b1fdd Jose A. Lopes
    """Called when a class should be created.
120 580b1fdd Jose A. Lopes

121 580b1fdd Jose A. Lopes
    @param mcs: The meta class
122 580b1fdd Jose A. Lopes
    @param name: Name of created class
123 580b1fdd Jose A. Lopes
    @param bases: Base classes
124 580b1fdd Jose A. Lopes
    @type attrs: dict
125 580b1fdd Jose A. Lopes
    @param attrs: Class attributes
126 580b1fdd Jose A. Lopes

127 580b1fdd Jose A. Lopes
    """
128 580b1fdd Jose A. Lopes
    assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name
129 580b1fdd Jose A. Lopes
130 580b1fdd Jose A. Lopes
    slots = mcs._GetSlots(attrs)
131 580b1fdd Jose A. Lopes
    assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
132 580b1fdd Jose A. Lopes
      "Class '%s' uses unknown field in OP_DSC_FIELD" % name
133 580b1fdd Jose A. Lopes
    assert ("OP_DSC_FORMATTER" not in attrs or
134 580b1fdd Jose A. Lopes
            callable(attrs["OP_DSC_FORMATTER"])), \
135 580b1fdd Jose A. Lopes
      ("Class '%s' uses non-callable in OP_DSC_FORMATTER (%s)" %
136 580b1fdd Jose A. Lopes
       (name, type(attrs["OP_DSC_FORMATTER"])))
137 580b1fdd Jose A. Lopes
138 580b1fdd Jose A. Lopes
    attrs["OP_ID"] = _NameToId(name)
139 580b1fdd Jose A. Lopes
140 580b1fdd Jose A. Lopes
    return outils.AutoSlots.__new__(mcs, name, bases, attrs)
141 580b1fdd Jose A. Lopes
142 580b1fdd Jose A. Lopes
  @classmethod
143 580b1fdd Jose A. Lopes
  def _GetSlots(mcs, attrs):
144 580b1fdd Jose A. Lopes
    """Build the slots out of OP_PARAMS.
145 580b1fdd Jose A. Lopes

146 580b1fdd Jose A. Lopes
    """
147 580b1fdd Jose A. Lopes
    # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams
148 580b1fdd Jose A. Lopes
    params = attrs.setdefault("OP_PARAMS", [])
149 580b1fdd Jose A. Lopes
150 580b1fdd Jose A. Lopes
    # Use parameter names as slots
151 580b1fdd Jose A. Lopes
    return [pname for (pname, _, _, _) in params]
152 580b1fdd Jose A. Lopes
153 580b1fdd Jose A. Lopes
154 580b1fdd Jose A. Lopes
class BaseOpCode(outils.ValidatedSlots):
155 580b1fdd Jose A. Lopes
  """A simple serializable object.
156 580b1fdd Jose A. Lopes

157 580b1fdd Jose A. Lopes
  This object serves as a parent class for OpCode without any custom
158 580b1fdd Jose A. Lopes
  field handling.
159 580b1fdd Jose A. Lopes

160 580b1fdd Jose A. Lopes
  """
161 580b1fdd Jose A. Lopes
  # pylint: disable=E1101
162 580b1fdd Jose A. Lopes
  # as OP_ID is dynamically defined
163 580b1fdd Jose A. Lopes
  __metaclass__ = _AutoOpParamSlots
164 580b1fdd Jose A. Lopes
165 a9e1819b Klaus Aehlig
  def __init__(self, **kwargs):
166 a9e1819b Klaus Aehlig
    outils.ValidatedSlots.__init__(self, **kwargs)
167 a9e1819b Klaus Aehlig
    for key, default, _, _ in self.__class__.GetAllParams():
168 a9e1819b Klaus Aehlig
      if not hasattr(self, key):
169 a9e1819b Klaus Aehlig
        setattr(self, key, default)
170 a9e1819b Klaus Aehlig
171 580b1fdd Jose A. Lopes
  def __getstate__(self):
172 580b1fdd Jose A. Lopes
    """Generic serializer.
173 580b1fdd Jose A. Lopes

174 580b1fdd Jose A. Lopes
    This method just returns the contents of the instance as a
175 580b1fdd Jose A. Lopes
    dictionary.
176 580b1fdd Jose A. Lopes

177 580b1fdd Jose A. Lopes
    @rtype:  C{dict}
178 580b1fdd Jose A. Lopes
    @return: the instance attributes and their values
179 580b1fdd Jose A. Lopes

180 580b1fdd Jose A. Lopes
    """
181 580b1fdd Jose A. Lopes
    state = {}
182 580b1fdd Jose A. Lopes
    for name in self.GetAllSlots():
183 580b1fdd Jose A. Lopes
      if hasattr(self, name):
184 580b1fdd Jose A. Lopes
        state[name] = getattr(self, name)
185 580b1fdd Jose A. Lopes
    return state
186 580b1fdd Jose A. Lopes
187 580b1fdd Jose A. Lopes
  def __setstate__(self, state):
188 580b1fdd Jose A. Lopes
    """Generic unserializer.
189 580b1fdd Jose A. Lopes

190 580b1fdd Jose A. Lopes
    This method just restores from the serialized state the attributes
191 580b1fdd Jose A. Lopes
    of the current instance.
192 580b1fdd Jose A. Lopes

193 580b1fdd Jose A. Lopes
    @param state: the serialized opcode data
194 580b1fdd Jose A. Lopes
    @type state:  C{dict}
195 580b1fdd Jose A. Lopes

196 580b1fdd Jose A. Lopes
    """
197 580b1fdd Jose A. Lopes
    if not isinstance(state, dict):
198 580b1fdd Jose A. Lopes
      raise ValueError("Invalid data to __setstate__: expected dict, got %s" %
199 580b1fdd Jose A. Lopes
                       type(state))
200 580b1fdd Jose A. Lopes
201 580b1fdd Jose A. Lopes
    for name in self.GetAllSlots():
202 580b1fdd Jose A. Lopes
      if name not in state and hasattr(self, name):
203 580b1fdd Jose A. Lopes
        delattr(self, name)
204 580b1fdd Jose A. Lopes
205 580b1fdd Jose A. Lopes
    for name in state:
206 580b1fdd Jose A. Lopes
      setattr(self, name, state[name])
207 580b1fdd Jose A. Lopes
208 580b1fdd Jose A. Lopes
  @classmethod
209 580b1fdd Jose A. Lopes
  def GetAllParams(cls):
210 580b1fdd Jose A. Lopes
    """Compute list of all parameters for an opcode.
211 580b1fdd Jose A. Lopes

212 580b1fdd Jose A. Lopes
    """
213 580b1fdd Jose A. Lopes
    slots = []
214 580b1fdd Jose A. Lopes
    for parent in cls.__mro__:
215 580b1fdd Jose A. Lopes
      slots.extend(getattr(parent, "OP_PARAMS", []))
216 580b1fdd Jose A. Lopes
    return slots
217 580b1fdd Jose A. Lopes
218 580b1fdd Jose A. Lopes
  def Validate(self, set_defaults): # pylint: disable=W0221
219 580b1fdd Jose A. Lopes
    """Validate opcode parameters, optionally setting default values.
220 580b1fdd Jose A. Lopes

221 580b1fdd Jose A. Lopes
    @type set_defaults: bool
222 580b1fdd Jose A. Lopes
    @param set_defaults: Whether to set default values
223 580b1fdd Jose A. Lopes
    @raise errors.OpPrereqError: When a parameter value doesn't match
224 580b1fdd Jose A. Lopes
                                 requirements
225 580b1fdd Jose A. Lopes

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

261 580b1fdd Jose A. Lopes
  @type relative: bool
262 580b1fdd Jose A. Lopes
  @param relative: Whether to accept relative job IDs (negative)
263 580b1fdd Jose A. Lopes
  @rtype: callable
264 580b1fdd Jose A. Lopes

265 580b1fdd Jose A. Lopes
  """
266 580b1fdd Jose A. Lopes
  if relative:
267 580b1fdd Jose A. Lopes
    job_id = ht.TOr(ht.TJobId, ht.TRelativeJobId)
268 580b1fdd Jose A. Lopes
  else:
269 580b1fdd Jose A. Lopes
    job_id = ht.TJobId
270 580b1fdd Jose A. Lopes
271 580b1fdd Jose A. Lopes
  job_dep = \
272 580b1fdd Jose A. Lopes
    ht.TAnd(ht.TOr(ht.TListOf(ht.TAny), ht.TTuple),
273 580b1fdd Jose A. Lopes
            ht.TIsLength(2),
274 580b1fdd Jose A. Lopes
            ht.TItems([job_id,
275 580b1fdd Jose A. Lopes
                       ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))]))
276 580b1fdd Jose A. Lopes
277 580b1fdd Jose A. Lopes
  return ht.TMaybe(ht.TListOf(job_dep))
278 580b1fdd Jose A. Lopes
279 580b1fdd Jose A. Lopes
280 580b1fdd Jose A. Lopes
TNoRelativeJobDependencies = BuildJobDepCheck(False)