~ubuntu-branches/ubuntu/trusty/python-keystoneclient/trusty-proposed

« back to all changes in this revision

Viewing changes to keystoneclient/base.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Adam Gandelman, Chuck Short
  • Date: 2013-11-14 10:51:32 UTC
  • mfrom: (1.1.23)
  • Revision ID: package-import@ubuntu.com-20131114105132-p1o428l7fclasv9e
Tags: 1:0.4.1-0ubuntu1
[ Adam Gandelman ]
* debian/patches: Refreshed.
* debian/patches/use-mox-dependency.patch: Use mox instead of mox3
  dependency.

[ Chuck Short ]
* New upstream release.
* debian/control:
  - open icehouse release.
  - Dropped python-d2to1 and python-httplib2 dependency.
* debian/patches/skip-tests-ubuntu.patch: Dropped no longer needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
2
 
1
3
# Copyright 2010 Jacob Kaplan-Moss
2
 
# Copyright 2011 OpenStack LLC.
 
4
# Copyright 2011 OpenStack Foundation
 
5
# Copyright 2013 OpenStack Foundation
3
6
# All Rights Reserved.
4
7
#
5
8
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
22
25
import functools
23
26
import urllib
24
27
 
 
28
import six
 
29
 
25
30
from keystoneclient import exceptions
26
 
 
27
 
 
28
 
# Python 2.4 compat
29
 
try:
30
 
    all
31
 
except NameError:
32
 
    def all(iterable):
33
 
        return True not in (not x for x in iterable)
 
31
from keystoneclient.openstack.common import strutils
34
32
 
35
33
 
36
34
def getid(obj):
37
 
    """Abstracts the common pattern of allowing both an object or an object's
38
 
    ID (UUID) as a parameter when dealing with relationships.
 
35
    """Return id if argument is a Resource.
 
36
 
 
37
    Abstracts the common pattern of allowing both an object or an object's ID
 
38
    (UUID) as a parameter when dealing with relationships.
39
39
    """
40
 
 
41
 
    # Try to return the object's UUID first, if we have a UUID.
42
40
    try:
43
41
        if obj.uuid:
44
42
            return obj.uuid
74
72
 
75
73
 
76
74
class Manager(object):
77
 
    """Managers interact with a particular type of API (servers, flavors,
78
 
    images, etc.) and provide CRUD operations for them.
 
75
    """Basic manager type providing common operations.
 
76
 
 
77
    Managers interact with a particular type of API (servers, flavors, images,
 
78
    etc.) and provide CRUD operations for them.
79
79
    """
80
80
    resource_class = None
81
81
 
82
 
    def __init__(self, api):
83
 
        self.api = api
 
82
    def __init__(self, client):
 
83
        """Initializes Manager with `client`.
 
84
 
 
85
        :param client: instance of BaseClient descendant for HTTP requests
 
86
        """
 
87
        super(Manager, self).__init__()
 
88
        self.client = client
 
89
 
 
90
    @property
 
91
    def api(self):
 
92
        """Deprecated. Use `client` instead.
 
93
        """
 
94
        return self.client
84
95
 
85
96
    def _list(self, url, response_key, obj_class=None, body=None):
86
 
        resp = None
 
97
        """List the collection.
 
98
 
 
99
        :param url: a partial URL, e.g., '/servers'
 
100
        :param response_key: the key to be looked up in response dictionary,
 
101
            e.g., 'servers'
 
102
        :param obj_class: class for constructing the returned objects
 
103
            (self.resource_class will be used by default)
 
104
        :param body: data that will be encoded as JSON and passed in POST
 
105
            request (GET will be sent by default)
 
106
        """
87
107
        if body:
88
 
            resp, body = self.api.post(url, body=body)
 
108
            resp, body = self.client.post(url, body=body)
89
109
        else:
90
 
            resp, body = self.api.get(url)
 
110
            resp, body = self.client.get(url)
91
111
 
92
112
        if obj_class is None:
93
113
            obj_class = self.resource_class
95
115
        data = body[response_key]
96
116
        # NOTE(ja): keystone returns values as list as {'values': [ ... ]}
97
117
        #           unlike other services which just return the list...
98
 
        if type(data) is dict:
 
118
        try:
99
119
            data = data['values']
 
120
        except (KeyError, TypeError):
 
121
            pass
 
122
 
100
123
        return [obj_class(self, res, loaded=True) for res in data if res]
101
124
 
102
125
    def _get(self, url, response_key):
103
 
        resp, body = self.api.get(url)
 
126
        """Get an object from collection.
 
127
 
 
128
        :param url: a partial URL, e.g., '/servers'
 
129
        :param response_key: the key to be looked up in response dictionary,
 
130
            e.g., 'server'
 
131
        """
 
132
        resp, body = self.client.get(url)
104
133
        return self.resource_class(self, body[response_key], loaded=True)
105
134
 
106
135
    def _head(self, url):
107
 
        resp, body = self.api.head(url)
 
136
        """Retrieve request headers for an object.
 
137
 
 
138
        :param url: a partial URL, e.g., '/servers'
 
139
        """
 
140
        resp, body = self.client.head(url)
108
141
        return resp.status_code == 204
109
142
 
110
143
    def _create(self, url, body, response_key, return_raw=False):
111
 
        resp, body = self.api.post(url, body=body)
 
144
        """Deprecated. Use `_post` instead.
 
145
        """
 
146
        return self._post(url, body, response_key, return_raw)
 
147
 
 
148
    def _post(self, url, body, response_key, return_raw=False):
 
