trove/trove/tests/fakes/swift.py
Michael Basnight 7ab80b554d Rename from reddwarf to trove.
Implements Blueprint reddwarf-trove-rename

Change-Id: Ia9ee609bbc06a1d8b9d6917642529f30347541fd
2013-06-24 14:11:15 -07:00

403 lines
16 KiB
Python

import uuid
import logging
from mockito import when, any
import swiftclient.client as swift_client
import swiftclient
# Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
# 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 httplib
import json
import os
import socket
from swiftclient import client as swift
LOG = logging.getLogger(__name__)
class FakeSwiftClient(object):
"""Logs calls instead of executing."""
def __init__(self, *args, **kwargs):
pass
@classmethod
def Connection(self, *args, **kargs):
LOG.debug("fake FakeSwiftClient Connection")
return FakeSwiftConnection()
class FakeSwiftConnection(object):
"""Logging calls instead of executing"""
def __init__(self, *args, **kwargs):
pass
def get_auth(self):
return (
u"http://127.0.0.1:8080/v1/AUTH_c7b038976df24d96bf1980f5da17bd89",
u'MIINrwYJKoZIhvcNAQcCoIINoDCCDZwCAQExCTAHBgUrDgMCGjCCDIgGCSqGSIb3'
u'DQEHAaCCDHkEggx1eyJhY2Nlc3MiOiB7InRva2VuIjogeyJpc3N1ZWRfYXQiOiAi'
u'MjAxMy0wMy0xOFQxODoxMzoyMC41OTMyNzYiLCAiZXhwaXJlcyI6ICIyMDEzLTAz'
u'LTE5VDE4OjEzOjIwWiIsICJpZCI6ICJwbGFjZWhvbGRlciIsICJ0ZW5hbnQiOiB7'
u'ImVuYWJsZWQiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiBudWxsLCAibmFtZSI6ICJy'
u'ZWRkd2FyZiIsICJpZCI6ICJjN2IwMzg5NzZkZjI0ZDk2YmYxOTgwZjVkYTE3YmQ4'
u'OSJ9fSwgInNlcnZpY2VDYXRhbG9nIjogW3siZW5kcG9pbnRzIjogW3siYWRtaW5')
def get_account(self):
return ({'content-length': '2', 'accept-ranges': 'bytes',
'x-timestamp': '1363049003.92304',
'x-trans-id': 'tx9e5da02c49ed496395008309c8032a53',
'date': 'Tue, 10 Mar 2013 00:43:23 GMT',
'x-account-bytes-used': '0',
'x-account-container-count': '0',
'content-type': 'application/json; charset=utf-8',
'x-account-object-count': '0'}, [])
def head_container(self, container):
LOG.debug("fake head_container(%s)" % container)
if container == 'missing_container':
raise swift.ClientException('fake exception',
http_status=httplib.NOT_FOUND)
elif container == 'unauthorized_container':
raise swift.ClientException('fake exception',
http_status=httplib.UNAUTHORIZED)
elif container == 'socket_error_on_head':
raise socket.error(111, 'ECONNREFUSED')
pass
def put_container(self, container):
LOG.debug("fake put_container(%s)" % container)
pass
def get_container(self, container, **kwargs):
LOG.debug("fake get_container(%s)" % container)
fake_header = None
fake_body = [{'name': 'backup_001'},
{'name': 'backup_002'},
{'name': 'backup_003'}]
return fake_header, fake_body
def head_object(self, container, name):
LOG.debug("fake put_container(%s, %s)" % (container, name))
return {'etag': 'fake-md5-sum'}
def get_object(self, container, name):
LOG.debug("fake get_object(%s, %s)" % (container, name))
if container == 'socket_error_on_get':
raise socket.error(111, 'ECONNREFUSED')
if 'metadata' in name:
fake_object_header = None
metadata = {}
if container == 'unsupported_version':
metadata['version'] = '9.9.9'
else:
metadata['version'] = '1.0.0'
metadata['backup_id'] = 123
metadata['volume_id'] = 123
metadata['backup_name'] = 'fake backup'
metadata['backup_description'] = 'fake backup description'
metadata['created_at'] = '2013-02-19 11:20:54,805'
metadata['objects'] = [{
'backup_001': {'compression': 'zlib', 'length': 10},
'backup_002': {'compression': 'zlib', 'length': 10},
'backup_003': {'compression': 'zlib', 'length': 10}
}]
metadata_json = json.dumps(metadata, sort_keys=True, indent=2)
fake_object_body = metadata_json
return (fake_object_header, fake_object_body)
fake_header = None
fake_object_body = os.urandom(1024 * 1024)
return (fake_header, fake_object_body)
def put_object(self, container, name, reader):
LOG.debug("fake put_object(%s, %s)" % (container, name))
if container == 'socket_error_on_put':
raise socket.error(111, 'ECONNREFUSED')
return 'fake-md5-sum'
def delete_object(self, container, name):
LOG.debug("fake delete_object(%s, %s)" % (container, name))
if container == 'socket_error_on_delete':
raise socket.error(111, 'ECONNREFUSED')
pass
class SwiftClientStub(object):
"""
Component for controlling behavior of Swift Client Stub. Instantiated
before tests are invoked in "fake" mode. Invoke methods to control
behavior so that systems under test can interact with this as it is a
real swift client with a real backend
example:
if FAKE:
swift_stub = SwiftClientStub()
swift_stub.with_account('xyz')
# returns swift account info and auth token
component_using_swift.get_swift_account()
if FAKE:
swift_stub.with_container('test-container-name')
# returns swift container information - mostly faked
component_using.swift.create_container('test-container-name')
component_using_swift.get_container_info('test-container-name')
if FAKE:
swift_stub.with_object('test-container-name', 'test-object-name',
'test-object-contents')
# returns swift object info and contents
component_using_swift.create_object('test-container-name',
'test-object-name', 'test-contents')
component_using_swift.get_object('test-container-name', 'test-object-name')
if FAKE:
swift_stub.without_object('test-container-name', 'test-object-name')
# allows object to be removed ONCE
component_using_swift.remove_object('test-container-name',
'test-object-name')
# throws ClientException - 404
component_using_swift.get_object('test-container-name', 'test-object-name')
component_using_swift.remove_object('test-container-name',
'test-object-name')
if FAKE:
swift_stub.without_object('test-container-name', 'test-object-name')
# allows container to be removed ONCE
component_using_swift.remove_container('test-container-name')
# throws ClientException - 404
component_using_swift.get_container('test-container-name')
component_using_swift.remove_container('test-container-name')
"""
def __init__(self):
self._connection = swift_client.Connection()
# simulate getting an unknown container
when(swift_client.Connection).get_container(any()).thenRaise(
swiftclient.ClientException('Resource Not Found', http_status=404))
self._containers = {}
self._containers_list = []
self._objects = {}
def _remove_object(self, name, some_list):
idx = [i for i, obj in enumerate(some_list) if obj['name'] == name]
if len(idx) == 1:
del some_list[idx[0]]
def _ensure_object_exists(self, container, name):
self._connection.get_object(container, name)
def with_account(self, account_id):
"""
setups up account headers
example:
if FAKE:
swift_stub = SwiftClientStub()
swift_stub.with_account('xyz')
# returns swift account info and auth token
component_using_swift.get_swift_account()
:param account_id: account id
"""
def account_resp():
return ({'content-length': '2', 'accept-ranges': 'bytes',
'x-timestamp': '1363049003.92304',
'x-trans-id': 'tx9e5da02c49ed496395008309c8032a53',
'date': 'Tue, 10 Mar 2013 00:43:23 GMT',
'x-account-bytes-used': '0',
'x-account-container-count': '0',
'content-type': 'application/json; charset=utf-8',
'x-account-object-count': '0'}, self._containers_list)
when(swift_client.Connection).get_auth().thenReturn((
u"http://127.0.0.1:8080/v1/AUTH_c7b038976df24d96bf1980f5da17bd89",
u'MIINrwYJKoZIhvcNAQcCoIINoDCCDZwCAQExCTAHBgUrDgMCGjCCDIgGCSqGSIb3'
u'DQEHAaCCDHkEggx1eyJhY2Nlc3MiOiB7InRva2VuIjogeyJpc3N1ZWRfYXQiOiAi'
u'MjAxMy0wMy0xOFQxODoxMzoyMC41OTMyNzYiLCAiZXhwaXJlcyI6ICIyMDEzLTAz'
u'LTE5VDE4OjEzOjIwWiIsICJpZCI6ICJwbGFjZWhvbGRlciIsICJ0ZW5hbnQiOiB7'
u'ImVuYWJsZWQiOiB0cnVlLCAiZGVzY3JpcHRpb24iOiBudWxsLCAibmFtZSI6ICJy'
u'ZWRkd2FyZiIsICJpZCI6ICJjN2IwMzg5NzZkZjI0ZDk2YmYxOTgwZjVkYTE3YmQ4'
u'OSJ9fSwgInNlcnZpY2VDYXRhbG9nIjogW3siZW5kcG9pbnRzIjogW3siYWRtaW5')
)
when(swift_client.Connection).get_account().thenReturn(account_resp())
return self
def _create_container(self, container_name):
container = {'count': 0, 'bytes': 0, 'name': container_name}
self._containers[container_name] = container
self._containers_list.append(container)
self._objects[container_name] = []
def _ensure_container_exists(self, container):
self._connection.get_container(container)
def _delete_container(self, container):
self._remove_object(container, self._containers_list)
del self._containers[container]
del self._objects[container]
def with_container(self, container_name):
"""
sets expectations for creating a container and subsequently getting its
information
example:
if FAKE:
swift_stub.with_container('test-container-name')
# returns swift container information - mostly faked
component_using.swift.create_container('test-container-name')
component_using_swift.get_container_info('test-container-name')
:param container_name: container name that is expected to be created
"""
def container_resp(container):
return ({'content-length': '2', 'x-container-object-count': '0',
'accept-ranges': 'bytes', 'x-container-bytes-used': '0',
'x-timestamp': '1363370869.72356',
'x-trans-id': 'tx7731801ac6ec4e5f8f7da61cde46bed7',
'date': 'Fri, 10 Mar 2013 18:07:58 GMT',
'content-type': 'application/json; charset=utf-8'},
self._objects[container])
# if this is called multiple times then nothing happens
when(swift_client.Connection).put_container(container_name).thenReturn(
None)
self._create_container(container_name)
# return container headers
when(swift_client.Connection).get_container(container_name).thenReturn(
container_resp(container_name))
return self
def without_container(self, container):
"""
sets expectations for removing a container and subsequently throwing an
exception for further interactions
example:
if FAKE:
swift_stub.without_container('test-container-name')
# returns swift container information - mostly faked
component_using.swift.remove_container('test-container-name')
# throws exception "Resource Not Found - 404"
component_using_swift.get_container_info('test-container-name')
:param container: container name that is expected to be removed
"""
# first ensure container
self._ensure_container_exists(container)
# allow one call to get container and then throw exceptions (may need
# to be revised
when(swift_client.Connection).delete_container(container).thenRaise(
swiftclient.ClientException("Resource Not Found", http_status=404))
when(swift_client.Connection).get_container(container).thenRaise(
swiftclient.ClientException("Resource Not Found", http_status=404))
self._delete_container(container)
return self
def with_object(self, container, name, contents):
"""
sets expectations for creating an object and subsequently getting its
contents
example:
if FAKE:
swift_stub.with_object('test-container-name', 'test-object-name',
'test-object-contents')
# returns swift object info and contents
component_using_swift.create_object('test-container-name',
'test-object-name', 'test-contents')
component_using_swift.get_object('test-container-name',
'test-object-name')
:param container: container name that is the object belongs
:param name: the name of the object expected to be created
:param contents: the contents of the object
"""
self._connection.get_container(container)
when(swift_client.Connection).put_object(container, name,
contents).thenReturn(
uuid.uuid1())
when(swift_client.Connection).get_object(container, name).thenReturn(
({'content-length': len(contents), 'accept-ranges': 'bytes',
'last-modified': 'Mon, 10 Mar 2013 01:06:34 GMT',
'etag': 'eb15a6874ce265e2c3eb1b4891567bab',
'x-timestamp': '1363568794.67584',
'x-trans-id': 'txef3aaf26c897420c8e77c9750ce6a501',
'date': 'Mon, 10 Mar 2013 05:35:14 GMT',
'content-type': 'application/octet-stream'}, contents)
)
self._remove_object(name, self._objects[container])
self._objects[container].append(
{'bytes': 13, 'last_modified': '2013-03-15T22:10:49.361950',
'hash': 'ccc55aefbf92aa66f42b638802c5e7f6', 'name': name,
'content_type': 'application/octet-stream'})
return self
def without_object(self, container, name):
"""
sets expectations for deleting an object
example:
if FAKE:
swift_stub.without_object('test-container-name', 'test-object-name')
# allows container to be removed ONCE
component_using_swift.remove_container('test-container-name')
# throws ClientException - 404
component_using_swift.get_container('test-container-name')
component_using_swift.remove_container('test-container-name')
:param container: container name that is the object belongs
:param name: the name of the object expected to be removed
"""
self._ensure_container_exists(container)
self._ensure_object_exists(container, name)
# throw exception if someone calls get object
when(swift_client.Connection).get_object(container, name).thenRaise(
swiftclient.ClientException('Resource Not found', http_status=404))
when(swift_client.Connection).delete_object(
container, name).thenReturn(None).thenRaise(
swiftclient.ClientException('Resource Not Found',
http_status=404))
self._remove_object(name, self._objects[container])
return self
def fake_create_swift_client(*args):
return FakeSwiftClient.Connection(*args)