7ab80b554d
Implements Blueprint reddwarf-trove-rename Change-Id: Ia9ee609bbc06a1d8b9d6917642529f30347541fd
403 lines
16 KiB
Python
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)
|