Merge "Tempest tests to cover live-block-migration"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 9c7868c..2987c56 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -206,3 +206,18 @@
 # 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
diff --git a/etc/tempest.conf.tpl b/etc/tempest.conf.tpl
index 67fc025..5e2ee7f 100644
--- a/etc/tempest.conf.tpl
+++ b/etc/tempest.conf.tpl
@@ -176,3 +176,18 @@
 # 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%
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 71f887f..fbe05e7 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -171,6 +171,9 @@
     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))
@@ -241,6 +244,11 @@
             elif 'error' in resp_body:  # Keystone errors
                 message = resp_body['error']['message']
                 raise exceptions.IdentityError(message)
+            elif 'message' in resp_body:
+                message = resp_body['message']
+            else:
+                message = resp_body
+
             raise exceptions.ComputeFault(message)
 
         if resp.status >= 400:
diff --git a/tempest/common/utils/data_utils.py b/tempest/common/utils/data_utils.py
index fc7c112..15afd0a 100644
--- a/tempest/common/utils/data_utils.py
+++ b/tempest/common/utils/data_utils.py
@@ -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 @@
     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
diff --git a/tempest/config.py b/tempest/config.py
index 95dffa8..c46a007 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -389,6 +389,31 @@
         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"""
@@ -437,6 +462,7 @@
         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."""
diff --git a/tempest/manager.py b/tempest/manager.py
index dc4b289..ce7cf93 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -21,7 +21,10 @@
 import glanceclient
 import keystoneclient.v2_0.client
 import novaclient.client
-import quantumclient.v2_0.client
+try:
+    import quantumclient.v2_0.client
+except ImportError:
+    pass
 
 import tempest.config
 from tempest import exceptions
@@ -127,7 +130,7 @@
         client_args = (username, password, tenant_name, auth_url)
 
         # Create our default Nova client to use in testing
-        service_type = self.config.compute.catalog_type,
+        service_type = self.config.compute.catalog_type
         return novaclient.client.Client(self.NOVACLIENT_VERSION,
                                         *client_args,
                                         service_type=service_type,
diff --git a/tempest/openstack.py b/tempest/openstack.py
index c8bd238..359e6c6 100644
--- a/tempest/openstack.py
+++ b/tempest/openstack.py
@@ -53,6 +53,9 @@
 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 @@
             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):
diff --git a/tempest/services/nova/json/servers_client.py b/tempest/services/nova/json/servers_client.py
index a5e06c9..a96dacb 100644
--- a/tempest/services/nova/json/servers_client.py
+++ b/tempest/services/nova/json/servers_client.py
@@ -164,7 +164,7 @@
 
             server_status = body['status']
             if server_status == 'ERROR' and not ignore_error:
-                raise exceptions.BuildErrorException
+                raise exceptions.BuildErrorException(server_id=server_id)
 
             if int(time.time()) - start_time >= self.build_timeout:
                 raise exceptions.TimeoutException
diff --git a/tempest/services/object_storage/__init__.py b/tempest/services/object_storage/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/object_storage/__init__.py
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
new file mode 100644
index 0000000..ade94d4
--- /dev/null
+++ b/tempest/services/object_storage/account_client.py
@@ -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
diff --git a/tempest/services/object_storage/container_client.py b/tempest/services/object_storage/container_client.py
new file mode 100644
index 0000000..56dffde
--- /dev/null
+++ b/tempest/services/object_storage/container_client.py
@@ -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
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
new file mode 100644
index 0000000..0fc7ad4
--- /dev/null
+++ b/tempest/services/object_storage/object_client.py
@@ -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
diff --git a/tempest/tests/compute/base.py b/tempest/tests/compute/base.py
index ac5f524..ebf3b54 100644
--- a/tempest/tests/compute/base.py
+++ b/tempest/tests/compute/base.py
@@ -121,7 +121,7 @@
                 msg = ('Unable to create isolated tenant %s because ' +
                        'it already exists. If this is related to a ' +
                        'previous test failure, try using ' +
-                       'allow_tentant_reuse in tempest.conf') % tenant_name
+                       'allow_tenant_reuse in tempest.conf') % tenant_name
                 raise exceptions.Duplicate(msg)
 
         try:
