135 lines
4.8 KiB
Python
135 lines
4.8 KiB
Python
# 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 glance.store
|
|
|
|
|
|
class SwiftBackend(glance.store.Backend):
|
|
"""
|
|
An implementation of the swift backend adapter.
|
|
"""
|
|
EXAMPLE_URL = "swift://user:password@auth_url/container/file.gz.0"
|
|
|
|
CHUNKSIZE = 65536
|
|
|
|
@classmethod
|
|
def get(cls, parsed_uri, expected_size, conn_class=None):
|
|
"""
|
|
Takes a parsed_uri in the format of:
|
|
swift://user:password@auth_url/container/file.gz.0, connects to the
|
|
swift instance at auth_url and downloads the file. Returns the
|
|
generator resp_body provided by get_object.
|
|
"""
|
|
(user, key, authurl, container, obj) = \
|
|
cls._parse_swift_tokens(parsed_uri)
|
|
|
|
# TODO(sirp): snet=False for now, however, if the instance of
|
|
# swift we're talking to is within our same region, we should set
|
|
# snet=True
|
|
connection_class = get_connection_class(conn_class)
|
|
|
|
swift_conn = conn_class(
|
|
authurl=authurl, user=user, key=key, snet=False)
|
|
|
|
(resp_headers, resp_body) = swift_conn.get_object(
|
|
container=container, obj=obj, resp_chunk_size=cls.CHUNKSIZE)
|
|
|
|
obj_size = int(resp_headers['content-length'])
|
|
if obj_size != expected_size:
|
|
raise glance.store.BackendException(
|
|
"Expected %s byte file, Swift has %s bytes" %
|
|
(expected_size, obj_size))
|
|
|
|
return resp_body
|
|
|
|
@classmethod
|
|
def delete(cls, parsed_uri, conn_class=None):
|
|
"""
|
|
Deletes the swift object(s) at the parsed_uri location
|
|
"""
|
|
(user, key, authurl, container, obj) = \
|
|
cls._parse_swift_tokens(parsed_uri)
|
|
|
|
# TODO(sirp): snet=False for now, however, if the instance of
|
|
# swift we're talking to is within our same region, we should set
|
|
# snet=True
|
|
connection_class = get_connection_class(conn_class)
|
|
|
|
swift_conn = conn_class(
|
|
authurl=authurl, user=user, key=key, snet=False)
|
|
|
|
(resp_headers, resp_body) = swift_conn.delete_object(
|
|
container=container, obj=obj)
|
|
|
|
# TODO(jaypipes): What to return here? After reading the docs
|
|
# at swift.common.client, I'm not sure what to check for...
|
|
|
|
@classmethod
|
|
def _parse_swift_tokens(cls, parsed_uri):
|
|
"""
|
|
Parsing the swift uri is three phases:
|
|
1) urlparse to split the tokens
|
|
2) use RE to split on @ and /
|
|
3) reassemble authurl
|
|
"""
|
|
path = parsed_uri.path.lstrip('//')
|
|
netloc = parsed_uri.netloc
|
|
|
|
try:
|
|
try:
|
|
creds, netloc = netloc.split('@')
|
|
path = '/'.join([netloc, path])
|
|
except ValueError:
|
|
# Python 2.6.1 compat
|
|
# see lp659445 and Python issue7904
|
|
creds, path = path.split('@')
|
|
|
|
user, key = creds.split(':')
|
|
path_parts = path.split('/')
|
|
obj = path_parts.pop()
|
|
container = path_parts.pop()
|
|
except (ValueError, IndexError):
|
|
raise glance.store.BackendException(
|
|
"Expected four values to unpack in: swift:%s. "
|
|
"Should have received something like: %s."
|
|
% (parsed_uri.path, cls.EXAMPLE_URL))
|
|
|
|
authurl = "https://%s" % '/'.join(path_parts)
|
|
|
|
return user, key, authurl, container, obj
|
|
|
|
|
|
def get_connection_class(conn_class):
|
|
if conn_class:
|
|
pass # Use the provided conn_class
|
|
else:
|
|
# NOTE(sirp): A standard import statement won't work here because
|
|
# this file ('swift.py') is shadowing the swift module, and since
|
|
# the import statement searches locally before globally, we'd end
|
|
# up importing ourselves.
|
|
#
|
|
# NOTE(jaypipes): This can be resolved by putting this code in
|
|
# /glance/store/swift/__init__.py
|
|
#
|
|
# see http://docs.python.org/library/functions.html#__import__
|
|
PERFORM_ABSOLUTE_IMPORTS = 0
|
|
swift = __import__('swift.common.client', globals(), locals(), [],
|
|
PERFORM_ABSOLUTE_IMPORTS)
|
|
|
|
conn_class = swift.common.client.Connection
|
|
return conn_class
|