Merge "Clean up pep8 E502 violations"
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index d2babcb..32dd124 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -132,7 +132,13 @@
 
             mgmt_url = None
             for ep in auth_data['serviceCatalog']:
-                if ep["type"] == service:
+                if ep["type"] == service and service != 'volume':
+                    mgmt_url = ep['endpoints'][self.region][self.endpoint_url]
+                    tenant_id = auth_data['token']['tenant']['id']
+                    break
+
+                elif ep["type"] == service and ep['name'] == 'cinder' \
+                    and service == 'volume':
                     mgmt_url = ep['endpoints'][self.region][self.endpoint_url]
                     tenant_id = auth_data['token']['tenant']['id']
                     break
diff --git a/tempest/common/ssh.py b/tempest/common/ssh.py
index 085fce3..faf182a 100644
--- a/tempest/common/ssh.py
+++ b/tempest/common/ssh.py
@@ -18,6 +18,7 @@
 import time
 import socket
 import warnings
+import select
 
 from tempest import exceptions
 
@@ -37,7 +38,8 @@
         self.look_for_keys = look_for_keys
         self.key_filename = key_filename
         self.timeout = int(timeout)
-        self.channel_timeout = int(channel_timeout)
+        self.channel_timeout = float(channel_timeout)
+        self.buf_size = 1024
 
     def _get_ssh_connection(self):
         """Returns an ssh connection to the specified host"""
@@ -85,24 +87,48 @@
             return
 
     def exec_command(self, cmd):
-        """Execute the specified command on the server.
+        """
+        Execute the specified command on the server.
 
-        :returns: data read from standard output of the command
+        Note that this method is reading whole command outputs to memory, thus
+        shouldn't be used for large outputs.
 
