Package yakumo :: Module base
[hide private]
[frames] | no frames]

Source Code for Module yakumo.base

  1  # Copyright 2014-2017 by Akira Yoshiyama <akirayoshiyama@gmail.com>. 
  2  # All Rights Reserved. 
  3  # 
  4  #    Licensed under the Apache License, Version 2.0 (the "License"); you may 
  5  #    not use this file except in compliance with the License. You may obtain 
  6  #    a copy of the License at 
  7  # 
  8  #         http://www.apache.org/licenses/LICENSE-2.0 
  9  # 
 10  #    Unless required by applicable law or agreed to in writing, software 
 11  #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
 12  #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
 13  #    License for the specific language governing permissions and limitations 
 14  #    under the License. 
 15   
 16  """ 
 17  Abstract classes for resource management 
 18  """ 
 19   
 20  import copy 
 21  import inspect 
 22  import time 
 23   
 24  from . import constant 
 25  from . import exception 
 26  from . import mapper 
 27  from . import utils 
 28   
 29   
 30  WRAPPER_METHODS = [] 
 31  BAD_ATTRS = ['self'] 
 32   
 33   
34 -class Resource(object):
35 """Base class for resources.""" 36 37 _id = None 38 _attrs = [] 39 _loaded = True 40 _sub_manager_list = {} 41 _state_attr = 'status' 42 _stable_state = [] 43
44 - def __init__(self, manager, *args, **kwargs):
45 """ 46 Create a resource object 47 48 Don't call this method directly; use Manager methods instead. 49 50 @param manager: Manager object 51 @type manager: yakumo.base.Manager 52 @return: Resource object 53 @rtype: yakumo.base.Resource 54 """ 55 self._manager = manager 56 self._attr2json = manager._attr2json 57 self._client = manager._client 58 self._has_extra_attr = manager._has_extra_attr 59 self._http = manager._http 60 self._id_attr = manager._id_attr 61 self._json2attr = manager._json2attr 62 self._json_resource_key = manager._json_resource_key 63 self._no_such_api = manager._no_such_api 64 self._update_method = manager._update_method 65 self._url_resource_path = manager._url_resource_path 66 self._verbose = manager._verbose 67 68 id = kwargs.get(self._id_attr) 69 if isinstance(id, Resource): 70 self._id = id._id 71 else: 72 self._id = id 73 self._attrs = [attr 74 for attr, json_attr, _mapper 75 in manager._attr_mapping] 76 self._set_attrs(kwargs) 77 if len(kwargs) == 1 and self._id_attr in kwargs: 78 self._loaded = False 79 if len(kwargs) >= 1 and self._id_attr in kwargs: 80 for attr, sub_manager in self._sub_manager_list.items(): 81 setattr(self, attr, 82 sub_manager(self, *args, **kwargs))
83
84 - def __eq__(self, other):
85 if not isinstance(other, Resource): 86 return False 87 return self._id == other._id
88
89 - def __ne__(self, other):
90 if not isinstance(other, Resource): 91 return True 92 return self._id != other._id
93
94 - def __repr__(self):
95 if 'name' in self._attrs and self._id_attr != 'name' and self._loaded: 96 return '<%s.%s (%s="%s", name="%s")>' % ( 97 self.__module__, self.__class__.__name__, self._id_attr, 98 self._id, self.name) 99 empty = '' 100 if not self._loaded: 101 empty = ' empty' 102 return '<%s.%s (%s="%s"%s)>' % ( 103 self.__module__, self.__class__.__name__, self._id_attr, 104 self._id, empty)
105
106 - def __str__(self):
107 if self._verbose: 108 attrs = self.get_attrs() 109 return '<%s.%s (%s)>' % ( 110 self.__module__, self.__class__.__name__, attrs) 111 else: 112 return self.__repr__()
113
114 - def __getattr__(self, name):
115 if not self._has_extra_attr and name not in self._attrs: 116 raise AttributeError(name) 117 if name == self._id_attr: 118 return self._id 119 if not self._loaded: 120 self.reload() 121 return self.__dict__.get(name) 122 return None
123
124 - def _set_attrs(self, kwargs):
125 _kwargs = copy.copy(kwargs) 126 for attr in self._attrs: 127 if attr in _kwargs: 128 setattr(self, attr, _kwargs.pop(attr)) 129 if not self._has_extra_attr: 130 return 131 for key, value in _kwargs.items(): 132 if not key.startswith('_') and key not in BAD_ATTRS: 133 setattr(self, key, value)
134
135 - def _clear_attrs(self):
136 """ 137 Clear attributes 138 139 @rtype: None 140 """ 141 ret = {} 142 if not self._loaded: 143 return 144 for key in dir(self): 145 if key.startswith('_'): 146 continue 147 value = getattr(self, key) 148 if inspect.ismethod(value) or inspect.isfunction(value): 149 continue 150 delattr(self, key)
151
152 - def get_id(self):
153 """ 154 Query ID of a resource 155 156 @return: ID 157 @rtype: str 158 """ 159 return self._id
160
161 - def get_attrs(self):
162 """ 163 Aquire attributes as a dictionary 164 165 @return: attributes 166 @rtype: dict 167 """ 168 ret = {} 169 if not self._loaded: 170 self.reload() 171 self._loaded = True 172 for key in dir(self): 173 if key.startswith('_'): 174 continue 175 value = getattr(self, key) 176 if inspect.ismethod(value) or inspect.isfunction(value): 177 continue 178 ret[key] = value 179 return ret
180
181 - def reload(self):
182 """ 183 (Re)load attributes of a resource 184 185 @return: Whether attributes are updated 186 @rtype: bool 187 """ 188 x = self._manager.get(getattr(self, self._id_attr)) 189 if x: 190 self._clear_attrs() 191 self._set_attrs(x.__dict__) 192 self._loaded = True 193 return True 194 return False
195
196 - def update(self, **kwargs):
197 """ 198 Update a resource and reload it. 199 kwargs: attributes and their values to update 200 201 @rtype: None 202 """ 203 json_params = self._attr2json(kwargs) 204 method = getattr(self._http, self._update_method) 205 method(utils.join_path(self._url_resource_path, self._id), 206 data={self._json_resource_key: json_params}) 207 self.reload()
208
209 - def delete(self):
210 """ 211 Delete a resource 212 213 @rtype: None 214 """ 215 self._http.delete(utils.join_path(self._url_resource_path, self._id))
216
217 - def wait_for_finished(self, count=10, interval=10):
218 """ 219 Wait for task finished 220 221 @keyword count: Maximum polling time 222 @type count: int 223 @keyword interval: Polling interval in seconds 224 @type interval: int 225 @rtype: None 226 """ 227 for i in range(count): 228 time.sleep(interval) 229 try: 230 self.reload() 231 except exception.NotFound: 232 return 233 if getattr(self, self._state_attr, None) in self._stable_state: 234 return
235 236
237 -class Manager(object):
238 """Base class for resource managers.""" 239 240 resource_class = None 241 service_type = '' 242 _attr_mapping = [] 243 _has_detail = True 244 _has_extra_attr = False 245 _hidden_methods = None 246 _json_resource_key = '' 247 _json_resources_key = '' 248 _id_attr = 'id' 249 _update_method = 'put' 250 _url_resource_path = '' 251 _url_resource_list_path = '' 252
253 - def __init__(self, client, verbose=False, **kwargs):
254 """ 255 Create a Manager object 256 257 kwargs: options 258 259 @param client: client object 260 @type client: yakumo.Client 261 @keyword verbose: Whether str(Resource) displays all attributes 262 @type verbose: bool 263 @return: Manager object 264 @rtype: yakumo.base.Manager 265 """ 266 self._client = client 267 self._session = client._session 268 self._http = self._session.get_proxy(self.service_type) 269 self._to_json_mapping = {} 270 self._to_attr_mapping = {} 271 self._verbose = verbose 272 if not self._url_resource_list_path: 273 self._url_resource_list_path = self._url_resource_path 274 if self.resource_class is None: 275 return 276 mapper.make_mappings(self._attr_mapping, 277 self._to_json_mapping, 278 self._to_attr_mapping) 279 if self._hidden_methods is not None: 280 for method in self._hidden_methods: 281 setattr(self, method, self._no_such_api)
282
283 - def _no_such_api(self, *args):
284 raise exception.NoSuchAPI()
285
286 - def _json2attr(self, json_params):
287 result = {} 288 for key, value in json_params.items(): 289 _map = self._to_attr_mapping.get(key) 290 if _map is not None: 291 result[_map['attr']] = _map['mapper'].to_attr(self, value) 292 elif self._has_extra_attr and \ 293 not key.startswith('_') and key not in BAD_ATTRS: 294 result[key] = value 295 return result
296
297 - def _attr2json(self, attrs):
298 result = {} 299 for key, value in attrs.items(): 300 if value is constant.UNDEF: 301 continue 302 _map = self._to_json_mapping.get(key) 303 if _map is not None: 304 result[_map['json_attr']] = \ 305 _map['mapper'].to_json(self, value) 306 elif self._has_extra_attr \ 307 and not key.startswith('_') and key not in BAD_ATTRS: 308 result[key] = value 309 return result
310
311 - def get_empty(self, id):
312 """ 313 Create a resource object without attributes 314 315 @return: Resource object (empty) 316 @rtype: yakumo.base.Resource 317 """ 318 if id is None: 319 return None 320 kwargs = {self._id_attr: id} 321 return self.resource_class(self, **kwargs)
322
323 - def create(self, **kwargs):
324 """ 325 Create a new resource 326 327 kwargs: attributes of the resource 328 329 @return: Resource object (empty) 330 @rtype: yakumo.base.Resource 331 """ 332 json_params = self._attr2json(kwargs) 333 ret = self._http.post(self._url_resource_path, 334 data={self._json_resource_key: json_params}) 335 attrs = self._json2attr(ret[self._json_resource_key]) 336 return self.get_empty(attrs[self._id_attr])
337
338 - def get(self, id):
339 """ 340 Aquire an existing resource object 341 342 @param id: ID 343 @type id: str 344 @return: Resource object 345 @rtype: yakumo.base.Resource 346 """ 347 try: 348 ret = self._http.get(utils.join_path(self._url_resource_path, id)) 349 json_params = ret.get(self._json_resource_key) 350 attrs = self._json2attr(json_params) 351 return self.resource_class(self, **attrs) 352 except exception.NotFound: 353 raise 354 except: 355 return None
356
357 - def _find_gen(self, **kwargs):
358 if self._has_detail: 359 ret = self._http.get(self._url_resource_list_path) 360 for x in ret[self._json_resources_key]: 361 attrs = self._json2attr(x) 362 for k, v in kwargs.items(): 363 if attrs.get(k) != v: 364 break 365 else: 366 yield self.resource_class(self, **attrs) 367 else: 368 try: 369 ret = self._http.get(self._url_resource_list_path) 370 except: 371 return 372 for x in ret[self._json_resources_key]: 373 ret = self.get(x['id']) 374 for k, v in kwargs.items(): 375 if getattr(ret, k, None) != v: 376 break 377 else: 378 yield ret
379
380 - def find(self, **kwargs):
381 """ 382 Query existing resource object matched the conditions 383 384 kwargs is key=value style query conditions. 385 Returns empty list if no matched resource. 386 387 @return: List of Resource object 388 @rtype: yakumo.base.Resource 389 """ 390 return list(self._find_gen(**kwargs))
391
392 - def find_one(self, **kwargs):
393 """ 394 Aquire an existing resource object matched the conditions 395 396 kwargs is key=value style query conditions. 397 Returns None if no matched resource. 398 399 @return: Resource object 400 @rtype: yakumo.base.Resource 401 """ 402 try: 403 return self._find_gen(**kwargs).next() 404 except StopIteration: 405 return None
406
407 - def list(self):
408 """ 409 Aquire an existing resource object 410 411 @return: List of Resource objects 412 @rtype: [yakumo.base.Resource] 413 """ 414 return self.find()
415 416
417 -class SubManager(Manager):
418 """Base class for sub resource managers.""" 419
420 - def __init__(self, parent_resource, *args, **kwargs):
421 self.parent_resource = parent_resource 422 try: 423 self._url_resource_path = \ 424 self._url_resource_path % self.parent_resource._id 425 except TypeError: 426 pass 427 super(SubManager, self).__init__( 428 self.parent_resource._manager._client, *args, **kwargs)
429 430
431 -class GlanceV2Resource(Resource):
432 """Base class for resource managers which don't use _json_resource_key.""" 433
434 - def update(self, **kwargs):
435 """ 436 Update a resource and reload it. 437 kwargs: attributes and their values to update 438 439 @rtype: None 440 """ 441 json_params = self._attr2json(kwargs) 442 self._http.put(utils.join_path(self._url_resource_path, self._id), 443 data=json_params)
444 445
446 -class GlanceV2Manager(Manager):
447 """Base class for resource managers which don't use _json_resource_key.""" 448 449 _has_extra_attr = True 450
451 - def create(self, **kwargs):
452 """ 453 Create a new resource 454 455 kwargs: attributes of the resource 456 457 @return: Resource object (empty) 458 @rtype: yakumo.base.Resource 459 """ 460 json_params = self._attr2json(kwargs) 461 ret = self._http.post(self._url_resource_path, data=json_params) 462 attrs = self._json2attr(ret) 463 return self.get_empty(attrs[self._id_attr])
464
465 - def get(self, id):
466 """ 467 Aquire an existing resource object 468 469 @param id: ID 470 @type id: str 471 @return: Resource object 472 @rtype: yakumo.base.Resource 473 """ 474 try: 475 json_params = self._http.get( 476 utils.join_path(self._url_resource_path, id)) 477 attrs = self._json2attr(json_params) 478 return self.resource_class(self, **attrs) 479 except exception.NotFound: 480 raise 481 except: 482 return None
483 484
485 -class GlanceV2SubManager(SubManager, GlanceV2Manager):
486 """Base class for sub resource managers for GlanceV2Manager.""" 487 pass
488