Merge "Fixes LP#921409 * Adds /servers filter tests * Re-ordered resource building in fixtures to improve   execution time"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index dd14e13..da35c8f 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -9,6 +9,7 @@
 ssh_timeout=300
 build_interval=10
 build_timeout=600
+catalog_name=nova
 
 [environment]
 image_ref=3
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 119494d..754a1f6 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -128,6 +128,11 @@
             self._log(req_url, body, resp, resp_body)
             raise exceptions.BadRequest(resp_body['badRequest']['message'])
 
+        if resp.status == 409:
+            resp_body = json.loads(resp_body)
+            self._log(req_url, body, resp, resp_body)
+            raise exceptions.Duplicate(resp_body)
+
         if resp.status == 413:
             resp_body = json.loads(resp_body)
             self._log(req_url, body, resp, resp_body)
@@ -150,4 +155,9 @@
                 message = resp_body['computeFault']['message']
             raise exceptions.ComputeFault(message)
 
+        if resp.status >= 400:
+            resp_body = json.loads(resp_body)
+            self._log(req_url, body, resp, resp_body)
+            raise exceptions.TempestException(str(resp.status))
+
         return resp, resp_body
diff --git a/tempest/config.py b/tempest/config.py
index 6f816d2..960ff74 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -88,6 +88,11 @@
         """Timeout in seconds to wait for an entity to build."""
         return float(self.get("build_timeout", 300))
 
+    @property
+    def catalog_name(self):
+        """Catalog name of the Nova service."""
+        return self.get("catalog_name", 'nova')
+
 
 class EnvironmentConfig(object):
     def __init__(self, conf):
@@ -135,6 +140,11 @@
         """ What auth method does the environment use (basic|keystone) """
         return self.get("authentication", 'keystone')
 