+        :returns: data read from standard output of the command.
+        :raises: SSHExecCommandFailed if command returns nonzero
+                 status. The exception contains command status stderr content.
         """
         ssh = self._get_ssh_connection()
-        stdin, stdout, stderr = ssh.exec_command(cmd)
-        stdin.flush()
-        stdin.channel.shutdown_write()
-        stdout.channel.settimeout(self.channel_timeout)
-        status = stdout.channel.recv_exit_status()
-        try:
-            output = stdout.read()
-        except socket.timeout:
-            if status == 0:
-                return None, status
-        ssh.close()
-        return status, output
+        transport = ssh.get_transport()
+        channel = transport.open_session()
+        channel.exec_command(cmd)
+        channel.shutdown_write()
+        out_data = []
+        err_data = []
+
+        select_params = [channel], [], [], self.channel_timeout
+        while True:
+            ready = select.select(*select_params)
+            if not any(ready):
+                raise exceptions.TimeoutException(
+                        "Command: '{0}' executed on host '{1}'.".format(
+                            cmd, self.host))
+            if not ready[0]:        # If there is nothing to read.
+                continue
+            out_chunk = err_chunk = None
+            if channel.recv_ready():
+                out_chunk = channel.recv(self.buf_size)
+                out_data += out_chunk,
+            if channel.recv_stderr_ready():
+                err_chunk = channel.recv_stderr(self.buf_size)
+                err_data += err_chunk,
+            if channel.closed and not err_chunk and not out_chunk:
+                break
+        exit_status = channel.recv_exit_status()
+        if 0 != exit_status:
+            raise exceptions.SSHExecCommandFailed(
+                    command=cmd, exit_status=exit_status,
+                    strerror=''.join(err_data))
+        return ''.join(out_data)
 
     def test_connection_auth(self):
         """ Returns true if ssh can connect to server"""
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 03cf163..7154b80 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -91,7 +91,13 @@
 
 class SSHTimeout(TempestException):
     message = ("Connection to the %(host)s via SSH timed out.\n"
-                "User: %(user)s, Password: %(password)s")
+               "User: %(user)s, Password: %(password)s")
+
+
+class SSHExecCommandFailed(TempestException):
+    ''' Raised when remotely executed command returns nonzero status.  '''
+    message = ("Command '%(command)s', exit status: %(exit_status)d, "
+               "Error:\n%(strerror)s")
 
 
 class ServerUnreachable(TempestException):
diff --git a/tempest/manager.py b/tempest/manager.py
index 7a5d6f5..bab7ea7 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -49,7 +49,7 @@
 FloatingIPsClient = floating_ips_client.FloatingIPsClientJSON
 KeyPairsClient = keypairs_client.KeyPairsClientJSON
 VolumesExtensionsClient = volumes_extensions_client.VolumesExtensionsClientJSON
-VolumesClient = volumes_client.VolumesClient
+VolumesClient = volumes_client.VolumesClientJSON
 ConsoleOutputsClient = console_output_client.ConsoleOutputsClient
 
 LOG = logging.getLogger(__name__)
diff --git a/tempest/openstack.py b/tempest/openstack.py
index ec0c257..d9abb64 100644
--- a/tempest/openstack.py
+++ b/tempest/openstack.py
@@ -19,6 +19,10 @@
 
 from tempest import config
 from tempest import exceptions
+from tempest.services.identity.json.admin_client import AdminClientJSON
+from tempest.services.identity.json.admin_client import TokenClientJSON
+from tempest.services.identity.xml.admin_client import AdminClientXML
+from tempest.services.identity.xml.admin_client import TokenClientXML
 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
@@ -45,7 +49,9 @@
 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 VolumesClient
+from tempest.services.volume.json.volumes_client import VolumesClientJSON
+from tempest.services.volume.xml.volumes_client import VolumesClientXML
+
 
 LOG = logging.getLogger(__name__)
 
@@ -89,6 +95,22 @@
     "xml": FloatingIPsClientXML,
 }
 
+VOLUMES_CLIENTS = {
+    "json": VolumesClientJSON,
+    "xml": VolumesClientXML,
+}
+
+
+ADMIN_CLIENT = {
+    "json": AdminClientJSON,
+    "xml": AdminClientXML,
+}
+
+TOKEN_CLIENT = {
+    "json": TokenClientJSON,
+    "xml": TokenClientXML,
+}
+
 
 class Manager(object):
 
@@ -141,13 +163,15 @@
             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)
+            self.admin_client = ADMIN_CLIENT[interface](*client_args)
+            self.token_client = TOKEN_CLIENT[interface](self.config)
         except KeyError:
             msg = "Unsupported interface type `%s'" % interface
             raise exceptions.InvalidConfiguration(msg)
         self.security_groups_client = SecurityGroupsClient(*client_args)
         self.console_outputs_client = ConsoleOutputsClient(*client_args)
         self.network_client = NetworkClient(*client_args)
-        self.volumes_client = VolumesClient(*client_args)
 
 
 class AltManager(Manager):
@@ -190,3 +214,33 @@
         self.services = {}
         self.services['image'] = image_service.Service(self.config)
         self.images = self.services['image']
+
+
+class IdentityManager(Manager):
+
+    """
+    Manager object that uses the alt_XXX credentials for its
+    managed client objects
+    """
+
+    def __init__(self, interface='json'):
+        conf = config.TempestConfig()
+        super(IdentityManager, self).__init__(conf.identity_admin.username,
+                                         conf.identity_admin.password,
+                                         conf.identity_admin.tenant_name,
+                                         interface)
+
+
+class IdentityNaManager(Manager):
+
+    """
+    Manager object that uses the alt_XXX credentials for its
+    managed client objects
+    """
+
+    def __init__(self, interface='json'):
+        conf = config.TempestConfig()
+        super(IdentityNaManager, self).__init__(conf.compute.username,
+                                         conf.compute.password,
+                                         conf.compute.tenant_name,
+                                         interface)
diff --git a/tempest/services/identity/json/admin_client.py b/tempest/services/identity/json/admin_client.py
index cb9c10b..a42a690 100644
--- a/tempest/services/identity/json/admin_client.py
+++ b/tempest/services/identity/json/admin_client.py
@@ -4,10 +4,10 @@
 import json
 
 
-class AdminClient(RestClient):
+class AdminClientJSON(RestClient):
 
     def __init__(self, config, username, password, auth_url, tenant_name=None):
-        super(AdminClient, self).__init__(config, username, password,
+        super(AdminClientJSON, self).__init__(config, username, password,
                                                     auth_url, tenant_name)
         self.service = self.config.identity.catalog_type
         self.endpoint_url = 'adminURL'
@@ -204,7 +204,7 @@
         return self.delete(url)
 
 
-class TokenClient(RestClient):
+class TokenClientJSON(RestClient):
 
     def __init__(self, config):
         self.auth_url = config.identity.auth_url
diff --git a/tempest/services/identity/xml/__init__.py b/tempest/services/identity/xml/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/identity/xml/__init__.py
diff --git a/tempest/services/identity/xml/admin_client.py b/tempest/services/identity/xml/admin_client.py
new file mode 100644
index 0000000..948c643
--- /dev/null
+++ b/tempest/services/identity/xml/admin_client.py
@@ -0,0 +1,267 @@
+# 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 logging
+from lxml import etree
+from tempest.common.rest_client import RestClient
+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 import exceptions
+import httplib2
+import json
+
+XMLNS = "http://docs.openstack.org/identity/api/v2.0"
+
+
+class AdminClientXML(RestClientXML):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(AdminClientXML, self).__init__(config, username, password,
+                                                    auth_url, tenant_name)
+        self.service = self.config.identity.catalog_type
+        self.endpoint_url = 'adminURL'
+
+    def _parse_array(self, node):
+        array = []
+        for child in node.getchildren():
+            array.append(xml_to_json(child))
+        return array
+
+    def _parse_body(self, body):
+        json = xml_to_json(body)
+        return json
+
+    def has_admin_extensions(self):
+        """
+        Returns True if the KSADM Admin Extensions are supported
+        False otherwise
+        """
+        if hasattr(self, '_has_admin_extensions'):
+            return self._has_admin_extensions
+        resp, body = self.list_roles()
+        self._has_admin_extensions = ('status' in resp and resp.status != 503)
+        return self._has_admin_extensions
+
+    def create_role(self, name):
+        """Create a role"""
+        create_role = Element("role",
+                                xmlns=XMLNS,
+                                name=name)
+        resp, body = self.post('OS-KSADM/roles', str(Document(create_role)),
+                                      self.headers)
+        body = self._parse_body(etree.fromstring(body))
+        return resp, body
+
+    def create_tenant(self, name, **kwargs):
+        """
+        Create a tenant
+        name (required): New tenant name
+        description: Description of new tenant (default is none)
+        enabled <true|false>: Initial tenant status (default is true)
+        """
+        en = kwargs.get('enabled', 'true')
+        create_tenant = Element("tenant",
+                                xmlns=XMLNS,
+                                name=name,
+                                description=kwargs.get('description', ''),
+                                enabled=str(en).lower())
+        resp, body = self.post('tenants', str(Document(create_tenant)),
+                                      self.headers)
+        body = self._parse_body(etree.fromstring(body))
+        return resp, body
+
+    def delete_role(self, role_id):
+        """Delete a role"""
+        resp, body = self.delete('OS-KSADM/roles/%s' % str(role_id),
+                                 self.headers)
+        return resp, body
+
+    def list_user_roles(self, tenant_id, user_id):
+        """Returns a list of roles assigned to a user for a tenant"""
+        url = '/tenants/%s/users/%s/roles' % (tenant_id, user_id)
+        resp, body = self.get(url, self.headers)
+        body = self._parse_array(etree.fromstring(body))
+        return resp, body
+
+    def assign_user_role(self, tenant_id, user_id, role_id):
+        """Add roles to a user on a tenant"""
+        resp, body = self.put('/tenants/%s/users/%s/roles/OS-KSADM/%s'
+                                % (tenant_id, user_id, role_id),
+                                '', self.headers)
+        body = self._parse_body(etree.fromstring(body))
+        return resp, body
+
+    def remove_user_role(self, tenant_id, user_id, role_id):
+        """Removes a role assignment for a user on a tenant"""
+        return self.delete('/tenants/%s/users/%s/roles/OS-KSADM/%s'
+               % (tenant_id, user_id, role_id), self.headers)
+
+    def delete_tenant(self, tenant_id):
+        """Delete a tenant"""
+        resp, body = self.delete('tenants/%s' % str(tenant_id), self.headers)
+        return resp, body
+
+    def get_tenant(self, tenant_id):
+        """Get tenant details"""
+        resp, body = self.get('tenants/%s' % str(tenant_id), self.headers)
+        body = self._parse_body(etree.fromstring(body))
+        return resp, body
+
+    def list_roles(self):
+        """Returns roles"""
+        resp, body = self.get('OS-KSADM/roles', self.headers)
+        body = self._parse_array(etree.fromstring(body))
+        return resp, body
+
+    def list_tenants(self):
+        """Returns tenants"""
+        resp, body = self.get('tenants', self.headers)
+        body = self._parse_array(etree.fromstring(body))
+        return resp, body
+
+    def update_tenant(self, tenant_id, **kwargs):
+        """Updates a tenant"""
+        resp, body = self.get_tenant(tenant_id)
+        name = kwargs.get('name', body['name'])
+        desc = kwargs.get('description', body['description'])
+        en = kwargs.get('enabled', body['enabled'])
+        update_tenant = Element("tenant",
+                                xmlns=XMLNS,
+                                id=tenant_id,
+                                name=name,
+                                description=desc,
+                                enabled=str(en).lower())
+
+        resp, body = self.post('tenants/%s' % tenant_id,
+                               str(Document(update_tenant)),
+                               self.headers)
+        body = self._parse_body(etree.fromstring(body))
+        return resp, body
+
+    def create_user(self, name, password, tenant_id, email):
+        """Create a user"""
+        create_user = Element("user",
+                                xmlns=XMLNS,
+                                name=name,
+                                password=password,
+                                tenantId=tenant_id,
+                                email=email)
+        resp, body = self.post('users', str(Document(create_user)),
+                               self.headers)
+        body = self._parse_body(etree.fromstring(body))
+        return resp, body
+
+    def delete_user(self, user_id):
+        """Delete a user"""
+        resp, body = self.delete("users/%s" % user_id, self.headers)
+        return resp, body
+
+    def get_users(self):
+        """Get the list of users"""
+        resp, body = self.get("users", self.headers)
+        body = self._parse_array(etree.fromstring(body))
+        return resp, body
+
+    def enable_disable_user(self, user_id, enabled):
+        """Enables or disables a user"""
+        enable_user = Element("user",
+                                enabled=str(enabled).lower())
+        resp, body = self.put('users/%s/enabled' % user_id,
+                str(Document(enable_user)), self.headers)
+        body = self._parse_array(etree.fromstring(body))
+        return resp, body
+
+    def delete_token(self, token_id):
+        """Delete a token"""
+        resp, body = self.delete("tokens/%s" % token_id, self.headers)
+        return resp, body
+
+    def list_users_for_tenant(self, tenant_id):
+        """List users for a Tenant"""
+        resp, body = self.get('/tenants/%s/users' % tenant_id, self.headers)
+        body = self._parse_array(etree.fromstring(body))
+        return resp, body
+
+    def create_service(self, name, type, **kwargs):
+        """Create a service"""
+        OS_KSADM = "http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0"
+        create_service = Element("service",
+                                xmlns=OS_KSADM,
+                                name=name,
+                                type=type,
+                                description=kwargs.get('description'))
+        resp, body = self.post('OS-KSADM/services',
+                               str(Document(create_service)),
+                                    self.headers)
+        body = self._parse_body(etree.fromstring(body))
+        return resp, body
+
+    def get_service(self, service_id):
+        """Get Service"""
+        url = '/OS-KSADM/services/%s' % service_id
+        resp, body = self.get(url, self.headers)
+        body = self._parse_body(etree.fromstring(body))
+        return resp, body
+
+    def delete_service(self, service_id):
+        """Delete Service"""
+        url = '/OS-KSADM/services/%s' % service_id
+        return self.delete(url, self.headers)
+
+
+class TokenClientXML(RestClientXML):
+
+    def __init__(self, config):
+        self.auth_url = config.identity.auth_url
+
+    def auth(self, user, password, tenant):
+        passwordCreds = Element("passwordCredentials",
+                         username=user,
+                         password=password)
+        auth = Element("auth",
+                                tenantName=tenant)
+        auth.append(passwordCreds)
+        headers = {'Content-Type': 'application/xml'}
+        resp, body = self.post(self.auth_url, headers=headers,
+                               body=str(Document(auth)))
+        return resp, body
+
+    def request(self, method, url, headers=None, body=None):
+        """A simple HTTP request interface."""
+        self.http_obj = httplib2.Http()
+        if headers == None:
+            headers = {}
+
+        resp, resp_body = self.http_obj.request(url, method,
+                                                headers=headers, body=body)
+
+        if resp.status in (401, 403):
+            resp_body = json.loads(resp_body)
+            raise exceptions.Unauthorized(resp_body['error']['message'])
+
+        return resp, resp_body
+
+    def get_token(self, user, password, tenant):
+        resp, body = self.auth(user, password, tenant)
+        if resp['status'] != '202':
+            body = json.loads(body)
+            access = body['access']
+            token = access['token']
+            return token['id']
diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
index 68c7a86..3a42d51 100644
--- a/tempest/services/volume/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -22,13 +22,13 @@
 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.volume.catalog_type
@@ -92,6 +92,25 @@
         """Deletes the Specified Volume"""
         return self.delete("volumes/%s" % str(volume_id))
 
+    def attach_volume(self, volume_id, instance_uuid, mountpoint):
+        """Attaches a volume to a given instance on a given mountpoint"""
+        post_body = {
+                    'instance_uuid': instance_uuid,
+                    'mountpoint': mountpoint
+                    }
+        post_body = json.dumps({'os-attach': post_body})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body, self.headers)
+        return resp, body
+
+    def detach_volume(self, volume_id):
+        """Detaches a volume from an instance"""
+        post_body = {}
+        post_body = json.dumps({'os-detach': post_body})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body, self.headers)
+        return resp, body
+
     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)
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/tests/compute/base.py b/tempest/tests/compute/base.py
index ee3bf9e..f36c8f2 100644
--- a/tempest/tests/compute/base.py
+++ b/tempest/tests/compute/base.py
@@ -26,7 +26,6 @@
 from tempest import openstack
 from tempest import exceptions
 from tempest.common.utils.data_utils import rand_name
-from tempest.services.identity.json.admin_client import AdminClient
 
 __all__ = ['BaseComputeTest', 'BaseComputeTestJSON', 'BaseComputeTestXML',
            'BaseComputeAdminTestJSON', 'BaseComputeAdminTestXML']
@@ -79,12 +78,8 @@
         """
         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)
