Package ganeti :: Package rapi :: Module baserlib
[hide private]
[frames] | no frames]

Source Code for Module ganeti.rapi.baserlib

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2008 Google Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, but 
 12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 14  # General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the Free Software 
 18  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
 19  # 02110-1301, USA. 
 20   
 21   
 22  """Remote API base resources library. 
 23   
 24  """ 
 25   
 26  # pylint: disable-msg=C0103 
 27   
 28  # C0103: Invalid name, since the R_* names are not conforming 
 29   
 30  import logging 
 31   
 32  from ganeti import luxi 
 33  from ganeti import rapi 
 34  from ganeti import http 
 35  from ganeti import ssconf 
 36  from ganeti import constants 
 37  from ganeti import opcodes 
 38  from ganeti import errors 
 39   
 40   
 41  # Dummy value to detect unchanged parameters 
 42  _DEFAULT = object() 
 43   
 44   
45 -def BuildUriList(ids, uri_format, uri_fields=("name", "uri")):
46 """Builds a URI list as used by index resources. 47 48 @param ids: list of ids as strings 49 @param uri_format: format to be applied for URI 50 @param uri_fields: optional parameter for field IDs 51 52 """ 53 (field_id, field_uri) = uri_fields 54 55 def _MapId(m_id): 56 return { field_id: m_id, field_uri: uri_format % m_id, }
57 58 # Make sure the result is sorted, makes it nicer to look at and simplifies 59 # unittests. 60 ids.sort() 61 62 return map(_MapId, ids) 63 64
65 -def ExtractField(sequence, index):
66 """Creates a list containing one column out of a list of lists. 67 68 @param sequence: sequence of lists 69 @param index: index of field 70 71 """ 72 return map(lambda item: item[index], sequence)
73 74
75 -def MapFields(names, data):
76 """Maps two lists into one dictionary. 77 78 Example:: 79 >>> MapFields(["a", "b"], ["foo", 123]) 80 {'a': 'foo', 'b': 123} 81 82 @param names: field names (list of strings) 83 @param data: field data (list) 84 85 """ 86 if len(names) != len(data): 87 raise AttributeError("Names and data must have the same length") 88 return dict(zip(names, data))
89 90
91 -def _Tags_GET(kind, name):
92 """Helper function to retrieve tags. 93 94 """ 95 if kind == constants.TAG_INSTANCE or kind == constants.TAG_NODE: 96 if not name: 97 raise http.HttpBadRequest("Missing name on tag request") 98 cl = GetClient() 99 if kind == constants.TAG_INSTANCE: 100 fn = cl.QueryInstances 101 else: 102 fn = cl.QueryNodes 103 result = fn(names=[name], fields=["tags"], use_locking=False) 104 if not result or not result[0]: 105 raise http.HttpBadGateway("Invalid response from tag query") 106 tags = result[0][0] 107 elif kind == constants.TAG_CLUSTER: 108 ssc = ssconf.SimpleStore() 109 tags = ssc.GetClusterTags() 110 111 return list(tags)
112 113
114 -def _Tags_PUT(kind, tags, name, dry_run):
115 """Helper function to set tags. 116 117 """ 118 return SubmitJob([opcodes.OpAddTags(kind=kind, name=name, 119 tags=tags, dry_run=dry_run)])
120 121
122 -def _Tags_DELETE(kind, tags, name, dry_run):
123 """Helper function to delete tags. 124 125 """ 126 return SubmitJob([opcodes.OpDelTags(kind=kind, name=name, 127 tags=tags, dry_run=dry_run)])
128 129
130 -def MapBulkFields(itemslist, fields):
131 """Map value to field name in to one dictionary. 132 133 @param itemslist: a list of items values 134 @param fields: a list of items names 135 136 @return: a list of mapped dictionaries 137 138 """ 139 items_details = [] 140 for item in itemslist: 141 mapped = MapFields(fields, item) 142 items_details.append(mapped) 143 return items_details
144 145
146 -def MakeParamsDict(opts, params):
147 """Makes params dictionary out of a option set. 148 149 This function returns a dictionary needed for hv or be parameters. But only 150 those fields which provided in the option set. Takes parameters frozensets 151 from constants. 152 153 @type opts: dict 154 @param opts: selected options 155 @type params: frozenset 156 @param params: subset of options 157 @rtype: dict 158 @return: dictionary of options, filtered by given subset. 159 160 """ 161 result = {} 162 163 for p in params: 164 try: 165 value = opts[p] 166 except KeyError: 167 continue 168 result[p] = value 169 170 return result
171 172
173 -def SubmitJob(op, cl=None):
174 """Generic wrapper for submit job, for better http compatibility. 175 176 @type op: list 177 @param op: the list of opcodes for the job 178 @type cl: None or luxi.Client 179 @param cl: optional luxi client to use 180 @rtype: string 181 @return: the job ID 182 183 """ 184 try: 185 if cl is None: 186 cl = GetClient() 187 return cl.SubmitJob(op) 188 except errors.JobQueueFull: 189 raise http.HttpServiceUnavailable("Job queue is full, needs archiving") 190 except errors.JobQueueDrainError: 191 raise http.HttpServiceUnavailable("Job queue is drained, cannot submit") 192 except luxi.NoMasterError, err: 193 raise http.HttpBadGateway("Master seems to unreachable: %s" % str(err)) 194 except luxi.TimeoutError, err: 195 raise http.HttpGatewayTimeout("Timeout while talking to the master" 196 " daemon. Error: %s" % str(err))
197 198
199 -def HandleItemQueryErrors(fn, *args, **kwargs):
200 """Converts errors when querying a single item. 201 202 """ 203 try: 204 return fn(*args, **kwargs) 205 except errors.OpPrereqError, err: 206 if len(err.args) == 2 and err.args[1] == errors.ECODE_NOENT: 207 raise http.HttpNotFound() 208 209 raise
210 211
212 -def GetClient():
213 """Geric wrapper for luxi.Client(), for better http compatiblity. 214 215 """ 216 try: 217 return luxi.Client() 218 except luxi.NoMasterError, err: 219 raise http.HttpBadGateway("Master seems to unreachable: %s" % str(err))
220 221
222 -def FeedbackFn(ts, log_type, log_msg): # pylint: disable-msg=W0613
223 """Feedback logging function for http case. 224 225 We don't have a stdout for printing log messages, so log them to the 226 http log at least. 227 228 @param ts: the timestamp (unused) 229 230 """ 231 logging.info("%s: %s", log_type, log_msg) 232 233
234 -def CheckType(value, exptype, descr):
235 """Abort request if value type doesn't match expected type. 236 237 @param value: Value 238 @type exptype: type 239 @param exptype: Expected type 240 @type descr: string 241 @param descr: Description of value 242 @return: Value (allows inline usage) 243 244 """ 245 if not isinstance(value, exptype): 246 raise http.HttpBadRequest("%s: Type is '%s', but '%s' is expected" % 247 (descr, type(value).__name__, exptype.__name__)) 248 249 return value
250 251
252 -def CheckParameter(data, name, default=_DEFAULT, exptype=_DEFAULT):
253 """Check and return the value for a given parameter. 254 255 If no default value was given and the parameter doesn't exist in the input 256 data, an error is raise. 257 258 @type data: dict 259 @param data: Dictionary containing input data 260 @type name: string 261 @param name: Parameter name 262 @param default: Default value (can be None) 263 @param exptype: Expected type (can be None) 264 265 """ 266 try: 267 value = data[name] 268 except KeyError: 269 if default is not _DEFAULT: 270 return default 271 272 raise http.HttpBadRequest("Required parameter '%s' is missing" % 273 name) 274 275 if exptype is _DEFAULT: 276 return value 277 278 return CheckType(value, exptype, "'%s' parameter" % name)
279 280
281 -class R_Generic(object):
282 """Generic class for resources. 283 284 """ 285 # Default permission requirements 286 GET_ACCESS = [] 287 PUT_ACCESS = [rapi.RAPI_ACCESS_WRITE] 288 POST_ACCESS = [rapi.RAPI_ACCESS_WRITE] 289 DELETE_ACCESS = [rapi.RAPI_ACCESS_WRITE] 290
291 - def __init__(self, items, queryargs, req):
292 """Generic resource constructor. 293 294 @param items: a list with variables encoded in the URL 295 @param queryargs: a dictionary with additional options from URL 296 297 """ 298 self.items = items 299 self.queryargs = queryargs 300 self.req = req 301 self.sn = None
302
303 - def getSerialNumber(self):
304 """Get Serial Number. 305 306 """ 307 return self.sn
308
309 - def _checkIntVariable(self, name, default=0):
310 """Return the parsed value of an int argument. 311 312 """ 313 val = self.queryargs.get(name, default) 314 if isinstance(val, list): 315 if val: 316 val = val[0] 317 else: 318 val = default 319 try: 320 val = int(val) 321 except (ValueError, TypeError): 322 raise http.HttpBadRequest("Invalid value for the" 323 " '%s' parameter" % (name,)) 324 return val
325
326 - def _checkStringVariable(self, name, default=None):
327 """Return the parsed value of an int argument. 328 329 """ 330 val = self.queryargs.get(name, default) 331 if isinstance(val, list): 332 if val: 333 val = val[0] 334 else: 335 val = default 336 return val
337
338 - def getBodyParameter(self, name, *args):
339 """Check and return the value for a given parameter. 340 341 If a second parameter is not given, an error will be returned, 342 otherwise this parameter specifies the default value. 343 344 @param name: the required parameter 345 346 """ 347 if args: 348 return CheckParameter(self.req.request_body, name, default=args[0]) 349 350 return CheckParameter(self.req.request_body, name)
351
352 - def useLocking(self):
353 """Check if the request specifies locking. 354 355 """ 356 return self._checkIntVariable('lock')
357
358 - def useBulk(self):
359 """Check if the request specifies bulk querying. 360 361 """ 362 return self._checkIntVariable('bulk')
363
364 - def useForce(self):
365 """Check if the request specifies a forced operation. 366 367 """ 368 return self._checkIntVariable('force')
369
370 - def dryRun(self):
371 """Check if the request specifies dry-run mode. 372 373 """ 374 return self._checkIntVariable('dry-run')
375