diff options
Diffstat (limited to 'app-admin/glance/files/glance-swift-authv2.patch')
-rw-r--r-- | app-admin/glance/files/glance-swift-authv2.patch | 914 |
1 files changed, 0 insertions, 914 deletions
diff --git a/app-admin/glance/files/glance-swift-authv2.patch b/app-admin/glance/files/glance-swift-authv2.patch deleted file mode 100644 index 7c8f507..0000000 --- a/app-admin/glance/files/glance-swift-authv2.patch +++ /dev/null @@ -1,914 +0,0 @@ -diff -urN glance-2012.1.orig/Authors glance-2012.1/Authors ---- glance-2012.1.orig/Authors 2012-03-21 10:51:12.000000000 +0100 -+++ glance-2012.1/Authors 2012-03-28 17:48:31.000000000 +0200 -@@ -27,6 +27,7 @@ - Johannes Erdfelt <johannes.erdfelt@rackspace.com> - Josh Durgin <josh.durgin@dreamhost.com> - Josh Kearney <josh@jk0.org> -+Juerg Haefliger <juerg.haefliger@hp.com> - Justin Santa Barbara <justin@fathomdb.com> - Justin Shepherd <jshepher@rackspace.com> - Ken Pepple <ken.pepple@gmail.com> -diff -urN glance-2012.1.orig/Authors.orig glance-2012.1/Authors.orig ---- glance-2012.1.orig/Authors.orig 1970-01-01 01:00:00.000000000 +0100 -+++ glance-2012.1/Authors.orig 2012-03-21 10:51:12.000000000 +0100 -@@ -0,0 +1,62 @@ -+Adam Gandelman <adam.gandelman@canonical.com> -+Alex Meade <alex.meade@rackspace.com> -+Andrew Hutchings <andrew@linuxjedi.co.uk> -+Andrey Brindeyev <abrindeyev@griddynamics.com> -+Brian Lamar <brian.lamar@rackspace.com> -+Brian Waldon <brian.waldon@rackspace.com> -+Chris Behrens <cbehrens@codestud.com> -+Christopher MacGown <chris@pistoncloud.com> -+Chuck Short <chuck.short@canonical.com> -+Cory Wright <corywright@gmail.com> -+Dan Prince <dprince@redhat.com> -+Dean Troyer <dtroyer@gmail.com> -+Derek Higgins <derekh@redhat.com> -+Donal Lafferty <donal.lafferty@citrix.com> -+Eldar Nugaev <enugaev@griddynamics.com> -+Eoghan Glynn <eglynn@redhat.com> -+Ewan Mellor <ewan.mellor@citrix.com> -+Gabriel Hurley <gabriel@strikeawe.com> -+Hengqing Hu <hudayou@hotmail.com> -+Isaku Yamahata <yamahata@valinux.co.jp> -+Jason Koelker <jason@koelker.net> -+Jay Pipes <jaypipes@gmail.com> -+James E. Blair <jeblair@hp.com> -+Jesse Andrews <anotherjesse@gmail.com> -+Jinwoo 'Joseph' Suh <jsuh@isi.edu> -+Joe Gordon <jogo@cloudscaling.com> -+Johannes Erdfelt <johannes.erdfelt@rackspace.com> -+Josh Durgin <josh.durgin@dreamhost.com> -+Josh Kearney <josh@jk0.org> -+Justin Santa Barbara <justin@fathomdb.com> -+Justin Shepherd <jshepher@rackspace.com> -+Ken Pepple <ken.pepple@gmail.com> -+Ken Thomas <krt@yahoo-inc.com> -+Kevin L. Mitchell <kevin.mitchell@rackspace.com> -+Lorin Hochstein <lorin@isi.edu> -+Major Hayden <major@mhtx.net> -+Mark McLoughlin <markmc@redhat.com> -+Mark Washenberger <mark.washenberger@rackspace.com> -+Maru Newby <mnewby@internap.com> -+Matt Dietz <matt.dietz@rackspace.com> -+Mike Lundy <mike@pistoncloud.com> -+Monty Taylor <mordred@inaugust.com> -+Pádraig Brady <P@draigBrady.com> -+Paul Bourke <paul-david.bourke@hp.com> -+Pavan Kumar Sunkara <pavan.sss1991@gmail.com> -+Peng Yong <ppyy@pubyun.com> -+Pete Zaitcev <zaitcev@kotori.zaitcev.us> -+Rick Clark <rick@openstack.org> -+Rick Harris <rconradharris@gmail.com> -+Reynolds Chin <benzwt@gmail.com> -+Russell Bryant <rbryant@redhat.com> -+Soren Hansen <soren.hansen@rackspace.com> -+Stuart McLaren <stuart.mclaren@hp.com> -+Taku Fukushima <tfukushima@dcl.info.waseda.ac.jp> -+Thierry Carrez <thierry@openstack.org> -+Tom Hancock <tom.hancock@hp.com> -+Wayne A. Walls <wayne.walls@rackspace.com> -+William Wolf <throughnothing@gmail.com> -+Vishvananda Ishaya <vishvananda@gmail.com> -+Yaguang Tang <heut2008@gmail.com> -+Yuriy Taraday <yorik.sar@gmail.com> -+Zhongyue Luo <lzyeval@gmail.com> -diff -urN glance-2012.1.orig/etc/glance-api.conf glance-2012.1/etc/glance-api.conf ---- glance-2012.1.orig/etc/glance-api.conf 2012-03-21 10:51:12.000000000 +0100 -+++ glance-2012.1/etc/glance-api.conf 2012-03-28 17:48:31.000000000 +0200 -@@ -130,10 +130,15 @@ - - # ============ Swift Store Options ============================= - -+# Version of the authentication service to use -+# Valid versions are '2' for keystone and '1' for swauth and rackspace -+swift_store_auth_version = 2 -+ - # Address where the Swift authentication service lives - # Valid schemes are 'http://' and 'https://' - # If no scheme specified, default to 'https://' --swift_store_auth_address = 127.0.0.1:8080/v1.0/ -+# For swauth, use something like '127.0.0.1:8080/v1.0/' -+swift_store_auth_address = 127.0.0.1:35357/v2.0/ - - # User to authenticate against the Swift authentication service - # If you use Swift authentication service, set it to 'account':'user' -diff -urN glance-2012.1.orig/glance/store/swift.py glance-2012.1/glance/store/swift.py ---- glance-2012.1.orig/glance/store/swift.py 2012-03-21 10:51:12.000000000 +0100 -+++ glance-2012.1/glance/store/swift.py 2012-03-28 17:50:34.000000000 +0200 -@@ -192,9 +192,10 @@ - cfg.StrOpt('swift_store_auth_address'), - cfg.StrOpt('swift_store_user'), - cfg.StrOpt('swift_store_key'), -+ cfg.StrOpt('swift_store_auth_version', default='2'), - cfg.StrOpt('swift_store_container', - default=DEFAULT_CONTAINER), -- cfg.IntOpt('swift_store_large_object_size', -+ cfg.IntOpt('swift_store_large_object_size', - default=DEFAULT_LARGE_OBJECT_SIZE), - cfg.IntOpt('swift_store_large_object_chunk_size', - default=DEFAULT_LARGE_OBJECT_CHUNK_SIZE), -@@ -204,6 +205,7 @@ - def configure(self): - self.conf.register_opts(self.opts) - self.snet = self.conf.swift_enable_snet -+ self.auth_version = self._option_get('swift_store_auth_version') - - def configure_add(self): - """ -@@ -300,11 +302,14 @@ - Creates a connection using the Swift client library. - """ - snet = self.snet -+ auth_version = self.auth_version - logger.debug(_("Creating Swift connection with " - "(auth_address=%(auth_url)s, user=%(user)s, " -- "snet=%(snet)s)") % locals()) -+ "snet=%(snet)s, auth_version=%(auth_version)s)") % -+ locals()) - return swift_client.Connection( -- authurl=auth_url, user=user, key=key, snet=snet) -+ authurl=auth_url, user=user, key=key, snet=snet, -+ auth_version=auth_version) - - def _option_get(self, param): - result = getattr(self.conf, param) -diff -urN glance-2012.1.orig/glance/store/swift.py.orig glance-2012.1/glance/store/swift.py.orig ---- glance-2012.1.orig/glance/store/swift.py.orig 1970-01-01 01:00:00.000000000 +0100 -+++ glance-2012.1/glance/store/swift.py.orig 2012-03-21 10:51:12.000000000 +0100 -@@ -0,0 +1,560 @@ -+# vim: tabstop=4 shiftwidth=4 softtabstop=4 -+ -+# Copyright 2010-2011 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. -+ -+"""Storage backend for SWIFT""" -+ -+from __future__ import absolute_import -+ -+import hashlib -+import httplib -+import logging -+import math -+import urlparse -+ -+from glance.common import cfg -+from glance.common import exception -+import glance.store -+import glance.store.base -+import glance.store.location -+ -+try: -+ from swift.common import client as swift_client -+except ImportError: -+ pass -+ -+DEFAULT_CONTAINER = 'glance' -+DEFAULT_LARGE_OBJECT_SIZE = 5 * 1024 # 5GB -+DEFAULT_LARGE_OBJECT_CHUNK_SIZE = 200 # 200M -+ONE_MB = 1000 * 1024 -+ -+logger = logging.getLogger('glance.store.swift') -+ -+ -+class StoreLocation(glance.store.location.StoreLocation): -+ -+ """ -+ Class describing a Swift URI. A Swift URI can look like any of -+ the following: -+ -+ swift://user:pass@authurl.com/container/obj-id -+ swift://account:user:pass@authurl.com/container/obj-id -+ swift+http://user:pass@authurl.com/container/obj-id -+ swift+https://user:pass@authurl.com/container/obj-id -+ -+ The swift+http:// URIs indicate there is an HTTP authentication URL. -+ The default for Swift is an HTTPS authentication URL, so swift:// and -+ swift+https:// are the same... -+ """ -+ -+ def process_specs(self): -+ self.scheme = self.specs.get('scheme', 'swift+https') -+ self.user = self.specs.get('user') -+ self.key = self.specs.get('key') -+ self.authurl = self.specs.get('authurl') -+ self.container = self.specs.get('container') -+ self.obj = self.specs.get('obj') -+ -+ def _get_credstring(self): -+ if self.user: -+ return '%s:%s@' % (self.user, self.key) -+ return '' -+ -+ def get_uri(self): -+ authurl = self.authurl -+ if authurl.startswith('http://'): -+ authurl = authurl[7:] -+ elif authurl.startswith('https://'): -+ authurl = authurl[8:] -+ -+ credstring = self._get_credstring() -+ authurl = authurl.strip('/') -+ container = self.container.strip('/') -+ obj = self.obj.strip('/') -+ -+ return '%s://%s%s/%s/%s' % (self.scheme, credstring, authurl, -+ container, obj) -+ -+ def parse_uri(self, uri): -+ """ -+ Parse URLs. This method fixes an issue where credentials specified -+ in the URL are interpreted differently in Python 2.6.1+ than prior -+ versions of Python. It also deals with the peculiarity that new-style -+ Swift URIs have where a username can contain a ':', like so: -+ -+ swift://account:user:pass@authurl.com/container/obj -+ """ -+ # Make sure that URIs that contain multiple schemes, such as: -+ # swift://user:pass@http://authurl.com/v1/container/obj -+ # are immediately rejected. -+ if uri.count('://') != 1: -+ reason = _( -+ "URI cannot contain more than one occurrence of a scheme." -+ "If you have specified a URI like " -+ "swift://user:pass@http://authurl.com/v1/container/obj" -+ ", you need to change it to use the swift+http:// scheme, " -+ "like so: " -+ "swift+http://user:pass@authurl.com/v1/container/obj" -+ ) -+ raise exception.BadStoreUri(uri, reason) -+ -+ pieces = urlparse.urlparse(uri) -+ assert pieces.scheme in ('swift', 'swift+http', 'swift+https') -+ self.scheme = pieces.scheme -+ netloc = pieces.netloc -+ path = pieces.path.lstrip('/') -+ if netloc != '': -+ # > Python 2.6.1 -+ if '@' in netloc: -+ creds, netloc = netloc.split('@') -+ else: -+ creds = None -+ else: -+ # Python 2.6.1 compat -+ # see lp659445 and Python issue7904 -+ if '@' in path: -+ creds, path = path.split('@') -+ else: -+ creds = None -+ netloc = path[0:path.find('/')].strip('/') -+ path = path[path.find('/'):].strip('/') -+ if creds: -+ cred_parts = creds.split(':') -+ -+ # User can be account:user, in which case cred_parts[0:2] will be -+ # the account and user. Combine them into a single username of -+ # account:user -+ if len(cred_parts) == 1: -+ reason = (_("Badly formed credentials '%(creds)s' in Swift " -+ "URI") % locals()) -+ raise exception.BadStoreUri(uri, reason) -+ elif len(cred_parts) == 3: -+ user = ':'.join(cred_parts[0:2]) -+ else: -+ user = cred_parts[0] -+ key = cred_parts[-1] -+ self.user = user -+ self.key = key -+ else: -+ self.user = None -+ path_parts = path.split('/') -+ try: -+ self.obj = path_parts.pop() -+ self.container = path_parts.pop() -+ if not netloc.startswith('http'): -+ # push hostname back into the remaining to build full authurl -+ path_parts.insert(0, netloc) -+ self.authurl = '/'.join(path_parts) -+ except IndexError: -+ reason = _("Badly formed Swift URI") -+ raise exception.BadStoreUri(uri, reason) -+ -+ @property -+ def swift_auth_url(self): -+ """ -+ Creates a fully-qualified auth url that the Swift client library can -+ use. The scheme for the auth_url is determined using the scheme -+ included in the `location` field. -+ -+ HTTPS is assumed, unless 'swift+http' is specified. -+ """ -+ if self.scheme in ('swift+https', 'swift'): -+ auth_scheme = 'https://' -+ else: -+ auth_scheme = 'http://' -+ -+ full_url = ''.join([auth_scheme, self.authurl]) -+ return full_url -+ -+ -+class Store(glance.store.base.Store): -+ """An implementation of the swift backend adapter.""" -+ -+ EXAMPLE_URL = "swift://<USER>:<KEY>@<AUTH_ADDRESS>/<CONTAINER>/<FILE>" -+ -+ CHUNKSIZE = 65536 -+ -+ opts = [ -+ cfg.BoolOpt('swift_enable_snet', default=False), -+ cfg.StrOpt('swift_store_auth_address'), -+ cfg.StrOpt('swift_store_user'), -+ cfg.StrOpt('swift_store_key'), -+ cfg.StrOpt('swift_store_container', -+ default=DEFAULT_CONTAINER), -+ cfg.IntOpt('swift_store_large_object_size', -+ default=DEFAULT_LARGE_OBJECT_SIZE), -+ cfg.IntOpt('swift_store_large_object_chunk_size', -+ default=DEFAULT_LARGE_OBJECT_CHUNK_SIZE), -+ cfg.BoolOpt('swift_store_create_container_on_put', default=False), -+ ] -+ -+ def configure(self): -+ self.conf.register_opts(self.opts) -+ self.snet = self.conf.swift_enable_snet -+ -+ def configure_add(self): -+ """ -+ Configure the Store to use the stored configuration options -+ Any store that needs special configuration should implement -+ this method. If the store was not able to successfully configure -+ itself, it should raise `exception.BadStoreConfiguration` -+ """ -+ self.auth_address = self._option_get('swift_store_auth_address') -+ self.user = self._option_get('swift_store_user') -+ self.key = self._option_get('swift_store_key') -+ self.container = self.conf.swift_store_container -+ try: -+ # The config file has swift_store_large_object_*size in MB, but -+ # internally we store it in bytes, since the image_size parameter -+ # passed to add() is also in bytes. -+ self.large_object_size = \ -+ self.conf.swift_store_large_object_size * ONE_MB -+ self.large_object_chunk_size = \ -+ self.conf.swift_store_large_object_chunk_size * ONE_MB -+ except cfg.ConfigFileValueError, e: -+ reason = _("Error in configuration conf: %s") % e -+ logger.error(reason) -+ raise exception.BadStoreConfiguration(store_name="swift", -+ reason=reason) -+ -+ self.scheme = 'swift+https' -+ if self.auth_address.startswith('http://'): -+ self.scheme = 'swift+http' -+ self.full_auth_address = self.auth_address -+ elif self.auth_address.startswith('https://'): -+ self.full_auth_address = self.auth_address -+ else: # Defaults https -+ self.full_auth_address = 'https://' + self.auth_address -+ -+ def get(self, location): -+ """ -+ Takes a `glance.store.location.Location` object that indicates -+ where to find the image file, and returns a tuple of generator -+ (for reading the image file) and image_size -+ -+ :param location `glance.store.location.Location` object, supplied -+ from glance.store.location.get_location_from_uri() -+ :raises `glance.exception.NotFound` if image does not exist -+ """ -+ loc = location.store_location -+ swift_conn = self._make_swift_connection( -+ auth_url=loc.swift_auth_url, user=loc.user, key=loc.key) -+ -+ try: -+ (resp_headers, resp_body) = swift_conn.get_object( -+ container=loc.container, obj=loc.obj, -+ resp_chunk_size=self.CHUNKSIZE) -+ except swift_client.ClientException, e: -+ if e.http_status == httplib.NOT_FOUND: -+ uri = location.get_store_uri() -+ raise exception.NotFound(_("Swift could not find image at " -+ "uri %(uri)s") % locals()) -+ else: -+ raise -+ -+ class ResponseIndexable(glance.store.Indexable): -+ def another(self): -+ try: -+ return self.wrapped.next() -+ except StopIteration: -+ return '' -+ -+ length = resp_headers.get('content-length') -+ return (ResponseIndexable(resp_body, length), length) -+ -+ def get_size(self, location): -+ """ -+ Takes a `glance.store.location.Location` object that indicates -+ where to find the image file, and returns the image_size (or 0 -+ if unavailable) -+ -+ :param location `glance.store.location.Location` object, supplied -+ from glance.store.location.get_location_from_uri() -+ """ -+ loc = location.store_location -+ swift_conn = self._make_swift_connection( -+ auth_url=loc.swift_auth_url, user=loc.user, key=loc.key) -+ -+ try: -+ resp_headers = swift_conn.head_object(container=loc.container, -+ obj=loc.obj) -+ return resp_headers.get('content-length', 0) -+ except Exception: -+ return 0 -+ -+ def _make_swift_connection(self, auth_url, user, key): -+ """ -+ Creates a connection using the Swift client library. -+ """ -+ snet = self.snet -+ logger.debug(_("Creating Swift connection with " -+ "(auth_address=%(auth_url)s, user=%(user)s, " -+ "snet=%(snet)s)") % locals()) -+ return swift_client.Connection( -+ authurl=auth_url, user=user, key=key, snet=snet) -+ -+ def _option_get(self, param): -+ result = getattr(self.conf, param) -+ if not result: -+ reason = (_("Could not find %(param)s in configuration " -+ "options.") % locals()) -+ logger.error(reason) -+ raise exception.BadStoreConfiguration(store_name="swift", -+ reason=reason) -+ return result -+ -+ def add(self, image_id, image_file, image_size): -+ """ -+ Stores an image file with supplied identifier to the backend -+ storage system and returns an `glance.store.ImageAddResult` object -+ containing information about the stored image. -+ -+ :param image_id: The opaque image identifier -+ :param image_file: The image data to write, as a file-like object -+ :param image_size: The size of the image data to write, in bytes -+ -+ :retval `glance.store.ImageAddResult` object -+ :raises `glance.common.exception.Duplicate` if the image already -+ existed -+ -+ Swift writes the image data using the scheme: -+ ``swift://<USER>:<KEY>@<AUTH_ADDRESS>/<CONTAINER>/<ID>` -+ where: -+ <USER> = ``swift_store_user`` -+ <KEY> = ``swift_store_key`` -+ <AUTH_ADDRESS> = ``swift_store_auth_address`` -+ <CONTAINER> = ``swift_store_container`` -+ <ID> = The id of the image being added -+ -+ :note Swift auth URLs by default use HTTPS. To specify an HTTP -+ auth URL, you can specify http://someurl.com for the -+ swift_store_auth_address config option -+ -+ :note Swift cannot natively/transparently handle objects >5GB -+ in size. So, if the image is greater than 5GB, we write -+ chunks of image data to Swift and then write an manifest -+ to Swift that contains information about the chunks. -+ This same chunking process is used by default for images -+ of an unknown size, as pushing them directly to swift would -+ fail if the image turns out to be greater than 5GB. -+ """ -+ swift_conn = self._make_swift_connection( -+ auth_url=self.full_auth_address, user=self.user, key=self.key) -+ -+ create_container_if_missing(self.container, swift_conn, self.conf) -+ -+ obj_name = str(image_id) -+ location = StoreLocation({'scheme': self.scheme, -+ 'container': self.container, -+ 'obj': obj_name, -+ 'authurl': self.auth_address, -+ 'user': self.user, -+ 'key': self.key}) -+ -+ logger.debug(_("Adding image object '%(obj_name)s' " -+ "to Swift") % locals()) -+ try: -+ if image_size > 0 and image_size < self.large_object_size: -+ # Image size is known, and is less than large_object_size. -+ # Send to Swift with regular PUT. -+ obj_etag = swift_conn.put_object(self.container, obj_name, -+ image_file, -+ content_length=image_size) -+ else: -+ # Write the image into Swift in chunks. -+ chunk_id = 1 -+ if image_size > 0: -+ total_chunks = str(int( -+ math.ceil(float(image_size) / -+ float(self.large_object_chunk_size)))) -+ else: -+ # image_size == 0 is when we don't know the size -+ # of the image. This can occur with older clients -+ # that don't inspect the payload size. -+ logger.debug(_("Cannot determine image size. Adding as a " -+ "segmented object to Swift.")) -+ total_chunks = '?' -+ -+ checksum = hashlib.md5() -+ combined_chunks_size = 0 -+ while True: -+ chunk_size = self.large_object_chunk_size -+ if image_size == 0: -+ content_length = None -+ else: -+ left = image_size - combined_chunks_size -+ if left == 0: -+ break -+ if chunk_size > left: -+ chunk_size = left -+ content_length = chunk_size -+ -+ chunk_name = "%s-%05d" % (obj_name, chunk_id) -+ reader = ChunkReader(image_file, checksum, chunk_size) -+ chunk_etag = swift_conn.put_object( -+ self.container, chunk_name, reader, -+ content_length=content_length) -+ bytes_read = reader.bytes_read -+ logger.debug(_("Wrote chunk %(chunk_id)d/" -+ "%(total_chunks)s of length %(bytes_read)d " -+ "to Swift returning MD5 of content: " -+ "%(chunk_etag)s") -+ % locals()) -+ -+ if bytes_read == 0: -+ # Delete the last chunk, because it's of zero size. -+ # This will happen if image_size == 0. -+ logger.debug(_("Deleting final zero-length chunk")) -+ swift_conn.delete_object(self.container, chunk_name) -+ break -+ -+ chunk_id += 1 -+ combined_chunks_size += bytes_read -+ -+ # In the case we have been given an unknown image size, -+ # set the image_size to the total size of the combined chunks. -+ if image_size == 0: -+ image_size = combined_chunks_size -+ -+ # Now we write the object manifest and return the -+ # manifest's etag... -+ manifest = "%s/%s" % (self.container, obj_name) -+ headers = {'ETag': hashlib.md5("").hexdigest(), -+ 'X-Object-Manifest': manifest} -+ -+ # The ETag returned for the manifest is actually the -+ # MD5 hash of the concatenated checksums of the strings -+ # of each chunk...so we ignore this result in favour of -+ # the MD5 of the entire image file contents, so that -+ # users can verify the image file contents accordingly -+ swift_conn.put_object(self.container, obj_name, -+ None, headers=headers) -+ obj_etag = checksum.hexdigest() -+ -+ # NOTE: We return the user and key here! Have to because -+ # location is used by the API server to return the actual -+ # image data. We *really* should consider NOT returning -+ # the location attribute from GET /images/<ID> and -+ # GET /images/details -+ -+ return (location.get_uri(), image_size, obj_etag) -+ except swift_client.ClientException, e: -+ if e.http_status == httplib.CONFLICT: -+ raise exception.Duplicate(_("Swift already has an image at " -+ "location %s") % location.get_uri()) -+ msg = (_("Failed to add object to Swift.\n" -+ "Got error from Swift: %(e)s") % locals()) -+ logger.error(msg) -+ raise glance.store.BackendException(msg) -+ -+ def delete(self, location): -+ """ -+ Takes a `glance.store.location.Location` object that indicates -+ where to find the image file to delete -+ -+ :location `glance.store.location.Location` object, supplied -+ from glance.store.location.get_location_from_uri() -+ -+ :raises NotFound if image does not exist -+ """ -+ loc = location.store_location -+ swift_conn = self._make_swift_connection( -+ auth_url=loc.swift_auth_url, user=loc.user, key=loc.key) -+ -+ try: -+ # We request the manifest for the object. If one exists, -+ # that means the object was uploaded in chunks/segments, -+ # and we need to delete all the chunks as well as the -+ # manifest. -+ manifest = None -+ try: -+ headers = swift_conn.head_object(loc.container, loc.obj) -+ manifest = headers.get('x-object-manifest') -+ except swift_client.ClientException, e: -+ if e.http_status != httplib.NOT_FOUND: -+ raise -+ if manifest: -+ # Delete all the chunks before the object manifest itself -+ obj_container, obj_prefix = manifest.split('/', 1) -+ for segment in swift_conn.get_container(obj_container, -+ prefix=obj_prefix)[1]: -+ # TODO(jaypipes): This would be an easy area to parallelize -+ # since we're simply sending off parallelizable requests -+ # to Swift to delete stuff. It's not like we're going to -+ # be hogging up network or file I/O here... -+ swift_conn.delete_object(obj_container, segment['name']) -+ -+ else: -+ swift_conn.delete_object(loc.container, loc.obj) -+ -+ except swift_client.ClientException, e: -+ if e.http_status == httplib.NOT_FOUND: -+ uri = location.get_store_uri() -+ raise exception.NotFound(_("Swift could not find image at " -+ "uri %(uri)s") % locals()) -+ else: -+ raise -+ -+ -+class ChunkReader(object): -+ def __init__(self, fd, checksum, total): -+ self.fd = fd -+ self.checksum = checksum -+ self.total = total -+ self.bytes_read = 0 -+ -+ def read(self, i): -+ left = self.total - self.bytes_read -+ if i > left: -+ i = left -+ result = self.fd.read(i) -+ self.bytes_read += len(result) -+ self.checksum.update(result) -+ return result -+ -+ -+def create_container_if_missing(container, swift_conn, conf): -+ """ -+ Creates a missing container in Swift if the -+ ``swift_store_create_container_on_put`` option is set. -+ -+ :param container: Name of container to create -+ :param swift_conn: Connection to Swift -+ :param conf: Option mapping -+ """ -+ try: -+ swift_conn.head_container(container) -+ except swift_client.ClientException, e: -+ if e.http_status == httplib.NOT_FOUND: -+ if conf.swift_store_create_container_on_put: -+ try: -+ swift_conn.put_container(container) -+ except swift_client.ClientException, e: -+ msg = _("Failed to add container to Swift.\n" -+ "Got error from Swift: %(e)s") % locals() -+ raise glance.store.BackendException(msg) -+ else: -+ msg = (_("The container %(container)s does not exist in " -+ "Swift. Please set the " -+ "swift_store_create_container_on_put option" -+ "to add container to Swift automatically.") -+ % locals()) -+ raise glance.store.BackendException(msg) -+ else: -+ raise -+ -+ -+glance.store.register_store(__name__, ['swift', 'swift+http', 'swift+https']) -diff -urN glance-2012.1.orig/glance/store/swift.py.rej glance-2012.1/glance/store/swift.py.rej ---- glance-2012.1.orig/glance/store/swift.py.rej 1970-01-01 01:00:00.000000000 +0100 -+++ glance-2012.1/glance/store/swift.py.rej 2012-03-28 17:48:31.000000000 +0200 -@@ -0,0 +1,16 @@ -+*************** -+*** 192,197 **** -+ cfg.StrOpt('swift_store_auth_address'), -+ cfg.StrOpt('swift_store_user', secret=True), -+ cfg.StrOpt('swift_store_key', secret=True), -+ cfg.StrOpt('swift_store_container', -+ default=DEFAULT_CONTAINER), -+ cfg.IntOpt('swift_store_large_object_size', -+--- 192,198 ---- -+ cfg.StrOpt('swift_store_auth_address'), -+ cfg.StrOpt('swift_store_user', secret=True), -+ cfg.StrOpt('swift_store_key', secret=True), -++ cfg.StrOpt('swift_store_auth_version', default='2'), -+ cfg.StrOpt('swift_store_container', -+ default=DEFAULT_CONTAINER), -+ cfg.IntOpt('swift_store_large_object_size', -diff -urN glance-2012.1.orig/glance/tests/unit/test_swift_store.py glance-2012.1/glance/tests/unit/test_swift_store.py ---- glance-2012.1.orig/glance/tests/unit/test_swift_store.py 2012-03-21 10:51:12.000000000 +0100 -+++ glance-2012.1/glance/tests/unit/test_swift_store.py 2012-03-28 17:48:31.000000000 +0200 -@@ -52,7 +52,7 @@ - # We stub out as little as possible to ensure that the code paths - # between glance.store.swift and swift.common.client are tested - # thoroughly --def stub_out_swift_common_client(stubs): -+def stub_out_swift_common_client(stubs, conf): - - fixture_containers = ['glance'] - fixture_headers = {'glance/%s' % FAKE_UUID: -@@ -158,9 +158,13 @@ - def fake_http_connection(*args, **kwargs): - return None - -- def fake_get_auth(url, *args, **kwargs): -+ def fake_get_auth(url, user, key, snet, auth_version, **kwargs): - if 'http' in url and '://' not in url: - raise ValueError('Invalid url %s' % url) -+ # Check the auth version against the configured value -+ if conf['swift_store_auth_version'] != auth_version: -+ msg = 'AUTHENTICATION failed (version mismatch)' -+ raise swift.common.client.ClientException(msg) - return None, None - - stubs.Set(swift.common.client, -@@ -181,17 +185,16 @@ - 'http_connection', fake_http_connection) - - --class TestStore(unittest.TestCase): -+class SwiftTests(object): - -- def setUp(self): -- """Establish a clean test environment""" -- self.stubs = stubout.StubOutForTesting() -- stub_out_swift_common_client(self.stubs) -- self.store = Store(test_utils.TestConfigOpts(SWIFT_CONF)) -- -- def tearDown(self): -- """Clear the test environment""" -- self.stubs.UnsetAll() -+ def test_get_size(self): -+ """ -+ Test that we can get the size of an object in the swift store -+ """ -+ uri = "swift://user:key@auth_address/glance/%s" % FAKE_UUID -+ loc = get_location_from_uri(uri) -+ image_size = self.store.get_size(loc) -+ self.assertEqual(image_size, 5120) - - def test_get(self): - """Test a "normal" retrieval of an image in chunks""" -@@ -298,15 +301,14 @@ - expected_swift_contents = "*" * expected_swift_size - expected_checksum = \ - hashlib.md5(expected_swift_contents).hexdigest() -- new_conf = SWIFT_CONF.copy() -- new_conf['swift_store_auth_address'] = variation -+ self.conf['swift_store_auth_address'] = variation - - image_swift = StringIO.StringIO(expected_swift_contents) - - global SWIFT_PUT_OBJECT_CALLS - SWIFT_PUT_OBJECT_CALLS = 0 - -- self.store = Store(test_utils.TestConfigOpts(new_conf)) -+ self.store = Store(test_utils.TestConfigOpts(self.conf)) - location, size, checksum = self.store.add(image_id, image_swift, - expected_swift_size) - -@@ -328,11 +330,10 @@ - Tests that adding an image with a non-existing container - raises an appropriate exception - """ -- conf = SWIFT_CONF.copy() -- conf['swift_store_create_container_on_put'] = 'False' -- conf['swift_store_container'] = 'noexist' -+ self.conf['swift_store_create_container_on_put'] = 'False' -+ self.conf['swift_store_container'] = 'noexist' - image_swift = StringIO.StringIO("nevergonnamakeit") -- self.store = Store(test_utils.TestConfigOpts(conf)) -+ self.store = Store(test_utils.TestConfigOpts(self.conf)) - - global SWIFT_PUT_OBJECT_CALLS - SWIFT_PUT_OBJECT_CALLS = 0 -@@ -355,9 +356,8 @@ - Tests that adding an image with a non-existing container - creates the container automatically if flag is set - """ -- conf = SWIFT_CONF.copy() -- conf['swift_store_create_container_on_put'] = 'True' -- conf['swift_store_container'] = 'noexist' -+ self.conf['swift_store_create_container_on_put'] = 'True' -+ self.conf['swift_store_container'] = 'noexist' - expected_swift_size = FIVE_KB - expected_swift_contents = "*" * expected_swift_size - expected_checksum = hashlib.md5(expected_swift_contents).hexdigest() -@@ -369,7 +369,7 @@ - global SWIFT_PUT_OBJECT_CALLS - SWIFT_PUT_OBJECT_CALLS = 0 - -- self.store = Store(test_utils.TestConfigOpts(conf)) -+ self.store = Store(test_utils.TestConfigOpts(self.conf)) - location, size, checksum = self.store.add(expected_image_id, - image_swift, - expected_swift_size) -@@ -394,8 +394,7 @@ - and then verify that there have been a number of calls to - put_object()... - """ -- conf = SWIFT_CONF.copy() -- conf['swift_store_container'] = 'glance' -+ self.conf['swift_store_container'] = 'glance' - expected_swift_size = FIVE_KB - expected_swift_contents = "*" * expected_swift_size - expected_checksum = hashlib.md5(expected_swift_contents).hexdigest() -@@ -407,7 +406,7 @@ - global SWIFT_PUT_OBJECT_CALLS - SWIFT_PUT_OBJECT_CALLS = 0 - -- self.store = Store(test_utils.TestConfigOpts(conf)) -+ self.store = Store(test_utils.TestConfigOpts(self.conf)) - orig_max_size = self.store.large_object_size - orig_temp_size = self.store.large_object_chunk_size - try: -@@ -446,8 +445,7 @@ - - Bug lp:891738 - """ -- conf = SWIFT_CONF.copy() -- conf['swift_store_container'] = 'glance' -+ self.conf['swift_store_container'] = 'glance' - - # Set up a 'large' image of 5KB - expected_swift_size = FIVE_KB -@@ -463,7 +461,7 @@ - - # Temporarily set Swift MAX_SWIFT_OBJECT_SIZE to 1KB and add our image, - # explicitly setting the image_length to 0 -- self.store = Store(test_utils.TestConfigOpts(conf)) -+ self.store = Store(test_utils.TestConfigOpts(self.conf)) - orig_max_size = self.store.large_object_size - orig_temp_size = self.store.large_object_chunk_size - global MAX_SWIFT_OBJECT_SIZE -@@ -507,11 +505,10 @@ - FAKE_UUID, image_swift, 0) - - def _option_required(self, key): -- conf = SWIFT_CONF.copy() -- del conf[key] -+ del self.conf[key] - - try: -- self.store = Store(test_utils.TestConfigOpts(conf)) -+ self.store = Store(test_utils.TestConfigOpts(self.conf)) - return self.store.add == self.store.add_disabled - except: - return False -@@ -554,6 +551,32 @@ - self.assertRaises(exception.NotFound, self.store.delete, loc) - - -+class TestStoreAuthV1(unittest.TestCase, SwiftTests): -+ -+ def setUp(self): -+ """Establish a clean test environment""" -+ self.conf = SWIFT_CONF.copy() -+ self.conf['swift_store_auth_version'] = '1' -+ self.stubs = stubout.StubOutForTesting() -+ stub_out_swift_common_client(self.stubs, self.conf) -+ self.store = Store(test_utils.TestConfigOpts(self.conf)) -+ -+ def tearDown(self): -+ """Clear the test environment""" -+ self.stubs.UnsetAll() -+ -+ -+class TestStoreAuthV2(TestStoreAuthV1): -+ -+ def setUp(self): -+ """Establish a clean test environment""" -+ self.conf = SWIFT_CONF.copy() -+ self.conf['swift_store_auth_version'] = '2' -+ self.stubs = stubout.StubOutForTesting() -+ stub_out_swift_common_client(self.stubs, self.conf) -+ self.store = Store(test_utils.TestConfigOpts(self.conf)) -+ -+ - class TestChunkReader(unittest.TestCase): - - def test_read_all_data(self): |