Merge "Fix ssh.Client retval and deadlock danger LP#1038561"
diff --git a/.gitignore b/.gitignore
index 89962f0..e5106f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@
 *.log
 *.swp
 *.egg-info
+.tox
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 8e7d8fb..8d3f6c9 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -62,10 +62,10 @@
 flavor_ref_alt = 2
 
 # Number of seconds to wait while looping to check the status of an
-# instance or volume that is building.
+# instance that is building.
 build_interval = 10
 
-# Number of seconds to time out on waiting for an instance or volume
+# Number of seconds to time out on waiting for an instance
 # to build or reach an expected status
 build_timeout = 600
 
@@ -125,7 +125,7 @@
 path_to_private_key = /home/user/.ssh/id_rsa
 
 # Connection string to the database of Compute service
-db_uri = mysql:///user:pass@localhost/nova
+db_uri = mysql://user:pass@localhost/nova
 
 [image]
 # This section contains configuration options used when executing tests
@@ -167,7 +167,10 @@
 [network]
 # This section contains configuration options used when executing tests
 # against the OpenStack Network API.
+
+# Version of the Quantum API
 api_version = v1.1
+# Catalog type of the Quantum Service
 catalog_type = network
 
 [identity-admin]
@@ -181,3 +184,18 @@
 password = pass
 # The above administrative user's tenant name
 tenant_name = admin
+
+[volume]
+# This section contains the configuration options used when executng tests
+# against the OpenStack Block Storage API service
+
+# The type of endpoint for a Cinder or Block Storage API service.
+# Unless you have a custom Keystone service catalog implementation, you
+# probably want to leave this value as "volume"
+catalog_type = volume
+# Number of seconds to wait while looping to check the status of a
+# volume that is being made available
+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
diff --git a/etc/tempest.conf.tpl b/etc/tempest.conf.tpl
index ac1664a..ecb020a 100644
--- a/etc/tempest.conf.tpl
+++ b/etc/tempest.conf.tpl
@@ -58,12 +58,12 @@
 flavor_ref_alt = %FLAVOR_REF_ALT%
 
 # Number of seconds to wait while looping to check the status of an
-# instance or volume that is building.
-build_interval = %BUILD_INTERVAL%
+# instance that is building.
+build_interval = %COMPUTE_BUILD_INTERVAL%
 
-# Number of seconds to time out on waiting for an instance or volume
+# Number of seconds to time out on waiting for an instance
 # to build or reach an expected status
-build_timeout = %BUILD_TIMEOUT%
+build_timeout = %COMPUTE_BUILD_TIMEOUT%
 
 # The type of endpoint for a Compute API service. Unless you have a
 # custom Keystone service catalog implementation, you probably want to leave
@@ -154,3 +154,18 @@
 password = %IDENTITY_ADMIN_PASSWORD%
 # The above administrative user's tenant name
 tenant_name = %IDENTITY_ADMIN_TENANT_NAME%
+
+[volume]
+# This section contains the configuration options used when executing tests
+# against the OpenStack Block Storage API service
+
+# The type of endpoint for a Cinder or Block Storage API service.
+# Unless you have a custom Keystone service catalog implementation, you
+# probably want to leave this value as "volume"
+catalog_type = %VOLUME_CATALOG_TYPE%
+# Number of seconds to wait while looping to check the status of a
+# volume that is being made available
+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%
diff --git a/tempest/common/utils/data_utils.py b/tempest/common/utils/data_utils.py
index 752bc10..fc7c112 100644
--- a/tempest/common/utils/data_utils.py
+++ b/tempest/common/utils/data_utils.py
@@ -5,7 +5,7 @@
 
 
 def rand_name(name='test'):
