Package ganeti :: Module opcodes_base
[hide private]
[frames] | no frames]

Source Code for Module ganeti.opcodes_base

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc. 
  5  # All rights reserved. 
  6  # 
  7  # Redistribution and use in source and binary forms, with or without 
  8  # modification, are permitted provided that the following conditions are 
  9  # met: 
 10  # 
 11  # 1. Redistributions of source code must retain the above copyright notice, 
 12  # this list of conditions and the following disclaimer. 
 13  # 
 14  # 2. Redistributions in binary form must reproduce the above copyright 
 15  # notice, this list of conditions and the following disclaimer in the 
 16  # documentation and/or other materials provided with the distribution. 
 17  # 
 18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
 19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 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  # this are practically structures, so disable the message about too 
 42  # few public methods: 
 43  # pylint: disable=R0903 
 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  #: OP_ID conversion regular expression 
 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  #: Attribute name for dependencies 
 66  DEPEND_ATTR = "depends" 
 67   
 68  #: Attribute name for comment 
 69  COMMENT_ATTR = "comment" 
70 71 72 -def _NameComponents(name):
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 # Note: (?<=[a-z])(?=[A-Z]) would be ideal, since it wouldn't 83 # consume any input, and hence we would just have all the elements 84 # in the list, one by one; but it seems that split doesn't work on 85 # non-consuming input, hence we have to process the input string a 86 # bit 87 name = _OPID_RE.sub(r"\1,\2", name) 88 elems = name.split(",") 89 return elems
90
91 92 -def _NameToId(name):
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
105 106 -def NameToReasonSrc(name, prefix):
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
122 123 -class _AutoOpParamSlots(outils.AutoSlots):
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
152 - def _GetSlots(mcs, attrs):
153 """Build the slots out of OP_PARAMS. 154 155 """ 156 # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams 157 params = attrs.setdefault("OP_PARAMS", []) 158 159 # Use parameter names as slots 160 return [pname for (pname, _, _, _) in params]
161
162 163 -class BaseOpCode(outils.ValidatedSlots):
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 # pylint: disable=E1101 171 # as OP_ID is dynamically defined 172 __metaclass__ = _AutoOpParamSlots 173
174 - def __init__(self, **kwargs):
175 outils.ValidatedSlots.__init__(self, **kwargs) 176 for key, default, _, _ in self.__class__.GetAllParams(): 177 if not hasattr(self, key): 178 setattr(self, key, default)
179
180 - def __getstate__(self):
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
196 - def __setstate__(self, state):
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
218 - def GetAllParams(cls):
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
227 - def Validate(self, set_defaults): # pylint: disable=W0221
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
269 270 -def BuildJobDepCheck(relative):
271 """Builds check for job dependencies (L{DEPEND_ATTR}). 272 273 @type relative: bool 274 @param relative: Whether to accept relative job IDs (negative) 275 @rtype: callable 276 277 """ 278 if relative: 279 job_id = ht.TOr(ht.TJobId, ht.TRelativeJobId) 280 else: 281 job_id = ht.TJobId 282 283 job_dep = \ 284 ht.TAnd(ht.TOr(ht.TListOf(ht.TAny), ht.TTuple), 285 ht.TIsLength(2), 286 ht.TItems([job_id, 287 ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))])) 288 289 return ht.TMaybe(ht.TListOf(job_dep))
290 291 292 TNoRelativeJobDependencies = BuildJobDepCheck(False) 293