summaryrefslogtreecommitdiff
path: root/keystoneclient/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'keystoneclient/base.py')
-rw-r--r--keystoneclient/base.py116
1 files changed, 113 insertions, 3 deletions
diff --git a/keystoneclient/base.py b/keystoneclient/base.py
index 025362b..f19ed84 100644
--- a/keystoneclient/base.py
+++ b/keystoneclient/base.py
@@ -20,15 +20,17 @@ Base utilities to build API operation managers and objects on top of.
"""
import abc
+import copy
import functools
+import warnings
+from oslo_utils import strutils
import six
from six.moves import urllib
from keystoneclient import auth
from keystoneclient import exceptions
from keystoneclient.i18n import _
-from keystoneclient.openstack.common.apiclient import base
def getid(obj):
@@ -91,8 +93,17 @@ class Manager(object):
@property
def api(self):
- """Deprecated. Use `client` instead.
+ """The client.
+
+ .. warning::
+
+ This property is deprecated as of the 1.7.0 release in favor of
+ :meth:`client` and may be removed in the 2.0.0 release.
+
"""
+ warnings.warn(
+ 'api is deprecated as of the 1.7.0 release in favor of client and '
+ 'may be removed in the 2.0.0 release', DeprecationWarning)
return self.client
def _list(self, url, response_key, obj_class=None, body=None, **kwargs):
@@ -356,6 +367,17 @@ class CrudManager(Manager):
@filter_kwargs
def list(self, fallback_to_auth=False, **kwargs):
+ if 'id' in kwargs.keys():
+ # Ensure that users are not trying to call things like
+ # ``domains.list(id='default')`` when they should have used
+ # ``[domains.get(domain_id='default')]`` instead. Keystone supports
+ # ``GET /v3/domains/{domain_id}``, not ``GET
+ # /v3/domains?id={domain_id}``.
+ raise TypeError(
+ _("list() got an unexpected keyword argument 'id'. To "
+ "retrieve a single object using a globally unique "
+ "identifier, try using get() instead."))
+
url = self.build_url(dict_args_in_out=kwargs)
try:
@@ -418,11 +440,99 @@ class CrudManager(Manager):
return rl[0]
-class Resource(base.Resource):
+class Resource(object):
"""Base class for OpenStack resources (tenant, user, etc.).
This is pretty much just a bag for attributes.
"""
+ HUMAN_ID = False
+ NAME_ATTR = 'name'
+
+ def __init__(self, manager, info, loaded=False):
+ """Populate and bind to a manager.
+
+ :param manager: BaseManager object
+ :param info: dictionary representing resource attributes
+ :param loaded: prevent lazy-loading if set to True
+ """
+ self.manager = manager
+ self._info = info
+ self._add_details(info)
+ self._loaded = loaded
+
+ def __repr__(self):
+ reprkeys = sorted(k
+ for k in self.__dict__.keys()
+ if k[0] != '_' and k != 'manager')
+ info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
+ return "<%s %s>" % (self.__class__.__name__, info)
+
+ @property
+ def human_id(self):
+ """Human-readable ID which can be used for bash completion.
+ """
+ if self.HUMAN_ID:
+ name = getattr(self, self.NAME_ATTR, None)
+ if name is not None:
+ return strutils.to_slug(name)
+ return None
+
+ def _add_details(self, info):
+ for (k, v) in six.iteritems(info):
+ try:
+ setattr(self, k, v)
+ self._info[k] = v
+ except AttributeError:
+ # In this case we already defined the attribute on the class
+ pass
+
+ def __getattr__(self, k):
+ if k not in self.__dict__:
+ # NOTE(bcwaldon): disallow lazy-loading if already loaded once
+ if not self.is_loaded():
+ self.get()
+ return self.__getattr__(k)
+
+ raise AttributeError(k)
+ else:
+ return self.__dict__[k]
+
+ def get(self):
+ """Support for lazy loading details.
+
+ Some clients, such as novaclient have the option to lazy load the
+ details, details which can be loaded with this function.
+ """
+ # set_loaded() first ... so if we have to bail, we know we tried.
+ self.set_loaded(True)
+ if not hasattr(self.manager, 'get'):
+ return
+
+ new = self.manager.get(self.id)
+ if new:
+ self._add_details(new._info)
+ self._add_details(
+ {'x_request_id': self.manager.client.last_request_id})
+
+ def __eq__(self, other):
+ if not isinstance(other, Resource):
+ return NotImplemented
+ # two resources of different types are not equal
+ if not isinstance(other, self.__class__):
+ return False
+ if hasattr(self, 'id') and hasattr(other, 'id'):
+ return self.id == other.id
+ return self._info == other._info
+
+ def is_loaded(self):
+ return self._loaded
+
+ def set_loaded(self, val):
+ self._loaded = val
+
+ def to_dict(self):
+ return copy.deepcopy(self._info)
+
def delete(self):
return self.manager.delete(self)