-    return name + str(random.randint(1, 99999999999))
+    return name + str(random.randint(1, 999999))
 
 
 def build_url(host, port, api_version=None, path=None,
diff --git a/tempest/config.py b/tempest/config.py
index 003f03e..38455b2 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -208,7 +208,7 @@
 
     @property
     def build_timeout(self):
-        """Timeout in seconds to wait for an entity to build."""
+        """Timeout in seconds to wait for an instance to build."""
         return float(self.get("build_timeout", 300))
 
     @property
@@ -355,6 +355,30 @@
         return self.get("api_version", "v1.1")
 
 
+class VolumeConfig(BaseConfig):
+    """Provides configuration information for connecting to an OpenStack Block
+    Storage Service.
+    """
+
+    SECTION_NAME = "volume"
+
+    @property
+    def build_interval(self):
+        """Time in seconds between volume availability checks."""
+        return float(self.get("build_interval", 10))
+
+    @property
+    def build_timeout(self):
+        """Timeout in seconds to wait for a volume to become available."""
+        return float(self.get("build_timeout", 300))
+
+    @property
+    def catalog_type(self):
+        """Catalog type of the Volume Service"""
+        return self.get("catalog_type", 'volume')
+
+
+# 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"""
     instances = {}
@@ -402,6 +426,7 @@
         self.identity_admin = IdentityAdminConfig(self._conf)
         self.images = ImagesConfig(self._conf)
         self.network = NetworkConfig(self._conf)
+        self.volume = VolumeConfig(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 0679db9..bab7ea7 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -18,7 +18,7 @@
 import logging
 
 # Default client libs
-import glance.client
+import glanceclient
 import keystoneclient.v2_0.client
 import novaclient.client
 import quantumclient.v2_0.client
@@ -27,6 +27,7 @@
 from tempest import exceptions
 # Tempest REST Fuzz testing client libs
 from tempest.services.network.json import network_client
+from tempest.services.volume.json import volumes_client
 from tempest.services.nova.json import images_client
 from tempest.services.nova.json import flavors_client
 from tempest.services.nova.json import servers_client
@@ -35,19 +36,20 @@
 from tempest.services.nova.json import security_groups_client
 from tempest.services.nova.json import floating_ips_client
 from tempest.services.nova.json import keypairs_client
-from tempest.services.nova.json import volumes_client
+from tempest.services.nova.json import volumes_extensions_client
 from tempest.services.nova.json import console_output_client
 
 NetworkClient = network_client.NetworkClient
-ImagesClient = images_client.ImagesClient
+ImagesClient = images_client.ImagesClientJSON
 FlavorsClient = flavors_client.FlavorsClientJSON
 ServersClient = servers_client.ServersClientJSON
 LimitsClient = limits_client.LimitsClientJSON
-ExtensionsClient = extensions_client.ExtensionsClient
+ExtensionsClient = extensions_client.ExtensionsClientJSON
 SecurityGroupsClient = security_groups_client.SecurityGroupsClient
-FloatingIPsClient = floating_ips_client.FloatingIPsClient
+FloatingIPsClient = floating_ips_client.FloatingIPsClientJSON
 KeyPairsClient = keypairs_client.KeyPairsClientJSON
-VolumesClient = volumes_client.VolumesClient
+VolumesExtensionsClient = volumes_extensions_client.VolumesExtensionsClientJSON
+VolumesClient = volumes_client.VolumesClientJSON
 ConsoleOutputsClient = console_output_client.ConsoleOutputsClient
 
 LOG = logging.getLogger(__name__)
@@ -131,30 +133,11 @@
                         no_cache=True)
 
     def _get_image_client(self):
-        host = self.config.images.host
-        port = self.config.images.port
-        strategy = self.config.identity.strategy
-        auth_url = self.config.identity.auth_url
-        username = self.config.images.username
-        password = self.config.images.password
-        tenant_name = self.config.images.tenant_name
-
-        if None in (host, port, username, password, tenant_name):
-            msg = ("Missing required credentials for image client. "
-                    "host:%(host)s, port: %(port)s username: %(username)s, "
-                    "password: %(password)s, "
-                    "tenant_name: %(tenant_name)s") % locals()
-            raise exceptions.InvalidConfiguration(msg)
-        auth_url = self.config.identity.auth_url.rstrip('tokens')
-
-        creds = {'strategy': strategy,
-                 'username': username,
-                 'password': password,
-                 'tenant': tenant_name,
-                 'auth_url': auth_url}
-
-        # Create our default Glance client to use in testing
-        return glance.client.Client(host, port, creds=creds)
+        keystone = self._get_identity_client()
+        token = keystone.auth_token
+        endpoint = keystone.service_catalog.url_for(service_type='image',
+                                                    endpoint_type='publicURL')
+        return glanceclient.Client('1', endpoint=endpoint, token=token)
 
     def _get_identity_client(self):
         # This identity client is not intended to check the security
@@ -243,6 +226,7 @@
         self.keypairs_client = KeyPairsClient(*client_args)
         self.security_groups_client = SecurityGroupsClient(*client_args)
         self.floating_ips_client = FloatingIPsClient(*client_args)
+        self.volumes_extensions_client = VolumesExtensionsClient(*client_args)
         self.volumes_client = VolumesClient(*client_args)
         self.console_outputs_client = ConsoleOutputsClient(*client_args)
         self.network_client = NetworkClient(*client_args)
diff --git a/tempest/openstack.py b/tempest/openstack.py
index 241d8bd..89b7718 100644
--- a/tempest/openstack.py
+++ b/tempest/openstack.py
@@ -21,25 +21,40 @@
 from tempest import exceptions
 from tempest.services.image import service as image_service
 from tempest.services.network.json.network_client import NetworkClient
+from tempest.services.nova.json.extensions_client import ExtensionsClientJSON
 from tempest.services.nova.json.flavors_client import FlavorsClientJSON
-from tempest.services.nova.json.images_client import ImagesClient
+from tempest.services.nova.json.floating_ips_client import \
+FloatingIPsClientJSON
+from tempest.services.nova.json.images_client import ImagesClientJSON
 from tempest.services.nova.json.limits_client import LimitsClientJSON
 from tempest.services.nova.json.servers_client import ServersClientJSON
-from tempest.services.nova.json.extensions_client import ExtensionsClient
 from tempest.services.nova.json.security_groups_client \
 import SecurityGroupsClient
-from tempest.services.nova.json.floating_ips_client import FloatingIPsClient
 from tempest.services.nova.json.keypairs_client import KeyPairsClientJSON
-from tempest.services.nova.json.volumes_client import VolumesClient
+from tempest.services.nova.json.volumes_extensions_client \
+import VolumesExtensionsClientJSON
 from tempest.services.nova.json.console_output_client \
 import ConsoleOutputsClient
+from tempest.services.nova.xml.extensions_client import ExtensionsClientXML
 from tempest.services.nova.xml.flavors_client import FlavorsClientXML
+from tempest.services.nova.xml.floating_ips_client import \
+FloatingIPsClientXML
+from tempest.services.nova.xml.images_client import ImagesClientXML
 from tempest.services.nova.xml.keypairs_client import KeyPairsClientXML
 from tempest.services.nova.xml.limits_client import LimitsClientXML
 from tempest.services.nova.xml.servers_client import ServersClientXML
+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
 
 LOG = logging.getLogger(__name__)
 
+IMAGES_CLIENTS = {
+    "json": ImagesClientJSON,
+    "xml": ImagesClientXML,
+}
+
 KEYPAIRS_CLIENTS = {
     "json": KeyPairsClientJSON,
     "xml": KeyPairsClientXML,
@@ -60,6 +75,26 @@
     "xml": FlavorsClientXML
 }
 
+EXTENSIONS_CLIENTS = {
+    "json": ExtensionsClientJSON,
+    "xml": ExtensionsClientXML
+}
+
+VOLUMES_EXTENSIONS_CLIENTS = {
+    "json": VolumesExtensionsClientJSON,
+    "xml": VolumesExtensionsClientXML,
+}
+
+FLOAT_CLIENTS = {
+    "json": FloatingIPsClientJSON,
+    "xml": FloatingIPsClientXML,
+}
+
+VOLUMES_CLIENTS = {
+    "json": VolumesClientJSON,
+    "xml": VolumesClientXML,
+}
+
 
 class Manager(object):
 
@@ -82,37 +117,41 @@
 
         # If no creds are provided, we fall back on the defaults
         # in the config file for the Compute API.
-        username = username or self.config.compute.username
-        password = password or self.config.compute.password
-        tenant_name = tenant_name or self.config.compute.tenant_name
+        self.username = username or self.config.compute.username
+        self.password = password or self.config.compute.password
+        self.tenant_name = tenant_name or self.config.compute.tenant_name
 
-        if None in (username, password, tenant_name):
+        if None in (self.username, self.password, self.tenant_name):
             msg = ("Missing required credentials. "
                    "username: %(username)s, password: %(password)s, "
                    "tenant_name: %(tenant_name)s") % locals()
             raise exceptions.InvalidConfiguration(msg)
 
-        auth_url = self.config.identity.auth_url
+        self.auth_url = self.config.identity.auth_url
 
         if self.config.identity.strategy == 'keystone':
-            client_args = (self.config, username, password, auth_url,
-                           tenant_name)
+            client_args = (self.config, self.username, self.password,
+                           self.auth_url, self.tenant_name)
         else:
-            client_args = (self.config, username, password, auth_url)
+            client_args = (self.config, self.username, self.password,
+                           self.auth_url)
 
         try:
             self.servers_client = SERVERS_CLIENTS[interface](*client_args)
             self.limits_client = LIMITS_CLIENTS[interface](*client_args)
+            self.images_client = IMAGES_CLIENTS[interface](*client_args)
             self.keypairs_client = KEYPAIRS_CLIENTS[interface](*client_args)
             self.flavors_client = FLAVORS_CLIENTS[interface](*client_args)
+            self.extensions_client = \
+                    EXTENSIONS_CLIENTS[interface](*client_args)
+            self.volumes_extensions_client = \
+                    VOLUMES_EXTENSIONS_CLIENTS[interface](*client_args)
+            self.floating_ips_client = FLOAT_CLIENTS[interface](*client_args)
+            self.volumes_client = VOLUMES_CLIENTS[interface](*client_args)
         except KeyError:
             msg = "Unsupported interface type `%s'" % interface
             raise exceptions.InvalidConfiguration(msg)
-        self.images_client = ImagesClient(*client_args)
-        self.extensions_client = ExtensionsClient(*client_args)
         self.security_groups_client = SecurityGroupsClient(*client_args)
-        self.floating_ips_client = FloatingIPsClient(*client_args)
-        self.volumes_client = VolumesClient(*client_args)
         self.console_outputs_client = ConsoleOutputsClient(*client_args)
         self.network_client = NetworkClient(*client_args)
 
diff --git a/tempest/services/image/service.py b/tempest/services/image/service.py
index 9cfe502..154b5b8 100644
--- a/tempest/services/image/service.py
+++ b/tempest/services/image/service.py
@@ -36,22 +36,25 @@
         # Determine the Images API version
         self.api_version = int(config.images.api_version)
 
+        # We load the client class specific to the API version...
         if self.api_version == 1:
-            # We load the client class specific to the API version...
-            from glance import client
-            creds = {
-                'username': config.images.username,
-                'password': config.images.password,
-                'tenant': config.images.tenant_name,
-                # rstrip() is necessary here because Glance client
-                # automatically adds the tokens/ part...
-                'auth_url': config.identity.auth_url.rstrip('/tokens'),
-                'strategy': config.identity.strategy
-            }
-            self._client = client.Client(config.images.host,
-                                         config.images.port,
-                                         creds=creds,
-                                         configure_via_auth=False)
+            import glanceclient
+            import keystoneclient.v2_0.client
+
+            auth_url = self.config.identity.auth_url.rstrip('tokens')
+            keystone = keystoneclient.v2_0.client.Client(
+                    username=config.images.username,
+                    password=config.images.password,
+                    tenant_name=config.images.tenant_name,
+                    auth_url=auth_url)
+            token = keystone.auth_token
+            endpoint = keystone.service_catalog.url_for(
+                    service_type='image',
+                    endpoint_type='publicURL')
+
+            self._client = glanceclient.Client('1',
+                                               endpoint=endpoint,
+                                               token=token)
         else:
             raise NotImplementedError
 
diff --git a/tempest/services/nova/json/extensions_client.py b/tempest/services/nova/json/extensions_client.py
index 5627afc..8eeb382 100644
--- a/tempest/services/nova/json/extensions_client.py
+++ b/tempest/services/nova/json/extensions_client.py
@@ -2,10 +2,10 @@
 import json
 
 
-class ExtensionsClient(RestClient):
+class ExtensionsClientJSON(RestClient):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(ExtensionsClient, self).__init__(config, username, password,
+        super(ExtensionsClientJSON, self).__init__(config, username, password,
                                                auth_url, tenant_name)
         self.service = self.config.compute.catalog_type
 
diff --git a/tempest/services/nova/json/floating_ips_client.py b/tempest/services/nova/json/floating_ips_client.py
index 9f382ff..231b61a 100644
--- a/tempest/services/nova/json/floating_ips_client.py
+++ b/tempest/services/nova/json/floating_ips_client.py
@@ -3,9 +3,9 @@
 import json
 
 
-class FloatingIPsClient(RestClient):
+class FloatingIPsClientJSON(RestClient):
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(FloatingIPsClient, self).__init__(config, username, password,
+        super(FloatingIPsClientJSON, self).__init__(config, username, password,
                                                 auth_url, tenant_name)
         self.service = self.config.compute.catalog_type
 
@@ -15,8 +15,8 @@
         if params != None:
             param_list = []
             for param, value in params.iteritems():
-                param_list.append("%s=%s&" % (param, value))
-            url += '?' + ' '.join(param_list)
+                param_list.append("%s=%s" % (param, value))
+            url += '?' + ' &'.join(param_list)
         resp, body = self.get(url)
         body = json.loads(body)
         return resp, body['floating_ips']
diff --git a/tempest/services/nova/json/images_client.py b/tempest/services/nova/json/images_client.py
index 87cb403..7a29b2f 100644
--- a/tempest/services/nova/json/images_client.py
+++ b/tempest/services/nova/json/images_client.py
@@ -4,11 +4,11 @@
 import time
 
 
-class ImagesClient(RestClient):
+class ImagesClientJSON(RestClient):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(ImagesClient, self).__init__(config, username, password,
-                                            auth_url, tenant_name)
+        super(ImagesClientJSON, self).__init__(config, username, password,
+                                               auth_url, tenant_name)
         self.service = self.config.compute.catalog_type
         self.build_interval = self.config.compute.build_interval
         self.build_timeout = self.config.compute.build_timeout
diff --git a/tempest/services/nova/json/volumes_client.py b/tempest/services/nova/json/volumes_extensions_client.py
similarity index 89%
rename from tempest/services/nova/json/volumes_client.py
rename to tempest/services/nova/json/volumes_extensions_client.py
index fd577b7..ed71f71 100644
--- a/tempest/services/nova/json/volumes_client.py
+++ b/tempest/services/nova/json/volumes_extensions_client.py
@@ -4,14 +4,15 @@
 import time
 
 
-class VolumesClient(RestClient):
+class VolumesExtensionsClientJSON(RestClient):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(VolumesClient, self).__init__(config, username, password,
-                                           auth_url, tenant_name)
+        super(VolumesExtensionsClientJSON, self).__init__(config, username,
+                                                      password, auth_url,
+                                                      tenant_name)
         self.service = self.config.compute.catalog_type
-        self.build_interval = self.config.compute.build_interval
-        self.build_timeout = self.config.compute.build_timeout
+        self.build_interval = self.config.volume.build_interval
+        self.build_timeout = self.config.volume.build_timeout
 
     def list_volumes(self, params=None):
         """List all the volumes created"""
diff --git a/tempest/services/nova/xml/extensions_client.py b/tempest/services/nova/xml/extensions_client.py
new file mode 100644
index 0000000..7ae4214
--- /dev/null
+++ b/tempest/services/nova/xml/extensions_client.py
@@ -0,0 +1,28 @@
+from lxml import etree
+from tempest.common.rest_client import RestClientXML
+from tempest.services.nova.xml.common import xml_to_json
+
+
+class ExtensionsClientXML(RestClientXML):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(ExtensionsClientXML, self).__init__(config, username, password,
+                                               auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+
+    def _parse_array(self, node):
+        array = []
+        for child in node:
+            array.append(xml_to_json(child))
+        return array
+
+    def list_extensions(self):
+        url = 'extensions'
+        resp, body = self.get(url, self.headers)
+        body = self._parse_array(etree.fromstring(body))
+        return resp, {'extensions': body}
+
+    def is_enabled(self, extension):
+        _, extensions = self.list_extensions()
+        exts = extensions['extensions']
+        return any([e for e in exts if e['name'] == extension])
diff --git a/tempest/services/nova/xml/floating_ips_client.py b/tempest/services/nova/xml/floating_ips_client.py
new file mode 100644
index 0000000..4b91abb
--- /dev/null
+++ b/tempest/services/nova/xml/floating_ips_client.py
@@ -0,0 +1,103 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 IBM
+# 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.
+
+from lxml import etree
+
+from tempest.common.rest_client import RestClientXML
+from tempest import exceptions
+from tempest.services.nova.xml.common import xml_to_json
+from tempest.services.nova.xml.common import Document
+from tempest.services.nova.xml.common import Element
+
+
+class FloatingIPsClientXML(RestClientXML):
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(FloatingIPsClientXML, self).__init__(config, username, password,
+                                                auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+
+    def _parse_array(self, node):
+        array = []
+        for child in node.getchildren():
+            array.append(xml_to_json(child))
+        return array
+
+    def _parse_floating_ip(self, body):
+        json = xml_to_json(body)
+        return json
+
+    def list_floating_ips(self, params=None):
+        """Returns a list of all floating IPs filtered by any parameters"""
+        url = 'os-floating-ips'
+        if params != None:
+            param_list = []
+            for param, value in params.iteritems():
+                param_list.append("%s=%s" % (param, value))
+            url += "?" + "&".join(param_list)
+
+        resp, body = self.get(url, self.headers)
+        body = self._parse_array(etree.fromstring(body))
+        return resp, body
+
+    def get_floating_ip_details(self, floating_ip_id):
+        """Get the details of a floating IP"""
+        url = "os-floating-ips/%s" % str(floating_ip_id)
+        resp, body = self.get(url, self.headers)
+        body = self._parse_floating_ip(etree.fromstring(body))
+        if resp.status == 404:
+            raise exceptions.NotFound(body)
+        return resp, body
+
+    def create_floating_ip(self):
+        """Allocate a floating IP to the project"""
+        url = 'os-floating-ips'
+        resp, body = self.post(url, None, self.headers)
+        body = self._parse_floating_ip(etree.fromstring(body))
+        return resp, body
+
+    def delete_floating_ip(self, floating_ip_id):
+        """Deletes the provided floating IP from the project"""
+        url = "os-floating-ips/%s" % str(floating_ip_id)
+        resp, body = self.delete(url, self.headers)
+        return resp, body
+
+    def associate_floating_ip_to_server(self, floating_ip, server_id):
+        """Associate the provided floating IP to a specific server"""
+        url = "servers/%s/action" % str(server_id)
+        doc = Document()
+        server = Element("addFloatingIp")
+        doc.append(server)
+        server.add_attr("address", floating_ip)
+        resp, body = self.post(url, str(doc), self.headers)
+        return resp, body
+
+    def disassociate_floating_ip_from_server(self, floating_ip, server_id):
+        """Disassociate the provided floating IP from a specific server"""
+        url = "servers/%s/action" % str(server_id)
+        doc = Document()
+        server = Element("removeFloatingIp")
+        doc.append(server)
+        server.add_attr("address", floating_ip)
+        resp, body = self.post(url, str(doc), self.headers)
+        return resp, body
+
+    def is_resource_deleted(self, id):
+        try:
+            self.get_floating_ip_details(id)
+        except exceptions.NotFound:
+            return True
+        return False
diff --git a/tempest/services/nova/xml/images_client.py b/tempest/services/nova/xml/images_client.py
new file mode 100644
index 0000000..0df8dfc
--- /dev/null
+++ b/tempest/services/nova/xml/images_client.py
@@ -0,0 +1,198 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 IBM
+# 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 time
+import urllib
+
+from lxml import etree
+
+from tempest import exceptions
+from tempest.common.rest_client import RestClientXML
+from tempest.services.nova.xml.common import Document
+from tempest.services.nova.xml.common import Element
+from tempest.services.nova.xml.common import Text
+from tempest.services.nova.xml.common import xml_to_json
+from tempest.services.nova.xml.common import XMLNS_11
+
+
+class ImagesClientXML(RestClientXML):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(ImagesClientXML, self).__init__(config, username, password,
+                                              auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+        self.build_interval = self.config.compute.build_interval
+        self.build_timeout = self.config.compute.build_timeout
+
+    def _parse_server(self, node):
+        json = xml_to_json(node)
+        return self._parse_links(node, json)
+
+    def _parse_image(self, node):
+        """Parses detailed XML image information into dictionary"""
+        json = xml_to_json(node)
+
+        self._parse_links(node, json)
+
+        # parse all metadata
+        if 'metadata' in json:
+            tag = node.find('{%s}metadata' % XMLNS_11)
+            json['metadata'] = dict((x.get('key'), x.text)
+                                    for x in tag.getchildren())
+
+        # parse server information
+        if 'server' in json:
+            tag = node.find('{%s}server' % XMLNS_11)
+            json['server'] = self._parse_server(tag)
+        return json
+
+    def _parse_links(self, node, json):
+        """Append multiple links under a list"""
+        # look for links
+        if 'link' in json:
+            # remove single link element
+            del json['link']
+            json['links'] = [xml_to_json(x) for x in
+                             node.findall('{http://www.w3.org/2005/Atom}link')]
+        return json
+
+    def create_image(self, server_id, name, meta=None):
+        """Creates an image of the original server"""
+        post_body = Element('createImage', name=name)
+
+        if meta:
+            metadata = Element('metadata')
+            post_body.append(metadata)
+            for k, v in meta.items():
+                data = Element('meta', key=k)
+                data.append(Text(v))
+                metadata.append(data)
+        resp, body = self.post('servers/%s/action' % str(server_id),
+                               str(Document(post_body)), self.headers)
+        return resp, body
+
+    def list_images(self, params=None):
+        """Returns a list of all images filtered by any parameters"""
+        url = 'images'
+        if params:
+            param_list = urllib.urlencode(params)
+            url += "?" + param_list
+
+        resp, body = self.get(url, self.headers)
+        body = xml_to_json(etree.fromstring(body))
+        return resp, body['images']
+
+    def list_images_with_detail(self, params=None):
+        """Returns a detailed list of images filtered by any parameters"""
+        url = 'images/detail'
+        if params:
+            param_list = urllib.urlencode(params)
+
+            url = "images/detail?" + param_list
+
+        resp, body = self.get(url, self.headers)
+        body = xml_to_json(etree.fromstring(body))
+        return resp, body['images']
+
+    def get_image(self, image_id):
+        """Returns the details of a single image"""
+        resp, body = self.get("images/%s" % str(image_id), self.headers)
+        body = self._parse_image(etree.fromstring(body))
+        return resp, body
+
+    def delete_image(self, image_id):
+        """Deletes the provided image"""
+        return self.delete("images/%s" % str(image_id), self.headers)
+
+    def wait_for_image_resp_code(self, image_id, code):
+        """
+        Waits until the HTTP response code for the request matches the
+        expected value
+        """
+        resp, body = self.get("images/%s" % str(image_id), self.headers)
+        start = int(time.time())
+
+        while resp.status != code:
+            time.sleep(self.build_interval)
+            resp, body = self.get("images/%s" % str(image_id), self.headers)
+
+            if int(time.time()) - start >= self.build_timeout:
+                raise exceptions.TimeoutException
+
+    def wait_for_image_status(self, image_id, status):
+        """Waits for an image to reach a given status."""
+        resp, image = self.get_image(image_id)
+        start = int(time.time())
+
+        while image['status'] != status:
+            time.sleep(self.build_interval)
+            resp, image = self.get_image(image_id)
+            if image['status'] == 'ERROR':
+                raise exceptions.AddImageException(image_id=image_id)
+
+            if int(time.time()) - start >= self.build_timeout:
+                raise exceptions.TimeoutException
+
+    def list_image_metadata(self, image_id):
+        """Lists all metadata items for an image"""
+        resp, body = self.get("images/%s/metadata" % str(image_id),
+                              self.headers)
+        body = xml_to_json(etree.fromstring(body))
+        return resp, body['metadata']
+
+    def set_image_metadata(self, image_id, meta):
+        """Sets the metadata for an image"""
+        post_body = json.dumps({'metadata': meta})
+        resp, body = self.put('images/%s/metadata' % str(image_id),
+                              post_body, self.headers)
+        body = xml_to_json(etree.fromstring(body))
+        return resp, body['metadata']
+
+    def update_image_metadata(self, image_id, meta):
+        """Updates the metadata for an image"""
+        post_body = Element('metadata', meta)
+        for k, v in meta:
+            metadata = Element('meta', key=k)
+            text = Text(v)
+            metadata.append(text)
+            post_body.append(metadata)
+
+        resp, body = self.post('images/%s/metadata' % str(image_id),
+                               post_body, self.headers)
+        body = xml_to_json(etree.fromstring(body))
+        return resp, body['metadata']
+
+    def get_image_metadata_item(self, image_id, key):
+        """Returns the value for a specific image metadata key"""
+        resp, body = self.get("images/%s/metadata/%s.xml" %
+                              (str(image_id), key), self.headers)
+        body = xml_to_json(etree.fromstring(body))
+        return resp, body['meta']
+
+    def set_image_metadata_item(self, image_id, key, meta):
+        """Sets the value for a specific image metadata key"""
+        post_body = json.dumps({'meta': meta})
+        resp, body = self.put('images/%s/metadata/%s' % (str(image_id), key),
+                              post_body, self.headers)
+        body = xml_to_json(etree.fromstring(body))
+        return resp, body['meta']
+
+    def delete_image_metadata_item(self, image_id, key):
+        """Deletes a single image metadata key/value pair"""
+        resp, body = self.delete("images/%s/metadata/%s" % (str(image_id), key,
+                                 self.headers))
+        return resp, body
diff --git a/tempest/services/nova/xml/volumes_extensions_client.py b/tempest/services/nova/xml/volumes_extensions_client.py
new file mode 100644
index 0000000..fffea44
--- /dev/null
+++ b/tempest/services/nova/xml/volumes_extensions_client.py
@@ -0,0 +1,146 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 IBM
+# 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 time
+from lxml import etree
+
+from tempest import exceptions
+from tempest.common.rest_client import RestClientXML
+from tempest.services.nova.xml.common import xml_to_json
+from tempest.services.nova.xml.common import XMLNS_11
+from tempest.services.nova.xml.common import Element
+from tempest.services.nova.xml.common import Text
+from tempest.services.nova.xml.common import Document
+
+
+class VolumesExtensionsClientXML(RestClientXML):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(VolumesExtensionsClientXML, self).__init__(config,
+                                                         username, password,
+                                                         auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+        self.build_interval = self.config.compute.build_interval
+        self.build_timeout = self.config.compute.build_timeout
+
+    def _parse_volume(self, body):
+        vol = dict((attr, body.get(attr)) for attr in body.keys())
+
+        for child in body.getchildren():
+            tag = child.tag
+            if tag.startswith("{"):
+                ns, tag = tag.split("}", 1)
+            if tag == 'metadata':
+                vol['metadata'] = dict((meta.get('key'),
+                                        meta.text) for meta in list(child))
+            else:
+                vol[tag] = xml_to_json(child)
+        return vol
+
+    def list_volumes(self, params=None):
+        """List all the volumes created"""
+        url = 'os-volumes'
+
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url, self.headers)
+        body = etree.fromstring(body)
+        volumes = []
+        if body is not None:
+            volumes += [self._parse_volume(vol) for vol in list(body)]
+        return resp, volumes
+
+    def list_volumes_with_detail(self, params=None):
+        """List all the details of volumes"""
+        url = 'os-volumes/detail'
+
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url, self.headers)
+        body = etree.fromstring(body)
+        volumes = []
+        if body is not None:
+            volumes += [self._parse_volume(vol) for vol in list(body)]
+        return resp, volumes
+
+    def get_volume(self, volume_id):
+        """Returns the details of a single volume"""
+        url = "os-volumes/%s" % str(volume_id)
+        resp, body = self.get(url, self.headers)
+        body = etree.fromstring(body)
+        return resp, self._parse_volume(body)
+
+    def create_volume(self, size, display_name=None, metadata=None):
+        """Creates a new Volume.
+
+        :param size: Size of volume in GB. (Required)
+        :param display_name: Optional Volume Name.
+        :param metadata: An optional dictionary of values for metadata.
+        """
+        volume = Element("volume",
+                         xmlns=XMLNS_11,
+                         size=size)
+        if display_name:
+            volume.add_attr('display_name', display_name)
+
+        if metadata:
+            _metadata = Element('metadata')
+            volume.append(_metadata)
+            for key, value in metadata.items():
+                meta = Element('meta')
+                meta.add_attr('key', key)
+                meta.append(Text(value))
+                _metadata.append(meta)
+
+        resp, body = self.post('os-volumes', str(Document(volume)),
+                               self.headers)
+        body = xml_to_json(etree.fromstring(body))
+        return resp, body
+
+    def delete_volume(self, volume_id):
+        """Deletes the Specified Volume"""
+        return self.delete("os-volumes/%s" % str(volume_id))
+
+    def wait_for_volume_status(self, volume_id, status):
+        """Waits for a Volume to reach a given status"""
+        resp, body = self.get_volume(volume_id)
+        volume_name = body['displayName']
+        volume_status = body['status']
+        start = int(time.time())
+
+        while volume_status != status:
+            time.sleep(self.build_interval)
+            resp, body = self.get_volume(volume_id)
+            volume_status = body['status']
+            if volume_status == 'error':
+                raise exceptions.VolumeBuildErrorException(volume_id=volume_id)
+
+            if int(time.time()) - start >= self.build_timeout:
+                message = 'Volume %s failed to reach %s status within '\
+                          'the required time (%s s).' % (volume_name, status,
+                                                         self.build_timeout)
+                raise exceptions.TimeoutException(message)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.get_volume(id)
+        except exceptions.NotFound:
+            return True
+        return False
diff --git a/tempest/services/volume/__init__.py b/tempest/services/volume/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/volume/__init__.py
diff --git a/tempest/services/volume/json/__init__.py b/tempest/services/volume/json/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/volume/json/__init__.py
diff --git a/tempest/services/nova/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
similarity index 68%
copy from tempest/services/nova/json/volumes_client.py
copy to tempest/services/volume/json/volumes_client.py
index fd577b7..c2e8e45 100644
--- a/tempest/services/nova/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -1,35 +1,56 @@
-from tempest import exceptions
-from tempest.common.rest_client import RestClient
+# 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
 import time
 
+from tempest.common.rest_client import RestClient
+from tempest import exceptions
 
-class VolumesClient(RestClient):
+
+class VolumesClientJSON(RestClient):
+    """
+    Client class to send CRUD Volume API requests to a Cinder endpoint
+    """
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(VolumesClient, self).__init__(config, username, password,
+        super(VolumesClientJSON, self).__init__(config, username, password,
                                            auth_url, tenant_name)
-        self.service = self.config.compute.catalog_type
-        self.build_interval = self.config.compute.build_interval
-        self.build_timeout = self.config.compute.build_timeout
+
+        self.service = self.config.volume.catalog_type
+        self.build_interval = self.config.volume.build_interval
+        self.build_timeout = self.config.volume.build_timeout
 
     def list_volumes(self, params=None):
         """List all the volumes created"""
-        url = 'os-volumes'
+        url = 'volumes'
         if params != None:
             param_list = []
             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['volumes']
 
     def list_volumes_with_detail(self, params=None):
-        """List all the details of volumes"""
-        url = 'os-volumes/detail'
+        """List the details of all volumes"""
+        url = 'volumes/detail'
         if params != None:
             param_list = []
             for param, value in params.iteritems():
@@ -43,7 +64,7 @@
 
     def get_volume(self, volume_id):
         """Returns the details of a single volume"""
-        url = "os-volumes/%s" % str(volume_id)
+        url = "volumes/%s" % str(volume_id)
         resp, body = self.get(url)
         body = json.loads(body)
         return resp, body['volume']
@@ -63,18 +84,18 @@
             }
 
         post_body = json.dumps({'volume': post_body})
-        resp, body = self.post('os-volumes', post_body, self.headers)
+        resp, body = self.post('volumes', post_body, self.headers)
         body = json.loads(body)
         return resp, body['volume']
 
     def delete_volume(self, volume_id):
         """Deletes the Specified Volume"""
-        return self.delete("os-volumes/%s" % str(volume_id))
+        return self.delete("volumes/%s" % str(volume_id))
 
     def wait_for_volume_status(self, volume_id, status):
         """Waits for a Volume to reach a given status"""
         resp, body = self.get_volume(volume_id)
-        volume_name = body['displayName']
+        volume_name = body['display_name']
         volume_status = body['status']
         start = int(time.time())
 
diff --git a/tempest/services/volume/xml/__init__.py b/tempest/services/volume/xml/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/volume/xml/__init__.py
diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py
new file mode 100644
index 0000000..8bb8bff
--- /dev/null
+++ b/tempest/services/volume/xml/volumes_client.py
@@ -0,0 +1,148 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 IBM
+# 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 time
+
+from lxml import etree
+
+from tempest.common.rest_client import RestClientXML
+from tempest import exceptions
+from tempest.services.nova.xml.common import xml_to_json
+from tempest.services.nova.xml.common import XMLNS_11
+from tempest.services.nova.xml.common import Element
+from tempest.services.nova.xml.common import Text
+from tempest.services.nova.xml.common import Document
+
+
+class VolumesClientXML(RestClientXML):
+    """
+    Client class to send CRUD Volume API requests to a Cinder endpoint
+    """
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(VolumesClientXML, self).__init__(config, username, password,
+                                               auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+        self.build_interval = self.config.compute.build_interval
+        self.build_timeout = self.config.compute.build_timeout
+
+    def _parse_volume(self, body):
+        vol = dict((attr, body.get(attr)) for attr in body.keys())
+
+        for child in body.getchildren():
+            tag = child.tag
+            if tag.startswith("{"):
+                ns, tag = tag.split("}", 1)
+            if tag == 'metadata':
+                vol['metadata'] = dict((meta.get('key'),
+                                        meta.text) for meta in list(child))
+            else:
+                vol[tag] = xml_to_json(child)
+            return vol
+
+    def list_volumes(self, params=None):
+        """List all the volumes created"""
+        url = 'volumes'
+
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url, self.headers)
+        body = etree.fromstring(body)
+        volumes = []
+        if body is not None:
+            volumes += [self._parse_volume(vol) for vol in list(body)]
+        return resp, volumes
+
+    def list_volumes_with_detail(self, params=None):
+        """List all the details of volumes"""
+        url = 'volumes/detail'
+
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url, self.headers)
+        body = etree.fromstring(body)
+        volumes = []
+        if body is not None:
+            volumes += [self._parse_volume(vol) for vol in list(body)]
+        return resp, volumes
+
+    def get_volume(self, volume_id):
+        """Returns the details of a single volume"""
+        url = "volumes/%s" % str(volume_id)
+        resp, body = self.get(url, self.headers)
+        body = etree.fromstring(body)
+        return resp, self._parse_volume(body)
+
+    def create_volume(self, size, display_name=None, metadata=None):
+        """Creates a new Volume.
+
+        :param size: Size of volume in GB. (Required)
+        :param display_name: Optional Volume Name.
+        :param metadata: An optional dictionary of values for metadata.
+        """
+        volume = Element("volume",
+                          xmlns=XMLNS_11,
+                          size=size)
+        if display_name:
+            volume.add_attr('display_name', display_name)
+
+        if metadata:
+            _metadata = Element('metadata')
+            volume.append(_metadata)
+            for key, value in metadata.items():
+                meta = Element('meta')
+                meta.add_attr('key', key)
+                meta.append(Text(value))
+                _metadata.append(meta)
+
+        resp, body = self.post('volumes', str(Document(volume)),
+                               self.headers)
+        body = xml_to_json(etree.fromstring(body))
+        return resp, body
+
+    def delete_volume(self, volume_id):
+        """Deletes the Specified Volume"""
+        return self.delete("volumes/%s" % str(volume_id))
+
+    def wait_for_volume_status(self, volume_id, status):
+        """Waits for a Volume to reach a given status"""
+        resp, body = self.get_volume(volume_id)
+        volume_name = body['displayName']
+        volume_status = body['status']
+        start = int(time.time())
+
+        while volume_status != status:
+            time.sleep(self.build_interval)
+            resp, body = self.get_volume(volume_id)
+            volume_status = body['status']
+            if volume_status == 'error':
+                raise exceptions.VolumeBuildErrorException(volume_id=volume_id)
+
+            if int(time.time()) - start >= self.build_timeout:
+                message = 'Volume %s failed to reach %s status within '\
+                          'the required time (%s s).' % (volume_name, status,
+                                                         self.build_timeout)
+                raise exceptions.TimeoutException(message)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.get_volume(id)
+        except exceptions.NotFound:
+            return True
+        return False
diff --git a/tempest/test.py b/tempest/test.py
index 4bb8ca4..dc480d1 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -62,6 +62,29 @@
         del self.resource_keys[key]
 
 
+def call_until_true(func, duration, sleep_for):
+    """
+    Call the given function until it returns True (and return True) or
+    until the specified duration (in seconds) elapses (and return
+    False).
+
+    :param func: A zero argument callable that returns True on success.
+    :param duration: The number of seconds for which to attempt a successful
+                     call of the function.
+    :param sleep_for: The number of seconds to sleep after an unsuccessful
+                      invocation of the function.
+    """
+    now = time.time()
+    timeout = now + duration
+    while now < timeout:
+        if func():
+            return True
+        LOG.debug("Sleeping for %d seconds", sleep_for)
+        time.sleep(sleep_for)
+        now = time.time()
+    return False
+
+
 class DefaultClientTest(TestCase):
 
     """
@@ -78,10 +101,7 @@
         expected status to show. At any time, if the returned
         status of the thing is ERROR, fail out.
         """
-        now = time.time()
-        timeout = now + self.config.compute.build_timeout
-        sleep_for = self.config.compute.build_interval
-        while now < timeout:
+        def check_status():
             # python-novaclient has resources available to its client
             # that all implement a get() method taking an identifier
             # for the singular resource to retrieve.
@@ -92,13 +112,15 @@
                           "In ERROR state."
                           % thing)
             elif new_status == expected_status:
-                return  # All good.
+                return True  # All good.
             LOG.debug("Waiting for %s to get to %s status. "
                       "Currently in %s status",
                       thing, expected_status, new_status)
-            LOG.debug("Sleeping for %d seconds", sleep_for)
-        self.fail("Timed out waiting for thing %s to become %s"
-                  % (thing_id, expected_status))
+        if not call_until_true(check_status,
+                               self.config.compute.build_timeout,
+                               self.config.compute.build_interval):
+            self.fail("Timed out waiting for thing %s to become %s"
+                      % (thing_id, expected_status))
 
 
 class ComputeFuzzClientTest(TestCase):
@@ -132,10 +154,7 @@
                 resp, server = client.create_server('random_server')
                 self.status_timeout(client.get_server, server['id'], 'ACTIVE')
         """
-        now = time.time()
-        timeout = now + self.config.compute.build_timeout
-        sleep_for = self.config.compute.build_interval
-        while now < timeout:
+        def check_status():
             # Tempest REST client has resources available to its client
             # that all implement a various get_$resource() methods taking
             # an identifier for the singular resource to retrieve.
@@ -146,10 +165,12 @@
                           "In ERROR state."
                           % thing)
             elif new_status == expected_status:
-                return  # All good.
+                return True  # All good.
             LOG.debug("Waiting for %s to get to %s status. "
                       "Currently in %s status",
                       thing, expected_status, new_status)
-            LOG.debug("Sleeping for %d seconds", sleep_for)
-        self.fail("Timed out waiting for thing %s to become %s"
-                  % (thing_id, expected_status))
+        if not call_until_true(check_status,
+                               self.config.compute.build_timeout,
+                               self.config.compute.build_interval):
+            self.fail("Timed out waiting for thing %s to become %s"
+                      % (thing_id, expected_status))
diff --git a/tempest/tests/compute/admin/test_flavors.py b/tempest/tests/compute/admin/test_flavors.py
index be77411..9c38ff4 100644
--- a/tempest/tests/compute/admin/test_flavors.py
+++ b/tempest/tests/compute/admin/test_flavors.py
@@ -46,7 +46,6 @@
         cls.rxtx = 1
 
     @attr(type='positive')
-    @unittest.skip("Until Bug 1042539 is fixed")
     def test_create_flavor(self):
         """Create a flavor and ensure it is listed
         This operation requires the user to have 'admin' role"""
@@ -77,7 +76,6 @@
         self.assertEqual(resp.status, 202)
 
     @attr(type='positive')
-    @unittest.skip("Until Bug 1042539 is fixed")
     def test_create_flavor_verify_entry_in_list_details(self):
         """Create a flavor and ensure it's details are listed
         This operation requires the user to have 'admin' role"""
@@ -102,7 +100,6 @@
         self.assertEqual(resp.status, 202)
 
     @attr(type='negative')
-    @unittest.skip("Until Bug 1042539 is fixed")
     def test_get_flavor_details_for_deleted_flavor(self):
         """Delete a flavor and ensure it is not listed"""
         # Create a test flavor
diff --git a/tempest/tests/compute/base.py b/tempest/tests/compute/base.py
index 5f030fa..ee3bf9e 100644
--- a/tempest/tests/compute/base.py
+++ b/tempest/tests/compute/base.py
@@ -17,6 +17,7 @@
 
 import logging
 import time
+import nose
 
 import unittest2 as unittest
 import nose
@@ -33,7 +34,7 @@
 LOG = logging.getLogger(__name__)
 
 
-class BaseComputeTest(unittest.TestCase):
+class BaseCompTest(unittest.TestCase):
 
     """Base test case class for all Compute API tests"""
 
@@ -62,6 +63,7 @@
         cls.security_groups_client = os.security_groups_client
         cls.console_outputs_client = os.console_outputs_client
         cls.limits_client = os.limits_client
+        cls.volumes_extensions_client = os.volumes_extensions_client
         cls.volumes_client = os.volumes_client
         cls.build_interval = cls.config.compute.build_interval
         cls.build_timeout = cls.config.compute.build_timeout
@@ -204,7 +206,7 @@
             time.sleep(self.build_interval)
 
 
-class BaseComputeTestJSON(BaseComputeTest):
+class BaseComputeTestJSON(BaseCompTest):
     @classmethod
     def setUpClass(cls):
         cls._interface = "json"
@@ -214,7 +216,7 @@
 BaseComputeTest = BaseComputeTestJSON
 
 
-class BaseComputeTestXML(BaseComputeTest):
+class BaseComputeTestXML(BaseCompTest):
     @classmethod
     def setUpClass(cls):
         cls._interface = "xml"
diff --git a/tempest/tests/compute/test_authorization.py b/tempest/tests/compute/test_authorization.py
index 0f0a8dd..afa60b3 100644
--- a/tempest/tests/compute/test_authorization.py
+++ b/tempest/tests/compute/test_authorization.py
@@ -174,7 +174,6 @@
         """A create image request for another user's server should fail"""
         self.alt_images_client.create_image(self.server['id'], 'testImage')
 
-    @unittest.skip("Skipped until the Bug #1039608 is resolved")
     @raises(exceptions.BadRequest)
     @attr(type='negative')
     def test_create_server_with_unauthorized_image(self):
diff --git a/tempest/tests/compute/test_extensions.py b/tempest/tests/compute/test_extensions.py
index 1448f05..552c58c 100644
--- a/tempest/tests/compute/test_extensions.py
+++ b/tempest/tests/compute/test_extensions.py
@@ -17,15 +17,10 @@
 
 from nose.plugins.attrib import attr
 
-from tempest.tests.compute.base import BaseComputeTest
+from tempest.tests.compute import base
 
 
-class ExtensionsTest(BaseComputeTest):
-
-    @classmethod
-    def setUpClass(cls):
-        super(ExtensionsTest, cls).setUpClass()
-        cls.client = cls.extensions_client
+class ExtensionsTestBase(object):
 
     @attr(type='positive')
     def test_list_extensions(self):
@@ -33,3 +28,19 @@
         resp, extensions = self.client.list_extensions()
         self.assertTrue("extensions" in extensions)
         self.assertEqual(200, resp.status)
+
+
+class ExtensionsTestJSON(base.BaseComputeTest, ExtensionsTestBase):
+
+    @classmethod
+    def setUpClass(cls):
+        super(ExtensionsTestJSON, cls).setUpClass()
+        cls.client = cls.extensions_client
+
+
+class ExtensionsTestXML(base.BaseComputeTestXML, ExtensionsTestBase):
+
+    @classmethod
+    def setUpClass(cls):
+        super(ExtensionsTestXML, cls).setUpClass()
+        cls.client = cls.extensions_client
diff --git a/tempest/tests/compute/test_floating_ips_actions.py b/tempest/tests/compute/test_floating_ips_actions.py
index afd0a3f..2f7532b 100644
--- a/tempest/tests/compute/test_floating_ips_actions.py
+++ b/tempest/tests/compute/test_floating_ips_actions.py
@@ -21,16 +21,15 @@
 from tempest import openstack
 from tempest import exceptions
 from tempest.common.utils.data_utils import rand_name
-from tempest.tests.compute.base import BaseComputeTest
+from tempest.tests.compute import base
 
 
-class FloatingIPsTest(BaseComputeTest):
+class FloatingIPsTestBase(object):
     server_id = None
     floating_ip = None
 
-    @classmethod
+    @staticmethod
     def setUpClass(cls):
-        super(FloatingIPsTest, cls).setUpClass()
         cls.client = cls.floating_ips_client
         cls.servers_client = cls.servers_client
 
@@ -45,7 +44,7 @@
         resp, body = cls.client.create_floating_ip()
         cls.floating_ip_id = body['id']
         cls.floating_ip = body['ip']
-        #Generating a nonexistant floatingIP id
+        #Generating a nonexistent floatingIP id
         cls.floating_ip_ids = []
         resp, body = cls.client.list_floating_ips()
         for i in range(len(body)):
@@ -55,19 +54,18 @@
             if cls.non_exist_id not in cls.floating_ip_ids:
                 break
 
-    @classmethod
+    @staticmethod
     def tearDownClass(cls):
         #Deleting the server which is created in this method
         resp, body = cls.servers_client.delete_server(cls.server_id)
         #Deleting the floating IP which is created in this method
         resp, body = cls.client.delete_floating_ip(cls.floating_ip_id)
-        super(FloatingIPsTest, cls).tearDownClass()
 
     @attr(type='positive')
     def test_allocate_floating_ip(self):
         """
         Positive test:Allocation of a new floating IP to a project
-        should be succesfull
+        should be successful
         """
         try:
             resp, body = self.client.create_floating_ip()
@@ -86,7 +84,7 @@
     def test_delete_floating_ip(self):
         """
         Positive test:Deletion of valid floating IP from project
-        should be succesfull
+        should be successful
         """
         #Creating the floating IP that is to be deleted in this method
         resp, floating_ip_body = self.client.create_floating_ip()
@@ -103,7 +101,7 @@
     def test_associate_disassociate_floating_ip(self):
         """
         Positive test:Associate and disassociate the provided floating IP to a
-        specific server should be successfull
+        specific server should be successful
         """
         #Association of floating IP to fixed IP address
         resp, body =\
@@ -120,25 +118,25 @@
     def test_delete_nonexistant_floating_ip(self):
         """
 
-        Negative test:Deletion of a nonexistant floating IP
+        Negative test:Deletion of a nonexistent floating IP
         from project should fail
         """
-        #Deleting the non existant floating IP
+        #Deleting the non existent floating IP
         try:
             resp, body = self.client.delete_floating_ip(self.non_exist_id)
         except:
             pass
         else:
-            self.fail('Should not be able to delete a nonexistant floating IP')
+            self.fail('Should not be able to delete a nonexistent floating IP')
 
     @unittest.skip("Skipped until the Bug #957706 is resolved")
     @attr(type='negative')
     def test_associate_nonexistant_floating_ip(self):
         """
-        Negative test:Association of a non existant floating IP
+        Negative test:Association of a non existent floating IP
         to specific server should fail
         """
-        #Associating non existant floating IP
+        #Associating non existent floating IP
         try:
             resp, body = \
             self.client.associate_floating_ip_to_server("0.0.0.0",
@@ -147,14 +145,14 @@
             pass
         else:
             self.fail('Should not be able to associate'
-                      ' a nonexistant floating IP')
+                      ' a nonexistent floating IP')
 
     @attr(type='negative')
     def test_dissociate_nonexistant_floating_ip(self):
         """
-        Negative test:Dissociation of a non existant floating IP should fail
+        Negative test:Dissociation of a non existent floating IP should fail
         """
-        #Dissociating non existant floating IP
+        #Dissociating non existent floating IP
         try:
             resp, body = \
             self.client.disassociate_floating_ip_from_server("0.0.0.0",
@@ -163,14 +161,13 @@
             pass
         else:
             self.fail('Should not be able to dissociate'
-                      ' a nonexistant floating IP')
+                      ' a nonexistent floating IP')
 
-    @unittest.skip("Skipped until the Bug #1029911 is resolved")
-    @attr(type='negative')
+    @attr(type='positive')
     def test_associate_already_associated_floating_ip(self):
         """
-        Negative test:Association of an already associated floating IP
-        to specific server should raise BadRequest exception
+        positive test:Association of an already associated floating IP
+        to specific server should change the association of the Floating IP
         """
         #Create server so as to use for Multiple association
         resp, body = self.servers_client.create_server('floating-server2',
@@ -180,34 +177,32 @@
         self.new_server_id = body['id']
 
         #Associating floating IP for the first time
-        try:
-            resp, _ = \
-            self.client.associate_floating_ip_to_server(self.floating_ip,
-                                                        self.server_id)
+        resp, _ = \
+        self.client.associate_floating_ip_to_server(self.floating_ip,
+                                                    self.server_id)
         #Associating floating IP for the second time
-            resp = {}
-            resp['status'] = None
-            resp, body = \
-            self.client.associate_floating_ip_to_server(self.floating_ip,
-                                                        self.new_server_id)
-        except exceptions.BadRequest:
+        resp, body = \
+        self.client.associate_floating_ip_to_server(self.floating_ip,
+                                                    self.new_server_id)
+
+        #Make sure no longer associated with old server
+        try:
+            self.client.disassociate_floating_ip_from_server(\
+                self.floating_ip,
+                self.server_id)
+        except exceptions.NotFound:
             pass
         else:
-            self.fail('Association of an already associated floating IP'
-                      ' to specific server should raise BadRequest')
-        finally:
-            if (resp['status'] != None):
-                #Dissociation of the floating IP associated in this method
-                resp, _ = \
-                self.client.disassociate_floating_ip_from_server(\
-                    self.floating_ip,
-                    self.new_server_id)
+            self.fail('The floating IP should be associated to the second'
+                    'server')
+        if (resp['status'] != None):
             #Dissociation of the floating IP associated in this method
             resp, _ = \
-            self.client.disassociate_floating_ip_from_server(self.floating_ip,
-                                                        self.server_id)
-            #Deletion of server created in this method
-            resp, body = self.servers_client.delete_server(self.new_server_id)
+            self.client.disassociate_floating_ip_from_server(\
+                self.floating_ip,
+                self.new_server_id)
+       #Deletion of server created in this method
+        resp, body = self.servers_client.delete_server(self.new_server_id)
 
     @unittest.skip("Skipped until the Bug #957706 is resolved")
     @attr(type='negative')
@@ -225,3 +220,29 @@
         else:
             self.fail('Association of floating IP to specific server'
                       ' with out passing floating IP  should raise BadRequest')
+
+
+class FloatingIPsTestJSON(base.BaseComputeTestJSON,
+                            FloatingIPsTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super(FloatingIPsTestJSON, cls).setUpClass()
+        FloatingIPsTestBase.setUpClass(cls)
+
+    @classmethod
+    def tearDownClass(cls):
+        FloatingIPsTestBase.tearDownClass(cls)
+        super(FloatingIPsTestJSON, cls).tearDownClass()
+
+
+class FloatingIPsTestXML(base.BaseComputeTestXML,
+                            FloatingIPsTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super(FloatingIPsTestXML, cls).setUpClass()
+        FloatingIPsTestBase.setUpClass(cls)
+
+    @classmethod
+    def tearDownClass(cls):
+        FloatingIPsTestBase.tearDownClass(cls)
+        super(FloatingIPsTestXML, cls).tearDownClass()
diff --git a/tempest/tests/compute/test_images.py b/tempest/tests/compute/test_images.py
index 694965f..94bab4f 100644
--- a/tempest/tests/compute/test_images.py
+++ b/tempest/tests/compute/test_images.py
@@ -203,29 +203,27 @@
                       " characters")
 
     @attr(type='negative')
-    @unittest.skip("Until Bug 1005397 is fixed")
     def test_create_image_specify_uuid_35_characters_or_less(self):
         """Return an error if Image ID passed is 35 characters or less"""
         try:
             snapshot_name = rand_name('test-snap-')
             test_uuid = ('a' * 35)
-            self.assertRaises(exceptions.BadRequest, self.client.create_image,
+            self.assertRaises(exceptions.NotFound, self.client.create_image,
                               test_uuid, snapshot_name)
         except:
-            self.fail("Should return 400 Bad Request if server uuid is 35"
+            self.fail("Should return 404 Not Found if server uuid is 35"
                       " characters or less")
 
     @attr(type='negative')
-    @unittest.skip("Until Bug 1005397 is fixed")
     def test_create_image_specify_uuid_37_characters_or_more(self):
         """Return an error if Image ID passed is 37 characters or more"""
         try:
             snapshot_name = rand_name('test-snap-')
             test_uuid = ('a' * 37)
-            self.assertRaises(exceptions.BadRequest, self.client.create_image,
+            self.assertRaises(exceptions.NotFound, self.client.create_image,
                               test_uuid, snapshot_name)
         except:
-            self.fail("Should return 400 Bad Request if server uuid is 37"
+            self.fail("Should return 404 Not Found if server uuid is 37"
                       " characters or more")
 
     @attr(type='negative')
diff --git a/tempest/tests/compute/test_list_floating_ips.py b/tempest/tests/compute/test_list_floating_ips.py
index d2d9ba2..ac70b8f 100644
--- a/tempest/tests/compute/test_list_floating_ips.py
+++ b/tempest/tests/compute/test_list_floating_ips.py
@@ -20,14 +20,13 @@
 
 from tempest import exceptions
 from tempest.common.utils.data_utils import rand_name
-from tempest.tests.compute.base import BaseComputeTest
+from tempest.tests.compute import base
 
 
-class FloatingIPDetailsTest(BaseComputeTest):
+class FloatingIPDetailsTestBase(object):
 
-    @classmethod
+    @staticmethod
     def setUpClass(cls):
-        super(FloatingIPDetailsTest, cls).setUpClass()
         cls.client = cls.floating_ips_client
         cls.floating_ip = []
         cls.floating_ip_id = []
@@ -37,11 +36,10 @@
             cls.floating_ip.append(body)
             cls.floating_ip_id.append(body['id'])
 
-    @classmethod
+    @staticmethod
     def tearDownClass(cls):
         for i in range(3):
             cls.client.delete_floating_ip(cls.floating_ip_id[i])
-        super(FloatingIPDetailsTest, cls).tearDownClass()
 
     @attr(type='positive')
     def test_list_floating_ips(self):
@@ -101,3 +99,29 @@
         else:
             self.fail('Should not be able to GET the details from a'
                       'nonexistant floating IP')
+
+
+class FloatingIPDetailsTestJSON(base.BaseComputeTestJSON,
+                            FloatingIPDetailsTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super(FloatingIPDetailsTestJSON, cls).setUpClass()
+        FloatingIPDetailsTestBase.setUpClass(cls)
+
+    @classmethod
+    def tearDownClass(cls):
+        FloatingIPDetailsTestBase.tearDownClass(cls)
+        super(FloatingIPDetailsTestJSON, cls).tearDownClass()
+
+
+class FloatingIPDetailsTestXML(base.BaseComputeTestXML,
+                            FloatingIPDetailsTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super(FloatingIPDetailsTestXML, cls).setUpClass()
+        FloatingIPDetailsTestBase.setUpClass(cls)
+
+    @classmethod
+    def tearDownClass(cls):
+        FloatingIPDetailsTestBase.tearDownClass(cls)
+        super(FloatingIPDetailsTestXML, cls).tearDownClass()
diff --git a/tempest/tests/compute/test_server_actions.py b/tempest/tests/compute/test_server_actions.py
index edcf42b..e0bb47b 100644
--- a/tempest/tests/compute/test_server_actions.py
+++ b/tempest/tests/compute/test_server_actions.py
@@ -185,16 +185,15 @@
                 raise exceptions.TimeoutException(message)
 
     @attr(type='negative')
-    def test_reboot_nonexistant_server_soft(self):
+    def test_reboot_nonexistent_server_soft(self):
         """
-        Negative Test: The server reboot on non existant server should return
+        Negative Test: The server reboot on non existent server should return
         an error
         """
         self.assertRaises(exceptions.NotFound, self.client.reboot, 999, 'SOFT')
 
-    @unittest.skip('Until bug 963248 is fixed.')
     @attr(type='negative')
-    def test_rebuild_nonexistant_server(self):
+    def test_rebuild_nonexistent_server(self):
         """
         Negative test: The server rebuild for a non existing server should not
         be allowed
diff --git a/tempest/tests/compute/test_volumes_get.py b/tempest/tests/compute/test_volumes_get.py
index a7f21cb..fcdec96 100644
--- a/tempest/tests/compute/test_volumes_get.py
+++ b/tempest/tests/compute/test_volumes_get.py
@@ -16,24 +16,18 @@
 #    under the License.
 
 from nose.plugins.attrib import attr
-import unittest2 as unittest
 
 from tempest.common.utils.data_utils import rand_name
-from tempest.tests.compute.base import BaseComputeTest
+from tempest.tests.compute import base
 
 
-class VolumesGetTest(BaseComputeTest):
-
-    @classmethod
-    def setUpClass(cls):
-        super(VolumesGetTest, cls).setUpClass()
-        cls.client = cls.volumes_client
+class VolumesGetTestBase(object):
 
     @attr(type='smoke')
     def test_volume_create_get_delete(self):
         """CREATE, GET, DELETE Volume"""
         try:
-            v_name = rand_name('Volume-')
+            v_name = rand_name('Volume-%s-') % self._interface
             metadata = {'Type': 'work'}
             #Create volume
             resp, volume = self.client.create_volume(size=1,
@@ -64,6 +58,7 @@
                              fetched_volume['metadata'],
                              'The fetched Volume is different '
                              'from the created Volume')
+
         finally:
             #Delete the Volume created in this method
             resp, _ = self.client.delete_volume(volume['id'])
@@ -93,3 +88,21 @@
             #Delete the Volume created in this method
             resp, _ = self.client.delete_volume(volume['id'])
             self.assertEqual(202, resp.status)
+            #Checking if the deleted Volume still exists
+            self.client.wait_for_resource_deletion(volume['id'])
+
+
+class VolumesGetTestXML(base.BaseComputeTestXML, VolumesGetTestBase):
+    @classmethod
+    def setUpClass(cls):
+        cls._interface = "xml"
+        super(VolumesGetTestXML, cls).setUpClass()
+        cls.client = cls.volumes_extensions_client
+
+
+class VolumesGetTestJSON(base.BaseComputeTestJSON, VolumesGetTestBase):
+    @classmethod
+    def setUpClass(cls):
+        cls._interface = "json"
+        super(VolumesGetTestJSON, cls).setUpClass()
+        cls.client = cls.volumes_extensions_client
diff --git a/tempest/tests/compute/test_volumes_list.py b/tempest/tests/compute/test_volumes_list.py
index c3bde15..3f03996 100644
--- a/tempest/tests/compute/test_volumes_list.py
+++ b/tempest/tests/compute/test_volumes_list.py
@@ -15,40 +15,68 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import unittest2 as unittest
-
 import nose
 
 from tempest.common.utils.data_utils import rand_name
-from tempest.tests.compute.base import BaseComputeTest
+from tempest.tests.compute import base
 
 
-class VolumesTest(BaseComputeTest):
+class VolumesTestBase(object):
 
     """
     This test creates a number of 1G volumes. To run successfully,
     ensure that the backing file for the volume group that Nova uses
-    has space for at least 3 1G volumes! Devstack, by default, creates
-    a 2G volume backing file, which causes this test to fail because
-    the third volume gets created in ERROR state (out of disk space in
-    volume group...). If you are running a Devstack environment, set
-    VOLUME_BACKING_FILE_SIZE=4G in your localrc
+    has space for at least 3 1G volumes!
+    If you are running a Devstack environment, ensure that the
+    VOLUME_BACKING_FILE_SIZE is atleast 4G in your localrc
     """
 
+    def test_volume_list(self):
+        """Should return the list of Volumes"""
+        # Fetch all Volumes
+        resp, fetched_list = self.client.list_volumes()
+        self.assertEqual(200, resp.status)
+        # Now check if all the Volumes created in setup are in fetched list
+        missing_volumes = \
+                [v for v in self.volume_list if v not in fetched_list]
+
+        self.assertFalse(missing_volumes,
+                         "Failed to find volume %s in fetched list"
+                         % ', '.join(m_vol['displayName']
+                                        for m_vol in missing_volumes))
+
+    def test_volume_list_with_details(self):
+        """Should return the list of Volumes with details"""
+        #Fetch all Volumes
+        resp, fetched_list = self.client.list_volumes_with_detail()
+        self.assertEqual(200, resp.status)
+        #Now check if all the Volumes created in setup are in fetched list
+        missing_volumes = \
+                [v for v in self.volume_list if v not in fetched_list]
+
+        self.assertFalse(missing_volumes,
+                         "Failed to find volume %s in fetched list"
+                         % ', '.join(m_vol['displayName']
+                                        for m_vol in missing_volumes))
+
+
+class VolumesTestXML(base.BaseComputeTestXML, VolumesTestBase):
     @classmethod
     def setUpClass(cls):
-        super(VolumesTest, cls).setUpClass()
-        cls.client = cls.volumes_client
+        cls._interface = 'xml'
+        super(VolumesTestXML, cls).setUpClass()
+        cls.client = cls.volumes_extensions_client
         # Create 3 Volumes
         cls.volume_list = list()
         cls.volume_id_list = list()
         for i in range(3):
             v_name = rand_name('volume')
             metadata = {'Type': 'work'}
+            v_name += cls._interface
             try:
                 resp, volume = cls.client.create_volume(size=1,
-                                                         display_name=v_name,
-                                                         metadata=metadata)
+                                                        display_name=v_name,
+                                                        metadata=metadata)
                 cls.client.wait_for_volume_status(volume['id'],
                                                    'available')
                 resp, volume = cls.client.get_volume(volume['id'])
@@ -75,30 +103,52 @@
         # Delete the created Volumes
         for volume in cls.volume_list:
             resp, _ = cls.client.delete_volume(volume['id'])
-        super(VolumesTest, cls).tearDownClass()
+            cls.client.wait_for_resource_deletion(volume['id'])
+        super(VolumesTestXML, cls).tearDownClass()
 
-    def test_volume_list(self):
-        """Should return the list of Volumes"""
-        # Fetch all Volumes
-        resp, fetched_list = self.client.list_volumes()
-        self.assertEqual(200, resp.status)
-        # Now check if all the Volumes created in setup are in fetched list
-        missing_volumes =\
-        [v for v in self.volume_list if v not in fetched_list]
-        self.assertFalse(missing_volumes,
-                         "Failed to find volume %s in fetched list"
-                         % ', '.join(m_vol['displayName']
-                                        for m_vol in missing_volumes))
 
-    def test_volume_list_with_details(self):
-        """Should return the list of Volumes with details"""
-        #Fetch all Volumes
-        resp, fetched_list = self.client.list_volumes_with_detail()
-        self.assertEqual(200, resp.status)
-        #Now check if all the Volumes created in setup are in fetched list
-        missing_volumes =\
-        [v for v in self.volume_list if v not in fetched_list]
-        self.assertFalse(missing_volumes,
-                         "Failed to find volume %s in fetched list"
-                         % ', '.join(m_vol['displayName']
-                                        for m_vol in missing_volumes))
+class VolumesTestJSON(base.BaseComputeTestJSON, VolumesTestBase):
+    @classmethod
+    def setUpClass(cls):
+        cls._interface = 'json'
+        super(VolumesTestJSON, cls).setUpClass()
+        cls.client = cls.volumes_extensions_client
+        # Create 3 Volumes
+        cls.volume_list = []
+        cls.volume_id_list = []
+        for i in range(3):
+            v_name = rand_name('volume-%s')
+            metadata = {'Type': 'work'}
+            v_name += cls._interface
+            try:
+                resp, volume = cls.client.create_volume(size=1,
+                                                        display_name=v_name,
+                                                        metadata=metadata)
+                cls.client.wait_for_volume_status(volume['id'],
+                                                   'available')
+                resp, volume = cls.client.get_volume(volume['id'])
+                cls.volume_list.append(volume)
+                cls.volume_id_list.append(volume['id'])
+            except:
+                if cls.volume_list:
+                    # We could not create all the volumes, though we were able
+                    # to create *some* of the volumes. This is typically
+                    # because the backing file size of the volume group is
+                    # too small. So, here, we clean up whatever we did manage
+                    # to create and raise a SkipTest
+                    for volume in cls.volume_list:
+                        cls.client.delete_volume(volume)
+                    msg = ("Failed to create ALL necessary volumes to run "
+                           "test. This typically means that the backing file "
+                           "size of the nova-volumes group is too small to "
+                           "create the 3 volumes needed by this test case")
+                    raise nose.SkipTest(msg)
+                raise
+
+    @classmethod
+    def tearDownClass(cls):
+        # Delete the created Volumes
+        for volume in cls.volume_list:
+            resp, _ = cls.client.delete_volume(volume['id'])
+            cls.client.wait_for_resource_deletion(volume['id'])
+        super(VolumesTestJSON, cls).tearDownClass()
diff --git a/tempest/tests/compute/test_volumes_negative.py b/tempest/tests/compute/test_volumes_negative.py
index 8d67fff..5c93f07 100644
--- a/tempest/tests/compute/test_volumes_negative.py
+++ b/tempest/tests/compute/test_volumes_negative.py
@@ -17,19 +17,13 @@
 
 from nose.plugins.attrib import attr
 from nose.tools import raises
-import unittest2 as unittest
 
 from tempest import exceptions
 from tempest.common.utils.data_utils import rand_name
-from tempest.tests.compute.base import BaseComputeTest
+from tempest.tests.compute import base
 
 
-class VolumesNegativeTest(BaseComputeTest):
-
-    @classmethod
-    def setUpClass(cls):
-        super(VolumesNegativeTest, cls).setUpClass()
-        cls.client = cls.volumes_client
+class VolumesNegativeTestBase(object):
 
     @attr(type='negative')
     def test_volume_get_nonexistant_volume_id(self):
@@ -141,3 +135,21 @@
         Negative: Should not be able to delete volume when empty ID is passed
         """
         resp, volume = self.client.delete_volume('')
+
+
+class VolumesNegativeTestXML(base.BaseComputeTestXML,
+                                VolumesNegativeTestBase):
+    @classmethod
+    def setUpClass(cls):
+        cls._interface = "xml"
+        super(VolumesNegativeTestXML, cls).setUpClass()
+        cls.client = cls.volumes_extensions_client
+
+
+class VolumesNegativeTestJSON(base.BaseComputeTestJSON,
+                                VolumesNegativeTestBase):
+    @classmethod
+    def setUpClass(cls):
+        cls._interface = "json"
+        super(VolumesNegativeTestJSON, cls).setUpClass()
+        cls.client = cls.volumes_extensions_client
diff --git a/tempest/tests/image/test_images.py b/tempest/tests/image/test_images.py
index 0e515dc..c6b903b 100644
--- a/tempest/tests/image/test_images.py
+++ b/tempest/tests/image/test_images.py
@@ -25,8 +25,7 @@
 
 GLANCE_INSTALLED = False
 try:
-    from glance import client
-    from glance.common import exception
+    import glanceclient
     GLANCE_INSTALLED = True
 except ImportError:
     pass
@@ -51,7 +50,7 @@
     @classmethod
     def tearDownClass(cls):
         for image_id in cls.created_images:
-            cls.client.delete_image(image_id)
+            cls.client.images.delete(image_id)
 
     @attr(type='image')
     def test_register_with_invalid_data(self):
@@ -70,11 +69,10 @@
         ]
         for meta in metas:
             try:
-                results = self.client.add_image(meta)
-            except exception.Invalid:
+                self.client.images.create(**meta)
+            except glanceclient.exc.HTTPBadRequest:
                 continue
-            self.fail("Did not raise Invalid for meta %s. Got results: %s" %
-                      (meta, results))
+            self.fail("Did not raise Invalid for meta %s." % meta)
 
     @attr(type='image')
     def test_register_then_upload(self):
@@ -86,27 +84,27 @@
             'container_format': 'bare',
             'properties': {'prop1': 'val1'}
         }
-        results = self.client.add_image(meta)
-        self.assertTrue('id' in results)
-        image_id = results['id']
+        results = self.client.images.create(**meta)
+        self.assertTrue(hasattr(results, 'id'))
+        image_id = results.id
         self.created_images.append(image_id)
-        self.assertTrue('name' in results)
-        self.assertEqual(meta['name'], results['name'])
-        self.assertTrue('is_public' in results)
-        self.assertEqual(meta['is_public'], results['is_public'])
-        self.assertTrue('status' in results)
-        self.assertEqual('queued', results['status'])
-        self.assertTrue('properties' in results)
+        self.assertTrue(hasattr(results, 'name'))
+        self.assertEqual(meta['name'], results.name)
+        self.assertTrue(hasattr(results, 'is_public'))
+        self.assertEqual(meta['is_public'], results.is_public)
+        self.assertTrue(hasattr(results, 'status'))
+        self.assertEqual('queued', results.status)
+        self.assertTrue(hasattr(results, 'properties'))
         for key, val in meta['properties'].items():
-            self.assertEqual(val, results['properties'][key])
+            self.assertEqual(val, results.properties[key])
 
         # Now try uploading an image file
         image_file = StringIO.StringIO('*' * 1024)
-        results = self.client.update_image(image_id, image_data=image_file)
-        self.assertTrue('status' in results)
-        self.assertEqual('active', results['status'])
-        self.assertTrue('size' in results)
-        self.assertEqual(1024, results['size'])
+        results = self.client.images.update(image_id, data=image_file)
+        self.assertTrue(hasattr(results, 'status'))
+        self.assertEqual('active', results.status)
+        self.assertTrue(hasattr(results, 'size'))
+        self.assertEqual(1024, results.size)
 
     @attr(type='image')
     def test_register_remote_image(self):
@@ -118,16 +116,16 @@
             'container_format': 'bare',
             'location': 'http://example.com/someimage.iso'
         }
-        results = self.client.add_image(meta)
-        self.assertTrue('id' in results)
-        image_id = results['id']
+        results = self.client.images.create(**meta)
+        self.assertTrue(hasattr(results, 'id'))
+        image_id = results.id
         self.created_images.append(image_id)
-        self.assertTrue('name' in results)
-        self.assertEqual(meta['name'], results['name'])
-        self.assertTrue('is_public' in results)
-        self.assertEqual(meta['is_public'], results['is_public'])
-        self.assertTrue('status' in results)
-        self.assertEqual('active', results['status'])
+        self.assertTrue(hasattr(results, 'name'))
+        self.assertEqual(meta['name'], results.name)
+        self.assertTrue(hasattr(results, 'is_public'))
+        self.assertEqual(meta['is_public'], results.is_public)
+        self.assertTrue(hasattr(results, 'status'))
+        self.assertEqual('active', results.status)
 
 
 class ListImagesTest(unittest.TestCase):
@@ -143,7 +141,7 @@
         cls.os = openstack.ServiceManager()
         cls.client = cls.os.images.get_client()
         cls.created_images = []
-        cls.original_images = cls.client.get_images()
+        cls.original_images = list(cls.client.images.list())
 
         # We add a few images here to test the listing functionality of
         # the images API
@@ -157,7 +155,7 @@
     @classmethod
     def tearDownClass(cls):
         for image_id in cls.created_images:
-            cls.client.delete_image(image_id)
+            cls.client.images.delete(image_id)
 
     @classmethod
     def _create_remote_image(cls, x):
@@ -172,8 +170,8 @@
             'container_format': 'bare',
             'location': 'http://example.com/someimage_%s.iso' % x
         }
-        results = cls.client.add_image(meta)
-        image_id = results['id']
+        results = cls.client.images.create(**meta)
+        image_id = results.id
         return image_id
 
     @classmethod
@@ -183,15 +181,16 @@
         image. Note that the size of the new image is a random number between
         1024 and 4096
         """
+        image_file = StringIO.StringIO('*' * random.randint(1024, 4096))
         meta = {
             'name': 'New Standard Image %s' % x,
             'is_public': True,
             'disk_format': 'raw',
-            'container_format': 'bare'
+            'container_format': 'bare',
+            'data': image_file,
         }
-        image_file = StringIO.StringIO('*' * random.randint(1024, 4096))
-        results = cls.client.add_image(meta, image_file)
-        image_id = results['id']
+        results = cls.client.images.create(**meta)
+        image_id = results.id
         return image_id
 
     @attr(type='image')
@@ -199,5 +198,5 @@
         """
         Simple test to see all fixture images returned
         """
-        images = self.client.get_images()
+        images = list(self.client.images.list())
         self.assertEqual(10, len(images) - len(self.original_images))
diff --git a/tempest/tests/volume/__init__.py b/tempest/tests/volume/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/volume/__init__.py
diff --git a/tempest/tests/volume/base.py b/tempest/tests/volume/base.py
new file mode 100644
index 0000000..4862404
--- /dev/null
+++ b/tempest/tests/volume/base.py
@@ -0,0 +1,163 @@
+# 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 logging
+import time
+import nose
+
+import unittest2 as unittest
+
+from tempest import config
+from tempest import openstack
+from tempest.common.utils.data_utils import rand_name
+from tempest.services.identity.json.admin_client import AdminClient
+from tempest import exceptions
+
+LOG = logging.getLogger(__name__)
+
+
+class BaseVolumeTest(unittest.TestCase):
+
+    """Base test case class for all Cinder API tests"""
+
+    @classmethod
+    def setUpClass(cls):
+        cls.config = config.TempestConfig()
+        cls.isolated_creds = []
+
+        if cls.config.compute.allow_tenant_isolation:
+            creds = cls._get_isolated_creds()
+            username, tenant_name, password = creds
+            os = openstack.Manager(username=username,
+                                   password=password,
+                                   tenant_name=tenant_name)
+        else:
+            os = openstack.Manager()
+
+        cls.os = os
+        cls.volumes_client = os.volumes_client
+        cls.build_interval = cls.config.volume.build_interval
+        cls.build_timeout = cls.config.volume.build_timeout
+        cls.volumes = {}
+
+        skip_msg = ("%s skipped as Cinder endpoint is not available" %
+                                                            cls.__name__)
+        try:
+            cls.volumes_client.keystone_auth(cls.os.username,
+                                             cls.os.password,
+                                             cls.os.auth_url,
+                                             cls.volumes_client.service,
+                                             cls.os.tenant_name)
+        except exceptions.EndpointNotFound:
+            raise nose.SkipTest(skip_msg)
+
+    @classmethod
+    def _get_identity_admin_client(cls):
+        """
+        Returns an instance of the Identity Admin API client
+        """
+        client_args = (cls.config,
+                       cls.config.identity_admin.username,
+                       cls.config.identity_admin.password,
+                       cls.config.identity.auth_url)
+        tenant_name = cls.config.identity_admin.tenant_name
+        admin_client = AdminClient(*client_args, tenant_name=tenant_name)
+        return admin_client
+
+    @classmethod
+    def _get_isolated_creds(cls):
+        """
+        Creates a new set of user/tenant/password credentials for a
+        **regular** user of the Volume API so that a test case can
+        operate in an isolated tenant container.
+        """
+        admin_client = cls._get_identity_admin_client()
+        rand_name_root = cls.__name__
+        if cls.isolated_creds:
+            # Main user already created. Create the alt one...
+            rand_name_root += '-alt'
+        username = rand_name_root + "-user"
+        email = rand_name_root + "@example.com"
+        tenant_name = rand_name_root + "-tenant"
+        tenant_desc = tenant_name + "-desc"
+        password = "pass"
+
+        resp, tenant = admin_client.create_tenant(name=tenant_name,
+                                                  description=tenant_desc)
+        resp, user = admin_client.create_user(username,
+                                              password,
+                                              tenant['id'],
+                                              email)
+        # Store the complete creds (including UUID ids...) for later
+        # but return just the username, tenant_name, password tuple
+        # that the various clients will use.
+        cls.isolated_creds.append((user, tenant))
+
+        return username, tenant_name, password
+
+    @classmethod
+    def clear_isolated_creds(cls):
+        if not cls.isolated_creds:
+            pass
+        admin_client = cls._get_identity_admin_client()
+
+        for user, tenant in cls.isolated_creds:
+            admin_client.delete_user(user['id'])
+            admin_client.delete_tenant(tenant['id'])
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.clear_isolated_creds()
+
+    def create_volume(self, size=1, metadata={}):
+        """Wrapper utility that returns a test volume"""
+        display_name = rand_name(self.__class__.__name__ + "-volume")
+        resp, volume = self.volumes_client.create_volume(size=size,
+                                                    display_name=display_name,
+                                                    metdata=metadata)
+        self.volumes_client.wait_for_volume_status(volume['id'], 'available')
+        self.volumes.append(volume)
+        return volume
+
+    def wait_for(self, condition):
+        """Repeatedly calls condition() until a timeout"""
+        start_time = int(time.time())
+        while True:
+            try:
+                condition()
+            except:
+                pass
+            else:
+                return
+            if int(time.time()) - start_time >= self.build_timeout:
+                condition()
+                return
+            time.sleep(self.build_interval)
+
+
+class BaseVolumeTestJSON(BaseVolumeTest):
+    @classmethod
+    def setUpClass(cls):
+        cls._interface = "json"
+        super(BaseVolumeTestJSON, cls).setUpClass()
+
+
+class BaseVolumeTestXML(BaseVolumeTest):
+    @classmethod
+    def setUpClass(cls):
+        cls._interface = "xml"
+        super(BaseVolumeTestXML, cls).setUpClass()
diff --git a/tempest/tests/volume/test_volumes_get.py b/tempest/tests/volume/test_volumes_get.py
new file mode 100644
index 0000000..03ecb5a
--- /dev/null
+++ b/tempest/tests/volume/test_volumes_get.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.
+
+from nose.plugins.attrib import attr
+
+from tempest.common.utils.data_utils import rand_name
+from tempest.tests.volume import base
+
+
+class VolumesGetTestBase(object):
+
+    @attr(type='smoke')
+    def test_volume_create_get_delete(self):
+        """Create a volume, Get it's details and Delete the volume"""
+        try:
+            volume = {}
+            v_name = rand_name('Volume-')
+            metadata = {'Type': 'work'}
+            #Create a volume
+            resp, volume = self.client.create_volume(size=1,
+                                                     display_name=v_name,
+                                                     metadata=metadata)
+            self.assertEqual(200, resp.status)
+            self.assertTrue('id' in volume)
+            self.assertTrue('display_name' in volume)
+            self.assertEqual(volume['display_name'], v_name,
+            "The created volume name is not equal to the requested name")
+            self.assertTrue(volume['id'] is not None,
+            "Field volume id is empty or not found.")
+            self.client.wait_for_volume_status(volume['id'], 'available')
+            # Get Volume information
+            resp, fetched_volume = self.client.get_volume(volume['id'])
+            self.assertEqual(200, resp.status)
+            self.assertEqual(v_name,
+                             fetched_volume['display_name'],
+                             'The fetched Volume is different '
+                             'from the created Volume')
+            self.assertEqual(volume['id'],
+                             fetched_volume['id'],
+                             'The fetched Volume is different '
+                             'from the created Volume')
+            self.assertEqual(metadata,
+                             fetched_volume['metadata'],
+                             'The fetched Volume is different '
+                             'from the created Volume')
+        except:
+            self.fail("Could not create a volume")
+        finally:
+            if volume:
+                # Delete the Volume if it was created
+                resp, _ = self.client.delete_volume(volume['id'])
+                self.assertEqual(202, resp.status)
+                self.client.wait_for_resource_deletion(volume['id'])
+
+    @attr(type='positive')
+    def test_volume_get_metadata_none(self):
+        """Create a volume without passing metadata, get details, and delete"""
+        try:
+            volume = {}
+            v_name = rand_name('Volume-')
+            # Create a volume without metadata
+            resp, volume = self.client.create_volume(size=1,
+                                                     display_name=v_name,
+                                                     metadata={})
+            self.assertEqual(200, resp.status)
+            self.assertTrue('id' in volume)
+            self.assertTrue('display_name' in volume)
+            self.client.wait_for_volume_status(volume['id'], 'available')
+            #GET Volume
+            resp, fetched_volume = self.client.get_volume(volume['id'])
+            self.assertEqual(200, resp.status)
+            self.assertEqual(fetched_volume['metadata'], {})
+        except:
+            self.fail("Could not get volume metadata")
+        finally:
+            if volume:
+                # Delete the Volume if it was created
+                resp, _ = self.client.delete_volume(volume['id'])
+                self.assertEqual(202, resp.status)
+                self.client.wait_for_resource_deletion(volume['id'])
+
+
+class VolumesGetTestXML(base.BaseVolumeTestXML, VolumesGetTestBase):
+    @classmethod
+    def setUpClass(cls):
+        cls._interface = "xml"
+        super(VolumesGetTestXML, cls).setUpClass()
+        cls.client = cls.volumes_client
+
+
+class VolumesGetTestJSON(base.BaseVolumeTestJSON, VolumesGetTestBase):
+    @classmethod
+    def setUpClass(cls):
+        cls._interface = "json"
+        super(VolumesGetTestJSON, cls).setUpClass()
+        cls.client = cls.volumes_client
diff --git a/tempest/tests/volume/test_volumes_list.py b/tempest/tests/volume/test_volumes_list.py
new file mode 100644
index 0000000..6c6dac2
--- /dev/null
+++ b/tempest/tests/volume/test_volumes_list.py
@@ -0,0 +1,153 @@
+# 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
+from nose.plugins.attrib import attr
+
+from tempest.common.utils.data_utils import rand_name
+from tempest.tests.volume import base
+
+
+class VolumesListTestBase(object):
+
+    """
+    This test creates a number of 1G volumes. To run successfully,
+    ensure that the backing file for the volume group that Nova uses
+    has space for at least 3 1G volumes!
+    If you are running a Devstack environment, ensure that the
+    VOLUME_BACKING_FILE_SIZE is atleast 4G in your localrc
+    """
+
+    @attr(type='smoke')
+    def test_volume_list(self):
+        """Get a list of Volumes"""
+        # Fetch all volumes
+        resp, fetched_list = self.client.list_volumes()
+        self.assertEqual(200, resp.status)
+        # Now check if all the volumes created in setup are in fetched list
+        missing_vols = [v for v in self.volume_list if v not in fetched_list]
+        self.assertFalse(missing_vols,
+                         "Failed to find volume %s in fetched list"
+                         % ', '.join(m_vol['display_name']
+                                        for m_vol in missing_vols))
+
+    @attr(type='smoke')
+    def test_volume_list_with_details(self):
+        """Get a list of Volumes with details"""
+        # Fetch all Volumes
+        resp, fetched_list = self.client.list_volumes_with_detail()
+        self.assertEqual(200, resp.status)
+        # Verify that all the volumes are returned
+        missing_vols = [v for v in self.volume_list if v not in fetched_list]
+        self.assertFalse(missing_vols,
+                         "Failed to find volume %s in fetched list"
+                         % ', '.join(m_vol['display_name']
+                                        for m_vol in missing_vols))
+
+
+class VolumeListTestXML(base.BaseVolumeTestXML, VolumesListTestBase):
+    @classmethod
+    def setUpClass(cls):
+        cls._interface = 'xml'
+        super(VolumeListTestXML, cls).setUpClass()
+        cls.client = cls.volumes_client
+
+        # Create 3 test volumes
+        cls.volume_list = []
+        cls.volume_id_list = []
+        for i in range(3):
+            v_name = rand_name('volume')
+            metadata = {'Type': 'work'}
+            try:
+                resp, volume = cls.client.create_volume(size=1,
+                                                        display_name=v_name,
+                                                        metadata=metadata)
+                cls.client.wait_for_volume_status(volume['id'],
+                                                   'available')
+                resp, volume = cls.client.get_volume(volume['id'])
+                cls.volume_list.append(volume)
+                cls.volume_id_list.append(volume['id'])
+            except:
+                if cls.volume_list:
+                    # We could not create all the volumes, though we were able
+                    # to create *some* of the volumes. This is typically
+                    # because the backing file size of the volume group is
+                    # too small. So, here, we clean up whatever we did manage
+                    # to create and raise a SkipTest
+                    for volume in cls.volume_id_list:
+                        cls.client.delete_volume(volume)
+                    msg = ("Failed to create ALL necessary volumes to run "
+                           "test. This typically means that the backing file "
+                           "size of the nova-volumes group is too small to "
+                           "create the 3 volumes needed by this test case")
+                    raise nose.SkipTest(msg)
+                raise
+
+    @classmethod
+    def tearDownClass(cls):
+        # Delete the created volumes
+        for volume in cls.volume_id_list:
+            resp, _ = cls.client.delete_volume(volume)
+            cls.client.wait_for_resource_deletion(volume)
+        super(VolumeListTestXML, cls).tearDownClass()
+
+
+class VolumeListTestJSON(base.BaseVolumeTestJSON, VolumesListTestBase):
+    @classmethod
+    def setUpClass(cls):
+        cls._interface = 'json'
+        super(VolumeListTestJSON, cls).setUpClass()
+        cls.client = cls.volumes_client
+
+        # Create 3 test volumes
+        cls.volume_list = []
+        cls.volume_id_list = []
+        for i in range(3):
+            v_name = rand_name('volume')
+            metadata = {'Type': 'work'}
+            try:
+                resp, volume = cls.client.create_volume(size=1,
+                                                        display_name=v_name,
+                                                        metadata=metadata)
+                cls.client.wait_for_volume_status(volume['id'],
+                                                   'available')
+                resp, volume = cls.client.get_volume(volume['id'])
+                cls.volume_list.append(volume)
+                cls.volume_id_list.append(volume['id'])
+            except:
+                if cls.volume_list:
+                    # We could not create all the volumes, though we were able
+                    # to create *some* of the volumes. This is typically
+                    # because the backing file size of the volume group is
+                    # too small. So, here, we clean up whatever we did manage
+                    # to create and raise a SkipTest
+                    for volume in cls.volume_id_list:
+                        cls.client.delete_volume(volume)
+                    msg = ("Failed to create ALL necessary volumes to run "
+                           "test. This typically means that the backing file "
+                           "size of the nova-volumes group is too small to "
+                           "create the 3 volumes needed by this test case")
+                    raise nose.SkipTest(msg)
+                raise
+
+    @classmethod
+    def tearDownClass(cls):
+        # Delete the created volumes
+        for volume in cls.volume_id_list:
+            resp, _ = cls.client.delete_volume(volume)
+            cls.client.wait_for_resource_deletion(volume)
+        super(VolumeListTestJSON, cls).tearDownClass()
diff --git a/tempest/tests/volume/test_volumes_negative.py b/tempest/tests/volume/test_volumes_negative.py
new file mode 100644
index 0000000..bf7e5f0
--- /dev/null
+++ b/tempest/tests/volume/test_volumes_negative.py
@@ -0,0 +1,143 @@
+# 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.
+
+from nose.plugins.attrib import attr
+from nose.tools import raises
+
+from tempest import exceptions
+from tempest.common.utils.data_utils import rand_name
+from tempest.tests.volume import base
+
+
+class VolumesNegativeTestBase(object):
+
+    @raises(exceptions.NotFound)
+    @attr(type='negative')
+    def test_volume_get_nonexistant_volume_id(self):
+        """Should not be able to get a nonexistant volume"""
+        #Creating a nonexistant volume id
+        volume_id_list = []
+        resp, volumes = self.client.list_volumes()
+        for i in range(len(volumes)):
+            volume_id_list.append(volumes[i]['id'])
+        while True:
+            non_exist_id = rand_name('999')
+            if non_exist_id not in volume_id_list:
+                break
+        #Trying to Get a non existant volume
+        resp, volume = self.client.get_volume(non_exist_id)
+
+    @raises(exceptions.NotFound)
+    @attr(type='negative')
+    def test_volume_delete_nonexistant_volume_id(self):
+        """Should not be able to delete a nonexistant Volume"""
+        # Creating nonexistant volume id
+        volume_id_list = []
+        resp, volumes = self.client.list_volumes()
+        for i in range(len(volumes)):
+            volume_id_list.append(volumes[i]['id'])
+        while True:
+            non_exist_id = '12345678-abcd-4321-abcd-123456789098'
+            if non_exist_id not in volume_id_list:
+                break
+        # Try to Delete a non existant volume
+        resp, body = self.client.delete_volume(non_exist_id)
+
+    @raises(exceptions.BadRequest)
+    @attr(type='negative')
+    def test_create_volume_with_invalid_size(self):
+        """
+        Should not be able to create volume with invalid size
+        in request
+        """
+        v_name = rand_name('Volume-')
+        metadata = {'Type': 'work'}
+        resp, volume = self.client.create_volume(size='#$%',
+                                                 display_name=v_name,
+                                                 metadata=metadata)
+
+    @raises(exceptions.BadRequest)
+    @attr(type='negative')
+    def test_create_volume_with_out_passing_size(self):
+        """
+        Should not be able to create volume without passing size
+        in request
+        """
+        v_name = rand_name('Volume-')
+        metadata = {'Type': 'work'}
+        resp, volume = self.client.create_volume(size='',
+                                                 display_name=v_name,
+                                                 metadata=metadata)
+
+    @raises(exceptions.BadRequest)
+    @attr(type='negative')
+    def test_create_volume_with_size_zero(self):
+        """
+        Should not be able to create volume with size zero
+        """
+        v_name = rand_name('Volume-')
+        metadata = {'Type': 'work'}
+        resp, volume = self.client.create_volume(size='0',
+                                                 display_name=v_name,
+                                                 metadata=metadata)
+
+    @raises(exceptions.NotFound)
+    @attr(type='negative')
+    def test_get_invalid_volume_id(self):
+        """
+        Should not be able to get volume with invalid id
+        """
+        resp, volume = self.client.get_volume('#$%%&^&^')
+
+    @raises(exceptions.NotFound)
+    @attr(type='negative')
+    def test_get_volume_without_passing_volume_id(self):
+        """
+        Should not be able to get volume when empty ID is passed
+        """
+        resp, volume = self.client.get_volume('')
+
+    @raises(exceptions.NotFound)
+    @attr(type='negative')
+    def test_delete_invalid_volume_id(self):
+        """
+        Should not be able to delete volume when invalid ID is passed
+        """
+        resp, volume = self.client.delete_volume('!@#$%^&*()')
+
+    @raises(exceptions.NotFound)
+    @attr(type='negative')
+    def test_delete_volume_without_passing_volume_id(self):
+        """
+        Should not be able to delete volume when empty ID is passed
+        """
+        resp, volume = self.client.delete_volume('')
+
+
+class VolumesNegativeTestXML(base.BaseVolumeTestXML, VolumesNegativeTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super(VolumesNegativeTestXML, cls).setUpClass()
+        cls.client = cls.volumes_client
+
+
+class VolumesNegativeTestJSON(base.BaseVolumeTestJSON,
+                              VolumesNegativeTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super(VolumesNegativeTestJSON, cls).setUpClass()
+        cls.client = cls.volumes_client
diff --git a/tools/pip-requires b/tools/pip-requires
index 2e1dc96..d3f9db7 100644
--- a/tools/pip-requires
+++ b/tools/pip-requires
@@ -3,3 +3,4 @@
 httplib2>=0.7.0
 pika
 unittest2
+lxml