summaryrefslogtreecommitdiffstats
path: root/nova/image
diff options
context:
space:
mode:
authorjaypipes@gmail.com <>2010-10-05 20:38:43 +0000
committerTarmac <>2010-10-05 20:38:43 +0000
commit8e89d47958fc0e680582804ec07152ca05039854 (patch)
tree8ba2cbd584a998505250fd226ec2abbe8f6829f1 /nova/image
parent15cf92206627f2f56d30356ca974018d5b2244e9 (diff)
parentfbd1bc015bd5615963b9073eefb895ea04c55a3e (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.py223
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))