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 six 
 23  import time 
 24   
 25  from . import constant 
 26  from . import exception 
 27  from . import mapper 
 28  from . import utils 
 29   
 30   
 31  WRAPPER_METHODS = [] 
 32  BAD_ATTRS = ['self'] 
 33   
 34   
35 -class Resource(object):
36 """Base class for resources.""" 37 38 _id = None 39 _attrs = [] 40 _loaded = True 41 _sub_manager_list = {} 42 _state_attr = 'status' 43 _stable_state = [] 44
45 - def __init__(self, manager, *args, **kwargs):
46 """ 47 Create a resource object 48 49 Don't call this method directly; use Manager methods instead. 50 51 @param manager: Manager object 52 @type manager: yakumo.base.Manager 53 @return: Resource object 54 @rtype: yakumo.base.Resource 55 """ 56 self._manager = manager 57 self._attr2json = manager._attr2json 58 self._client = manager._client 59 self._has_extra_attr = manager._has_extra_attr 60 self._http = manager._http 61 self._id_attr = manager._id_attr 62 self._json2attr = manager._json2attr 63 self._json_resource_key = manager._json_resource_key 64 self._no_such_api = manager._no_such_api 65 self._update_method = manager._update_method 66 self._url_resource_path = manager._url_resource_path 67 self._verbose = manager._verbose 68 69 id = kwargs.get(self._id_attr) 70 if isinstance(id, Resource): 71 self._id = id._id 72 else: 73 self._id = id 74 self._attrs = [attr 75 for attr, json_attr, _mapper 76 in manager._attr_mapping] 77 self._set_attrs(kwargs) 78 if len(kwargs) == 1 and self._id_attr in kwargs: 79 self._loaded = False 80 if len(kwargs) >= 1 and self._id_attr in kwargs: 81 for attr, sub_manager in self._sub_manager_list.items(): 82 setattr(self, attr, 83 sub_manager(self, *args, **kwargs))
84
85 - def __enter__(self):
86 return self
87
88 - def __exit__(self, type, value, tb):
89 try: 90 self.delete() 91 self.wait_for_finished() 92 except: 93 pass
94
95 - def __eq__(self, other):
96 if not isinstance(other, Resource): 97 return False 98 return self._id == other._id
99
100 - def __ne__(self, other):
101 if not isinstance(other, Resource): 102 return True 103 return self._id != other._id
104
105 - def __repr__(self):
106 if 'name' in self._attrs and self._id_attr != 'name' and self._loaded: 107 return '<%s.%s (%s="%s", name="%s")>' % ( 108 self.__module__, self.__class__.__name__, self._id_attr, 109 self._id, self.name) 110 empty = '' 111 if not self._loaded: 112 empty = ' empty' 113 return '<%s.%s (%s="%s"%s)>' % ( 114 self.__module__, self.__class__.__name__, self._id_attr, 115 self._id, empty)
116
117 - def __str__(self):
118 if self._verbose: 119 attrs = self.get_attrs() 120 return '<%s.%s (%s)>' % ( 121 self.__module__, self.__class__.__name__, attrs) 122 else: 123 return self.__repr__()
124
125 - def __getattr__(self, name):
126 if not self._has_extra_attr and name not in self._attrs: 127 raise AttributeError(name) 128 if name == self._id_attr: 129 return self._id 130 if not self._loaded: 131 self.reload() 132 return self.__dict__.get(name) 133 return None
134
135 - def _set_attrs(self, kwargs):
136 _kwargs = copy.copy(kwargs) 137 for attr in self._attrs: 138 if attr in _kwargs: 139 setattr(self, attr, _kwargs.pop(attr)) 140 if not self._has_extra_attr: 141 return 142 for key, value in _kwargs.items(): 143 if not key.startswith('_') and key not in BAD_ATTRS: 144 setattr(self, key, value)
145
146 - def _clear_attrs(self):
147 """ 148 Clear attributes 149 150 @rtype: None 151 """ 152 ret = {} 153 if not self._loaded: 154 return 155 for key in dir(self): 156 if key.startswith('_'): 157 continue 158 if key in self._sub_manager_list: 159 continue 160 value = getattr(self, key) 161 if inspect.ismethod(value) or inspect.isfunction(value): 162 continue 163 delattr(self, key)
164
165 - def get_id(self):
166 """ 167 Query ID of a resource 168 169 @return: ID 170 @rtype: str 171 """ 172 return self._id
173
174 - def get_attrs(self):
175 """ 176 Aquire attributes as a dictionary 177 178 @return: attributes 179 @rtype: dict 180 """ 181 ret = {} 182 if not self._loaded: 183 self.reload() 184 self._loaded = True 185 for key in dir(self): 186 if key.startswith('_'): 187 continue 188 value = getattr(self, key) 189 if inspect.ismethod(value) or inspect.isfunction(value): 190 continue 191 ret[key] = value 192 return ret
193
194 - def reload(self):
195 """ 196 (Re)load attributes of a resource 197 198 @return: Whether attributes are updated 199 @rtype: bool 200 """ 201 x = self._manager.get(getattr(self, self._id_attr)) 202 if x: 203 self._clear_attrs() 204 self._set_attrs(x.__dict__) 205 self._loaded = True 206 return True 207 return False
208
209 - def update(self, **kwargs):
210 """ 211 Update a resource and reload it. 212 kwargs: attributes and their values to update 213 214 @rtype: None 215 """ 216 json_params = self._attr2json(kwargs) 217 method = getattr(self._http, self._update_method) 218 method(utils.join_path(self._url_resource_path, self._id), 219 data={self._json_resource_key: json_params}) 220 self.reload()
221
222 - def delete(self):
223 """ 224 Delete a resource 225 226 @rtype: None 227 """ 228 self._http.delete(utils.join_path(self._url_resource_path, self._id))
229
230 - def wait_for_finished(self, count=100, interval=15):
231 """ 232 Wait for task finished 233 234 @keyword count: Maximum polling time 235 @type count: int 236 @keyword interval: Polling interval in seconds 237 @type interval: int 238 @rtype: None 239 """ 240 if self._stable_state == []: 241 return 242 for i in range(count): 243 time.sleep(interval) 244 try: 245 self.reload() 246 except exception.NotFound: 247 return 248 if getattr(self, self._state_attr, None) in self._stable_state: 249 return
250 251
252 -class Manager(object):
253 """Base class for resource managers.""" 254 255 resource_class = None 256 service_type = '' 257 _attr_mapping = [] 258 _has_detail = True 259 _has_extra_attr = False 260 _hidden_methods = None 261 _json_resource_key = '' 262 _json_resources_key = '' 263 _id_attr = 'id' 264 _update_method = 'put' 265 _url_resource_path = '' 266 _url_resource_list_path = '' 267
268 - def __init__(self, client, verbose=False, **kwargs):
269 """ 270 Create a Manager object 271 272 kwargs: options 273 274 @param client: client object 275 @type client: yakumo.Client 276 @keyword verbose: Whether str(Resource) displays all attributes 277 @type verbose: bool 278 @return: Manager object 279 @rtype: yakumo.base.Manager 280 """ 281 self._client = client 282 self._session = client._session 283 self._http = self._session.get_proxy(self.service_type) 284 self._to_json_mapping = {} 285 self._to_attr_mapping = {} 286 self._verbose = verbose 287 if not self._url_resource_list_path: 288 self._url_resource_list_path = self._url_resource_path 289 if self.resource_class is None: 290 return 291 mapper.make_mappings(self._attr_mapping, 292 self._to_json_mapping, 293 self._to_attr_mapping) 294 if self._hidden_methods is not None: 295 for method in self._hidden_methods: 296 setattr(self, method, self._no_such_api)
297
298 - def _no_such_api(self, *args):
299 raise exception.NoSuchAPI()
300
301 - def _json2attr(self, json_params):
302 result = {} 303 for key, value in json_params.items(): 304 _map = self._to_attr_mapping.get(key) 305 if _map is not None: 306 result[_map['attr']] = _map['mapper'].to_attr(self, value) 307 elif self._has_extra_attr and \ 308 not key.startswith('_') and key not in BAD_ATTRS: 309 result[key] = value 310 return result
311
312 - def _attr2json(self, attrs):
313 result = {} 314 for key, value in attrs.items(): 315 if value is constant.UNDEF: 316 continue 317 _map = self._to_json_mapping.get(key) 318 if _map is not None: 319 result[_map['json_attr']] = \ 320 _map['mapper'].to_json(self, value) 321 elif self._has_extra_attr \ 322 and not key.startswith('_') and key not in BAD_ATTRS: 323 result[key] = value 324 return result
325
326 - def get_empty(self, id):
327 """ 328 Create a resource object without attributes 329 330 @return: Resource object (empty) 331 @rtype: yakumo.base.Resource 332 """ 333 if id is None: 334 return None 335 kwargs = {self._id_attr: id} 336 return self.resource_class(self, **kwargs)
337
338 - def create(self, **kwargs):
339 """ 340 Create a new resource 341 342 kwargs: attributes of the resource 343 344 @return: Resource object (empty) 345 @rtype: yakumo.base.Resource 346 """ 347 json_params = self._attr2json(kwargs) 348 ret = self._http.post(self._url_resource_path, 349 data={self._json_resource_key: json_params}) 350 attrs = self._json2attr(ret[self._json_resource_key]) 351 return self.get_empty(attrs[self._id_attr])
352
353 - def get(self, id):
354 """ 355 Aquire an existing resource object 356 357 @param id: ID 358 @type id: str 359 @return: Resource object 360 @rtype: yakumo.base.Resource 361 """ 362 try: 363 ret = self._http.get(utils.join_path(self._url_resource_path, id)) 364 json_params = ret.get(self._json_resource_key) 365 attrs = self._json2attr(json_params) 366 return self.resource_class(self, **attrs) 367 except exception.NotFound: 368 raise 369 except: 370 return None
371
372 - def _find_gen(self, **kwargs):
373 if self._has_detail: 374 ret = self._http.get(self._url_resource_list_path) 375 for x in ret[self._json_resources_key]: 376 attrs = self._json2attr(x) 377 for k, v in kwargs.items(): 378 if attrs.get(k) != v: 379 break 380 else: 381 yield self.resource_class(self, **attrs) 382 else: 383 try: 384 ret = self._http.get(self._url_resource_list_path) 385 except: 386 return 387 for x in ret[self._json_resources_key]: 388 ret = self.get(x['id']) 389 for k, v in kwargs.items(): 390 if getattr(ret, k, None) != v: 391 break 392 else: 393 yield ret
394
395 - def find(self, **kwargs):
396 """ 397 Query existing resource object matched the conditions 398 399 kwargs is key=value style query conditions. 400 Returns empty list if no matched resource. 401 402 @return: List of Resource object 403 @rtype: yakumo.base.Resource 404 """ 405 return list(self._find_gen(**kwargs))
406
407 - def find_one(self, **kwargs):
408 """ 409 Aquire an existing resource object matched the conditions 410 411 kwargs is key=value style query conditions. 412 Returns None if no matched resource. 413 414 @return: Resource object 415 @rtype: yakumo.base.Resource 416 """ 417 try: 418 return six.next(self._find_gen(**kwargs)) 419 except StopIteration: 420 return None
421
422 - def list(self):
423 """ 424 Aquire an existing resource object 425 426 @return: List of Resource objects 427 @rtype: [yakumo.base.Resource] 428 """ 429 return self.find()
430 431
432 -class SubManager(Manager):
433 """Base class for sub resource managers.""" 434
435 - def __init__(self, parent_resource, *args, **kwargs):
436 self.parent_resource = parent_resource 437 try: 438 self._url_resource_path = \ 439 self._url_resource_path % self.parent_resource._id 440 except TypeError: 441 pass 442 super(SubManager, self).__init__( 443 self.parent_resource._manager._client, *args, **kwargs)
444 445
446 -class GlanceV2Resource(Resource):
447 """Base class for resource managers which don't use _json_resource_key.""" 448
449 - def update(self, **kwargs):
450 """ 451 Update a resource and reload it. 452 kwargs: attributes and their values to update 453 454 @rtype: None 455 """ 456 json_params = self._attr2json(kwargs) 457 self._http.put(utils.join_path(self._url_resource_path, self._id), 458 data=json_params)
459 460
461 -class GlanceV2Manager(Manager):
462 """Base class for resource managers which don't use _json_resource_key.""" 463 464 _has_extra_attr = True 465
466 - def create(self, **kwargs):
467 """ 468 Create a new resource 469 470 kwargs: attributes of the resource 471 472 @return: Resource object (empty) 473 @rtype: yakumo.base.Resource 474 """ 475 json_params = self._attr2json(kwargs) 476 ret = self._http.post(self._url_resource_path, data=json_params) 477 attrs = self._json2attr(ret) 478 return self.get_empty(attrs[self._id_attr])
479
480 - def get(self, id):
481 """ 482 Aquire an existing resource object 483 484 @param id: ID 485 @type id: str 486 @return: Resource object 487 @rtype: yakumo.base.Resource 488 """ 489 try: 490 json_params = self._http.get( 491 utils.join_path(self._url_resource_path, id)) 492 attrs = self._json2attr(json_params) 493 return self.resource_class(self, **attrs) 494 except exception.NotFound: 495 raise 496 except: 497 return None
498 499
500 -class GlanceV2SubManager(SubManager, GlanceV2Manager):
501 """Base class for sub resource managers for GlanceV2Manager.""" 502 pass
503 504
505 -class SwiftV1Resource(Resource):
506 """resource class for resources on Object Storage V1 API""" 507
508 - def update(self, **kwargs):
509 """ 510 Update a volume 511 512 kwargs: attributes of the resource 513 @rtype: None 514 """ 515 attrs = self.get_attrs() 516 for key, value in kwargs.items(): 517 if value is constant.UNDEF: 518 continue 519 attrs[key] = value 520 headers = self._attr2json(attrs) 521 self._http.post_raw(self._url_resource_path, self._id, 522 headers=headers) 523 self.reload()
524
525 - def get_metadata(self):
526 """ 527 Get metadata of a volume 528 529 @return: Metadata 530 @rtype: dict 531 """ 532 ret = self._http.head(self._url_resource_path, self._id) 533 attrs = self._json2attr(ret) 534 return attrs.get('metadata', {})
535
536 - def set_metadata(self, **metadata):
537 """ 538 Update metadata of a volume 539 540 @keyword metadata: key=value style. 541 @type metadata: dict 542 @rtype: None 543 """ 544 if self.metadata is None: 545 self.metadata = {} 546 self.metadata.update(metadata) 547 self.update()
548
549 - def unset_metadata(self, *keys):
550 """ 551 Delete metadata of a volume 552 553 @param key: key of the metadata 554 @type keys: [str] 555 @rtype: None 556 """ 557 if self.metadata is None: 558 return 559 for key in keys: 560 if key in self.metadata: 561 self.metadata.pop(key) 562 self.update()
563 564
565 -class SwiftV1Manager(Manager):
566 """manager class for resources on Object Storage V1 API""" 567 568 _id_attr = 'name' 569
570 - def _attr2json(self, attrs):
571 metadata = attrs.pop('metadata', {}) 572 result = super(SwiftV1Manager, self)._attr2json(attrs) 573 if isinstance(metadata, dict): 574 for key, value in metadata.items(): 575 x_key = "x-%s-meta-%s" % (self._json_resource_key, key) 576 if x_key not in attrs: 577 result[x_key] = value 578 return result
579
580 - def _json2attr(self, json_params):
581 result = {} 582 metadata = {} 583 for key, value in json_params.items(): 584 key = key.lower() 585 _map = self._to_attr_mapping.get(key) 586 if _map is not None: 587 result[_map['attr']] = _map['mapper'].to_attr(self, value) 588 else: 589 key_prefix = 'x-%s-meta-' % self._json_resource_key 590 if key.startswith(key_prefix): 591 _key = key[len(key_prefix):] 592 metadata[_key] = value 593 if metadata: 594 result['metadata'] = metadata 595 return result
596
597 - def create(self, name, data=None, **kwargs):
598 """ 599 Create a resource 600 601 kwargs: attributes of the resource 602 603 @keyword name: Resource name 604 @type name: str 605 @return: Resource object (empty) 606 @rtype: yakumo.base.Resource 607 """ 608 headers = self._attr2json(kwargs) 609 self._http.put_raw(self._url_resource_path, name, 610 headers=headers, data=data) 611 return self.get_empty(name)
612
613 - def get(self, name):
614 """ 615 Aquire an existing resource object 616 617 @param name: name 618 @type name: str 619 @return: Resource object 620 @rtype: yakumo.base.Resource 621 """ 622 try: 623 json_params = self._http.head( 624 utils.join_path(self._url_resource_path, name)) 625 json_params['name'] = name 626 attrs = self._json2attr(json_params) 627 return self.resource_class(self, **attrs) 628 except exception.NotFound: 629 raise 630 except: 631 return None
632
633 - def _find_gen(self, **kwargs):
634 try: 635 ret = self._http.get(self._url_resource_path) 636 except: 637 return 638 for x in ret: 639 if 'name' in x: 640 yield self.get_empty(x['name'])
641 642
643 -class SwiftV1SubManager(SubManager, SwiftV1Manager):
644 """Base class for sub resource managers for GlanceV2Manager.""" 645 pass
646