149
        """Create an object.
 
150
 
 
151
        :param url: a partial URL, e.g., '/servers'
 
152
        :param body: data that will be encoded as JSON and passed in POST
 
153
            request (GET will be sent by default)
 
154
        :param response_key: the key to be looked up in response dictionary,
 
155
            e.g., 'servers'
 
156
        :param return_raw: flag to force returning raw JSON instead of
 
157
            Python object of self.resource_class
 
158
        """
 
159
        resp, body = self.client.post(url, body=body)
112
160
        if return_raw:
113
161
            return body[response_key]
114
162
        return self.resource_class(self, body[response_key])
115
163
 
 
164
    def _put(self, url, body=None, response_key=None):
 
165
        """Update an object with PUT method.
 
166
 
 
167
        :param url: a partial URL, e.g., '/servers'
 
168
        :param body: data that will be encoded as JSON and passed in POST
 
169
            request (GET will be sent by default)
 
170
        :param response_key: the key to be looked up in response dictionary,
 
171
            e.g., 'servers'
 
172
        """
 
173
        resp, body = self.client.put(url, body=body)
 
174
        # PUT requests may not return a body
 
175
        if body is not None:
 
176
            if response_key is not None:
 
177
                return self.resource_class(self, body[response_key])
 
178
            else:
 
179
                return self.resource_class(self, body)
 
180
 
 
181
    def _patch(self, url, body=None, response_key=None):
 
182
        """Update an object with PATCH method.
 
183
 
 
184
        :param url: a partial URL, e.g., '/servers'
 
185
        :param body: data that will be encoded as JSON and passed in POST
 
186
            request (GET will be sent by default)
 
187
        :param response_key: the key to be looked up in response dictionary,
 
188
            e.g., 'servers'
 
189
        """
 
190
        resp, body = self.client.patch(url, body=body)
 
191
        if response_key is not None:
 
192
            return self.resource_class(self, body[response_key])
 
193
        else:
 
194
            return self.resource_class(self, body)
 
195
 
116
196
    def _delete(self, url):
117
 
        resp, body = self.api.delete(url)
 
197
        """Delete an object.
 
198
 
 
199
        :param url: a partial URL, e.g., '/servers/my-server'
 
200
        """
 
201
        return self.client.delete(url)
118
202
 
119
203
    def _update(self, url, body=None, response_key=None, method="PUT",
120
204
                management=True):
121
 
        methods = {"PUT": self.api.put,
122
 
                   "POST": self.api.post,
123
 
                   "PATCH": self.api.patch}
 
205
        methods = {"PUT": self.client.put,
 
206
                   "POST": self.client.post,
 
207
                   "PATCH": self.client.patch}
124
208
        try:
125
 
            if body is not None:
126
 
                resp, body = methods[method](url, body=body,
127
 
                                             management=management)
128
 
            else:
129
 
                resp, body = methods[method](url, management=management)
 
209
            resp, body = methods[method](url, body=body,
 
210
                                         management=management)
130
211
        except KeyError:
131
212
            raise exceptions.ClientException("Invalid update method: %s"
132
213
                                             % method)
136
217
 
137
218
 
138
219
class ManagerWithFind(Manager):
139
 
    """Like a `Manager`, but with additional `find()`/`findall()` methods.
140
 
    """
 
220
    """Manager with additional `find()`/`findall()` methods."""
141
221
 
142
222
    __metaclass__ = abc.ABCMeta
143
223
 
303
383
 
304
384
 
305
385
class Resource(object):
306
 
    """A resource represents a particular instance of an object (tenant, user,
307
 
    etc). This is pretty much just a bag for attributes.
 
386
    """Base class for OpenStack resources (tenant, user, etc.).
308
387
 
309
 
    :param manager: Manager object
310
 
    :param info: dictionary representing resource attributes
311
 
    :param loaded: prevent lazy-loading if set to True
 
388
    This is pretty much just a bag for attributes.
312
389
    """
 
390
 
 
391
    HUMAN_ID = False
 
392
    NAME_ATTR = 'name'
 
393
 
313
394
    def __init__(self, manager, info, loaded=False):
 
395
        """Populate and bind to a manager.
 
396
 
 
397
        :param manager: Manager object
 
398
        :param info: dictionary representing resource attributes
 
399
        :param loaded: prevent lazy-loading if set to True
 
400
        """
314
401
        self.manager = manager
315
 
        self._info = info
 
402
        self._info = {}
316
403
        self._add_details(info)
317
404
        self._loaded = loaded
318
405
 
 
406
    @property
 
407
    def human_id(self):
 
408
        """Human-readable ID which can be used for bash completion.
 
409
        """
 
410
        if self.NAME_ATTR in self.__dict__ and self.HUMAN_ID:
 
411
            return strutils.to_slug(getattr(self, self.NAME_ATTR))
 
412
        return None
 
413
 
319
414
    def _add_details(self, info):
320
 
        for (k, v) in info.iteritems():
 
415
        for (k, v) in six.iteritems(info):
321
416
            setattr(self, k, v)
 
417
            self._info[k] = v
322
418
 
323
419
    def __getattr__(self, k):
324
420
        if k not in self.__dict__:
351
447
        return self.manager.delete(self)
352
448
 
353
449
    def __eq__(self, other):
 
450
        if not isinstance(other, Resource):
 
451
            return NotImplemented
 
452
        # two resources of different types are not equal
354
453
        if not isinstance(other, self.__class__):
355
454
            return False
356
455
        if hasattr(self, 'id') and hasattr(other, 'id'):