+    @property
+    def release_name(self):
+        """ Which release is this? """
+        return self.get("release_name", 'essex')
+
 
 class ImagesConfig(object):
     """
diff --git a/tempest/services/nova/json/extensions_client.py b/tempest/services/nova/json/extensions_client.py
index abd8957..5f2cac4 100644
--- a/tempest/services/nova/json/extensions_client.py
+++ b/tempest/services/nova/json/extensions_client.py
@@ -6,8 +6,10 @@
 
     def __init__(self, config, username, key, auth_url, tenant_name=None):
         self.config = config
+        catalog_name = self.config.nova.catalog_name
         self.client = rest_client.RestClient(config, username, key,
-                                             auth_url, 'nova', tenant_name)
+                                             auth_url, catalog_name,
+                                             tenant_name)
 
     def list_extensions(self):
         url = 'extensions'
diff --git a/tempest/services/nova/json/flavors_client.py b/tempest/services/nova/json/flavors_client.py
index 60807a3..b592781 100644
--- a/tempest/services/nova/json/flavors_client.py
+++ b/tempest/services/nova/json/flavors_client.py
@@ -6,8 +6,10 @@
 
     def __init__(self, config, username, key, auth_url, tenant_name=None):
         self.config = config
+        catalog_name = self.config.nova.catalog_name
         self.client = rest_client.RestClient(config, username, key,
-                                             auth_url, 'nova', tenant_name)
+                                             auth_url, catalog_name,
+                                             tenant_name)
 
     def list_flavors(self, params=None):
         url = 'flavors'
diff --git a/tempest/services/nova/json/floating_ips_client.py b/tempest/services/nova/json/floating_ips_client.py
index 8bd276f..4a9f9eb 100644
--- a/tempest/services/nova/json/floating_ips_client.py
+++ b/tempest/services/nova/json/floating_ips_client.py
@@ -6,8 +6,12 @@
 class FloatingIPsClient(object):
     def __init__(self, config, username, key, auth_url, tenant_name=None):
         self.config = config
+        catalog_name = self.config.nova.catalog_name
         self.client = rest_client.RestClient(config, username, key,
-                                             auth_url, tenant_name)
+                                             auth_url, catalog_name,
+                                             tenant_name)
+        self.headers = {'Content-Type': 'application/json',
+                        'Accept': 'application/json'}
 
     def list_floating_ips(self, params=None):
         """Returns a list of all floating IPs filtered by any parameters"""
@@ -43,21 +47,28 @@
         resp, body = self.client.delete(url)
         return resp, body
 
-    def associate_floating_ip_to_server(self, floating_ip_id, fixed_ip_addr):
+    def associate_floating_ip_to_server(self, floating_ip, server_id):
         """Associate the provided floating IP to a specific server"""
-        url = "os-floating-ips/%s/associate" % str(floating_ip_id)
+        url = "servers/%s/action" % str(server_id)
         post_body = {
-            'associate_address': {
-                'fixed_ip': fixed_ip_addr,
+            'addFloatingIp': {
+                'address': floating_ip,
             }
         }
 
         post_body = json.dumps(post_body)
-        resp, body = self.client.post(url, post_body, None)
+        resp, body = self.client.post(url, post_body, self.headers)
         return resp, body
 
-    def disassociate_floating_ip_from_server(self, floating_ip_id):
+    def disassociate_floating_ip_from_server(self, floating_ip, server_id):
         """Disassociate the provided floating IP from a specific server"""
-        url = "os-floating-ips/%s/disassociate" % str(floating_ip_id)
-        resp, body = self.client.post(url, None, None)
+        url = "servers/%s/action" % str(server_id)
+        post_body = {
+            'removeFloatingIp': {
+                'address': floating_ip,
+            }
+        }
+
+        post_body = json.dumps(post_body)
+        resp, body = self.client.post(url, post_body, self.headers)
         return resp, body
diff --git a/tempest/services/nova/json/images_client.py b/tempest/services/nova/json/images_client.py
index 12c1774..435e2fa 100644
--- a/tempest/services/nova/json/images_client.py
+++ b/tempest/services/nova/json/images_client.py
@@ -8,8 +8,10 @@
 
     def __init__(self, config, username, key, auth_url, tenant_name=None):
         self.config = config
+        catalog_name = self.config.nova.catalog_name
         self.client = rest_client.RestClient(config, username, key,
-                                             auth_url, 'nova', tenant_name)
+                                             auth_url, catalog_name,
+                                             tenant_name)
 
         self.build_interval = self.config.nova.build_interval
         self.build_timeout = self.config.nova.build_timeout
diff --git a/tempest/services/nova/json/keypairs_client.py b/tempest/services/nova/json/keypairs_client.py
index 73b92a9..88dfcfe 100644
--- a/tempest/services/nova/json/keypairs_client.py
+++ b/tempest/services/nova/json/keypairs_client.py
@@ -6,8 +6,10 @@
 
     def __init__(self, config, username, key, auth_url, tenant_name=None):
         self.config = config
+        catalog_name = self.config.nova.catalog_name
         self.client = rest_client.RestClient(config, username, key,
-                                             auth_url, 'nova', tenant_name)
+                                             auth_url, catalog_name,
+                                             tenant_name)
         self.headers = {'Content-Type': 'application/json',
                         'Accept': 'application/json'}
 
diff --git a/tempest/services/nova/json/limits_client.py b/tempest/services/nova/json/limits_client.py
index f06235c..9b96fb4 100644
--- a/tempest/services/nova/json/limits_client.py
+++ b/tempest/services/nova/json/limits_client.py
@@ -5,8 +5,11 @@
 class LimitsClient(object):
 
     def __init__(self, config, username, key, auth_url, tenant_name=None):
+        self.config = config
+        catalog_name = self.config.nova.catalog_name
         self.client = rest_client.RestClient(config, username, key,
-                                             auth_url, 'nova', tenant_name)
+                                             auth_url, catalog_name,
+                                             tenant_name)
 
     def get_limits(self):
         resp, body = self.client.get("limits")
diff --git a/tempest/services/nova/json/security_groups_client.py b/tempest/services/nova/json/security_groups_client.py
index b365ce5..05ba61f 100644
--- a/tempest/services/nova/json/security_groups_client.py
+++ b/tempest/services/nova/json/security_groups_client.py
@@ -6,8 +6,10 @@
 
     def __init__(self, config, username, key, auth_url, tenant_name=None):
         self.config = config
+        catalog_name = self.config.nova.catalog_name
         self.client = rest_client.RestClient(config, username, key,
-                                             auth_url, 'nova', tenant_name)
+                                             auth_url, catalog_name,
+                                             tenant_name)
 
     def list_security_groups(self, params=None):
         """List all security groups for a user"""
diff --git a/tempest/services/nova/json/servers_client.py b/tempest/services/nova/json/servers_client.py
index 4f2e257..0d343d2 100644
--- a/tempest/services/nova/json/servers_client.py
+++ b/tempest/services/nova/json/servers_client.py
@@ -8,8 +8,10 @@
 
     def __init__(self, config, username, key, auth_url, tenant_name=None):
         self.config = config
+        catalog_name = self.config.nova.catalog_name
         self.client = rest_client.RestClient(config, username, key,
-                                             auth_url, 'nova', tenant_name)
+                                             auth_url, catalog_name,
+                                             tenant_name)
 
         self.build_interval = self.config.nova.build_interval
         self.build_timeout = self.config.nova.build_timeout
diff --git a/tempest/tests/test_flavors.py b/tempest/tests/test_flavors.py
index 7e87830..3ddcb58 100644
--- a/tempest/tests/test_flavors.py
+++ b/tempest/tests/test_flavors.py
@@ -2,10 +2,13 @@
 from nose.plugins.attrib import attr
 from tempest import exceptions
 from tempest import openstack
+import tempest.config
 
 
 class FlavorsTest(unittest.TestCase):
 
+    release = tempest.config.TempestConfig().env.release_name
+
     @classmethod
     def setUpClass(cls):
         cls.os = openstack.Manager()
@@ -41,7 +44,7 @@
         self.assertRaises(exceptions.NotFound, self.client.get_flavor_details,
                           999)
 
-    @unittest.expectedFailure
+    @unittest.skipIf(release == 'diablo', 'bug in diablo')
     @attr(type='positive', bug='lp912922')
     def test_list_flavors_limit_results(self):
         """Only the expected number of flavors should be returned"""
@@ -49,7 +52,7 @@
         resp, flavors = self.client.list_flavors(params)
         self.assertEqual(1, len(flavors))
 
-    @unittest.expectedFailure
+    @unittest.skipIf(release == 'diablo', 'bug in diablo')
     @attr(type='positive', bug='lp912922')
     def test_list_flavors_detailed_limit_results(self):
         """Only the expected number of flavors (detailed) should be returned"""
@@ -81,6 +84,7 @@
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]),
                         'The list of flavors did not start after the marker.')
 
+    @unittest.skipIf(release == 'diablo', 'bug in diablo')
     @attr(type='positive')
     def test_list_flavors_detailed_filter_by_min_disk(self):
         """The detailed list of flavors should be filtered by disk space"""
@@ -92,6 +96,7 @@
         resp, flavors = self.client.list_flavors_with_detail(params)
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
 
+    @unittest.skipIf(release == 'diablo', 'bug in diablo')
     @attr(type='positive')
     def test_list_flavors_detailed_filter_by_min_ram(self):
         """The detailed list of flavors should be filtered by RAM"""
@@ -103,6 +108,7 @@
         resp, flavors = self.client.list_flavors_with_detail(params)
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
 
+    @unittest.skipIf(release == 'diablo', 'bug in diablo')
     @attr(type='positive')
     def test_list_flavors_filter_by_min_disk(self):
         """The list of flavors should be filtered by disk space"""
@@ -114,6 +120,7 @@
         resp, flavors = self.client.list_flavors(params)
         self.assertFalse(any([i for i in flavors if i['id'] == flavor_id]))
 
+    @unittest.skipIf(release == 'diablo', 'bug in diablo')
     @attr(type='positive')
     def test_list_flavors_filter_by_min_ram(self):
         """The list of flavors should be filtered by RAM"""
diff --git a/tempest/tests/test_floating_ips_actions.py b/tempest/tests/test_floating_ips_actions.py
index ab9e22d..e5862c1 100644
--- a/tempest/tests/test_floating_ips_actions.py
+++ b/tempest/tests/test_floating_ips_actions.py
@@ -7,7 +7,7 @@
 
 class FloatingIPsTest(unittest.TestCase):
     server_id = None
-    floating_ip_id = None
+    floating_ip = None
 
     @classmethod
     def setUpClass(cls):
@@ -28,6 +28,7 @@
         #Floating IP creation
         resp, body = cls.client.create_floating_ip()
         cls.floating_ip_id = body['id']
+        cls.floating_ip = body['ip']
         #Generating a nonexistant floatingIP id
         cls.floating_ip_ids = []
         resp, body = cls.client.list_floating_ips()
@@ -89,12 +90,13 @@
        l"""
         #Association of floating IP to fixed IP address
         resp, body =\