@@ -135,10 +135,10 @@
                                                          username)
                 LOG.info('Re-using existing user %s' % user)
             else:
-                msg = ('Unable to create isolated tenant %s because ' +
+                msg = ('Unable to create isolated user %s because ' +
                        'it already exists. If this is related to a ' +
                        'previous test failure, try using ' +
-                       'allow_tentant_reuse in tempest.conf') % tenant_name
+                       'allow_tenant_reuse in tempest.conf') % tenant_name
                 raise exceptions.Duplicate(msg)
 
         # Store the complete creds (including UUID ids...) for later
@@ -159,19 +159,14 @@
             admin_client.delete_tenant(tenant['id'])
 
     @classmethod
-    def clear_remaining_servers(cls):
-        # NOTE(danms): Only nuke all left-over servers if we're in our
-        # own isolated tenant
-        if not cls.isolated_creds:
-            return
-        resp, servers = cls.servers_client.list_servers()
-        for server in servers['servers']:
+    def clear_servers(cls):
+        for server in cls.servers:
             try:
                 cls.servers_client.delete_server(server['id'])
             except Exception:
                 pass
 
-        for server in servers['servers']:
+        for server in cls.servers:
             try:
                 cls.servers_client.wait_for_server_termination(server['id'])
             except Exception:
@@ -179,20 +174,21 @@
 
     @classmethod
     def tearDownClass(cls):
-        cls.clear_remaining_servers()
+        cls.clear_servers()
         cls.clear_isolated_creds()
 
-    def create_server(self, image_id=None):
+    @classmethod
+    def create_server(cls, image_id=None):
         """Wrapper utility that returns a test server"""
-        server_name = rand_name(self.__class__.__name__ + "-instance")
-        flavor = self.flavor_ref
+        server_name = rand_name(cls.__name__ + "-instance")
+        flavor = cls.flavor_ref
         if not image_id:
-            image_id = self.image_ref
+            image_id = cls.image_ref
 
