Merge lp:~gundlach/nova/controllers-in-api into lp:~hudson-openstack/nova/trunk

Proposed by Michael Gundlach
Status: Merged
Approved by: Michael Gundlach
Approved revision: no longer in the revision history of the source branch.
Merged at revision: 259
Proposed branch: lp:~gundlach/nova/controllers-in-api
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 237 lines (+103/-70)
5 files modified
nova/api/rackspace/__init__.py (+4/-2)
nova/api/rackspace/_id_translator.py (+42/-0)
nova/api/rackspace/flavors.py (+37/-1)
nova/api/rackspace/images.py (+13/-60)
nova/compute/instance_types.py (+7/-7)
To merge this branch: bzr merge lp:~gundlach/nova/controllers-in-api
Reviewer Review Type Date Requested Status
Jay Pipes (community) Approve
Review via email: mp+33698@code.launchpad.net

Description of the change

Add Flavors controller supporting

GET /flavors

GET /flavors/detail

GET /flavors/<id>

Also add GET /images/detail

Turn the RackspaceAPIImageIdTranslator into a RackspaceAPIIdTranslator, so that it can be used to translate IDs for other rackspace API components as well (servers, backup schedules.) I thought I'd need it for flavors but it turns out flavors are so simple I could hard code their ids into compute.instance_types.INSTANCE_TYPES.

To post a comment you must log in.
Revision history for this message
Jay Pipes (jaypipes) wrote :

Yep, looks great.

Wondering why you chose to make _id_translator.py prefixed with an underscore...is that file meant to be a private utility? Is there some reason you didn't want it to be part of the set of "public" Python classes?

review: Approve
Revision history for this message
Michael Gundlach (gundlach) wrote :

Yes, I didn't want api.rackspace.id_translator to exist along with "servers|images|flavors|sharedipgroups" which are all controllers.

I could have put that class into api.rackspace.__init__ I guess with a _ prepended, but, whatever :)

lp:~gundlach/nova/controllers-in-api updated
266. By Michael Gundlach

get to look like trunk

267. By Michael Gundlach

