From b651008e7e4f60f2ccb07497c27d866814156209 Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Thu, 19 Aug 2010 16:05:27 -0400 Subject: 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.) --- nova/api/rackspace/images.py | 83 +++++++++++++++++++++++++++++-------------- nova/api/rackspace/notes.txt | 23 ++++++++++++ nova/api/services/__init__.py | 0 nova/api/services/image.py | 72 +++++++++++++++++++++++++++++++++++++ 4 files changed, 151 insertions(+), 27 deletions(-) create mode 100644 nova/api/rackspace/notes.txt create mode 100644 nova/api/services/__init__.py create mode 100644 nova/api/services/image.py (limited to 'nova/api') 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 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)) -- cgit From d38f21e0fb382bd8f01cfbc79cb34ea8710cd639 Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Mon, 23 Aug 2010 10:27:59 -0400 Subject: License --- nova/api/services/image.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'nova/api') diff --git a/nova/api/services/image.py b/nova/api/services/image.py index c5ea15ba1..bda50fc66 100644 --- a/nova/api/services/image.py +++ b/nova/api/services/image.py @@ -1,3 +1,20 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + import cPickle as pickle import os.path import string -- cgit From e3727d6d88a0631d3b896c4fcdcfec05510dad36 Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Mon, 23 Aug 2010 12:07:20 -0400 Subject: Support opaque id to rs int id as well --- nova/api/rackspace/images.py | 42 ++++++++++++++++++++++++++++++++---------- nova/api/services/image.py | 8 ++++---- 2 files changed, 36 insertions(+), 14 deletions(-) (limited to 'nova/api') diff --git a/nova/api/rackspace/images.py b/nova/api/rackspace/images.py index e29f737a5..c9cc8e85d 100644 --- a/nova/api/rackspace/images.py +++ b/nova/api/rackspace/images.py @@ -44,18 +44,24 @@ class Controller(base.Controller): strategy = self._svc.__class__.__name__ return self._id_xlator.to_rs_id(strategy, image_id) + def _from_rs_id(self, rs_image_id): + """ + Convert an image id from the Rackspace API format (an int) to the + format of our ImageService strategy. + """ + strategy = self._svc.__class__.__name__ + return self._id_xlator.from_rs_id(strategy, rs_image_id) + def index(self, req): """Return all public images.""" - data = self._svc.list() - for img in data: - img['id'] = self._to_rs_id(img['id']) - return dict(images=result) + data = dict((self._to_rs_id(id), val) + for id, val in self._svc.index().iteritems()) + return dict(images=data) def show(self, req, 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) + opaque_id = self._from_rs_id(id) + return dict(image=self._svc.show(opaque_id)) def delete(self, req, id): # Only public images are supported for now. @@ -80,14 +86,30 @@ class RackspaceApiImageIdTranslator(object): def __init__(self): self._store = datastore.Redis.instance() + self._key_template = "rsapi.idstrategies.image.%s.%s" 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 + key = self._key_template % (strategy_name, "fwd") 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: + # Store the mapping. nextid = self._store.incr("%s.lastid" % key) - self._store.hsetnx(key, str(opaque_id), nextid) - return nextid + if self._store.hsetnx(key, str(opaque_id), nextid): + # If someone else didn't beat us to it, store the reverse + # mapping as well. + key = self._key_template % (strategy_name, "rev") + self._store.hset(key, nextid, str(opaque_id)) + return nextid + else: + # Someone beat us to it; use their number instead, and + # discard nextid (which is OK -- we don't require that + # every int id be used.) + return int(self._store.hget(key, str(opaque_id))) + + def from_rs_id(self, strategy_name, rs_id): + """Convert a Rackspace id to a strategy-specific one.""" + key = self._key_template % (strategy_name, "rev") + return self._store.hget(key, rs_id) diff --git a/nova/api/services/image.py b/nova/api/services/image.py index bda50fc66..11e19804a 100644 --- a/nova/api/services/image.py +++ b/nova/api/services/image.py @@ -17,6 +17,7 @@ import cPickle as pickle import os.path +import random import string class ImageService(object): @@ -31,8 +32,7 @@ class ImageService(object): def index(self): """ - Return a list of image data dicts. Each dict will contain an - id key whose value is an opaque image id. + Return a dict from opaque image id to image data. """ def show(self, id): @@ -62,10 +62,10 @@ class LocalImageService(ImageService): def _ids(self): """The list of all image ids.""" - return os.path.listdir(self._path) + return os.listdir(self._path) def index(self): - return [ self.show(id) for id in self._ids() ] + return dict((id, self.show(id)) for id in self._ids()) def show(self, id): return pickle.load(open(self._path_to(id))) -- cgit From 030d01fd10f7f65cdafbea49e04f3b6b147a7348 Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Mon, 23 Aug 2010 12:46:29 -0400 Subject: Serialize properly --- nova/api/rackspace/base.py | 3 ++- nova/api/rackspace/images.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'nova/api') diff --git a/nova/api/rackspace/base.py b/nova/api/rackspace/base.py index c85fd7b8e..b995d9acc 100644 --- a/nova/api/rackspace/base.py +++ b/nova/api/rackspace/base.py @@ -36,4 +36,5 @@ class Controller(wsgi.Controller): MIME types to information needed to serialize to that type. """ _metadata = getattr(type(self), "_serialization_metadata", {}) - return Serializer(request.environ, _metadata).to_content_type(data) + serializer = wsgi.Serializer(request.environ, _metadata) + return serializer.to_content_type(data) diff --git a/nova/api/rackspace/images.py b/nova/api/rackspace/images.py index c9cc8e85d..62e0b24c5 100644 --- a/nova/api/rackspace/images.py +++ b/nova/api/rackspace/images.py @@ -56,12 +56,12 @@ class Controller(base.Controller): """Return all public images.""" data = dict((self._to_rs_id(id), val) for id, val in self._svc.index().iteritems()) - return dict(images=data) + return self.serialize(dict(images=data), req) def show(self, req, id): """Return data about the given image id.""" opaque_id = self._from_rs_id(id) - return dict(image=self._svc.show(opaque_id)) + return self.serialize(dict(image=self._svc.show(opaque_id)), req) def delete(self, req, id): # Only public images are supported for now. -- cgit From a50a200bc2547439a3da17e695224d3d434e14dd Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Mon, 23 Aug 2010 12:55:57 -0400 Subject: Move serialize() to wsgi.Controller so __call__ can serialize() action return values if they are dicts. --- nova/api/rackspace/base.py | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'nova/api') diff --git a/nova/api/rackspace/base.py b/nova/api/rackspace/base.py index b995d9acc..51841925e 100644 --- a/nova/api/rackspace/base.py +++ b/nova/api/rackspace/base.py @@ -28,13 +28,3 @@ class Controller(wsgi.Controller): return {cls.entity_name: cls.render(instance)} else: return { "TODO": "TODO" } - - def serialize(self, data, request): - """ - Serialize the given dict to the response type requested in request. - Uses self._serialization_metadata if it exists, which is a dict mapping - MIME types to information needed to serialize to that type. - """ - _metadata = getattr(type(self), "_serialization_metadata", {}) - serializer = wsgi.Serializer(request.environ, _metadata) - return serializer.to_content_type(data) -- cgit From f5c03fdd78a3bb8233e465c7624ed1fdb8f400fe Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Mon, 23 Aug 2010 13:06:40 -0400 Subject: Don't serialize in Controller subclass now that wsgi.Controller handles it for us --- nova/api/rackspace/images.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'nova/api') diff --git a/nova/api/rackspace/images.py b/nova/api/rackspace/images.py index 62e0b24c5..070100143 100644 --- a/nova/api/rackspace/images.py +++ b/nova/api/rackspace/images.py @@ -20,7 +20,6 @@ from nova.api.rackspace import base from nova.api.services.image import ImageService from webob import exc -#TODO(gundlach): Serialize return values class Controller(base.Controller): _serialization_metadata = { @@ -56,12 +55,12 @@ class Controller(base.Controller): """Return all public images.""" data = dict((self._to_rs_id(id), val) for id, val in self._svc.index().iteritems()) - return self.serialize(dict(images=data), req) + return dict(images=data) def show(self, req, id): """Return data about the given image id.""" opaque_id = self._from_rs_id(id) - return self.serialize(dict(image=self._svc.show(opaque_id)), req) + return dict(image=self._svc.show(opaque_id)) def delete(self, req, id): # Only public images are supported for now. -- cgit From 35a08780c41ece1b47b2ded98c061b103a400fea Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Mon, 23 Aug 2010 13:26:10 -0400 Subject: Get the output formatting correct. --- nova/api/rackspace/images.py | 9 ++++++--- nova/api/services/image.py | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'nova/api') diff --git a/nova/api/rackspace/images.py b/nova/api/rackspace/images.py index 070100143..7d32fa099 100644 --- a/nova/api/rackspace/images.py +++ b/nova/api/rackspace/images.py @@ -53,14 +53,17 @@ class Controller(base.Controller): def index(self, req): """Return all public images.""" - data = dict((self._to_rs_id(id), val) - for id, val in self._svc.index().iteritems()) + data = self._svc.index() + for img in data: + img['id'] = self._to_rs_id(img['id']) return dict(images=data) def show(self, req, id): """Return data about the given image id.""" opaque_id = self._from_rs_id(id) - return dict(image=self._svc.show(opaque_id)) + img = self._svc.show(opaque_id) + img['id'] = id + return dict(image=img) def delete(self, req, id): # Only public images are supported for now. diff --git a/nova/api/services/image.py b/nova/api/services/image.py index 11e19804a..1a7a258b7 100644 --- a/nova/api/services/image.py +++ b/nova/api/services/image.py @@ -65,7 +65,7 @@ class LocalImageService(ImageService): return os.listdir(self._path) def index(self): - return dict((id, self.show(id)) for id in self._ids()) + return [ self.show(id) for id in self._ids() ] def show(self, id): return pickle.load(open(self._path_to(id))) @@ -75,6 +75,7 @@ class LocalImageService(ImageService): Store the image data and return the new image id. """ id = ''.join(random.choice(string.letters) for _ in range(20)) + data['id'] = id self.update(id, data) return id -- cgit From 41e2e91ccfb1409f1ea47d92a9d15f47ab37e65d Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Mon, 23 Aug 2010 16:43:25 -0400 Subject: Merge fail --- nova/api/rackspace/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/rackspace/base.py b/nova/api/rackspace/base.py index 51841925e..dd2c6543c 100644 --- a/nova/api/rackspace/base.py +++ b/nova/api/rackspace/base.py @@ -27,4 +27,4 @@ class Controller(wsgi.Controller): if isinstance(instance, list): return {cls.entity_name: cls.render(instance)} else: - return { "TODO": "TODO" } + return {"TODO": "TODO"} -- cgit From d94eec3d2995c97c38006e4d6177740375860f8f Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Tue, 24 Aug 2010 11:19:51 -0400 Subject: Style fixes --- nova/api/rackspace/images.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'nova/api') diff --git a/nova/api/rackspace/images.py b/nova/api/rackspace/images.py index 7d32fa099..36a26688c 100644 --- a/nova/api/rackspace/images.py +++ b/nova/api/rackspace/images.py @@ -17,7 +17,7 @@ from nova import datastore from nova.api.rackspace import base -from nova.api.services.image import ImageService +from nova.api.services import image from webob import exc class Controller(base.Controller): @@ -32,28 +32,28 @@ class Controller(base.Controller): } def __init__(self): - self._svc = ImageService.load() - self._id_xlator = RackspaceApiImageIdTranslator() + self._service = image.ImageService.load() + self._id_translator = 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) + strategy = self._service.__class__.__name__ + return self._id_translator.to_rs_id(strategy, image_id) def _from_rs_id(self, rs_image_id): """ Convert an image id from the Rackspace API format (an int) to the format of our ImageService strategy. """ - strategy = self._svc.__class__.__name__ - return self._id_xlator.from_rs_id(strategy, rs_image_id) + strategy = self._service.__class__.__name__ + return self._id_translator.from_rs_id(strategy, rs_image_id) def index(self, req): """Return all public images.""" - data = self._svc.index() + data = self._service.index() for img in data: img['id'] = self._to_rs_id(img['id']) return dict(images=data) @@ -61,7 +61,7 @@ class Controller(base.Controller): def show(self, req, id): """Return data about the given image id.""" opaque_id = self._from_rs_id(id) - img = self._svc.show(opaque_id) + img = self._service.show(opaque_id) img['id'] = id return dict(image=img) @@ -80,7 +80,7 @@ class Controller(base.Controller): raise exc.HTTPNotFound() -class RackspaceApiImageIdTranslator(object): +class RackspaceAPIImageIdTranslator(object): """ Converts Rackspace API image ids to and from the id format for a given strategy. -- cgit From 09bc71460b976f28c7bc6a507006d6c7c12c5824 Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Tue, 24 Aug 2010 16:16:41 -0400 Subject: Move imageservice to its own directory --- nova/api/rackspace/images.py | 4 +- nova/api/services/image.py | 90 -------------------------------------------- 2 files changed, 2 insertions(+), 92 deletions(-) delete mode 100644 nova/api/services/image.py (limited to 'nova/api') diff --git a/nova/api/rackspace/images.py b/nova/api/rackspace/images.py index 36a26688c..b7668a1e1 100644 --- a/nova/api/rackspace/images.py +++ b/nova/api/rackspace/images.py @@ -17,7 +17,7 @@ from nova import datastore from nova.api.rackspace import base -from nova.api.services import image +from nova import image from webob import exc class Controller(base.Controller): @@ -32,7 +32,7 @@ class Controller(base.Controller): } def __init__(self): - self._service = image.ImageService.load() + self._service = image.service.ImageService.load() self._id_translator = RackspaceAPIImageIdTranslator() def _to_rs_id(self, image_id): diff --git a/nova/api/services/image.py b/nova/api/services/image.py deleted file mode 100644 index 1a7a258b7..000000000 --- a/nova/api/services/image.py +++ /dev/null @@ -1,90 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import cPickle as pickle -import os.path -import random -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 dict from opaque image id to image data. - """ - - 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.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)) - data['id'] = id - 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)) -- cgit From 5f832cd5ea9fb858f5e8b09992cbd47d8d16f665 Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Tue, 24 Aug 2010 16:17:06 -0400 Subject: Delete unused directory --- nova/api/services/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 nova/api/services/__init__.py (limited to 'nova/api') diff --git a/nova/api/services/__init__.py b/nova/api/services/__init__.py deleted file mode 100644 index e69de29bb..000000000 -- cgit From 96ae5e2776218adfee2cb22a4c0d7358a498a451 Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Tue, 24 Aug 2010 16:24:24 -0400 Subject: pep8 --- nova/api/rackspace/images.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/rackspace/images.py b/nova/api/rackspace/images.py index b7668a1e1..370980fe9 100644 --- a/nova/api/rackspace/images.py +++ b/nova/api/rackspace/images.py @@ -16,8 +16,8 @@ # under the License. from nova import datastore -from nova.api.rackspace import base from nova import image +from nova.api.rackspace import base from webob import exc class Controller(base.Controller): -- cgit