-        resp, server = self.servers_client.create_server(
+        resp, server = cls.servers_client.create_server(
                                                 server_name, image_id, flavor)
-        self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
-        self.servers.append(server)
+        cls.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+        cls.servers.append(server)
         return server
 
     def wait_for(self, condition):
diff --git a/tempest/tests/compute/test_authorization.py b/tempest/tests/compute/test_authorization.py
index 3f2feec..12fa94b 100644
--- a/tempest/tests/compute/test_authorization.py
+++ b/tempest/tests/compute/test_authorization.py
@@ -17,6 +17,7 @@
 
 from nose.plugins.attrib import attr
 from nose.tools import raises
+from nose import SkipTest
 import unittest2 as unittest
 
 from tempest import openstack
@@ -32,7 +33,7 @@
     def setUpClass(cls):
         if not compute.MULTI_USER:
             msg = "Need >1 user"
-            raise nose.SkipTest(msg)
+            raise SkipTest(msg)
 
         super(AuthorizationTest, cls).setUpClass()
 
diff --git a/tempest/tests/compute/test_list_servers_negative.py b/tempest/tests/compute/test_list_servers_negative.py
index 2279959..1be7480 100644
--- a/tempest/tests/compute/test_list_servers_negative.py
+++ b/tempest/tests/compute/test_list_servers_negative.py
@@ -19,6 +19,7 @@
 import sys
 
 import unittest2 as unittest
+import nose
 
 from tempest import exceptions
 from tempest import openstack
@@ -47,212 +48,97 @@
                 cls.alt_manager = openstack.AltManager()
             cls.alt_client = cls.alt_manager.servers_client
 
-    def tearDown(self):
-        """Terminate instances created by tests"""
-        try:
-            for server in self.servers:
-                resp, body = self.client.delete_server(server)
-                if resp['status'] == '204':
-                    self.client.wait_for_server_termination(server)
-        except exceptions.NotFound:
-            pass
-
-    def get_active_servers(self, server_count):
-        """Returns active test instances to calling test methods"""
-        resp, body = self.client.list_servers_with_detail()
+        # Under circumstances when there is not a tenant/user
+        # created for the test case, the test case checks
+        # to see if there are existing servers for the
+        # either the normal user/tenant or the alt user/tenant
+        # and if so, the whole test is skipped. We do this
+        # because we assume a baseline of no servers at the
+        # start of the test instead of destroying any existing
+        # servers.
+        resp, body = cls.client.list_servers()
         servers = body['servers']
-        active_servers = [
-            server for server in servers if server['status'] == 'ACTIVE'
-        ]
-        num_of_active_servers = len(active_servers)
+        num_servers = len(servers)
+        if num_servers > 0:
+            username = cls.os.username
+            tenant_name = cls.os.tenant_name
+            msg = ("User/tenant %(username)s/%(tenant_name)s already have "
+                   "existing server instances. Skipping test.") % locals()
+            raise nose.SkipTest(msg)
 
-        # Check if we already have enough active servers
-        if active_servers and num_of_active_servers >= server_count:
-            return active_servers[0:server_count]
+        resp, body = cls.alt_client.list_servers()
+        servers = body['servers']
+        num_servers = len(servers)
+        if num_servers > 0:
+            username = cls.alt_manager.username
+            tenant_name = cls.alt_manager.tenant_name
+            msg = ("Alt User/tenant %(username)s/%(tenant_name)s already have "
+                   "existing server instances. Skipping test.") % locals()
+            raise nose.SkipTest(msg)
 
-        # Otherwise create the remaining shortfall of servers
-        servers_needed = server_count - num_of_active_servers
+        # The following servers are created for use
+        # by the test methods in this class. These
+        # servers are cleaned up automatically in the
+        # tearDownClass method of the super-class.
+        cls.existing_fixtures = []
+        cls.deleted_fixtures = []
+        for x in xrange(2):
+            srv = cls.create_server()
+            cls.existing_fixtures.append(srv)
 
-        for i in range(0, servers_needed):
-            srv = self.create_server()
-            active_servers.append(srv)
+        srv = cls.create_server()
+        cls.client.delete_server(srv['id'])
+        # We ignore errors on termination because the server may
+        # be put into ERROR status on a quick spawn, then delete,
+        # as the compute node expects the instance local status
+        # to be spawning, not deleted. See LP Bug#1061167
+        cls.client.wait_for_server_termination(srv['id'],
+                                               ignore_error=True)
+        cls.deleted_fixtures.append(srv)
 
-        return active_servers
-
-    def test_list_servers_when_no_servers_running(self):
-        """Return an empty list when there are no active servers"""
-        # Delete Active servers
-        try:
-            resp, body = self.client.list_servers()
-            for server in body['servers']:
-                resp, body = self.client.delete_server(server['id'])
-                self.client.wait_for_server_termination(server['id'])
-        except exceptions.NotFound:
-            pass
-        # Verify empty list
+    def test_list_servers_with_a_deleted_server(self):
+        """Verify deleted servers do not show by default in list servers"""
+        # List servers and verify server not returned
         resp, body = self.client.list_servers()
         servers = body['servers']
+        deleted_ids = [s['id'] for s in self.deleted_fixtures]
+        actual = [srv for srv in servers
+                  if srv['id'] in deleted_ids]
+        self.assertEqual('200', resp['status'])
+        self.assertEqual([], actual)
+
+    def test_list_servers_by_non_existing_image(self):
+        """Listing servers for a non existing image returns empty list"""
+        non_existing_image = '1234abcd-zzz0-aaa9-ppp3-0987654abcde'
+        resp, body = self.client.list_servers(dict(image=non_existing_image))
+        servers = body['servers']
         self.assertEqual('200', resp['status'])
         self.assertEqual([], servers)
 
-    def test_list_servers_with_a_deleted_server(self):
-        """Create and delete a server and verify empty list"""
-        server = self.get_active_servers(1)[0]
-
-        # Delete the server
-        self.client.delete_server(server['id'])
-        self.client.wait_for_server_termination(server['id'])
-
-        # List servers and verify server not returned
-        resp, body = self.client.list_servers()
-        servers = body['servers']
-        actual = [srv for srv in servers if srv['id'] == server['id']]
-        self.assertEqual('200', resp['status'])
-        self.assertEqual([], actual)
-
-    def test_list_servers_by_existing_image(self):
-        """Server list is returned for a specific image and snapshot images"""
-        try:
-            base_server = self.get_active_servers(1)[0]
-
-            # Create a snapshot of the server
-            snapshot_name = "%s_snapshot" % base_server['id']
-            resp, body = self.client.create_image(base_server['id'],
-                                                  snapshot_name)
-            snapshot_url = resp['location']
-            match = re.search('/images/(?P<image_id>.+)', snapshot_url)
-            self.assertIsNotNone(match)
-            snapshot_id = match.groupdict()['image_id']
-
-            self.images_client.wait_for_image_status(snapshot_id, 'ACTIVE')
-
-            # Create a server from the snapshot
-            snap_server = self.create_server(image_id=snapshot_id)
-            self.servers.append(snap_server['id'])
-
-            # List base servers by image
-            resp, body = self.client.list_servers({'image': self.image_ref})
-            servers = body['servers']
-            self.assertEqual('200', resp['status'])
-            self.assertIn(base_server['id'], [server['id'] for server in
-                          servers])
-            self.assertTrue(len(body['servers']) > 0)
-
-            # List snapshotted server by image
-            resp, body = self.client.list_servers({'image': snapshot_id})
-            servers = body['servers']
-            self.assertEqual('200', resp['status'])
-            self.assertIn(snap_server['id'],
-                          [server['id'] for server in servers])
-            self.assertEqual(1, len(body['servers']))
-
-        finally:
-            self.images_client.delete_image(snapshot_id)
-
-    @unittest.skip("Until Bug 1002911 is fixed")
-    def test_list_servers_by_non_existing_image(self):
-        """Listing servers for a non existing image raises error"""
-        self.assertRaises(exceptions.NotFound, self.client.list_servers,
-                          {'image': '1234abcd-zzz0-aaa9-ppp3-0987654abcde'})
-
-    @unittest.skip("Until Bug 1002911 is fixed")
-    def test_list_servers_by_image_pass_overlimit_image(self):
-        """Return an error while listing server with too large image id"""
-        self.assertRaises(exceptions.OverLimit, self.client.list_servers,
-                          {'image': sys.maxint + 1})
-
-    @unittest.skip("Until Bug 1002911 is fixed")
-    def test_list_servers_by_image_pass_negative_id(self):
-        """Return an error while listing server with negative image id"""
-        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
-                          {'image': -1})
-
-    def test_list_servers_by_existing_flavor(self):
-        """List servers by flavor"""
-        self.get_active_servers(1)
-
-        resp, body = self.client.list_servers({'flavor': self.flavor_ref})
-        self.assertEqual('200', resp['status'])
-        self.assertTrue(len(body['servers']) > 0)
-
-    @unittest.skip("Until Bug 1002918 is fixed")
     def test_list_servers_by_non_existing_flavor(self):