work endpoint/images.py into an S3ImageService. The translation isn't perfect, but it's a start.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'nova/api/rackspace/__init__.py'
--- nova/api/rackspace/__init__.py 2010-08-18 15:56:33 +0000
+++ nova/api/rackspace/__init__.py 2010-08-25 21:50:55 +0000
@@ -74,8 +74,10 @@
74 def __init__(self):74 def __init__(self):
75 mapper = routes.Mapper()75 mapper = routes.Mapper()
76 mapper.resource("server", "servers", controller=servers.Controller())76 mapper.resource("server", "servers", controller=servers.Controller())
77 mapper.resource("image", "images", controller=images.Controller())77 mapper.resource("image", "images", controller=images.Controller(),
78 mapper.resource("flavor", "flavors", controller=flavors.Controller())78 collection={'detail': 'GET'})
79 mapper.resource("flavor", "flavors", controller=flavors.Controller(),
80 collection={'detail': 'GET'})
79 mapper.resource("sharedipgroup", "sharedipgroups",81 mapper.resource("sharedipgroup", "sharedipgroups",
80 controller=sharedipgroups.Controller())82 controller=sharedipgroups.Controller())
81 super(APIRouter, self).__init__(mapper)83 super(APIRouter, self).__init__(mapper)
8284
=== added file 'nova/api/rackspace/_id_translator.py'
--- nova/api/rackspace/_id_translator.py 1970-01-01 00:00:00 +0000
+++ nova/api/rackspace/_id_translator.py 2010-08-25 21:50:55 +0000
@@ -0,0 +1,42 @@
1from nova import datastore
2
3class RackspaceAPIIdTranslator(object):
4 """
5 Converts Rackspace API ids to and from the id format for a given
6 strategy.
7 """
8
9 def __init__(self, id_type, service_name):
10 """
11 Creates a translator for ids of the given type (e.g. 'flavor'), for the
12 given storage service backend class name (e.g. 'LocalFlavorService').
13 """
14
15 self._store = datastore.Redis.instance()
16 key_prefix = "rsapi.idtranslator.%s.%s" % (id_type, service_name)
17 # Forward (strategy format -> RS format) and reverse translation keys
18 self._fwd_key = "%s.fwd" % key_prefix
19 self._rev_key = "%s.rev" % key_prefix
20
21 def to_rs_id(self, opaque_id):
22 """Convert an id from a strategy-specific one to a Rackspace one."""
23 result = self._store.hget(self._fwd_key, str(opaque_id))
24 if result: # we have a mapping from opaque to RS for this strategy
25 return int(result)
26 else:
27 # Store the mapping.
28 nextid = self._store.incr("%s.lastid" % self._fwd_key)
29 if self._store.hsetnx(self._fwd_key, str(opaque_id), nextid):
30 # If someone else didn't beat us to it, store the reverse
31 # mapping as well.
32 self._store.hset(self._rev_key, nextid, str(opaque_id))
33 return nextid
34 else:
35 # Someone beat us to it; use their number instead, and
36 # discard nextid (which is OK -- we don't require that
37 # every int id be used.)
38 return int(self._store.hget(self._fwd_key, str(opaque_id)))
39
40 def from_rs_id(self, strategy_name, rs_id):
41 """Convert a Rackspace id to a strategy-specific one."""
42 return self._store.hget(self._rev_key, rs_id)
043
=== modified file 'nova/api/rackspace/flavors.py'
--- nova/api/rackspace/flavors.py 2010-08-18 15:56:33 +0000
+++ nova/api/rackspace/flavors.py 2010-08-25 21:50:55 +0000
@@ -15,4 +15,40 @@
15# License for the specific language governing permissions and limitations15# License for the specific language governing permissions and limitations
16# under the License.16# under the License.
1717
18class Controller(object): pass18from nova.api.rackspace import base
19from nova.compute import instance_types
20from webob import exc
21
22class Controller(base.Controller):
23 """Flavor controller for the Rackspace API."""
24
25 _serialization_metadata = {
26 'application/xml': {
27 "attributes": {
28 "flavor": [ "id", "name", "ram", "disk" ]
29 }
30 }
31 }
32
33 def index(self, req):
34 """Return all flavors in brief."""
35 return dict(flavors=[dict(id=flavor['id'], name=flavor['name'])
36 for flavor in self.detail(req)['flavors']])
37
38 def detail(self, req):
39 """Return all flavors in detail."""
40 items = [self.show(req, id)['flavor'] for id in self._all_ids()]
41 return dict(flavors=items)
42
43 def show(self, req, id):
44 """Return data about the given flavor id."""
45 for name, val in instance_types.INSTANCE_TYPES.iteritems():
46 if val['flavorid'] == int(id):
47 item = dict(ram=val['memory_mb'], disk=val['local_gb'],
48 id=val['flavorid'], name=name)
49 return dict(flavor=item)
50 raise exc.HTTPNotFound()
51
52 def _all_ids(self):
53 """Return the list of all flavorids."""
54 return [i['flavorid'] for i in instance_types.INSTANCE_TYPES.values()]
1955
=== modified file 'nova/api/rackspace/images.py'
--- nova/api/rackspace/images.py 2010-08-24 20:24:24 +0000
+++ nova/api/rackspace/images.py 2010-08-25 21:50:55 +0000
@@ -15,9 +15,9 @@
15# License for the specific language governing permissions and limitations15# License for the specific language governing permissions and limitations
16# under the License.16# under the License.
1717
18from nova import datastore18import nova.image.service
19from nova import image
20from nova.api.rackspace import base19from nova.api.rackspace import base
20from nova.api.rackspace import _id_translator
21from webob import exc21from webob import exc
2222
23class Controller(base.Controller):23class Controller(base.Controller):
@@ -32,35 +32,25 @@
32 }32 }
3333
34 def __init__(self):34 def __init__(self):
35 self._service = image.service.ImageService.load()35 self._service = nova.image.service.ImageService.load()
36 self._id_translator = RackspaceAPIImageIdTranslator()36 self._id_translator = _id_translator.RackspaceAPIIdTranslator(
3737 "image", self._service.__class__.__name__)
38 def _to_rs_id(self, image_id):
39 """
40 Convert an image id from the format of our ImageService strategy
41 to the Rackspace API format (an int).
42 """
43 strategy = self._service.__class__.__name__
44 return self._id_translator.to_rs_id(strategy, image_id)
45
46 def _from_rs_id(self, rs_image_id):
47 """
48 Convert an image id from the Rackspace API format (an int) to the
49 format of our ImageService strategy.
50 """
51 strategy = self._service.__class__.__name__
52 return self._id_translator.from_rs_id(strategy, rs_image_id)
5338
54 def index(self, req):39 def index(self, req):
55 """Return all public images."""40 """Return all public images in brief."""
41 return dict(images=[dict(id=img['id'], name=img['name'])
42 for img in self.detail(req)['images']])
43
44 def detail(self, req):
45 """Return all public images in detail."""
56 data = self._service.index()46 data = self._service.index()
57 for img in data:47 for img in data:
58 img['id'] = self._to_rs_id(img['id'])48 img['id'] = self._id_translator.to_rs_id(img['id'])
59 return dict(images=data)49 return dict(images=data)
6050
61 def show(self, req, id):51 def show(self, req, id):
62 """Return data about the given image id."""52 """Return data about the given image id."""
63 opaque_id = self._from_rs_id(id)53 opaque_id = self._id_translator.from_rs_id(id)
64 img = self._service.show(opaque_id)54 img = self._service.show(opaque_id)
65 img['id'] = id55 img['id'] = id
66 return dict(image=img)56 return dict(image=img)
@@ -78,40 +68,3 @@
78 # Users may not modify public images, and that's all that 68 # Users may not modify public images, and that's all that
79 # we support for now.69 # we support for now.
80 raise exc.HTTPNotFound()70 raise exc.HTTPNotFound()
81
82
83class RackspaceAPIImageIdTranslator(object):
84 """
85 Converts Rackspace API image ids to and from the id format for a given
86 strategy.
87 """
88
89 def __init__(self):
90 self._store = datastore.Redis.instance()
91 self._key_template = "rsapi.idstrategies.image.%s.%s"
92
93 def to_rs_id(self, strategy_name, opaque_id):
94 """Convert an id from a strategy-specific one to a Rackspace one."""
95 key = self._key_template % (strategy_name, "fwd")
96 result = self._store.hget(key, str(opaque_id))
97 if result: # we have a mapping from opaque to RS for this strategy
98 return int(result)
99 else:
100 # Store the mapping.
101 nextid = self._store.incr("%s.lastid" % key)
102 if self._store.hsetnx(key, str(opaque_id), nextid):
103 # If someone else didn't beat us to it, store the reverse
104 # mapping as well.
105 key = self._key_template % (strategy_name, "rev")
106 self._store.hset(key, nextid, str(opaque_id))
107 return nextid
108 else:
109 # Someone beat us to it; use their number instead, and
110 # discard nextid (which is OK -- we don't require that
111 # every int id be used.)
112 return int(self._store.hget(key, str(opaque_id)))
113
114 def from_rs_id(self, strategy_name, rs_id):
115 """Convert a Rackspace id to a strategy-specific one."""
116 key = self._key_template % (strategy_name, "rev")
117 return self._store.hget(key, rs_id)
11871
=== modified file 'nova/compute/instance_types.py'
--- nova/compute/instance_types.py 2010-07-18 17:15:12 +0000
+++ nova/compute/instance_types.py 2010-08-25 21:50:55 +0000
@@ -21,10 +21,10 @@
21The built-in instance properties.21The built-in instance properties.
22"""22"""
2323
24INSTANCE_TYPES = {}24INSTANCE_TYPES = {
25INSTANCE_TYPES['m1.tiny'] = {'memory_mb': 512, 'vcpus': 1, 'local_gb': 0}25 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1),
26INSTANCE_TYPES['m1.small'] = {'memory_mb': 1024, 'vcpus': 1, 'local_gb': 10}26 'm1.small': dict(memory_mb=1024, vcpus=1, local_gb=10, flavorid=2),
27INSTANCE_TYPES['m1.medium'] = {'memory_mb': 2048, 'vcpus': 2, 'local_gb': 10}27 'm1.medium': dict(memory_mb=2048, vcpus=2, local_gb=10, flavorid=3),
28INSTANCE_TYPES['m1.large'] = {'memory_mb': 4096, 'vcpus': 4, 'local_gb': 10}28 'm1.large': dict(memory_mb=4096, vcpus=4, local_gb=10, flavorid=4),
29INSTANCE_TYPES['m1.xlarge'] = {'memory_mb': 8192, 'vcpus': 4, 'local_gb': 10}29 'm1.xlarge': dict(memory_mb=8192, vcpus=4, local_gb=10, flavorid=5),
30INSTANCE_TYPES['c1.medium'] = {'memory_mb': 2048, 'vcpus': 4, 'local_gb': 10}30 'c1.medium': dict(memory_mb=2048, vcpus=4, local_gb=10, flavorid=6)}
3131
=== added file 'nova/image/__init__.py'