summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorMichael Gundlach <michael.gundlach@rackspace.com>2010-08-19 16:05:27 -0400
committerMichael Gundlach <michael.gundlach@rackspace.com>2010-08-19 16:05:27 -0400
commitb651008e7e4f60f2ccb07497c27d866814156209 (patch)
treeca88b73944b0eb77fb6f999a5a5700b13f5f104a /nova/api
parent8336a7cc6f9cd3a8b82e8b2cd5017d23b7da387f (diff)
Complete the Image API against a LocalImageService until Glance's API exists (at which point we'll make a GlanceImageService and make the choice of ImageService plugin configurable.)
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/rackspace/images.py83
-rw-r--r--nova/api/rackspace/notes.txt23
-rw-r--r--nova/api/services/__init__.py0
-rw-r--r--nova/api/services/image.py72
4 files changed, 151 insertions, 27 deletions
diff --git a/nova/api/rackspace/images.py b/nova/api/rackspace/images.py
index 57c03894a..e29f737a5 100644
--- a/nova/api/rackspace/images.py
+++ b/nova/api/rackspace/images.py
@@ -15,12 +15,13 @@
# License for the specific language governing permissions and limitations
# under the License.
-from nova.endpoint.rackspace.controllers.base import BaseController
-from nova.endpoint import images
+from nova import datastore
+from nova.api.rackspace import base
+from nova.api.services.image import ImageService
from webob import exc
#TODO(gundlach): Serialize return values
-class Controller(BaseController):
+class Controller(base.Controller):
_serialization_metadata = {
'application/xml': {
@@ -31,34 +32,62 @@ class Controller(BaseController):
}
}
+ def __init__(self):
+ self._svc = ImageService.load()
+ self._id_xlator = RackspaceApiImageIdTranslator()
+
+ def _to_rs_id(self, image_id):
+ """
+ Convert an image id from the format of our ImageService strategy
+ to the Rackspace API format (an int).
+ """
+ strategy = self._svc.__class__.__name__
+ return self._id_xlator.to_rs_id(strategy, image_id)
+
def index(self, req):
- context = req.environ['nova.api_request_context']
- return images.list(context)
+ """Return all public images."""
+ data = self._svc.list()
+ for img in data:
+ img['id'] = self._to_rs_id(img['id'])
+ return dict(images=result)
def show(self, req, id):
- context = req.environ['nova.api_request_context']
- return images.list(context, filter_list=[id])
+ """Return data about the given image id."""
+ img = self._svc.show(id)
+ img['id'] = self._to_rs_id(img['id'])
+ return dict(image=img)
def delete(self, req, id):
- context = req.environ['nova.api_request_context']
- # TODO(gundlach): make sure it's an image they may delete?
- return images.deregister(context, id)
+ # Only public images are supported for now.
+ raise exc.HTTPNotFound()
+
+ def create(self, req):
+ # Only public images are supported for now, so a request to
+ # make a backup of a server cannot be supproted.
+ raise exc.HTTPNotFound()
+
+ def update(self, req, id):
+ # Users may not modify public images, and that's all that
+ # we support for now.
+ raise exc.HTTPNotFound()
+
+
+class RackspaceApiImageIdTranslator(object):
+ """
+ Converts Rackspace API image ids to and from the id format for a given
+ strategy.
+ """
- def create(self, **kwargs):
- # TODO(gundlach): no idea how to hook this up. code below
- # is from servers.py.
- inst = self.build_server_instance(kwargs['server'])
- rpc.cast(
- FLAGS.compute_topic, {
- "method": "run_instance",
- "args": {"instance_id": inst.instance_id}})
+ def __init__(self):
+ self._store = datastore.Redis.instance()
- def update(self, **kwargs):
- # TODO (gundlach): no idea how to hook this up. code below
- # is from servers.py.
- instance_id = kwargs['id']
- instance = compute.InstanceDirectory().get(instance_id)
- if not instance:
- raise ServerNotFound("The requested server was not found")
- instance.update(kwargs['server'])
- instance.save()
+ def to_rs_id(self, strategy_name, opaque_id):
+ """Convert an id from a strategy-specific one to a Rackspace one."""
+ key = "rsapi.idstrategies.image.%s" % strategy_name
+ result = self._store.hget(key, str(opaque_id))
+ if result: # we have a mapping from opaque to RS for this strategy
+ return int(result)
+ else:
+ nextid = self._store.incr("%s.lastid" % key)
+ self._store.hsetnx(key, str(opaque_id), nextid)
+ return nextid
diff --git a/nova/api/rackspace/notes.txt b/nova/api/rackspace/notes.txt
new file mode 100644
index 000000000..e133bf5ea
--- /dev/null
+++ b/nova/api/rackspace/notes.txt
@@ -0,0 +1,23 @@
+We will need:
+
+ImageService
+a service that can do crud on image information. not user-specific. opaque
+image ids.
+
+GlanceImageService(ImageService):
+image ids are URIs.
+
+LocalImageService(ImageService):
+image ids are random strings.
+
+RackspaceAPITranslationStore:
+translates RS server/images/flavor/etc ids into formats required
+by a given ImageService strategy.
+
+api.rackspace.images.Controller:
+uses an ImageService strategy behind the scenes to do its fetching; it just
+converts int image id into a strategy-specific image id.
+
+who maintains the mapping from user to [images he owns]? nobody, because
+we have no way of enforcing access to his images, without kryptex which
+won't be in Austin.
diff --git a/nova/api/services/__init__.py b/nova/api/services/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/nova/api/services/__init__.py
diff --git a/nova/api/services/image.py b/nova/api/services/image.py
new file mode 100644
index 000000000..c5ea15ba1
--- /dev/null
+++ b/nova/api/services/image.py
@@ -0,0 +1,72 @@
+import cPickle as pickle
+import os.path
+import string
+
+class ImageService(object):
+ """Provides storage and retrieval of disk image objects."""
+
+ @staticmethod
+ def load():
+ """Factory method to return image service."""
+ #TODO(gundlach): read from config.
+ class_ = LocalImageService
+ return class_()
+
+ def index(self):
+ """
+ Return a list of image data dicts. Each dict will contain an
+ id key whose value is an opaque image id.
+ """
+
+ def show(self, id):
+ """
+ Returns a dict containing image data for the given opaque image id.
+ """
+
+
+class GlanceImageService(ImageService):
+ """Provides storage and retrieval of disk image objects within Glance."""
+ # TODO(gundlach): once Glance has an API, build this.
+ pass
+
+
+class LocalImageService(ImageService):
+ """Image service storing images to local disk."""
+
+ def __init__(self):
+ self._path = "/tmp/nova/images"
+ try:
+ os.makedirs(self._path)
+ except OSError: # exists
+ pass
+
+ def _path_to(self, image_id=''):
+ return os.path.join(self._path, image_id)
+
+ def _ids(self):
+ """The list of all image ids."""
+ return os.path.listdir(self._path)
+
+ def index(self):
+ return [ self.show(id) for id in self._ids() ]
+
+ def show(self, id):
+ return pickle.load(open(self._path_to(id)))
+
+ def create(self, data):
+ """
+ Store the image data and return the new image id.
+ """
+ id = ''.join(random.choice(string.letters) for _ in range(20))
+ self.update(id, data)
+ return id
+
+ def update(self, image_id, data):
+ """Replace the contents of the given image with the new data."""
+ pickle.dump(data, open(self._path_to(image_id), 'w'))
+
+ def delete(self, image_id):
+ """
+ Delete the given image. Raises OSError if the image does not exist.
+ """
+ os.unlink(self._path_to(image_id))