-        """Return an error while listing server by non existing flavor"""
-        self.assertRaises(exceptions.NotFound, self.client.list_servers,
-                          {'flavor': 1234})
-
-    @unittest.skip("Until Bug 1002918 is fixed")
-    def test_list_servers_by_flavor_pass_overlimit_flavor(self):
-        """Return an error while listing server with too large flavor value"""
-        self.assertRaises(exceptions.OverLimit, self.client.list_servers,
-                          {'flavor': sys.maxint + 1})
-
-    @unittest.skip("Until Bug 1002918 is fixed")
-    def test_list_servers_by_flavor_pass_negative_value(self):
-        """Return an error while listing server with negative flavor value"""
-        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
-                          {'flavor': -1})
-
-    def test_list_servers_by_server_name(self):
-        """Returns a list of servers containing an existing server name"""
-        server_name = rand_name('test-vm-')
-        resp, server = self.client.create_server(server_name, self.image_ref,
-                                                 self.flavor_ref)
-        self.servers.append(server['id'])
-
-        resp, body = self.client.list_servers({'name': server_name})
+        """Listing servers by non existing flavor returns empty list"""
+        non_existing_flavor = 1234
+        resp, body = self.client.list_servers(dict(flavor=non_existing_flavor))
+        servers = body['servers']
         self.assertEqual('200', resp['status'])