-        self.client.associate_floating_ip_to_server(self.floating_ip_id,
-                                                    self.fixed_ip_addr)
+        self.client.associate_floating_ip_to_server(self.floating_ip,
+                                                    self.server_id)
         self.assertEqual(202, resp.status)
         #Disassociation of floating IP that was associated in this method
         resp, body = \
-            self.client.disassociate_floating_ip_from_server(floating_ip_id)
+            self.client.disassociate_floating_ip_from_server(self.floating_ip,
+                                                             self.server_id)
 
     @attr(type='positive')
     def test_dissociate_floating_ip(self):
@@ -105,11 +107,12 @@
         #Association of floating IP to a specific server
         #so as to check dissociation
         resp, body = \
-            self.client.associate_floating_ip_to_server(self.floating_ip_id,
-                                                        self.fixed_ip_addr)
+            self.client.associate_floating_ip_to_server(self.floating_ip,
+                                                        self.server_id)
         #Disassociation of floating IP
         resp, body = \
-        self.client.disassociate_floating_ip_from_server(self.floating_ip_id)
+        self.client.disassociate_floating_ip_from_server(self.floating_ip,
+                                                         self.server_id)
         self.assertEqual(202, resp.status)
 
     @attr(type='negative')
@@ -122,7 +125,7 @@
         #Deleting the non existant floating IP
         try:
             resp, body = self.client.delete_floating_ip(self.non_exist_id)