+        os = openstack.IdentityManager(interface=cls._interface)
+        admin_client = os.admin_client
         return admin_client
 
     @classmethod
diff --git a/tempest/tests/identity/admin/test_roles.py b/tempest/tests/identity/admin/test_roles.py
index 813f64a..e0b180b 100644
--- a/tempest/tests/identity/admin/test_roles.py
+++ b/tempest/tests/identity/admin/test_roles.py
@@ -19,14 +19,13 @@
 
 from tempest import exceptions
 from tempest.common.utils.data_utils import rand_name
-from tempest.tests.identity.base import BaseIdentityAdminTest
+from tempest.tests.identity import base
 
 
-class RolesTest(BaseIdentityAdminTest):
+class RolesTestBase(object):
 
-    @classmethod
+    @staticmethod
     def setUpClass(cls):
-        super(RolesTest, cls).setUpClass()
 
         for _ in xrange(5):
             resp, role = cls.client.create_role(rand_name('role-'))
@@ -101,11 +100,25 @@
         self.client.delete_role(role1_id)
 
 
-class UserRolesTest(RolesTest):
+class RolesTestJSON(base.BaseIdentityAdminTestJSON,
+                    RolesTestBase):
 
     @classmethod
     def setUpClass(cls):
-        super(UserRolesTest, cls).setUpClass()
+        super(RolesTestJSON, cls).setUpClass()
+        RolesTestBase.setUpClass(cls)
+
+
+class RolesTestXML(base.BaseIdentityAdminTestXML,
+                   RolesTestBase):
+
+    @classmethod
+    def setUpClass(cls):
+        super(RolesTestXML, cls).setUpClass()
+        RolesTestBase.setUpClass(cls)
+
+
+class UserRolesTestBase(RolesTestBase):
 
     def test_assign_user_role(self):
         """Assign a role to a user on a tenant"""
