diff options
| author | Michael Gundlach <michael.gundlach@rackspace.com> | 2010-08-19 16:05:27 -0400 |
|---|---|---|
| committer | Michael Gundlach <michael.gundlach@rackspace.com> | 2010-08-19 16:05:27 -0400 |
| commit | b651008e7e4f60f2ccb07497c27d866814156209 (patch) | |
| tree | ca88b73944b0eb77fb6f999a5a5700b13f5f104a /nova/api | |
| parent | 8336a7cc6f9cd3a8b82e8b2cd5017d23b7da387f (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.py | 83 | ||||
| -rw-r--r-- | nova/api/rackspace/notes.txt | 23 | ||||
| -rw-r--r-- | nova/api/services/__init__.py | 0 | ||||
| -rw-r--r-- | nova/api/services/image.py | 72 |
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)) |
