Merge lp:~gundlach/nova/rsapi-pagination into lp:~hudson-openstack/nova/trunk

Proposed by Michael Gundlach
Status: Merged
Approved by: Michael Gundlach
Approved revision: 308
Merged at revision: 308
Proposed branch: lp:~gundlach/nova/rsapi-pagination
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 157 lines (+67/-7)
5 files modified
nova/api/rackspace/__init__.py (+20/-0)
nova/api/rackspace/flavors.py (+4/-1)
nova/api/rackspace/images.py (+2/-0)
nova/api/rackspace/servers.py (+12/-6)
nova/tests/api/rackspace/__init__.py (+29/-0)
To merge this branch: bzr merge lp:~gundlach/nova/rsapi-pagination
Reviewer Review Type Date Requested Status
Matt Dietz (community) Approve
Review via email: mp+37014@code.launchpad.net

Description of the change

Support the pagination interface in RS API -- the &offset and &limit parameters are now recognized.

This implementation is stupid (it just slices the full list at the last second), so in Bexar we should do this right.

To post a comment you must log in.
Revision history for this message
Matt Dietz (cerberus) wrote :

Seems fine

review: Approve
Revision history for this message
OpenStack Infra (hudson-openstack) wrote :

Attempt to merge lp:~gundlach/nova/rsapi-pagination into lp:nova failed due to merge conflicts:

text conflict in nova/api/rackspace/flavors.py

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'nova/api/rackspace/__init__.py'
2--- nova/api/rackspace/__init__.py 2010-09-29 13:39:24 +0000
3+++ nova/api/rackspace/__init__.py 2010-09-29 16:37:07 +0000
4@@ -168,3 +168,23 @@
5 controller=sharedipgroups.Controller())
6
7 super(APIRouter, self).__init__(mapper)
8+
9+
10+def limited(items, req):
11+ """Return a slice of items according to requested offset and limit.
12+
13+ items - a sliceable
14+ req - wobob.Request possibly containing offset and limit GET variables.
15+ offset is where to start in the list, and limit is the maximum number
16+ of items to return.
17+
18+ If limit is not specified, 0, or > 1000, defaults to 1000.
19+ """
20+ offset = int(req.GET.get('offset', 0))
21+ limit = int(req.GET.get('limit', 0))
22+ if not limit:
23+ limit = 1000
24+ limit = min(1000, limit)
25+ range_end = offset + limit
26+ return items[offset:range_end]
27+
28
29=== modified file 'nova/api/rackspace/flavors.py'
30--- nova/api/rackspace/flavors.py 2010-09-29 13:39:24 +0000
31+++ nova/api/rackspace/flavors.py 2010-09-29 16:37:07 +0000
32@@ -15,10 +15,12 @@
33 # License for the specific language governing permissions and limitations
34 # under the License.
35
36+from webob import exc
37+
38 from nova.api.rackspace import faults
39 from nova.compute import instance_types
40 from nova import wsgi
41-from webob import exc
42+import nova.api.rackspace
43
44 class Controller(wsgi.Controller):
45 """Flavor controller for the Rackspace API."""
46@@ -39,6 +41,7 @@
47 def detail(self, req):
48 """Return all flavors in detail."""
49 items = [self.show(req, id)['flavor'] for id in self._all_ids()]
50+ items = nova.api.rackspace.limited(items, req)
51 return dict(flavors=items)
52
53 def show(self, req, id):
54
55=== modified file 'nova/api/rackspace/images.py'
56--- nova/api/rackspace/images.py 2010-09-29 13:39:24 +0000
57+++ nova/api/rackspace/images.py 2010-09-29 16:37:07 +0000
58@@ -19,6 +19,7 @@
59
60 from nova import wsgi
61 from nova.api.rackspace import _id_translator
62+import nova.api.rackspace
63 import nova.image.service
64 from nova.api.rackspace import faults
65
66@@ -46,6 +47,7 @@
67 def detail(self, req):
68 """Return all public images in detail."""
69 data = self._service.index()
70+ data = nova.api.rackspace.limited(data, req)
71 for img in data:
72 img['id'] = self._id_translator.to_rs_id(img['id'])
73 return dict(images=data)
74
75=== modified file 'nova/api/rackspace/servers.py'
76--- nova/api/rackspace/servers.py 2010-09-29 14:17:10 +0000
77+++ nova/api/rackspace/servers.py 2010-09-29 16:37:07 +0000
78@@ -26,6 +26,7 @@
79 from nova.api.rackspace import _id_translator
80 from nova.api.rackspace import faults
81 from nova.compute import power_state
82+import nova.api.rackspace
83 import nova.image.service
84
85 FLAGS = flags.FLAGS
86@@ -102,16 +103,21 @@
87
88 def index(self, req):
89 """ Returns a list of server names and ids for a given user """
90- user_id = req.environ['nova.context']['user']['id']
91- instance_list = self.db_driver.instance_get_all_by_user(None, user_id)
92- res = [_entity_inst(inst)['server'] for inst in instance_list]
93- return _entity_list(res)
94+ return self._items(req, entity_maker=_entity_inst)
95
96 def detail(self, req):
97 """ Returns a list of server details for a given user """
98+ return self._items(req, entity_maker=_entity_detail)
99+
100+ def _items(self, req, entity_maker):
101+ """Returns a list of servers for a given user.
102+
103+ entity_maker - either _entity_detail or _entity_inst
104+ """
105 user_id = req.environ['nova.context']['user']['id']
106- res = [_entity_detail(inst)['server'] for inst in
107- self.db_driver.instance_get_all_by_user(None, user_id)]
108+ instance_list = self.db_driver.instance_get_all_by_user(None, user_id)
109+ limited_list = nova.api.rackspace.limited(instance_list, req)
110+ res = [entity_maker(inst)['server'] for inst in limited_list]
111 return _entity_list(res)
112
113 def show(self, req, id):
114
115=== modified file 'nova/tests/api/rackspace/__init__.py'
116--- nova/tests/api/rackspace/__init__.py 2010-09-15 17:54:38 +0000
117+++ nova/tests/api/rackspace/__init__.py 2010-09-29 16:37:07 +0000
118@@ -17,6 +17,7 @@
119
120 import unittest
121
122+from nova.api.rackspace import limited
123 from nova.api.rackspace import RateLimitingMiddleware
124 from nova.tests.api.test_helper import *
125 from webob import Request
126@@ -77,3 +78,31 @@
127 self.assertEqual(middleware.limiter.__class__.__name__, "Limiter")
128 middleware = RateLimitingMiddleware(APIStub(), service_host='foobar')
129 self.assertEqual(middleware.limiter.__class__.__name__, "WSGIAppProxy")
130+
131+
132+class LimiterTest(unittest.TestCase):
133+
134+ def testLimiter(self):
135+ items = range(2000)
136+ req = Request.blank('/')
137+ self.assertEqual(limited(items, req), items[ :1000])
138+ req = Request.blank('/?offset=0')
139+ self.assertEqual(limited(items, req), items[ :1000])
140+ req = Request.blank('/?offset=3')
141+ self.assertEqual(limited(items, req), items[3:1003])
142+ req = Request.blank('/?offset=2005')
143+ self.assertEqual(limited(items, req), [])
144+ req = Request.blank('/?limit=10')
145+ self.assertEqual(limited(items, req), items[ :10])
146+ req = Request.blank('/?limit=0')
147+ self.assertEqual(limited(items, req), items[ :1000])
148+ req = Request.blank('/?limit=3000')
149+ self.assertEqual(limited(items, req), items[ :1000])
150+ req = Request.blank('/?offset=1&limit=3')
151+ self.assertEqual(limited(items, req), items[1:4])
152+ req = Request.blank('/?offset=3&limit=0')
153+ self.assertEqual(limited(items, req), items[3:1003])
154+ req = Request.blank('/?offset=3&limit=1500')
155+ self.assertEqual(limited(items, req), items[3:1003])
156+ req = Request.blank('/?offset=3000&limit=10')
157+ self.assertEqual(limited(items, req), [])