-        except exceptions.NotFound:
+        except:
             pass
         else:
             self.fail('Should not be able to delete a nonexistant floating IP')
@@ -136,9 +139,9 @@
         #Associating non existant floating IP
         try:
             resp, body = \
-            self.client.associate_floating_ip_to_server(self.non_exist_id,
-                                                        self.fixed_ip_addr)
-        except exceptions.NotFound:
+            self.client.associate_floating_ip_to_server("0.0.0.0",
+                                                        self.server_id)
+        except:
             pass
         else:
             self.fail('Should not be able to associate'
@@ -152,8 +155,9 @@
         #Dissociating non existant floating IP
         try:
             resp, body = \
-            self.client.disassociate_floating_ip_from_server(self.non_exist_id)
-        except exceptions.NotFound:
+            self.client.disassociate_floating_ip_from_server("0.0.0.0",
+                                                             self.server_id)
+        except:
             pass
         else:
             self.fail('Should not be able to dissociate'
diff --git a/tempest/tests/test_keypairs.py b/tempest/tests/test_keypairs.py
index 4b75524..53fcd0b 100644
--- a/tempest/tests/test_keypairs.py
+++ b/tempest/tests/test_keypairs.py
@@ -2,10 +2,14 @@
 import unittest2 as unittest
 from tempest import openstack
 from tempest.common.utils.data_utils import rand_name
+import tempest.config
+from tempest import exceptions
 
 
 class KeyPairsTest(unittest.TestCase):
 
+    release = tempest.config.TempestConfig().env.release_name
+
     @classmethod
     def setUpClass(cls):
         cls.os = openstack.Manager()
@@ -62,26 +66,6 @@
         self.assertEqual(202, resp.status)
 
     @attr(type='smoke')
-    def test_keypair_create_get_delete(self):
-        """Keypair should be created, fetched and deleted"""
-        k_name = rand_name('keypair-')
-        resp, keypair = self.client.create_keypair(k_name)
-        self.assertEqual(200, resp.status)
-        #Need to pop these keys so that our compare doesn't fail later,
-        #as the keypair dicts from get API doesn't have them.
-        keypair.pop('private_key')
-        keypair.pop('user_id')
-        #Now fetch the created keypair by its name
-        resp, fetched_key = self.client.get_keypair(k_name)
-        self.assertEqual(200, resp.status)
-
-        self.assertEqual(keypair, fetched_key,
-                    "The fetched keypair is different from the created key")
-        #Delete the keypair
-        resp, _ = self.client.delete_keypair(k_name)
-        self.assertEqual(202, resp.status)
-
-    @attr(type='smoke')
     def test_keypair_create_with_pub_key(self):
         """Keypair should be created with a given public key"""
         k_name = rand_name('keypair-')
@@ -109,23 +93,23 @@
         """Keypair should not be created with a non RSA public key"""
         k_name = rand_name('keypair-')
         pub_key = "ssh-rsa JUNK nova@ubuntu"
-        resp, _ = self.client.create_keypair(k_name, pub_key)
-        self.assertEqual(400, resp.status)
-
-    @attr(type='negative')
-    def test_keypair_create_with_empty_pub_key(self):
-        """Keypair should not be created with an empty public key"""
-        k_name = rand_name('keypair-')
-        pub_key = ""
-        resp, _ = self.client.create_keypair(k_name, pub_key)
-        self.assertEqual(400, resp.status)
+        try:
+            resp, _ = self.client.create_keypair(k_name, pub_key)
+        except exceptions.BadRequest:
+            pass
+        else:
+            self.fail('Expected BadRequest for invalid public key')
 
     @attr(type='negative')
     def test_keypair_delete_nonexistant_key(self):
         """Non-existant key deletion should throw a proper error"""
         k_name = rand_name("keypair-non-existant-")
-        resp, _ = self.client.delete_keypair(k_name)
-        self.assertEqual(400, resp.status)
+        try:
+            resp, _ = self.client.delete_keypair(k_name)
+        except exceptions.NotFound:
+            pass
+        else:
+            self.fail('nonexistent key')
 
     @attr(type='negative')
     def test_create_keypair_with_duplicate_name(self):
@@ -134,19 +118,34 @@
         resp, _ = self.client.create_keypair(k_name)
         self.assertEqual(200, resp.status)
         #Now try the same keyname to ceate another key
-        resp, _ = self.client.create_keypair(k_name)
-        #Expect a HTTP 409 Conflict Error
-        self.assertEqual(409, resp.status)
+        try:
+            resp, _ = self.client.create_keypair(k_name)
+            #Expect a HTTP 409 Conflict Error
+        except exceptions.Duplicate:
+            pass
+        else:
+            self.fail('duplicate name')
+        resp, _ = self.client.delete_keypair(k_name)
+        self.assertEqual(202, resp.status)
 
     @attr(type='negative')
     def test_create_keypair_with_empty_name_string(self):
         """Keypairs with name being an empty string should not be created"""
-        resp, _ = self.client.create_keypair('')
-        self.assertEqual(400, resp.status)
+        try:
+            resp, _ = self.client.create_keypair('')
+        except exceptions.BadRequest:
+            pass
+        else:
+            self.fail('empty string')
 
+    @unittest.skipIf(release == 'diablo', 'bug in diablo')
     @attr(type='negative')
     def test_create_keypair_with_long_keynames(self):
         """Keypairs with name longer than 255 chars should not be created"""
         k_name = 'keypair-'.ljust(260, '0')
-        resp, _ = self.client.create_keypair(k_name)
-        self.assertEqual(400, resp.status)
+        try:
+            resp, _ = self.client.create_keypair(k_name)
+        except exceptions.BadRequest:
+            pass
+        else:
+            self.fail('too long')
diff --git a/tempest/tests/test_server_actions.py b/tempest/tests/test_server_actions.py
index 3581845..ab7c801 100644
--- a/tempest/tests/test_server_actions.py
+++ b/tempest/tests/test_server_actions.py
@@ -123,8 +123,7 @@
         Negative Test: The server reboot on non existant server should return
         an error
         """
-        resp, body = self.client.reboot(999, 'SOFT')
-        self.assertEqual(404, resp.status)
+        self.assertRaises(exceptions.NotFound, self.client.reboot, 999, 'SOFT')
 
     @attr(type='negative')
     def test_rebuild_nonexistant_server(self):
diff --git a/tempest/tests/test_server_metadata.py b/tempest/tests/test_server_metadata.py
index 1fc6de8..18491ca 100644
--- a/tempest/tests/test_server_metadata.py
+++ b/tempest/tests/test_server_metadata.py
@@ -54,6 +54,7 @@
         resp, resp_metadata = self.client.list_server_metadata(self.server_id)
         self.assertEqual(resp_metadata, req_metadata)
 
+    @attr(type='negative')
     def test_server_create_metadata_key_too_long(self):
         """
         Attempt to start a server with a meta-data key that is > 255 characters
@@ -63,10 +64,14 @@
             key = "k" * sz
             meta = {key: 'data1'}
             name = rand_name('server')
-            resp, server = self.client.create_server(name, self.image_ref,
-                                                     self.flavor_ref,
-                                                     meta=meta)
-            self.assertEqual(413, resp.status)
+            try:
+                resp, server = self.client.create_server(name, self.image_ref,
+                                                         self.flavor_ref,
+                                                         meta=meta)
+            except:
+                pass
+            else:
+                self.fail('Metadata should have been too long')
         # no teardown - all creates should fail
 
     def test_update_server_metadata(self):
@@ -170,8 +175,12 @@
         Negative test: Should not be able to delete metadata item from a
         nonexistant server
         """
-        meta = {'delkey': 'delvalue'}
+        meta = {'d': 'delvalue'}
 
         #Delete the metadata item
-        resp, metadata = self.client.delete_server_metadata_item(999, 'delkey')
-        self.assertEqual(404, resp.status)
+        try:
+            resp, metadata = self.client.delete_server_metadata_item(999, 'd')
+        except:
+            pass
+        else:
+            self.fail('A delete should not happen for a nonexistant image')
diff --git a/tempest/tools/conf_from_devstack b/tempest/tools/conf_from_devstack
index 15346d9..8baa8fb 100755
--- a/tempest/tools/conf_from_devstack
+++ b/tempest/tools/conf_from_devstack
@@ -159,7 +159,7 @@
 
 [environment]
 image_ref=%(base_image_uuid)s
-image_ref_alt=4
+image_ref_alt=%(base_image_uuid)s
 flavor_ref=1
 flavor_ref_alt=2
 create_image_enabled=true