-        self.assertEqual(server_name, body['servers'][0]['name'])
+        self.assertEqual([], servers)
 
-    @unittest.skip("Until Bug 1002892 is fixed")
     def test_list_servers_by_non_existing_server_name(self):
-        """Return an error while listing for a non existent server"""
-        resp, body = self.client.list_servers({'name': 'junk_serv_100'})
-        self.assertRaises(exceptions.NotFound, self.client.list_servers,
-                          {'name': 'junk_serv_100'})
-
-    @unittest.skip("Until Bug 1002892 is fixed")
-    def test_list_servers_by_server_name_empty(self):
-        """Return an error when an empty server name is passed"""
-        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
-                          {'name': ''})
-
-    @unittest.skip("Until Bug 1002892 is fixed")
-    def test_list_servers_by_server_name_too_large(self):
-        """Return an error for a very large value for server name listing"""
-        self.assertRaises(exceptions.OverLimit, self.client.list_servers,
-                          {'name': 'a' * 65})
-
-    @unittest.skip("Until Bug 1002892 is fixed")
-    def test_list_servers_by_name_pass_numeric_name(self):
-        """Return an error for a numeric server name listing"""
-        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
-                          {'name': 99})
-
-    def test_list_servers_by_active_status(self):
-        """Return a listing of servers by active status"""
-        self.create_server()
-        resp, body = self.client.list_servers({'status': 'ACTIVE'})
+        """Listing servers for a non existent server name returns empty list"""
+        non_existing_name = 'junk_server_1234'
+        resp, body = self.client.list_servers(dict(name=non_existing_name))
+        servers = body['servers']
         self.assertEqual('200', resp['status'])
-        self.assertTrue(len(body['servers']) > 0)
+        self.assertEqual([], servers)
 
-    def test_list_servers_by_building_status(self):
-        """Return a listing of servers in build state"""
-        server_name = rand_name('test-vm-')
-        resp, server = self.client.create_server(server_name, self.image_ref,
-                                                 self.flavor_ref)
-        self.servers.append(server['id'])
-        resp, body = self.client.list_servers({'status': 'BUILD'})
+    @unittest.skip("Skip until bug 1061712 is resolved")
+    def test_list_servers_status_non_existing(self):
+        """Return an empty list when invalid status is specified"""
+        non_existing_status = 'BALONEY'
+        resp, body = self.client.list_servers(dict(status=non_existing_status))
+        servers = body['servers']
         self.assertEqual('200', resp['status'])
-        self.assertEqual(1, len(body['servers']))
-        self.assertEqual(server['id'], body['servers'][0]['id'])
-
-        self.client.wait_for_server_status(server['id'], 'ACTIVE')
-
-    def test_list_servers_status_is_invalid(self):
-        """Return an error when invalid status is specified"""
-        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
-                          {'status': 'DEAD'})
+        self.assertEqual([], servers)
 
     def test_list_servers_pass_numeric_status(self):
         """Return an error when a numeric value for status is specified"""
@@ -261,17 +147,15 @@
 
     def test_list_servers_by_limits(self):
         """List servers by specifying limits"""
-        self.get_active_servers(2)
         resp, body = self.client.list_servers({'limit': 1})
         self.assertEqual('200', resp['status'])
         self.assertEqual(1, len(body['servers']))
 
     def test_list_servers_by_limits_greater_than_actual_count(self):
         """List servers by specifying a greater value for limit"""
-        self.get_active_servers(2)
         resp, body = self.client.list_servers({'limit': 100})
         self.assertEqual('200', resp['status'])
