glance/glance/tests/functional/store_utils.py

308 lines
10 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack Foundation
# Copyright 2012 Red Hat, Inc
# 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.
"""
Utility methods to set testcases up for Swift and/or S3 tests.
"""
from __future__ import print_function
import BaseHTTPServer
import ConfigParser
import httplib
import os
import random
import swiftclient
import thread
from glance.store.s3 import get_s3_location, get_calling_format
FIVE_KB = 5 * 1024
class RemoteImageHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_HEAD(self):
"""
Respond to an image HEAD request fake metadata
"""
if 'images' in self.path:
self.send_response(200)
self.send_header('Content-Type', 'application/octet-stream')
self.send_header('Content-Length', FIVE_KB)
self.end_headers()
return
else:
self.send_error(404, 'File Not Found: %s' % self.path)
return
def do_GET(self):
"""
Respond to an image GET request with fake image content.
"""
if 'images' in self.path:
self.send_response(200)
self.send_header('Content-Type', 'application/octet-stream')
self.send_header('Content-Length', FIVE_KB)
self.end_headers()
image_data = '*' * FIVE_KB
self.wfile.write(image_data)
self.wfile.close()
return
else:
self.send_error(404, 'File Not Found: %s' % self.path)
return
def log_message(self, format, *args):
"""
Simple override to prevent writing crap to stderr...
"""
pass
def setup_http(test):
server_class = BaseHTTPServer.HTTPServer
remote_server = server_class(('127.0.0.1', 0), RemoteImageHandler)
remote_ip, remote_port = remote_server.server_address
def serve_requests(httpd):
httpd.serve_forever()
thread.start_new_thread(serve_requests, (remote_server,))
test.http_server = remote_server
test.http_ip = remote_ip
test.http_port = remote_port
test.addCleanup(test.http_server.shutdown)
def get_http_uri(test, image_id):
uri = 'http://%(http_ip)s:%(http_port)d/images/' % test.__dict__
uri += image_id
return uri
def _uniq(value):
return '%s.%d' % (value, random.randint(0, 99999))
def setup_swift(test):
# Test machines can set the GLANCE_TEST_SWIFT_CONF variable
# to override the location of the config file for migration testing
CONFIG_FILE_PATH = os.environ.get('GLANCE_TEST_SWIFT_CONF')
if not CONFIG_FILE_PATH:
test.disabled_message = "GLANCE_TEST_SWIFT_CONF environ not set."
print("GLANCE_TEST_SWIFT_CONF environ not set.")
test.disabled = True
return
if os.path.exists(CONFIG_FILE_PATH):
cp = ConfigParser.RawConfigParser()
try:
cp.read(CONFIG_FILE_PATH)
defaults = cp.defaults()
for key, value in defaults.items():
if key == 'swift_store_container':
test.__dict__[key] = (_uniq(value))
else:
test.__dict__[key] = value
except ConfigParser.ParsingError as e:
test.disabled_message = ("Failed to read test_swift.conf "
"file. Got error: %s" % e)
test.disabled = True
return
try:
swift_host = test.swift_store_auth_address
if not swift_host.startswith('http'):
swift_host = 'https://' + swift_host
user = test.swift_store_user
key = test.swift_store_key
container_name = test.swift_store_container
except AttributeError as e:
test.disabled_message = ("Failed to find required configuration "
"options for Swift store. "
"Got error: %s" % e)
test.disabled = True
return
swift_conn = swiftclient.Connection(
authurl=swift_host, auth_version=test.swift_store_auth_version,
user=user, key=key, snet=False, retries=1)
try:
_resp_headers, containers = swift_conn.get_account()
except Exception as e:
test.disabled_message = ("Failed to get_account from Swift "
"Got error: %s" % e)
test.disabled = True
return
try:
for container in containers:
if container == container_name:
swift_conn.delete_container(container)
except swiftclient.ClientException as e:
test.disabled_message = ("Failed to delete container from Swift "
"Got error: %s" % e)
test.disabled = True
return
test.swift_conn = swift_conn
try:
swift_conn.put_container(container_name)
except swiftclient.ClientException as e:
test.disabled_message = ("Failed to create container. "
"Got error: %s" % e)
test.disabled = True
return
def teardown_swift(test):
swift_conn = test.swift_conn
container_name = test.swift_store_container
if not test.disabled:
try:
_resp_headers, containers = swift_conn.get_account()
# Delete all containers matching the container name prefix
for container in containers:
if container == container_name:
swift_conn.delete_container(container)
except swiftclient.ClientException as e:
if e.http_status == httplib.CONFLICT:
pass
else:
raise
test.swift_conn.put_container(test.swift_store_container)
def get_swift_uri(test, image_id):
# Apparently we must use HTTPS with Cloud Files now, otherwise
# we will get a 301 Moved.... :(
user = swiftclient.quote('%(swift_store_user)s' % test.__dict__)
creds = (user + ':%(swift_store_key)s' % test.__dict__)
uri = 'swift+https://' + creds
uri += ('@%(swift_store_auth_address)s/%(swift_store_container)s/' %
test.__dict__)
uri += image_id
uri = uri.replace('@http://', '@')
uri = uri.replace('@https://', '@')
return uri
def setup_s3(test):
# Test machines can set the GLANCE_TEST_S3_CONF variable
# to override the location of the config file for S3 testing
CONFIG_FILE_PATH = os.environ.get('GLANCE_TEST_S3_CONF')
if not CONFIG_FILE_PATH:
test.disabled_message = "GLANCE_TEST_S3_CONF environ not set."
test.disabled = True
return
if os.path.exists(CONFIG_FILE_PATH):
cp = ConfigParser.RawConfigParser()
try:
cp.read(CONFIG_FILE_PATH)
defaults = cp.defaults()
for key, value in defaults.items():
test.__dict__[key] = (_uniq(value)
if key == 's3_store_bucket' else value)
except ConfigParser.ParsingError as e:
test.disabled_message = ("Failed to read test_s3.conf config "
"file. Got error: %s" % e)
test.disabled = True
return
from boto.s3.connection import S3Connection
from boto.exception import S3ResponseError
try:
s3_host = test.s3_store_host
access_key = test.s3_store_access_key
secret_key = test.s3_store_secret_key
bucket_name = test.s3_store_bucket
except AttributeError as e:
test.disabled_message = ("Failed to find required configuration "
"options for S3 store. Got error: %s" % e)
test.disabled = True
return
calling_format = get_calling_format(test.s3_store_bucket_url_format)
s3_conn = S3Connection(access_key, secret_key,
host=s3_host,
is_secure=False,
calling_format=calling_format)
test.bucket = None
try:
buckets = s3_conn.get_all_buckets()
for bucket in buckets:
if bucket.name == bucket_name:
test.bucket = bucket
except S3ResponseError as e:
test.disabled_message = ("Failed to connect to S3 with "
"credentials, to find bucket. "
"Got error: %s" % e)
test.disabled = True
return
except TypeError as e:
# This hack is necessary because of a bug in boto 1.9b:
# http://code.google.com/p/boto/issues/detail?id=540
test.disabled_message = ("Failed to connect to S3 with "
"credentials. Got error: %s" % e)
test.disabled = True
return
test.s3_conn = s3_conn
if not test.bucket:
location = get_s3_location(test.s3_store_host)
try:
test.bucket = s3_conn.create_bucket(bucket_name,
location=location)
except S3ResponseError as e:
test.disabled_message = ("Failed to create bucket. "
"Got error: %s" % e)
test.disabled = True
return
else:
for key in test.bucket.list():
key.delete()
def teardown_s3(test):
if not test.disabled:
# It's not possible to simply clear a bucket. You
# need to loop over all the keys and delete them
# all first...
for key in test.bucket.list():
key.delete()
test.s3_conn.delete_bucket(test.s3_store_bucket)
def get_s3_uri(test, image_id):
uri = ('s3://%(s3_store_access_key)s:%(s3_store_secret_key)s' %
test.__dict__)
uri += '@%(s3_conn)s/' % test.__dict__
uri += '%(s3_store_bucket)s/' % test.__dict__
uri += image_id
return uri.replace('S3Connection:', '')