Adds multi tenant support for swift backend.
Updates the swift store to support multiple tenants: * Added configuration option for swift_store_multi_tenant. * Updated the swift connection creation logic so that in multi-tenant mode the token and storage URL from the service catalog are used to create swift connection. * When in multi-tenant mode locations URL's (stored in the DB) do not contain hard coded swift credentials. Includes unit tests to verify multi-tenant swift storage URLs. Partially implements blueprint: swift-tenant-specific-storage. Change-Id: I45fc97027e6f211ac353513c2d9d6da51ccf4489
This commit is contained in:
parent
53e210a0b3
commit
8b2d038185
@ -380,6 +380,19 @@ Can only be specified in configuration files.
|
|||||||
When doing a large object manifest, what size, in MB, should
|
When doing a large object manifest, what size, in MB, should
|
||||||
Glance write chunks to Swift? The default is 200MB.
|
Glance write chunks to Swift? The default is 200MB.
|
||||||
|
|
||||||
|
* ``swift_store_multi_tenant=False``
|
||||||
|
|
||||||
|
Optional. Default: ``False``
|
||||||
|
|
||||||
|
Can only be specified in configuration files.
|
||||||
|
|
||||||
|
`This option is specific to the Swift storage backend.`
|
||||||
|
|
||||||
|
If set to True enables multi-tenant storage mode which causes Glance images
|
||||||
|
to be stored in tenant specific Swift accounts. When set to False Glance
|
||||||
|
stores all images in a single Swift account.
|
||||||
|
|
||||||
|
|
||||||
Configuring the S3 Storage Backend
|
Configuring the S3 Storage Backend
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -203,6 +203,10 @@ swift_store_large_object_chunk_size = 200
|
|||||||
# Ex. https://example.com/v1.0/ -> https://snet-example.com/v1.0/
|
# Ex. https://example.com/v1.0/ -> https://snet-example.com/v1.0/
|
||||||
swift_enable_snet = False
|
swift_enable_snet = False
|
||||||
|
|
||||||
|
# If set to True enables multi-tenant storage mode which causes Glance images
|
||||||
|
# to be stored in tenant specific Swift accounts.
|
||||||
|
# swift_store_multi_tenant = False
|
||||||
|
|
||||||
# ============ S3 Store Options =============================
|
# ============ S3 Store Options =============================
|
||||||
|
|
||||||
# Address where the S3 authentication service lives
|
# Address where the S3 authentication service lives
|
||||||
|
@ -163,7 +163,7 @@ def legacy_parse_uri(self, uri):
|
|||||||
if not netloc.startswith('http'):
|
if not netloc.startswith('http'):
|
||||||
# push hostname back into the remaining to build full authurl
|
# push hostname back into the remaining to build full authurl
|
||||||
path_parts.insert(0, netloc)
|
path_parts.insert(0, netloc)
|
||||||
self.authurl = '/'.join(path_parts)
|
self.auth_or_store_url = '/'.join(path_parts)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
reason = _("Badly formed S3 URI: %s") % uri
|
reason = _("Badly formed S3 URI: %s") % uri
|
||||||
LOG.error(message=reason)
|
LOG.error(message=reason)
|
||||||
|
@ -35,7 +35,7 @@ be the host:port of that Glance API server along with /images/<IMAGE_ID>.
|
|||||||
|
|
||||||
The Glance storage URI is an internal URI structure that Glance
|
The Glance storage URI is an internal URI structure that Glance
|
||||||
uses to maintain critical information about how to access the images
|
uses to maintain critical information about how to access the images
|
||||||
that it stores in its storage backends. It **does contain** security
|
that it stores in its storage backends. It **may contain** security
|
||||||
credentials and is **not** user-facing.
|
credentials and is **not** user-facing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -61,6 +61,7 @@ def get_location_from_uri(uri):
|
|||||||
Example URIs:
|
Example URIs:
|
||||||
https://user:pass@example.com:80/images/some-id
|
https://user:pass@example.com:80/images/some-id
|
||||||
http://images.oracle.com/123456
|
http://images.oracle.com/123456
|
||||||
|
swift://example.com/container/obj-id
|
||||||
swift://user:account:pass@authurl.com/container/obj-id
|
swift://user:account:pass@authurl.com/container/obj-id
|
||||||
swift+http://user:account:pass@authurl.com/container/obj-id
|
swift+http://user:account:pass@authurl.com/container/obj-id
|
||||||
s3://accesskey:secretkey@s3.amazonaws.com/bucket/key-id
|
s3://accesskey:secretkey@s3.amazonaws.com/bucket/key-id
|
||||||
|
@ -25,6 +25,7 @@ import math
|
|||||||
import urllib
|
import urllib
|
||||||
import urlparse
|
import urlparse
|
||||||
|
|
||||||
|
from glance.common import auth
|
||||||
from glance.common import exception
|
from glance.common import exception
|
||||||
from glance.openstack.common import cfg
|
from glance.openstack.common import cfg
|
||||||
import glance.openstack.common.log as logging
|
import glance.openstack.common.log as logging
|
||||||
@ -57,6 +58,7 @@ swift_opts = [
|
|||||||
cfg.IntOpt('swift_store_large_object_chunk_size',
|
cfg.IntOpt('swift_store_large_object_chunk_size',
|
||||||
default=DEFAULT_LARGE_OBJECT_CHUNK_SIZE),
|
default=DEFAULT_LARGE_OBJECT_CHUNK_SIZE),
|
||||||
cfg.BoolOpt('swift_store_create_container_on_put', default=False),
|
cfg.BoolOpt('swift_store_create_container_on_put', default=False),
|
||||||
|
cfg.BoolOpt('swift_store_multi_tenant', default=False),
|
||||||
]
|
]
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@ -74,6 +76,10 @@ class StoreLocation(glance.store.location.StoreLocation):
|
|||||||
swift+http://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
|
swift+https://user:pass@authurl.com/container/obj-id
|
||||||
|
|
||||||
|
When using multi-tenant a URI might look like this (a storage URL):
|
||||||
|
|
||||||
|
swift+https://example.com/container/obj-id
|
||||||
|
|
||||||
The swift+http:// URIs indicate there is an HTTP authentication URL.
|
The swift+http:// URIs indicate there is an HTTP authentication URL.
|
||||||
The default for Swift is an HTTPS authentication URL, so swift:// and
|
The default for Swift is an HTTPS authentication URL, so swift:// and
|
||||||
swift+https:// are the same...
|
swift+https:// are the same...
|
||||||
@ -83,28 +89,28 @@ class StoreLocation(glance.store.location.StoreLocation):
|
|||||||
self.scheme = self.specs.get('scheme', 'swift+https')
|
self.scheme = self.specs.get('scheme', 'swift+https')
|
||||||
self.user = self.specs.get('user')
|
self.user = self.specs.get('user')
|
||||||
self.key = self.specs.get('key')
|
self.key = self.specs.get('key')
|
||||||
self.authurl = self.specs.get('authurl')
|
self.auth_or_store_url = self.specs.get('auth_or_store_url')
|
||||||
self.container = self.specs.get('container')
|
self.container = self.specs.get('container')
|
||||||
self.obj = self.specs.get('obj')
|
self.obj = self.specs.get('obj')
|
||||||
|
|
||||||
def _get_credstring(self):
|
def _get_credstring(self):
|
||||||
if self.user:
|
if self.user and self.key:
|
||||||
return '%s:%s@' % (urllib.quote(self.user), urllib.quote(self.key))
|
return '%s:%s@' % (urllib.quote(self.user), urllib.quote(self.key))
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def get_uri(self):
|
def get_uri(self):
|
||||||
authurl = self.authurl
|
auth_or_store_url = self.auth_or_store_url
|
||||||
if authurl.startswith('http://'):
|
if auth_or_store_url.startswith('http://'):
|
||||||
authurl = authurl[7:]
|
auth_or_store_url = auth_or_store_url[len('http://'):]
|
||||||
elif authurl.startswith('https://'):
|
elif auth_or_store_url.startswith('https://'):
|
||||||
authurl = authurl[8:]
|
auth_or_store_url = auth_or_store_url[len('https://'):]
|
||||||
|
|
||||||
credstring = self._get_credstring()
|
credstring = self._get_credstring()
|
||||||
authurl = authurl.strip('/')
|
auth_or_store_url = auth_or_store_url.strip('/')
|
||||||
container = self.container.strip('/')
|
container = self.container.strip('/')
|
||||||
obj = self.obj.strip('/')
|
obj = self.obj.strip('/')
|
||||||
|
|
||||||
return '%s://%s%s/%s/%s' % (self.scheme, credstring, authurl,
|
return '%s://%s%s/%s/%s' % (self.scheme, credstring, auth_or_store_url,
|
||||||
container, obj)
|
container, obj)
|
||||||
|
|
||||||
def parse_uri(self, uri):
|
def parse_uri(self, uri):
|
||||||
@ -163,6 +169,7 @@ class StoreLocation(glance.store.location.StoreLocation):
|
|||||||
self.key = urllib.unquote(key)
|
self.key = urllib.unquote(key)
|
||||||
else:
|
else:
|
||||||
self.user = None
|
self.user = None
|
||||||
|
self.key = None
|
||||||
path_parts = path.split('/')
|
path_parts = path.split('/')
|
||||||
try:
|
try:
|
||||||
self.obj = path_parts.pop()
|
self.obj = path_parts.pop()
|
||||||
@ -170,14 +177,14 @@ class StoreLocation(glance.store.location.StoreLocation):
|
|||||||
if not netloc.startswith('http'):
|
if not netloc.startswith('http'):
|
||||||
# push hostname back into the remaining to build full authurl
|
# push hostname back into the remaining to build full authurl
|
||||||
path_parts.insert(0, netloc)
|
path_parts.insert(0, netloc)
|
||||||
self.authurl = '/'.join(path_parts)
|
self.auth_or_store_url = '/'.join(path_parts)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
reason = _("Badly formed Swift URI: %s") % uri
|
reason = _("Badly formed Swift URI: %s") % uri
|
||||||
LOG.error(reason)
|
LOG.error(reason)
|
||||||
raise exception.BadStoreUri()
|
raise exception.BadStoreUri()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def swift_auth_url(self):
|
def swift_url(self):
|
||||||
"""
|
"""
|
||||||
Creates a fully-qualified auth url that the Swift client library can
|
Creates a fully-qualified auth url that the Swift client library can
|
||||||
use. The scheme for the auth_url is determined using the scheme
|
use. The scheme for the auth_url is determined using the scheme
|
||||||
@ -190,7 +197,7 @@ class StoreLocation(glance.store.location.StoreLocation):
|
|||||||
else:
|
else:
|
||||||
auth_scheme = 'http://'
|
auth_scheme = 'http://'
|
||||||
|
|
||||||
full_url = ''.join([auth_scheme, self.authurl])
|
full_url = ''.join([auth_scheme, self.auth_or_store_url])
|
||||||
return full_url
|
return full_url
|
||||||
|
|
||||||
|
|
||||||
@ -206,7 +213,10 @@ class Store(glance.store.base.Store):
|
|||||||
|
|
||||||
def configure(self):
|
def configure(self):
|
||||||
self.snet = CONF.swift_enable_snet
|
self.snet = CONF.swift_enable_snet
|
||||||
|
self.multi_tenant = CONF.swift_store_multi_tenant
|
||||||
self.auth_version = self._option_get('swift_store_auth_version')
|
self.auth_version = self._option_get('swift_store_auth_version')
|
||||||
|
self.storage_url = None
|
||||||
|
self.token = None
|
||||||
|
|
||||||
def configure_add(self):
|
def configure_add(self):
|
||||||
"""
|
"""
|
||||||
@ -219,6 +229,20 @@ class Store(glance.store.base.Store):
|
|||||||
self.user = self._option_get('swift_store_user')
|
self.user = self._option_get('swift_store_user')
|
||||||
self.key = self._option_get('swift_store_key')
|
self.key = self._option_get('swift_store_key')
|
||||||
self.container = CONF.swift_store_container
|
self.container = CONF.swift_store_container
|
||||||
|
|
||||||
|
if self.multi_tenant:
|
||||||
|
if context is None:
|
||||||
|
reason = _("Multi-tenant Swift storage requires a context.")
|
||||||
|
raise exception.BadStoreConfiguration(store_name="swift",
|
||||||
|
reason=reason)
|
||||||
|
self.token = context.auth_tok
|
||||||
|
self.key = None # multi-tenant uses tokens, not (passwords)
|
||||||
|
if context.tenant and context.user:
|
||||||
|
self.user = context.tenant + ':' + context.user
|
||||||
|
if context.service_catalog:
|
||||||
|
service_catalog = context.service_catalog
|
||||||
|
self.storage_url = self._get_swift_endpoint(service_catalog)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# The config file has swift_store_large_object_*size in MB, but
|
# The config file has swift_store_large_object_*size in MB, but
|
||||||
# internally we store it in bytes, since the image_size parameter
|
# internally we store it in bytes, since the image_size parameter
|
||||||
@ -242,6 +266,9 @@ class Store(glance.store.base.Store):
|
|||||||
else: # Defaults https
|
else: # Defaults https
|
||||||
self.full_auth_address = 'https://' + self.auth_address
|
self.full_auth_address = 'https://' + self.auth_address
|
||||||
|
|
||||||
|
def _get_swift_endpoint(self, service_catalog):
|
||||||
|
return auth.get_endpoint(service_catalog, service_type='object-store')
|
||||||
|
|
||||||
def get(self, location):
|
def get(self, location):
|
||||||
"""
|
"""
|
||||||
Takes a `glance.store.location.Location` object that indicates
|
Takes a `glance.store.location.Location` object that indicates
|
||||||
@ -253,8 +280,7 @@ class Store(glance.store.base.Store):
|
|||||||
:raises `glance.exception.NotFound` if image does not exist
|
:raises `glance.exception.NotFound` if image does not exist
|
||||||
"""
|
"""
|
||||||
loc = location.store_location
|
loc = location.store_location
|
||||||
swift_conn = self._make_swift_connection(
|
swift_conn = self._swift_connection_for_location(loc)
|
||||||
auth_url=loc.swift_auth_url, user=loc.user, key=loc.key)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
(resp_headers, resp_body) = swift_conn.get_object(
|
(resp_headers, resp_body) = swift_conn.get_object(
|
||||||
@ -288,8 +314,7 @@ class Store(glance.store.base.Store):
|
|||||||
from glance.store.location.get_location_from_uri()
|
from glance.store.location.get_location_from_uri()
|
||||||
"""
|
"""
|
||||||
loc = location.store_location
|
loc = location.store_location
|
||||||
swift_conn = self._make_swift_connection(
|
swift_conn = self._swift_connection_for_location(loc)
|
||||||
auth_url=loc.swift_auth_url, user=loc.user, key=loc.key)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
resp_headers = swift_conn.head_object(container=loc.container,
|
resp_headers = swift_conn.head_object(container=loc.container,
|
||||||
@ -298,9 +323,31 @@ class Store(glance.store.base.Store):
|
|||||||
except Exception:
|
except Exception:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def _make_swift_connection(self, auth_url, user, key):
|
def _swift_connection_for_location(self, loc):
|
||||||
|
if loc.user:
|
||||||
|
return self._make_swift_connection(
|
||||||
|
loc.swift_url, loc.user, loc.key)
|
||||||
|
else:
|
||||||
|
if self.multi_tenant:
|
||||||
|
return self._make_swift_connection(
|
||||||
|
None, self.user, None,
|
||||||
|
storage_url=loc.swift_url, token=self.token)
|
||||||
|
else:
|
||||||
|
reason = (_("Location is missing user:password information."))
|
||||||
|
LOG.error(reason)
|
||||||
|
raise exception.BadStoreUri(message=reason)
|
||||||
|
|
||||||
|
def _make_swift_connection(self, auth_url, user, key, storage_url=None,
|
||||||
|
token=None):
|
||||||
"""
|
"""
|
||||||
Creates a connection using the Swift client library.
|
Creates a connection using the Swift client library.
|
||||||
|
|
||||||
|
:param auth_url The authentication for v1 style Swift auth or
|
||||||
|
v2 style Keystone auth.
|
||||||
|
:param user A string containing the tenant:user information.
|
||||||
|
:param key A string containing the key/password for the connection.
|
||||||
|
:param storage_url A string containing the storage URL.
|
||||||
|
:param token A string containing the token
|
||||||
"""
|
"""
|
||||||
snet = self.snet
|
snet = self.snet
|
||||||
auth_version = self.auth_version
|
auth_version = self.auth_version
|
||||||
@ -320,8 +367,14 @@ class Store(glance.store.base.Store):
|
|||||||
raise exception.BadStoreUri()
|
raise exception.BadStoreUri()
|
||||||
(tenant_name, user) = tenant_user
|
(tenant_name, user) = tenant_user
|
||||||
|
|
||||||
|
if self.multi_tenant:
|
||||||
|
#NOTE: multi-tenant supports v2 auth only
|
||||||
return swiftclient.Connection(
|
return swiftclient.Connection(
|
||||||
authurl=full_auth_url, user=user, key=key, snet=snet,
|
None, user, None, preauthurl=storage_url, preauthtoken=token,
|
||||||
|
snet=snet, tenant_name=tenant_name, auth_version='2')
|
||||||
|
else:
|
||||||
|
return swiftclient.Connection(
|
||||||
|
full_auth_url, user, key, snet=snet,
|
||||||
tenant_name=tenant_name, auth_version=auth_version)
|
tenant_name=tenant_name, auth_version=auth_version)
|
||||||
|
|
||||||
def _option_get(self, param):
|
def _option_get(self, param):
|
||||||
@ -370,7 +423,8 @@ class Store(glance.store.base.Store):
|
|||||||
fail if the image turns out to be greater than 5GB.
|
fail if the image turns out to be greater than 5GB.
|
||||||
"""
|
"""
|
||||||
swift_conn = self._make_swift_connection(
|
swift_conn = self._make_swift_connection(
|
||||||
auth_url=self.full_auth_address, user=self.user, key=self.key)
|
self.full_auth_address, self.user, self.key,
|
||||||
|
storage_url=self.storage_url, token=self.token)
|
||||||
|
|
||||||
create_container_if_missing(self.container, swift_conn)
|
create_container_if_missing(self.container, swift_conn)
|
||||||
|
|
||||||
@ -378,7 +432,7 @@ class Store(glance.store.base.Store):
|
|||||||
location = StoreLocation({'scheme': self.scheme,
|
location = StoreLocation({'scheme': self.scheme,
|
||||||
'container': self.container,
|
'container': self.container,
|
||||||
'obj': obj_name,
|
'obj': obj_name,
|
||||||
'authurl': self.auth_address,
|
'auth_or_store_url': self.auth_address,
|
||||||
'user': self.user,
|
'user': self.user,
|
||||||
'key': self.key})
|
'key': self.key})
|
||||||
|
|
||||||
@ -488,8 +542,7 @@ class Store(glance.store.base.Store):
|
|||||||
:raises NotFound if image does not exist
|
:raises NotFound if image does not exist
|
||||||
"""
|
"""
|
||||||
loc = location.store_location
|
loc = location.store_location
|
||||||
swift_conn = self._make_swift_connection(
|
swift_conn = self._swift_connection_for_location(loc)
|
||||||
auth_url=loc.swift_auth_url, user=loc.user, key=loc.key)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# We request the manifest for the object. If one exists,
|
# We request the manifest for the object. If one exists,
|
||||||
|
@ -43,6 +43,7 @@ class TestStoreLocation(base.StoreClearingUnitTest):
|
|||||||
'https://user:pass@example.com:80/images/some-id',
|
'https://user:pass@example.com:80/images/some-id',
|
||||||
'http://images.oracle.com/123456',
|
'http://images.oracle.com/123456',
|
||||||
'swift://account%3Auser:pass@authurl.com/container/obj-id',
|
'swift://account%3Auser:pass@authurl.com/container/obj-id',
|
||||||
|
'swift://storeurl.com/container/obj-id',
|
||||||
'swift+https://account%3Auser:pass@authurl.com/container/obj-id',
|
'swift+https://account%3Auser:pass@authurl.com/container/obj-id',
|
||||||
's3://accesskey:secretkey@s3.amazonaws.com/bucket/key-id',
|
's3://accesskey:secretkey@s3.amazonaws.com/bucket/key-id',
|
||||||
's3://accesskey:secretwith/aslash@s3.amazonaws.com/bucket/key-id',
|
's3://accesskey:secretwith/aslash@s3.amazonaws.com/bucket/key-id',
|
||||||
@ -143,8 +144,8 @@ class TestStoreLocation(base.StoreClearingUnitTest):
|
|||||||
loc.parse_uri(uri)
|
loc.parse_uri(uri)
|
||||||
|
|
||||||
self.assertEqual("swift", loc.scheme)
|
self.assertEqual("swift", loc.scheme)
|
||||||
self.assertEqual("example.com", loc.authurl)
|
self.assertEqual("example.com", loc.auth_or_store_url)
|
||||||
self.assertEqual("https://example.com", loc.swift_auth_url)
|
self.assertEqual("https://example.com", loc.swift_url)
|
||||||
self.assertEqual("images", loc.container)
|
self.assertEqual("images", loc.container)
|
||||||
self.assertEqual("1", loc.obj)
|
self.assertEqual("1", loc.obj)
|
||||||
self.assertEqual(None, loc.user)
|
self.assertEqual(None, loc.user)
|
||||||
@ -154,8 +155,8 @@ class TestStoreLocation(base.StoreClearingUnitTest):
|
|||||||
loc.parse_uri(uri)
|
loc.parse_uri(uri)
|
||||||
|
|
||||||
self.assertEqual("swift+https", loc.scheme)
|
self.assertEqual("swift+https", loc.scheme)
|
||||||
self.assertEqual("authurl.com", loc.authurl)
|
self.assertEqual("authurl.com", loc.auth_or_store_url)
|
||||||
self.assertEqual("https://authurl.com", loc.swift_auth_url)
|
self.assertEqual("https://authurl.com", loc.swift_url)
|
||||||
self.assertEqual("images", loc.container)
|
self.assertEqual("images", loc.container)
|
||||||
self.assertEqual("1", loc.obj)
|
self.assertEqual("1", loc.obj)
|
||||||
self.assertEqual("user", loc.user)
|
self.assertEqual("user", loc.user)
|
||||||
@ -166,8 +167,8 @@ class TestStoreLocation(base.StoreClearingUnitTest):
|
|||||||
loc.parse_uri(uri)
|
loc.parse_uri(uri)
|
||||||
|
|
||||||
self.assertEqual("swift+https", loc.scheme)
|
self.assertEqual("swift+https", loc.scheme)
|
||||||
self.assertEqual("authurl.com/v1", loc.authurl)
|
self.assertEqual("authurl.com/v1", loc.auth_or_store_url)
|
||||||
self.assertEqual("https://authurl.com/v1", loc.swift_auth_url)
|
self.assertEqual("https://authurl.com/v1", loc.swift_url)
|
||||||
self.assertEqual("container", loc.container)
|
self.assertEqual("container", loc.container)
|
||||||
self.assertEqual("12345", loc.obj)
|
self.assertEqual("12345", loc.obj)
|
||||||
self.assertEqual("user", loc.user)
|
self.assertEqual("user", loc.user)
|
||||||
@ -179,14 +180,27 @@ class TestStoreLocation(base.StoreClearingUnitTest):
|
|||||||
loc.parse_uri(uri)
|
loc.parse_uri(uri)
|
||||||
|
|
||||||
self.assertEqual("swift+http", loc.scheme)
|
self.assertEqual("swift+http", loc.scheme)
|
||||||
self.assertEqual("authurl.com/v1", loc.authurl)
|
self.assertEqual("authurl.com/v1", loc.auth_or_store_url)
|
||||||
self.assertEqual("http://authurl.com/v1", loc.swift_auth_url)
|
self.assertEqual("http://authurl.com/v1", loc.swift_url)
|
||||||
self.assertEqual("container", loc.container)
|
self.assertEqual("container", loc.container)
|
||||||
self.assertEqual("12345", loc.obj)
|
self.assertEqual("12345", loc.obj)
|
||||||
self.assertEqual("a:user@example.com", loc.user)
|
self.assertEqual("a:user@example.com", loc.user)
|
||||||
self.assertEqual("p@ss", loc.key)
|
self.assertEqual("p@ss", loc.key)
|
||||||
self.assertEqual(uri, loc.get_uri())
|
self.assertEqual(uri, loc.get_uri())
|
||||||
|
|
||||||
|
# multitenant puts store URL in the location (not auth)
|
||||||
|
uri = ('swift+http://storeurl.com/v1/container/12345')
|
||||||
|
loc.parse_uri(uri)
|
||||||
|
|
||||||
|
self.assertEqual("swift+http", loc.scheme)
|
||||||
|
self.assertEqual("storeurl.com/v1", loc.auth_or_store_url)
|
||||||
|
self.assertEqual("http://storeurl.com/v1", loc.swift_url)
|
||||||
|
self.assertEqual("container", loc.container)
|
||||||
|
self.assertEqual("12345", loc.obj)
|
||||||
|
self.assertEqual(None, loc.user)
|
||||||
|
self.assertEqual(None, loc.key)
|
||||||
|
self.assertEqual(uri, loc.get_uri())
|
||||||
|
|
||||||
bad_uri = 'swif://'
|
bad_uri = 'swif://'
|
||||||
self.assertRaises(Exception, loc.parse_uri, bad_uri)
|
self.assertRaises(Exception, loc.parse_uri, bad_uri)
|
||||||
|
|
||||||
|
@ -613,6 +613,13 @@ class TestStoreAuthV2(TestStoreAuthV1):
|
|||||||
self.store.get,
|
self.store.get,
|
||||||
loc)
|
loc)
|
||||||
|
|
||||||
|
def test_v2_multi_tenant_location(self):
|
||||||
|
conf = self.getConfig()
|
||||||
|
conf['swift_store_multi_tenant'] = True
|
||||||
|
uri = "swift://auth_address/glance/%s" % (FAKE_UUID)
|
||||||
|
loc = get_location_from_uri(uri)
|
||||||
|
self.assertEqual('swift', loc.store_name)
|
||||||
|
|
||||||
|
|
||||||
class TestChunkReader(base.StoreClearingUnitTest):
|
class TestChunkReader(base.StoreClearingUnitTest):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user