-        self.assertTrue(len(body['servers']) >= 2)
+        self.assertEqual(len(self.existing_fixtures), len(body['servers']))
 
     def test_list_servers_by_limits_pass_string(self):
         """Return an error if a string value is passed for limit"""
@@ -283,120 +167,34 @@
         self.assertRaises(exceptions.BadRequest, self.client.list_servers,
                           {'limit': -1})
 
-    @unittest.skip("Until Bug 1002924 is fixed")
-    def test_list_servers_by_limits_pass_overlimit_value(self):
-        """Return an error if too large value for limit is passed"""
-        self.assertRaises(exceptions.OverLimit, self.client.list_servers,
-                          {'limit': sys.maxint + 1})
-
     def test_list_servers_by_changes_since(self):
         """Servers are listed by specifying changes-since date"""
-        self.get_active_servers(2)
         resp, body = self.client.list_servers(
                          {'changes-since': '2011-01-01T12:34:00Z'})
         self.assertEqual('200', resp['status'])
-        self.assertTrue(len(body['servers']) >= 2)
+        # changes-since returns all instances, including deleted.
+        num_expected = (len(self.existing_fixtures) +
+                        len(self.deleted_fixtures))
+        self.assertEqual(num_expected, len(body['servers']))
 
     def test_list_servers_by_changes_since_invalid_date(self):
         """Return an error when invalid date format is passed"""
         self.assertRaises(exceptions.BadRequest, self.client.list_servers,
                           {'changes-since': '2011/01/01'})
 
-    @unittest.skip("Until Bug 1002926 is fixed")
     def test_list_servers_by_changes_since_future_date(self):
-        """Return an error when a date in the future is passed"""
-        self.assertRaises(exceptions.BadRequest, self.client.list_servers,
-                          {'changes-since': '2051-01-01T12:34:00Z'})
-
-    @unittest.skip("Until Bug 1002935 is fixed")
-    def test_list_servers_list_another_tenant_servers(self):
-        """Return an error when a user lists servers in another tenant"""
-        # Create a server by a user in it's tenant
-        server_name = rand_name('test-vm-')
-        resp, server = self.client.create_server(server_name, self.image_ref,
-                                                 self.flavor_ref)
-        self.servers.append(server['id'])
-
-        # List the servers by alternate user in the base user's tenant
-        self.assertRaises(exceptions.NotFound, self.alt_client.list_servers,
-                          {'name': server_name})
-
-    def test_list_servers_detail_when_no_servers_running(self):
-        """Return an empty list for servers detail when no active servers"""
-        # Delete active servers
-        try:
-            resp, body = self.client.list_servers()
-            for server in body['servers']:
-                resp, body = self.client.delete_server(server['id'])
-                self.client.wait_for_server_termination(server['id'])
-        except exceptions.NotFound:
-            pass
-        # Verify empty list
-        resp, body = self.client.list_servers_with_detail()
+        """Return an empty list when a date in the future is passed"""
+        resp, body = self.client.list_servers(
+                         {'changes-since': '2051-01-01T12:34:00Z'})
         self.assertEqual('200', resp['status'])
-        servers = body['servers']
-        actual = [srv for srv in servers if srv['status'] == 'ACTIVE']
-        self.assertEqual([], actual)
-
-    def test_list_servers_detail_server_is_building(self):
-        """Server in Build state is listed"""
-        server_name = rand_name('test-vm-')
-        resp, server = self.client.create_server(server_name, self.image_ref,
-                                                 self.flavor_ref)
-
-        self.servers.append(server['id'])
-        resp, body = self.client.list_servers_with_detail()
-        self.assertEqual('200', resp['status'])
-        self.assertEqual('BUILD', body['servers'][0]['status'])
+        self.assertEqual(0, len(body['servers']))
 
     def test_list_servers_detail_server_is_deleted(self):
         """Server details are not listed for a deleted server"""
