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):
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 @rtype: string 112 @return: the name in the OP_XXXX_YYYY format 113 114 """ 115 if not name.startswith("Op"): 116 return None 117 return "%s:%s" % (constants.OPCODE_REASON_SRC_OPCODE, 118 "_".join(n.lower() for n in _NameComponents(name)))
119
120 121 -class _AutoOpParamSlots(outils.AutoSlots):
122 """Meta class for opcode definitions. 123 124 """
125 - def __new__(mcs, name, bases, attrs):
126 """Called when a class should be created. 127 128 @param mcs: The meta class 129 @param name: Name of created class 130 @param bases: Base classes 131 @type attrs: dict 132 @param attrs: Class attributes 133 134 """ 135 assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name 136 137 slots = mcs._GetSlots(attrs) 138 assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \ 139 "Class '%s' uses unknown field in OP_DSC_FIELD" % name 140 assert ("OP_DSC_FORMATTER" not in attrs or 141 callable(attrs["OP_DSC_FORMATTER"])), \ 142 ("Class '%s' uses non-callable in OP_DSC_FORMATTER (%s)" % 143 (name, type(attrs["OP_DSC_FORMATTER"]))) 144 145 attrs["OP_ID"] = _NameToId(name) 146 147 return outils.AutoSlots.__new__(mcs, name, bases, attrs)
148 149 @classmethod
150 - def _GetSlots(mcs, attrs):
151 """Build the slots out of OP_PARAMS. 152 153 """ 154 # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams 155 params = attrs.setdefault("OP_PARAMS", []) 156 157 # Use parameter names as slots 158 return [pname for (pname, _, _, _) in params]
159
160 161 -class BaseOpCode(outils.ValidatedSlots):
162 """A simple serializable object. 163 164 This object serves as a parent class for OpCode without any custom 165 field handling. 166 167 """ 168 # pylint: disable=E1101 169 # as OP_ID is dynamically defined 170 __metaclass__ = _AutoOpParamSlots 171
172 - def __getstate__(self):
173 """Generic serializer. 174 175 This method just returns the contents of the instance as a 176 dictionary. 177 178 @rtype: C{dict} 179 @return: the instance attributes and their values 180 181 """ 182 state = {} 183 for name in self.GetAllSlots(): 184 if hasattr(self, name): 185 state[name] = getattr(self, name) 186 return state
187
188 - def __setstate__(self, state):
189 """Generic unserializer. 190 191 This method just restores from the serialized state the attributes 192 of the current instance. 193 194 @param state: the serialized opcode data 195 @type state: C{dict} 196 197 """ 198 if not isinstance(state, dict): 199 raise ValueError("Invalid data to __setstate__: expected dict, got %s" % 200 type(state)) 201 202 for name in self.GetAllSlots(): 203 if name not in state and hasattr(self, name): 204 delattr(self, name) 205 206 for name in state: 207 setattr(self, name, state[name])
208 209 @classmethod
210 - def GetAllParams(cls):
211 """Compute list of all parameters for an opcode. 212 213 """ 214 slots = [] 215 for parent in cls.__mro__: 216 slots.extend(getattr(parent, "OP_PARAMS", [])) 217 return slots
218
219 - def Validate(self, set_defaults): # pylint: disable=W0221
220 """Validate opcode parameters, optionally setting default values. 221 222 @type set_defaults: bool 223 @param set_defaults: Whether to set default values 224 @raise errors.OpPrereqError: When a parameter value doesn't match 225 requirements 226 227 """ 228 for (attr_name, default, test, _) in self.GetAllParams(): 229 assert callable(test) 230 231 if hasattr(self, attr_name): 232 attr_val = getattr(self, attr_name) 233 else: 234 attr_val = copy.deepcopy(default) 235 236 if test(attr_val): 237 if set_defaults: 238 setattr(self, attr_name, attr_val) 239 elif ht.TInt(attr_val) and test(float(attr_val)): 240 if set_defaults: 241 setattr(self, attr_name, float(attr_val)) 242 else: 243 logging.error("OpCode %s, parameter %s, has invalid type %s/value" 244 " '%s' expecting type %s", 245 self.OP_ID, attr_name, type(attr_val), attr_val, test) 246 247 if attr_val is None: 248 logging.error("OpCode %s, parameter %s, has default value None which" 249 " is does not check against the parameter's type: this" 250 " means this parameter is required but no value was" 251 " given", 252 self.OP_ID, attr_name) 253 254 raise errors.OpPrereqError("Parameter '%s.%s' fails validation" % 255 (self.OP_ID, attr_name), 256 errors.ECODE_INVAL)
257
258 259 -def BuildJobDepCheck(relative):
260 """Builds check for job dependencies (L{DEPEND_ATTR}). 261 262 @type relative: bool 263 @param relative: Whether to accept relative job IDs (negative) 264 @rtype: callable 265 266 """ 267 if relative: 268 job_id = ht.TOr(ht.TJobId, ht.TRelativeJobId) 269 else: 270 job_id = ht.TJobId 271 272 job_dep = \ 273 ht.TAnd(ht.TOr(ht.TListOf(ht.TAny), ht.TTuple), 274 ht.TIsLength(2), 275 ht.TItems([job_id, 276 ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))])) 277 278 return ht.TMaybe(ht.TListOf(job_dep))
279 280 281 TNoRelativeJobDependencies = BuildJobDepCheck(False) 282