@@ -249,3 +262,19 @@
         (user, tenant, role) = self._get_role_params()
         self.assertRaises(exceptions.NotFound, self.client.list_user_roles,
         tenant['id'], 'junk-role-aabbcc11')
+
+
+class UserRolesTestJSON(RolesTestJSON,
+                        UserRolesTestBase):
+
+    @classmethod
+    def setUpClass(cls):
+        super(UserRolesTestJSON, cls).setUpClass()
+
+
+class UserRolesTestXML(RolesTestXML,
+                        UserRolesTestBase):
+
+    @classmethod
+    def setUpClass(cls):
+        super(UserRolesTestXML, cls).setUpClass()
diff --git a/tempest/tests/identity/admin/test_services.py b/tempest/tests/identity/admin/test_services.py
index d107a6e..fa4178a 100644
--- a/tempest/tests/identity/admin/test_services.py
+++ b/tempest/tests/identity/admin/test_services.py
@@ -20,10 +20,10 @@
 
 from tempest import exceptions
 from tempest.common.utils.data_utils import rand_name
-from tempest.tests.identity.base import BaseIdentityAdminTest
+from tempest.tests.identity import base
 
 
-class ServicesTest(BaseIdentityAdminTest):
+class ServicesTestBase(object):
 
     def test_create_get_delete_service(self):
         """GET Service"""