-        server = self.get_active_servers(1)[0]
-
-        self.client.delete_server(server['id'])
-        self.client.wait_for_server_termination(server['id'])
+        deleted_ids = [s['id'] for s in self.deleted_fixtures]
         resp, body = self.client.list_servers_with_detail()
         servers = body['servers']
-        actual = [srv for srv in servers if srv['id'] == server['id']]
+        actual = [srv for srv in servers
+                  if srv['id'] in deleted_ids]
         self.assertEqual('200', resp['status'])
         self.assertEqual([], actual)
-
-    def test_get_server_details_non_existent_id(self):
-        """Return an error during get server details using non-existent id"""
-        self.assertRaises(exceptions.NotFound, self.client.get_server,
-                          'junk-123ab-45cd')
-
-    def test_get_server_details_another_tenant_server(self):
-        """Return an error when querying details of server in another tenant"""
-        server_name = rand_name('test-vm-')
-        resp, server = self.client.create_server(server_name, self.image_ref,
-                                                 self.flavor_ref)
-        self.servers.append(server['id'])
-        self.assertRaises(exceptions.NotFound, self.alt_client.get_server,
-                          server['id'])
-
-    def test_get_server_details_pass_string_uuid(self):
-        """Return an error when a string value is passed for uuid"""
-        try:
-            self.assertRaises(exceptions.NotFound, self.client.get_server,
-                              'junk-server-uuid')
-        except Exception as e:
-            self.fail("Incorrect Exception raised: %s" % e)
-
-    @unittest.skip("Until Bug 1002901 is fixed")
-    def test_get_server_details_pass_negative_uuid(self):
-        """Return an error when a negative value is passed for uuid"""
-        try:
-            self.assertRaises(exceptions.BadRequest, self.client.get_server,
-                              -1)
-        except Exception as e:
-            self.fail("Incorrect Exception raised: %s" % e)
-
-    @unittest.skip("Until Bug 1002901 is fixed")
-    def test_get_server_details_pass_overlimit_length_uuid(self):
-        """Return an error when a very large value is passed for uuid"""
-        try:
-            self.assertRaises(exceptions.OverLimit, self.client.get_server,
-                              'a' * 37)
-        except Exception as e:
-            self.fail("Incorrect Exception raised: %s" % e)
diff --git a/tempest/tests/identity/admin/test_services.py b/tempest/tests/identity/admin/test_services.py
index b8b99f7..da697ab 100644
--- a/tempest/tests/identity/admin/test_services.py
+++ b/tempest/tests/identity/admin/test_services.py
@@ -15,8 +15,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from nose.plugins.attrib import attr
-import unittest2 as unittest
+import nose
 
 from tempest import exceptions
 from tempest.common.utils.data_utils import rand_name
@@ -77,3 +76,4 @@
     @classmethod
     def setUpClass(cls):
         super(ServicesTestXML, cls).setUpClass()
+        raise nose.SkipTest("Skipping until Bug #1061738 resolved")
diff --git a/tempest/tests/object_storage/__init__.py b/tempest/tests/object_storage/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/object_storage/__init__.py
diff --git a/tempest/tests/object_storage/base.py b/tempest/tests/object_storage/base.py
new file mode 100644
index 0000000..8edb3d2
--- /dev/null
+++ b/tempest/tests/object_storage/base.py
@@ -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)
diff --git a/tempest/tests/object_storage/test_account_services.py b/tempest/tests/object_storage/test_account_services.py
new file mode 100644
index 0000000..6d34bb8
--- /dev/null
+++ b/tempest/tests/object_storage/test_account_services.py
@@ -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!')
diff --git a/tempest/tests/object_storage/test_container_services.py b/tempest/tests/object_storage/test_container_services.py
new file mode 100644
index 0000000..639698b
--- /dev/null
+++ b/tempest/tests/object_storage/test_container_services.py
@@ -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)
diff --git a/tempest/tests/object_storage/test_object_services.py b/tempest/tests/object_storage/test_object_services.py
new file mode 100644
index 0000000..ab92d26
--- /dev/null
+++ b/tempest/tests/object_storage/test_object_services.py
@@ -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)