Merge "Add XML support to the volumes tests."
diff --git a/tempest/manager.py b/tempest/manager.py
index b13226d..7a5d6f5 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -40,13 +40,13 @@
 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.ExtensionsClientJSON
 SecurityGroupsClient = security_groups_client.SecurityGroupsClient
-FloatingIPsClient = floating_ips_client.FloatingIPsClient
+FloatingIPsClient = floating_ips_client.FloatingIPsClientJSON
 KeyPairsClient = keypairs_client.KeyPairsClientJSON
 VolumesExtensionsClient = volumes_extensions_client.VolumesExtensionsClientJSON
 VolumesClient = volumes_client.VolumesClient
diff --git a/tempest/openstack.py b/tempest/openstack.py
index 204265e..ec0c257 100644
--- a/tempest/openstack.py
+++ b/tempest/openstack.py
@@ -23,12 +23,13 @@
 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.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_extensions_client \
 import VolumesExtensionsClientJSON
@@ -36,6 +37,9 @@
 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
@@ -45,6 +49,11 @@
 
 LOG = logging.getLogger(__name__)
 
+IMAGES_CLIENTS = {
+    "json": ImagesClientJSON,
+    "xml": ImagesClientXML,
+}
+
 KEYPAIRS_CLIENTS = {
     "json": KeyPairsClientJSON,
     "xml": KeyPairsClientXML,
@@ -75,6 +84,11 @@
     "xml": VolumesExtensionsClientXML,
 }
 
+FLOAT_CLIENTS = {
+    "json": FloatingIPsClientJSON,
+    "xml": FloatingIPsClientXML,
+}
+
 
 class Manager(object):
 
@@ -119,18 +133,18 @@
         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)
         except KeyError:
             msg = "Unsupported interface type `%s'" % interface
             raise exceptions.InvalidConfiguration(msg)
-        self.images_client = ImagesClient(*client_args)
         self.security_groups_client = SecurityGroupsClient(*client_args)
-        self.floating_ips_client = FloatingIPsClient(*client_args)
         self.console_outputs_client = ConsoleOutputsClient(*client_args)
         self.network_client = NetworkClient(*client_args)
         self.volumes_client = VolumesClient(*client_args)
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/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/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/test_floating_ips_actions.py b/tempest/tests/compute/test_floating_ips_actions.py
index afd0a3f..fd0252b 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
 
@@ -55,13 +54,12 @@
             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):
@@ -225,3 +223,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()