Merge "Initial add of Swift tests"
This commit is contained in:
commit
d246eb4350
@ -199,3 +199,18 @@ build_interval = 10
|
||||
# Number of seconds to time out on waiting for a volume
|
||||
# to be available or reach an expected status
|
||||
build_timeout = 300
|
||||
|
||||
[object-storage]
|
||||
# This section contains configuration options used when executing tests
|
||||
# against the OpenStack Object Storage API.
|
||||
# This should be the username of a user WITHOUT administrative privileges
|
||||
username = admin
|
||||
# The above non-administrative user's password
|
||||
password = password
|
||||
# The above non-administrative user's tenant name
|
||||
tenant_name = admin
|
||||
|
||||
# The type of endpoint for an Object Storage API service. Unless you have a
|
||||
# custom Keystone service catalog implementation, you probably want to leave
|
||||
# this value as "object-store"
|
||||
catalog_type = object-store
|
||||
|
@ -169,3 +169,18 @@ build_interval = %VOLUME_BUILD_INTERVAL%
|
||||
# Number of seconds to time out on waiting for a volume
|
||||
# to be available or reach an expected status
|
||||
build_timeout = %VOLUME_BUILD_TIMEOUT%
|
||||
|
||||
[object-storage]
|
||||
# This section contains configuration options used when executing tests
|
||||
# against the OpenStack Object Storage API.
|
||||
# This should be the username of a user WITHOUT administrative privileges
|
||||
username = %USERNAME%
|
||||
# The above non-administrative user's password
|
||||
password = %PASSWORD%
|
||||
# The above non-administrative user's tenant name
|
||||
tenant_name = %TENANT_NAME%
|
||||
|
||||
# The type of endpoint for an Object Storage API service. Unless you have a
|
||||
# custom Keystone service catalog implementation, you probably want to leave
|
||||
# this value as "object-store"
|
||||
catalog_type = %OBJECT_CATALOG_TYPE%
|
||||
|
@ -171,6 +171,9 @@ class RestClient(object):
|
||||
def put(self, url, body, headers):
|
||||
return self.request('PUT', url, headers, body)
|
||||
|
||||
def head(self, url, headers=None):
|
||||
return self.request('HEAD', url, headers=None)
|
||||
|
||||
def _log(self, req_url, body, resp, resp_body):
|
||||
self.log.error('Request URL: ' + req_url)
|
||||
self.log.error('Request Body: ' + str(body))
|
||||
|
@ -1,3 +1,20 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 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 random
|
||||
import re
|
||||
import urllib
|
||||
@ -44,3 +61,27 @@ def parse_image_id(image_ref):
|
||||
temp = image_ref.rsplit('/')
|
||||
#Return the last item, which is the image id
|
||||
return temp[len(temp) - 1]
|
||||
|
||||
|
||||
def arbitrary_string(size=4, base_text=None):
|
||||
"""Return exactly size bytes worth of base_text as a string"""
|
||||
|
||||
if (base_text is None) or (base_text == ''):
|
||||
base_text = 'test'
|
||||
|
||||
if size <= 0:
|
||||
return ''
|
||||
|
||||
extra = size % len(base_text)
|
||||
body = ''
|
||||
|
||||
if extra == 0:
|
||||
body = base_text * size
|
||||
|
||||
if extra == size:
|
||||
body = base_text[:size]
|
||||
|
||||
if extra > 0 and extra < size:
|
||||
body = (size / len(base_text)) * base_text + base_text[:extra]
|
||||
|
||||
return body
|
||||
|
@ -378,6 +378,31 @@ class VolumeConfig(BaseConfig):
|
||||
return self.get("catalog_type", 'volume')
|
||||
|
||||
|
||||
class ObjectStorageConfig(BaseConfig):
|
||||
|
||||
SECTION_NAME = "object-storage"
|
||||
|
||||
@property
|
||||
def username(self):
|
||||
"""Username to use for Object-Storage API requests."""
|
||||
return self.get("username", "admin")
|
||||
|
||||
@property
|
||||
def tenant_name(self):
|
||||
"""Tenant name to use for Object-Storage API requests."""
|
||||
return self.get("tenant_name", "admin")
|
||||
|
||||
@property
|
||||
def password(self):
|
||||
"""API key to use when authenticating."""
|
||||
return self.get("password", "password")
|
||||
|
||||
@property
|
||||
def catalog_type(self):
|
||||
"""Catalog type of the Object-Storage service."""
|
||||
return self.get("catalog_type", 'object-store')
|
||||
|
||||
|
||||
# TODO(jaypipes): Move this to a common utils (not data_utils...)
|
||||
def singleton(cls):
|
||||
"""Simple wrapper for classes that should only have a single instance"""
|
||||
@ -426,6 +451,7 @@ class TempestConfig:
|
||||
self.images = ImagesConfig(self._conf)
|
||||
self.network = NetworkConfig(self._conf)
|
||||
self.volume = VolumeConfig(self._conf)
|
||||
self.object_storage = ObjectStorageConfig(self._conf)
|
||||
|
||||
def load_config(self, path):
|
||||
"""Read configuration from given path and return a config object."""
|
||||
|
@ -53,6 +53,9 @@ from tempest.services.nova.xml.volumes_extensions_client \
|
||||
import VolumesExtensionsClientXML
|
||||
from tempest.services.volume.json.volumes_client import VolumesClientJSON
|
||||
from tempest.services.volume.xml.volumes_client import VolumesClientXML
|
||||
from tempest.services.object_storage.account_client import AccountClient
|
||||
from tempest.services.object_storage.container_client import ContainerClient
|
||||
from tempest.services.object_storage.object_client import ObjectClient
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -179,6 +182,9 @@ class Manager(object):
|
||||
raise exceptions.InvalidConfiguration(msg)
|
||||
self.console_outputs_client = ConsoleOutputsClient(*client_args)
|
||||
self.network_client = NetworkClient(*client_args)
|
||||
self.account_client = AccountClient(*client_args)
|
||||
self.container_client = ContainerClient(*client_args)
|
||||
self.object_client = ObjectClient(*client_args)
|
||||
|
||||
|
||||
class AltManager(Manager):
|
||||
|
0
tempest/services/object_storage/__init__.py
Normal file
0
tempest/services/object_storage/__init__.py
Normal file
80
tempest/services/object_storage/account_client.py
Normal file
80
tempest/services/object_storage/account_client.py
Normal file
@ -0,0 +1,80 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 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 json
|
||||
|
||||
from tempest.common.rest_client import RestClient
|
||||
|
||||
|
||||
class AccountClient(RestClient):
|
||||
def __init__(self, config, username, password, auth_url, tenant_name=None):
|
||||
super(AccountClient, self).__init__(config, username, password,
|
||||
auth_url, tenant_name)
|
||||
self.service = self.config.object_storage.catalog_type
|
||||
self.format = 'json'
|
||||
|
||||
def list_account_metadata(self):
|
||||
"""
|
||||
HEAD on the storage URL
|
||||
Returns all account metadata headers
|
||||
"""
|
||||
|
||||
headers = {"X-Storage-Token", self.token}
|
||||
resp, body = self.head('', headers=headers)
|
||||
return resp, body
|
||||
|
||||
def create_account_metadata(self, metadata,
|
||||
metadata_prefix='X-Account-Meta-'):
|
||||
"""Creates an account metadata entry"""
|
||||
headers = {}
|
||||
for key in metadata:
|
||||
headers[metadata_prefix + key] = metadata[key]
|
||||
|
||||
resp, body = self.post('', headers=headers, body=None)
|
||||
return resp, body
|
||||
|
||||
def list_account_containers(self, params=None):
|
||||
"""
|
||||
GET on the (base) storage URL
|
||||
Given the X-Storage-URL and a valid X-Auth-Token, returns
|
||||
a list of all containers for the account.
|
||||
|
||||
Optional Arguments:
|
||||
limit=[integer value N]
|
||||
Limits the number of results to at most N values
|
||||
DEFAULT: 10,000
|
||||
|
||||
marker=[string value X]
|
||||
Given string value X, return object names greater in value
|
||||
than the specified marker.
|
||||
DEFAULT: No Marker
|
||||
|
||||
format=[string value, either 'json' or 'xml']
|
||||
Specify either json or xml to return the respective serialized
|
||||
response.
|
||||
DEFAULT: Python-List returned in response body
|
||||
"""
|
||||
|
||||
param_list = ['format=%s&' % self.format]
|
||||
if params is not None:
|
||||
for param, value in params.iteritems():
|
||||
param_list.append("%s=%s&" % (param, value))
|
||||
url = '?' + ''.join(param_list)
|
||||
|
||||
resp, body = self.get(url)
|
||||
body = json.loads(body)
|
||||
return resp, body
|
152
tempest/services/object_storage/container_client.py
Normal file
152
tempest/services/object_storage/container_client.py
Normal file
@ -0,0 +1,152 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 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 json
|
||||
|
||||
from tempest.common.rest_client import RestClient
|
||||
|
||||
|
||||
class ContainerClient(RestClient):
|
||||
def __init__(self, config, username, password, auth_url, tenant_name=None):
|
||||
super(ContainerClient, self).__init__(config, username, password,
|
||||
auth_url, tenant_name)
|
||||
|
||||
#Overwrites json-specific header encoding in RestClient
|
||||
self.headers = {}
|
||||
self.service = self.config.object_storage.catalog_type
|
||||
self.format = 'json'
|
||||
|
||||
def create_container(self, container_name, metadata=None,
|
||||
metadata_prefix='X-Container-Meta-'):
|
||||
"""
|
||||
Creates a container, with optional metadata passed in as a
|
||||
dictonary
|
||||
"""
|
||||
url = container_name
|
||||
headers = {}
|
||||
|
||||
if metadata is not None:
|
||||
for key in metadata:
|
||||
headers[metadata_prefix + key] = metadata[key]
|
||||
|
||||
resp, body = self.put(url, body=None, headers=headers)
|
||||
return resp, body
|
||||
|
||||
def delete_container(self, container_name):
|
||||
"""Deletes the container (if it's empty)"""
|
||||
url = container_name
|
||||
resp, body = self.delete(url)
|
||||
return resp, body
|
||||
|
||||
def update_container_metadata(self, container_name, metadata,
|
||||
metadata_prefix='X-Container-Meta-'):
|
||||
"""Updates arbitrary metadata on container"""
|
||||
url = container_name
|
||||
headers = {}
|
||||
|
||||
if metadata is not None:
|
||||
for key in metadata:
|
||||
headers[metadata_prefix + key] = metadata[key]
|
||||
|
||||
resp, body = self.post(url, body=None, headers=headers)
|
||||
|
||||
return resp. body
|
||||
|
||||
def list_all_container_objects(self, container, params=None):
|
||||
"""
|
||||
Returns complete list of all objects in the container, even if
|
||||
item count is beyond 10,000 item listing limit.
|
||||
Does not require any paramaters aside from container name.
|
||||
"""
|
||||
#TODO: Rewite using json format to avoid newlines at end of obj names
|
||||
#Set limit to API limit - 1 (max returned items = 9999)
|
||||
limit = 9999
|
||||
marker = None
|
||||
if params is not None:
|
||||
if 'limit' in params:
|
||||
limit = params['limit']
|
||||
|
||||
if 'marker' in params:
|
||||
limit = params['marker']
|
||||
|
||||
resp, objlist = self.list_container_contents(container,
|
||||
params={'limit': limit})
|
||||
return objlist
|
||||
"""tmp = []
|
||||
for obj in objlist:
|
||||
tmp.append(obj['name'])
|
||||
objlist = tmp
|
||||
|
||||
if len(objlist) >= limit:
|
||||
|
||||
#Increment marker
|
||||
marker = objlist[len(objlist) - 1]
|
||||
|
||||
#Get the next chunk of the list
|
||||
objlist.extend(_list_all_container_objects(container,
|
||||
params={'marker': marker,
|
||||
'limit': limit}))
|
||||
return objlist
|
||||
else:
|
||||
#Return final, complete list
|
||||
return objlist"""
|
||||
|
||||
def list_container_contents(self, container, params=None):
|
||||
"""
|
||||
List the objects in a container, given the container name
|
||||
|
||||
Returns the container object listing as a plain text list, or as
|
||||
xml or json if that option is specified via the 'format' argument.
|
||||
|
||||
Optional Arguments:
|
||||
limit = integer
|
||||
For an integer value n, limits the number of results to at most
|
||||
n values.
|
||||
|
||||
marker = 'string'
|
||||
Given a string value x, return object names greater in value
|
||||
than the specified marker.
|
||||
|
||||
prefix = 'string'
|
||||
For a string value x, causes the results to be limited to names
|
||||
beginning with the substring x.
|
||||
|
||||
format = 'json' or 'xml'
|
||||
Specify either json or xml to return the respective serialized
|
||||
response.
|
||||
If json, returns a list of json objects
|
||||
if xml, returns a string of xml
|
||||
|
||||
path = 'string'
|
||||
For a string value x, return the object names nested in the
|
||||
pseudo path (assuming preconditions are met - see below).
|
||||
|
||||
delimiter = 'character'
|
||||
For a character c, return all the object names nested in the
|
||||
container (without the need for the directory marker objects).
|
||||
"""
|
||||
|
||||
url = str(container)
|
||||
param_list = ['format=%s&' % self.format]
|
||||
if params is not None:
|
||||
for param, value in params.iteritems():
|
||||
param_list.append("%s=%s&" % (param, value))
|
||||
url += '?' + ''.join(param_list)
|
||||
|
||||
resp, body = self.get(url)
|
||||
body = json.loads(body)
|
||||
return resp, body
|
63
tempest/services/object_storage/object_client.py
Normal file
63
tempest/services/object_storage/object_client.py
Normal file
@ -0,0 +1,63 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 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 re
|
||||
from tempest.common.rest_client import RestClient
|
||||
|
||||
|
||||
class ObjectClient(RestClient):
|
||||
def __init__(self, config, username, password, auth_url, tenant_name=None):
|
||||
super(ObjectClient, self).__init__(config, username, password,
|
||||
auth_url, tenant_name)
|
||||
|
||||
self.service = self.config.object_storage.catalog_type
|
||||
|
||||
def create_object(self, container, object_name, data):
|
||||
"""Create storage object"""
|
||||
|
||||
url = "%s/%s" % (str(container), str(object_name))
|
||||
resp, body = self.put(url, data, self.headers)
|
||||
return resp, body
|
||||
|
||||
def update_object(self, container, object_name, data):
|
||||
"""Upload data to replace current storage object"""
|
||||
return create_object(container, object_name, data)
|
||||
|
||||
def delete_object(self, container, object_name):
|
||||
"""Delete storage object"""
|
||||
url = "%s/%s" % (str(container), str(object_name))
|
||||
resp, body = self.delete(url)
|
||||
return resp, body
|
||||
|
||||
def update_object_metadata(self, container, object_name, metadata,
|
||||
metadata_prefix='X-Object-Meta-'):
|
||||
"""Add, remove, or change X-Object-Meta metadata for storage object"""
|
||||
|
||||
headers = {}
|
||||
for key in metadata:
|
||||
headers["%s%s" % (str(metadata_prefix), str(key))] = metadata[key]
|
||||
|
||||
url = "%s/%s" % (str(container), str(object_name))
|
||||
resp, body = self.post(url, None, headers=headers)
|
||||
return resp, body
|
||||
|
||||
def list_object_metadata(self, container, object_name):
|
||||
"""List all storage object X-Object-Meta- metadata"""
|
||||
|
||||
url = "%s/%s" % (str(container), str(object_name))
|
||||
resp, body = self.head(url)
|
||||
return resp, body
|
0
tempest/tests/object_storage/__init__.py
Normal file
0
tempest/tests/object_storage/__init__.py
Normal file
41
tempest/tests/object_storage/base.py
Normal file
41
tempest/tests/object_storage/base.py
Normal file
@ -0,0 +1,41 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 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 nose
|
||||
import unittest2 as unittest
|
||||
|
||||
import tempest.config
|
||||
from tempest import exceptions
|
||||
from tempest import openstack
|
||||
|
||||
|
||||
class BaseObjectTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.os = openstack.Manager()
|
||||
cls.object_client = cls.os.object_client
|
||||
cls.container_client = cls.os.container_client
|
||||
cls.account_client = cls.os.account_client
|
||||
cls.config = cls.os.config
|
||||
|
||||
try:
|
||||
cls.account_client.list_account_containers()
|
||||
except exceptions.EndpointNotFound:
|
||||
enabled = False
|
||||
skip_msg = "No OpenStack Object Storage API endpoint"
|
||||
raise nose.SkipTest(skip_msg)
|
76
tempest/tests/object_storage/test_account_services.py
Normal file
76
tempest/tests/object_storage/test_account_services.py
Normal file
@ -0,0 +1,76 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 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 unittest2 as unittest
|
||||
import tempest.config
|
||||
import re
|
||||
|
||||
from nose.plugins.attrib import attr
|
||||
from tempest import exceptions
|
||||
from tempest import openstack
|
||||
from tempest.common.utils.data_utils import rand_name
|
||||
from tempest.tests.object_storage import base
|
||||
|
||||
|
||||
class AccountTest(base.BaseObjectTest):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(AccountTest, cls).setUpClass()
|
||||
|
||||
#Create a container
|
||||
cls.container_name = rand_name(name='TestContainer')
|
||||
cls.container_client.create_container(cls.container_name)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.container_client.delete_container(cls.container_name)
|
||||
|
||||
@attr(type='smoke')
|
||||
def test_list_containers(self):
|
||||
"""List of all containers should not be empty"""
|
||||
|
||||
params = {'format': 'json'}
|
||||
resp, container_list = \
|
||||
self.account_client.list_account_containers(params=params)
|
||||
|
||||
self.assertIsNotNone(container_list)
|
||||
container_names = [c['name'] for c in container_list]
|
||||
self.assertTrue(self.container_name in container_names)
|
||||
|
||||
@attr(type='smoke')
|
||||
def test_list_account_metadata(self):
|
||||
"""List all account metadata"""
|
||||
|
||||
resp, metadata = self.account_client.list_account_metadata()
|
||||
self.assertEqual(resp['status'], '204')
|
||||
self.assertIn('x-account-object-count', resp)
|
||||
self.assertIn('x-account-container-count', resp)
|
||||
self.assertIn('x-account-bytes-used', resp)
|
||||
|
||||
@attr(type='smoke')
|
||||
def test_create_account_metadata(self):
|
||||
"""Add metadata to account"""
|
||||
|
||||
metadata = {'test-account-meta': 'Meta!'}
|
||||
resp, _ = \
|
||||
self.account_client.create_account_metadata(metadata=metadata)
|
||||
self.assertEqual(resp['status'], '204')
|
||||
|
||||
resp, metadata = self.account_client.list_account_metadata()
|
||||
self.assertIn('x-account-meta-test-account-meta', resp)
|
||||
self.assertEqual(resp['x-account-meta-test-account-meta'], 'Meta!')
|
110
tempest/tests/object_storage/test_container_services.py
Normal file
110
tempest/tests/object_storage/test_container_services.py
Normal file
@ -0,0 +1,110 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 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 re
|
||||
import unittest2 as unittest
|
||||
import tempest.config
|
||||
|
||||
from nose.plugins.attrib import attr
|
||||
from tempest import exceptions
|
||||
from tempest import openstack
|
||||
from tempest.common.utils.data_utils import rand_name, arbitrary_string
|
||||
from tempest.tests.object_storage import base
|
||||
|
||||
|
||||
class ContainerTest(base.BaseObjectTest):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(ContainerTest, cls).setUpClass()
|
||||
cls.containers = []
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
for container in cls.containers:
|
||||
#Get list of all object in the container
|
||||
objlist = \
|
||||
cls.container_client.list_all_container_objects(container)
|
||||
|
||||
#Attempt to delete every object in the container
|
||||
for obj in objlist:
|
||||
resp, _ = \
|
||||
cls.object_client.delete_object(container, obj['name'])
|
||||
|
||||
#Attempt to delete the container
|
||||
resp, _ = cls.container_client.delete_container(container)
|
||||
|
||||
@attr(type='smoke')
|
||||
def test_create_container(self):
|
||||
"""Create a container, test responses"""
|
||||
|
||||
#Create a container
|
||||
container_name = rand_name(name='TestContainer')
|
||||
resp, body = self.container_client.create_container(container_name)
|
||||
self.containers.append(container_name)
|
||||
|
||||
self.assertTrue(resp['status'] in ('202', '201'))
|
||||
|
||||
@attr(type='smoke')
|
||||
def test_delete_container(self):
|
||||
"""Create and Delete a container, test responses"""
|
||||
|
||||
#Create a container
|
||||
container_name = rand_name(name='TestContainer')
|
||||
resp, _ = self.container_client.create_container(container_name)
|
||||
self.containers.append(container_name)
|
||||
|
||||
#Delete Container
|
||||
resp, _ = self.container_client.delete_container(container_name)
|
||||
self.assertEqual(resp['status'], '204')
|
||||
self.containers.remove(container_name)
|
||||
|
||||
@attr(type='smoke')
|
||||
def test_list_container_contents_json(self):
|
||||
"""Add metadata to object"""
|
||||
|
||||
#Create a container
|
||||
container_name = rand_name(name='TestContainer')
|
||||
resp, _ = self.container_client.create_container(container_name)
|
||||
self.containers.append(container_name)
|
||||
|
||||
#Create Object
|
||||
object_name = rand_name(name='TestObject')
|
||||
data = arbitrary_string()
|
||||
resp, _ = self.object_client.create_object(container_name,
|
||||
object_name, data)
|
||||
|
||||
#Set Object Metadata
|
||||
meta_key = rand_name(name='Meta-Test-')
|
||||
meta_value = rand_name(name='MetaValue-')
|
||||
orig_metadata = {meta_key: meta_value}
|
||||
|
||||
resp, _ = self.object_client.update_object_metadata(container_name,
|
||||
object_name,
|
||||
orig_metadata)
|
||||
|
||||
#Get Container contents list json format
|
||||
params = {'format': 'json'}
|
||||
resp, object_list = \
|
||||
self.container_client.\
|
||||
list_container_contents(container_name, params=params)
|
||||
|
||||
self.assertEqual(resp['status'], '200')
|
||||
self.assertIsNotNone(object_list)
|
||||
|
||||
object_names = [obj['name'] for obj in object_list]
|
||||
self.assertIn(object_name, object_names)
|
112
tempest/tests/object_storage/test_object_services.py
Normal file
112
tempest/tests/object_storage/test_object_services.py
Normal file
@ -0,0 +1,112 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 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 re
|
||||
import unittest2 as unittest
|
||||
import tempest.config
|
||||
|
||||
from nose.plugins.attrib import attr
|
||||
from tempest import exceptions
|
||||
from tempest import openstack
|
||||
from tempest.common.utils.data_utils import rand_name, arbitrary_string
|
||||
from tempest.tests.object_storage import base
|
||||
|
||||
|
||||
class ObjectTest(base.BaseObjectTest):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(ObjectTest, cls).setUpClass()
|
||||
|
||||
#Create a container
|
||||
cls.container_name = rand_name(name='TestContainer')
|
||||
cls.container_client.create_container(cls.container_name)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
#Get list of all object in the container
|
||||
objlist = \
|
||||
cls.container_client.list_all_container_objects(cls.container_name)
|
||||
|
||||
#Attempt to delete every object in the container
|
||||
for obj in objlist:
|
||||
resp, _ = cls.object_client.delete_object(cls.container_name,
|
||||
obj['name'])
|
||||
|
||||
#Attempt to delete the container
|
||||
resp, _ = cls.container_client.delete_container(cls.container_name)
|
||||
|
||||
@attr(type='smoke')
|
||||
def test_create_object(self):
|
||||
"""Create storage object, test response"""
|
||||
|
||||
#Create Object
|
||||
object_name = rand_name(name='TestObject')
|
||||
data = arbitrary_string()
|
||||
resp, _ = self.object_client.create_object(self.container_name,
|
||||
object_name, data)
|
||||
|
||||
#Create another Object
|
||||
object_name = rand_name(name='TestObject')
|
||||
data = arbitrary_string()
|
||||
resp, _ = self.object_client.create_object(self.container_name,
|
||||
object_name, data)
|
||||
self.assertEqual(resp['status'], '201')
|
||||
|
||||
@attr(type='smoke')
|
||||
def test_delete_object(self):
|
||||
"""Create and delete a storage object, test responses"""
|
||||
|
||||
#Create Object
|
||||
object_name = rand_name(name='TestObject')
|
||||
data = arbitrary_string()
|
||||
resp, _ = self.object_client.create_object(self.container_name,
|
||||
object_name, data)
|
||||
|
||||
resp, _ = self.object_client.delete_object(self.container_name,
|
||||
object_name)
|
||||
self.assertEqual(resp['status'], '204')
|
||||
|
||||
@attr(type='smoke')
|
||||
def test_object_metadata(self):
|
||||
"""Add metadata to storage object, test if metadata is retrievable"""
|
||||
|
||||
#Create Object
|
||||
object_name = rand_name(name='TestObject')
|
||||
data = arbitrary_string()
|
||||
resp, _ = self.object_client.create_object(self.container_name,
|
||||
object_name, data)
|
||||
|
||||
#Set Object Metadata
|
||||
meta_key = rand_name(name='test-')
|
||||
meta_value = rand_name(name='MetaValue-')
|
||||
orig_metadata = {meta_key: meta_value}
|
||||
|
||||
resp, _ = \
|
||||
self.object_client.update_object_metadata(self.container_name,
|
||||
object_name,
|
||||
orig_metadata)
|
||||
self.assertEqual(resp['status'], '202')
|
||||
|
||||
#Get Object Metadata
|
||||
resp, resp_metadata = \
|
||||
self.object_client.list_object_metadata(self.container_name,
|
||||
object_name)
|
||||
self.assertEqual(resp['status'], '200')
|
||||
actual_meta_key = 'x-object-meta-' + meta_key
|
||||
self.assertTrue(actual_meta_key in resp)
|
||||
self.assertEqual(resp[actual_meta_key], meta_value)
|
Loading…
Reference in New Issue
Block a user