diff options
| author | jaypipes@gmail.com <> | 2010-10-05 20:38:43 +0000 |
|---|---|---|
| committer | Tarmac <> | 2010-10-05 20:38:43 +0000 |
| commit | 8e89d47958fc0e680582804ec07152ca05039854 (patch) | |
| tree | 8ba2cbd584a998505250fd226ec2abbe8f6829f1 /nova/image | |
| parent | 15cf92206627f2f56d30356ca974018d5b2244e9 (diff) | |
| parent | fbd1bc015bd5615963b9073eefb895ea04c55a3e (diff) | |
Adds stubs and tests for GlanceImageService and LocalImageService.
Adds basic plumbing for ParallaxClient and TellerClient and hooks that into the GlanceImageService.
Fixes lp654843
Diffstat (limited to 'nova/image')
| -rw-r--r-- | nova/image/service.py | 223 |
1 files changed, 208 insertions, 15 deletions
diff --git a/nova/image/service.py b/nova/image/service.py index 1a7a258b7..2e570e8a4 100644 --- a/nova/image/service.py +++ b/nova/image/service.py @@ -16,38 +16,215 @@ # under the License. import cPickle as pickle +import httplib +import json +import logging import os.path import random import string +import urlparse -class ImageService(object): - """Provides storage and retrieval of disk image objects.""" +import webob.exc - @staticmethod - def load(): - """Factory method to return image service.""" - #TODO(gundlach): read from config. - class_ = LocalImageService - return class_() +from nova import utils +from nova import flags +from nova import exception + + +FLAGS = flags.FLAGS + + +flags.DEFINE_string('glance_teller_address', 'http://127.0.0.1', + 'IP address or URL where Glance\'s Teller service resides') +flags.DEFINE_string('glance_teller_port', '9191', + 'Port for Glance\'s Teller service') +flags.DEFINE_string('glance_parallax_address', 'http://127.0.0.1', + 'IP address or URL where Glance\'s Parallax service resides') +flags.DEFINE_string('glance_parallax_port', '9292', + 'Port for Glance\'s Parallax service') + + +class BaseImageService(object): + + """Base class for providing image search and retrieval services""" def index(self): """ Return a dict from opaque image id to image data. """ + raise NotImplementedError def show(self, id): """ Returns a dict containing image data for the given opaque image id. + + :raises NotFound if the image does not exist + """ + raise NotImplementedError + + def create(self, data): + """ + Store the image data and return the new image id. + + :raises AlreadyExists if the image already exist. + + """ + raise NotImplementedError + + def update(self, image_id, data): + """Replace the contents of the given image with the new data. + + :raises NotFound if the image does not exist. + + """ + raise NotImplementedError + + def delete(self, image_id): + """ + Delete the given image. + + :raises NotFound if the image does not exist. + + """ + raise NotImplementedError + + +class TellerClient(object): + + def __init__(self): + self.address = FLAGS.glance_teller_address + self.port = FLAGS.glance_teller_port + url = urlparse.urlparse(self.address) + self.netloc = url.netloc + self.connection_type = {'http': httplib.HTTPConnection, + 'https': httplib.HTTPSConnection}[url.scheme] + + +class ParallaxClient(object): + + def __init__(self): + self.address = FLAGS.glance_parallax_address + self.port = FLAGS.glance_parallax_port + url = urlparse.urlparse(self.address) + self.netloc = url.netloc + self.connection_type = {'http': httplib.HTTPConnection, + 'https': httplib.HTTPSConnection}[url.scheme] + + def get_images(self): + """ + Returns a list of image data mappings from Parallax + """ + try: + c = self.connection_type(self.netloc, self.port) + c.request("GET", "images") + res = c.getresponse() + if res.status == 200: + # Parallax returns a JSONified dict(images=image_list) + data = json.loads(res.read())['images'] + return data + else: + logging.warn("Parallax returned HTTP error %d from " + "request for /images", res.status_int) + return [] + finally: + c.close() + + def get_image_metadata(self, image_id): + """ + Returns a mapping of image metadata from Parallax + """ + try: + c = self.connection_type(self.netloc, self.port) + c.request("GET", "images/%s" % image_id) + res = c.getresponse() + if res.status == 200: + # Parallax returns a JSONified dict(image=image_info) + data = json.loads(res.read())['image'] + return data + else: + # TODO(jaypipes): log the error? + return None + finally: + c.close() + + def add_image_metadata(self, image_metadata): + """ + Tells parallax about an image's metadata """ + pass + + def update_image_metadata(self, image_id, image_metadata): + """ + Updates Parallax's information about an image + """ + pass + + def delete_image_metadata(self, image_id): + """ + Deletes Parallax's information about an image + """ + pass -class GlanceImageService(ImageService): +class GlanceImageService(BaseImageService): + """Provides storage and retrieval of disk image objects within Glance.""" - # TODO(gundlach): once Glance has an API, build this. - pass + def __init__(self): + self.teller = TellerClient() + self.parallax = ParallaxClient() + + def index(self): + """ + Calls out to Parallax for a list of images available + """ + images = self.parallax.get_images() + return images + + def show(self, id): + """ + Returns a dict containing image data for the given opaque image id. + """ + image = self.parallax.get_image_metadata(id) + if image: + return image + raise exception.NotFound + + def create(self, data): + """ + Store the image data and return the new image id. + + :raises AlreadyExists if the image already exist. + + """ + return self.parallax.add_image_metadata(data) + + def update(self, image_id, data): + """Replace the contents of the given image with the new data. + + :raises NotFound if the image does not exist. + + """ + self.parallax.update_image_metadata(image_id, data) + + def delete(self, image_id): + """ + Delete the given image. + + :raises NotFound if the image does not exist. + + """ + self.parallax.delete_image_metadata(image_id) + + def delete_all(self): + """ + Clears out all images + """ + pass + + +class LocalImageService(BaseImageService): -class LocalImageService(ImageService): """Image service storing images to local disk.""" def __init__(self): @@ -68,7 +245,10 @@ class LocalImageService(ImageService): return [ self.show(id) for id in self._ids() ] def show(self, id): - return pickle.load(open(self._path_to(id))) + try: + return pickle.load(open(self._path_to(id))) + except IOError: + raise exception.NotFound def create(self, data): """ @@ -81,10 +261,23 @@ class LocalImageService(ImageService): 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')) + try: + pickle.dump(data, open(self._path_to(image_id), 'w')) + except IOError: + raise exception.NotFound def delete(self, image_id): """ Delete the given image. Raises OSError if the image does not exist. """ - os.unlink(self._path_to(image_id)) + try: + os.unlink(self._path_to(image_id)) + except IOError: + raise exception.NotFound + + def delete_all(self): + """ + Clears out all images in local directory + """ + for f in os.listdir(self._path): + os.unlink(self._path_to(f)) |