@@ -64,3 +64,17 @@
             #Checking whether service is deleted successfully
             self.assertRaises(exceptions.NotFound, self.client.get_service,
                               service_data['id'])
+
+
+class ServicesTestJSON(base.BaseIdentityAdminTestJSON,
+                      ServicesTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super(ServicesTestJSON, cls).setUpClass()
+
+
+class ServicesTestXML(base.BaseIdentityAdminTestXML,
+                      ServicesTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super(ServicesTestXML, cls).setUpClass()
diff --git a/tempest/tests/identity/admin/test_tenants.py b/tempest/tests/identity/admin/test_tenants.py
index 253e52e..d8b6c33 100644
--- a/tempest/tests/identity/admin/test_tenants.py
+++ b/tempest/tests/identity/admin/test_tenants.py
@@ -19,15 +19,13 @@
 
 from tempest import exceptions
 from tempest.common.utils.data_utils import rand_name
-from tempest.tests.identity.base import BaseIdentityAdminTest
+from tempest.tests.identity import base
 
 
-class TenantsTest(BaseIdentityAdminTest):
+class TenantsTestBase(object):
 
-    @classmethod
+    @staticmethod
     def setUpClass(cls):
-        super(TenantsTest, cls).setUpClass()
-
         for _ in xrange(5):
             resp, tenant = cls.client.create_tenant(rand_name('tenant-'))
             cls.data.tenants.append(tenant)
@@ -133,10 +131,12 @@
         st1 = resp['status']
         en1 = body['enabled']
         self.assertTrue(st1.startswith('2'))
-        self.assertFalse(en1, 'Enable should be False in response')
+        self.assertEqual('false', str(en1).lower(),
+                         'Enable should be False in response')
         resp, body = self.client.get_tenant(tenant_id)
         en2 = body['enabled']
-        self.assertFalse(en2, 'Enable should be False in lookup')
+        self.assertEqual('false', str(en2).lower(),
+                         'Enable should be False in lookup')
         self.client.delete_tenant(tenant_id)
 
     def test_tenant_create_duplicate(self):
@@ -246,7 +246,25 @@
         resp3_en = body['enabled']
 
         self.assertNotEqual(resp1_en, resp3_en)
-        self.assertEqual(t_en, resp1_en)
+        self.assertEqual('false', str(resp1_en).lower())
         self.assertEqual(resp2_en, resp3_en)
 
         self.client.delete_tenant(t_id)
+
+
+class TenantsTestJSON(base.BaseIdentityAdminTestJSON,
+                      TenantsTestBase):
+
+    @classmethod
+    def setUpClass(cls):
+        super(TenantsTestJSON, cls).setUpClass()
+        TenantsTestBase.setUpClass(cls)
+
+
+class TenantsTestXML(base.BaseIdentityAdminTestXML,
+                      TenantsTestBase):
+
+    @classmethod
+    def setUpClass(cls):
+        super(TenantsTestXML, cls).setUpClass()
+        TenantsTestBase.setUpClass(cls)
diff --git a/tempest/tests/identity/admin/test_users.py b/tempest/tests/identity/admin/test_users.py
index 1e1b752..07c032e 100644
--- a/tempest/tests/identity/admin/test_users.py
+++ b/tempest/tests/identity/admin/test_users.py
@@ -20,10 +20,10 @@
 
 from tempest import exceptions
 from tempest.common.utils.data_utils import rand_name
-from tempest.tests.identity.base import BaseIdentityAdminTest
+from tempest.tests.identity import base
 
 
-class UsersTest(BaseIdentityAdminTest):
+class UsersTestBase(object):
 
     alt_user = rand_name('test_user_')
     alt_password = rand_name('pass_')
@@ -331,3 +331,17 @@
         if len(fail) != 0:
             self.fail('Should raise Not Found when list users with invalid'
                           'tenant ids %s' % fail)
+
+
+class UsersTestJSON(base.BaseIdentityAdminTestJSON,
+                    UsersTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super(UsersTestJSON, cls).setUpClass()
+
+
+class UsersTestXML(base.BaseIdentityAdminTestXML,
+                    UsersTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super(UsersTestXML, cls).setUpClass()
diff --git a/tempest/tests/identity/base.py b/tempest/tests/identity/base.py
index 60037ca..f397a5b 100644
--- a/tempest/tests/identity/base.py
+++ b/tempest/tests/identity/base.py
@@ -18,50 +18,25 @@
 import nose
 import unittest2 as unittest
 
-import tempest.config
 from tempest.common.utils.data_utils import rand_name
-from tempest.services.identity.json.admin_client import AdminClient
-from tempest.services.identity.json.admin_client import TokenClient
+from tempest import openstack
 
 
-class BaseIdentityAdminTest(unittest.TestCase):
+class BaseIdAdminTest(unittest.TestCase):
 
     @classmethod
     def setUpClass(cls):
-        cls.config = tempest.config.TempestConfig()
-        cls.username = cls.config.identity_admin.username
-        cls.password = cls.config.identity_admin.password
-        cls.tenant_name = cls.config.identity_admin.tenant_name
-
-        if not (cls.username
-                and cls.password
-                and cls.tenant_name):
-            raise nose.SkipTest("Missing Admin credentials in configuration")
-
-        client_args = (cls.config,
-                       cls.username,
-                       cls.password,
-                       cls.config.identity.auth_url)
-        cls.client = AdminClient(*client_args, tenant_name=cls.tenant_name)
-        cls.token_client = TokenClient(cls.config)
+        os = openstack.IdentityManager(interface=cls._interface)
+        cls.client = os.admin_client
+        cls.token_client = os.token_client
 
         if not cls.client.has_admin_extensions():
             raise nose.SkipTest("Admin extensions disabled")
 
         cls.data = DataGenerator(cls.client)
 
-        # Create an admin client with regular Compute API credentials. This
-        # client is used in tests to validate Unauthorized is returned
-        # for non-admin users accessing Identity Admin API commands
-        cls.na_username = cls.config.compute.username
-        cls.na_password = cls.config.compute.password
-        cls.na_tenant_name = cls.config.compute.tenant_name
-        na_client_args = (cls.config,
-                       cls.na_username,
-                       cls.na_password,
-                       cls.config.identity.auth_url)
-        cls.non_admin_client = AdminClient(*na_client_args,
-                                           tenant_name=cls.na_tenant_name)
+        os = openstack.IdentityNaManager(interface=cls._interface)
+        cls.non_admin_client = os.admin_client
 
     @classmethod
     def tearDownClass(cls):
@@ -94,6 +69,22 @@
             return role[0]
 
 
+class BaseIdentityAdminTestJSON(BaseIdAdminTest):
+    @classmethod
+    def setUpClass(cls):
+        cls._interface = "json"
+        super(BaseIdentityAdminTestJSON, cls).setUpClass()
+
+BaseIdentityAdminTest = BaseIdentityAdminTestJSON
+
+
+class BaseIdentityAdminTestXML(BaseIdAdminTest):
+    @classmethod
+    def setUpClass(cls):
+        cls._interface = "xml"
+        super(BaseIdentityAdminTestXML, cls).setUpClass()
+
+
 class DataGenerator(object):
 
         def __init__(self, client):
diff --git a/tempest/tests/volume/base.py b/tempest/tests/volume/base.py
index 41f08fe..2e016d5 100644
--- a/tempest/tests/volume/base.py
+++ b/tempest/tests/volume/base.py
@@ -24,7 +24,6 @@
 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__)
@@ -50,6 +49,9 @@
 
         cls.os = os
         cls.volumes_client = os.volumes_client
+        cls.servers_client = os.servers_client
+        cls.image_ref = cls.config.compute.image_ref
+        cls.flavor_ref = cls.config.compute.flavor_ref
         cls.build_interval = cls.config.volume.build_interval
         cls.build_timeout = cls.config.volume.build_timeout
         cls.volumes = {}
@@ -63,6 +65,7 @@
                                              cls.volumes_client.service,
                                              cls.os.tenant_name)
         except exceptions.EndpointNotFound:
+            cls.clear_isolated_creds()
             raise nose.SkipTest(skip_msg)
 
     @classmethod
@@ -70,13 +73,8 @@
         """
         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
+        os = openstack.IdentityManager()
+        return os.admin_client
 
     @classmethod
     def _get_isolated_creds(cls):
@@ -147,3 +145,17 @@
                 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_actions.py b/tempest/tests/volume/test_volumes_actions.py
new file mode 100644
index 0000000..2b6028e
--- /dev/null
+++ b/tempest/tests/volume/test_volumes_actions.py
@@ -0,0 +1,90 @@
+# 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.base import BaseVolumeTest
+
+
+class VolumesActionsTest(BaseVolumeTest):
+
+    @classmethod
+    def setUpClass(cls):
+        super(VolumesActionsTest, cls).setUpClass()
+        cls.client = cls.volumes_client
+        cls.servers_client = cls.servers_client
+
+        # Create a test shared instance and volume for attach/detach tests
+        srv_name = rand_name('Instance-')
+        vol_name = rand_name('Volume-')
+        resp, cls.server = cls.servers_client.create_server(srv_name,
+                                                            cls.image_ref,
+                                                            cls.flavor_ref)
+        cls.servers_client.wait_for_server_status(cls.server['id'], 'ACTIVE')
+
+        resp, cls.volume = cls.client.create_volume(size=1,
+                                                    display_name=vol_name)
+        cls.client.wait_for_volume_status(cls.volume['id'], 'available')
+
+    @classmethod
+    def tearDownClass(cls):
+        super(VolumesActionsTest, cls).tearDownClass()
+        # Delete the test instance and volume
+        cls.client.delete_volume(cls.volume['id'])
+        cls.servers_client.delete_server(cls.server['id'])
+
+    @attr(type='smoke')
+    def test_attach_detach_volume_to_instance(self):
+        """Volume is attached and detached successfully from an instance"""
+        try:
+            mountpoint = '/dev/vdc'
+            resp, body = self.client.attach_volume(self.volume['id'],
+                                                   self.server['id'],
+                                                   mountpoint)
+            self.assertEqual(202, resp.status)
+            self.client.wait_for_volume_status(self.volume['id'], 'in-use')
+        except:
+            self.fail("Could not attach volume to instance")
+        finally:
+            # Detach the volume from the instance
+            resp, body = self.client.detach_volume(self.volume['id'])
+            self.assertEqual(202, resp.status)
+            self.client.wait_for_volume_status(self.volume['id'], 'available')
+
+    def test_get_volume_attachment(self):
+        """Verify that a volume's attachment information is retrieved"""
+        mountpoint = '/dev/vdc'
+        resp, body = self.client.attach_volume(self.volume['id'],
+                                                   self.server['id'],
+                                                   mountpoint)
+        self.client.wait_for_volume_status(self.volume['id'], 'in-use')
+        self.assertEqual(202, resp.status)
+        try:
+            resp, volume = self.client.get_volume(self.volume['id'])
+            self.assertEqual(200, resp.status)
+            self.assertTrue('attachments' in volume)
+            attachment = volume['attachments'][0]
+            self.assertEqual(mountpoint, attachment['device'])
+            self.assertEqual(self.server['id'], attachment['server_id'])
+            self.assertEqual(self.volume['id'], attachment['id'])
+            self.assertEqual(self.volume['id'], attachment['volume_id'])
+        except:
+            self.fail("Could not get attachment details from volume")
+        finally:
+            self.client.detach_volume(self.volume['id'])
+            self.client.wait_for_volume_status(self.volume['id'], 'available')
diff --git a/tempest/tests/volume/test_volumes_get.py b/tempest/tests/volume/test_volumes_get.py
index 4305c67..03ecb5a 100644
--- a/tempest/tests/volume/test_volumes_get.py
+++ b/tempest/tests/volume/test_volumes_get.py
@@ -18,15 +18,10 @@
 from nose.plugins.attrib import attr
 
 from tempest.common.utils.data_utils import rand_name
-from tempest.tests.volume.base import BaseVolumeTest
+from tempest.tests.volume import base
 
 
-class VolumesGetTest(BaseVolumeTest):
-
-    @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):
@@ -97,3 +92,19 @@
                 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
index 24055af..6c6dac2 100644
--- a/tempest/tests/volume/test_volumes_list.py
+++ b/tempest/tests/volume/test_volumes_list.py
@@ -19,10 +19,10 @@
 from nose.plugins.attrib import attr
 
 from tempest.common.utils.data_utils import rand_name
-from tempest.tests.volume.base import BaseVolumeTest
+from tempest.tests.volume import base
 
 
-class VolumesListTest(BaseVolumeTest):
+class VolumesListTestBase(object):
 
     """
     This test creates a number of 1G volumes. To run successfully,
@@ -32,9 +32,38 @@
     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):
-        super(VolumesListTest, cls).setUpClass()
+        cls._interface = 'xml'
+        super(VolumeListTestXML, cls).setUpClass()
         cls.client = cls.volumes_client
 
         # Create 3 test volumes
@@ -74,30 +103,51 @@
         for volume in cls.volume_id_list:
             resp, _ = cls.client.delete_volume(volume)
             cls.client.wait_for_resource_deletion(volume)
-        super(VolumesListTest, cls).tearDownClass()
+        super(VolumeListTestXML, cls).tearDownClass()
 
-    @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 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
index 63b209e..bf7e5f0 100644
--- a/tempest/tests/volume/test_volumes_negative.py
+++ b/tempest/tests/volume/test_volumes_negative.py
@@ -20,15 +20,10 @@
 
 from tempest import exceptions
 from tempest.common.utils.data_utils import rand_name
-from tempest.tests.volume.base import BaseVolumeTest
+from tempest.tests.volume import base
 
 
-class VolumesNegativeTest(BaseVolumeTest):
-
-    @classmethod
-    def setUpClass(cls):
-        super(VolumesNegativeTest, cls).setUpClass()
-        cls.client = cls.volumes_client
+class VolumesNegativeTestBase(object):
 
     @raises(exceptions.NotFound)
     @attr(type='negative')
@@ -131,3 +126,18 @@
         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