Merge "orchestration add resource limit API test"
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..c9b6467
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,4 @@
+[run]
+branch = True
+source = tempest
+omit = tempest/tests/*,tempest/openstack/*
diff --git a/.gitignore b/.gitignore
index 28a9b9c..1777cb9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,4 +16,5 @@
 build
 .testrepository
 .coverage*
+!.coveragerc
 cover/
diff --git a/HACKING.rst b/HACKING.rst
index c0df0fb..8652971 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -120,13 +120,14 @@
  - A json schema: defines properties for a request.
 
 After that a test class must be added to automatically generate test scenarios
-out of the given interface description:
+out of the given interface description::
+
+    load_tests = test.NegativeAutoTest.load_tests
 
     class SampeTestNegativeTestJSON(<your base class>, test.NegativeAutoTest):
         _interface = 'json'
         _service = 'compute'
-        _schema_file = 'compute/servers/get_console_output.json'
-        scenarios = test.NegativeAutoTest.generate_scenario(_schema_file)
+        _schema_file = <your Schema file>
 
 Negative tests must be marked with a negative attribute::
 
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 4a50595..c4dad7e 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -261,7 +261,7 @@
 # IP version used for SSH connections. (integer value)
 #ip_version_for_ssh=4
 
-# Dose the SSH uses Floating IP? (boolean value)
+# Does SSH use Floating IPs? (boolean value)
 #use_floatingip_for_ssh=true
 
 # Catalog type of the Compute service. (string value)
@@ -958,6 +958,9 @@
 # Runs Cinder volumes backup test (boolean value)
 #backup=true
 
+# Runs Cinder volume snapshot test (boolean value)
+#snapshot=true
+
 # A list of enabled volume extensions with a special entry all
 # which indicates every extension is enabled (list value)
 #api_extensions=all
diff --git a/tempest/api/compute/admin/test_flavors_negative.py b/tempest/api/compute/admin/test_flavors_negative.py
index 162e419..b37d32c 100644
--- a/tempest/api/compute/admin/test_flavors_negative.py
+++ b/tempest/api/compute/admin/test_flavors_negative.py
@@ -101,13 +101,9 @@
                           self.flavor_ref_alt)
 
 
+@test.SimpleNegativeAutoTest
 class FlavorCreateNegativeTestJSON(base.BaseV2ComputeAdminTest,
                                    test.NegativeAutoTest):
     _interface = 'json'
     _service = 'compute'
     _schema_file = 'compute/admin/flavor_create.json'
-
-    @test.attr(type=['negative', 'gate'])
-    def test_create_flavor(self):
-        # flavor details are not returned for non-existent flavors
-        self.execute(self._schema_file)
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index 797b780..9fa07f6 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -14,11 +14,16 @@
 
 import uuid
 
+import testtools
+
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest import config
 from tempest import exceptions
 from tempest import test
 
+CONF = config.CONF
+
 
 class ServersAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
 
@@ -119,6 +124,8 @@
                           self.client.migrate_server,
                           str(uuid.uuid4()))
 
+    @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+                          'Suspend is not available.')
     @test.attr(type=['negative', 'gate'])
     def test_migrate_server_invalid_state(self):
         # create server.
diff --git a/tempest/api/compute/certificates/test_certificates.py b/tempest/api/compute/certificates/test_certificates.py
index 5299d13..f6cadf7 100644
--- a/tempest/api/compute/certificates/test_certificates.py
+++ b/tempest/api/compute/certificates/test_certificates.py
@@ -20,12 +20,15 @@
 class CertificatesTestJSON(base.BaseV2ComputeTest):
 
     @test.attr(type='gate')
-    def test_create_and_get_root_certificate(self):
+    def test_create_root_certificate(self):
         # create certificates
-        resp, create_body = self.certificates_client.create_certificate()
+        resp, body = self.certificates_client.create_certificate()
         self.assertEqual(200, resp.status)
-        self.assertIn('data', create_body)
-        self.assertIn('private_key', create_body)
+        self.assertIn('data', body)
+        self.assertIn('private_key', body)
+
+    @test.attr(type='gate')
+    def test_get_root_certificate(self):
         # get the root certificate
         resp, body = self.certificates_client.get_certificate('root')
         self.assertEqual(200, resp.status)
diff --git a/tempest/api/compute/flavors/test_flavors_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
index a81b7d9..1638f2d 100644
--- a/tempest/api/compute/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -21,16 +21,14 @@
 load_tests = test.NegativeAutoTest.load_tests
 
 
-class FlavorsListNegativeTestJSON(base.BaseV2ComputeTest,
-                                  test.NegativeAutoTest):
+@test.SimpleNegativeAutoTest
+class FlavorsListWithDetailsNegativeTestJSON(base.BaseV2ComputeTest,
+                                             test.NegativeAutoTest):
     _service = 'compute'
     _schema_file = 'compute/flavors/flavors_list.json'
 
-    @test.attr(type=['negative', 'gate'])
-    def test_list_flavors_with_detail(self):
-        self.execute(self._schema_file)
 
-
+@test.SimpleNegativeAutoTest
 class FlavorDetailsNegativeTestJSON(base.BaseV2ComputeTest,
                                     test.NegativeAutoTest):
     _service = 'compute'
@@ -40,8 +38,3 @@
     def setUpClass(cls):
         super(FlavorDetailsNegativeTestJSON, cls).setUpClass()
         cls.set_resource("flavor", cls.flavor_ref)
-
-    @test.attr(type=['negative', 'gate'])
-    def test_get_flavor_details(self):
-        # flavor details are not returned for non-existent flavors
-        self.execute(self._schema_file)
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index f6eed00..297b300 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -127,7 +127,7 @@
         _ifs = self._test_delete_interface(server, ifs)
         self.assertEqual(len(ifs) - 1, len(_ifs))
 
-    @test.attr(type='gate')
+    @test.attr(type='smoke')
     def test_add_remove_fixed_ip(self):
         # Add and Remove the fixed IP to server.
         server, ifs = self._create_server_get_interfaces()
diff --git a/tempest/api/compute/servers/test_delete_server.py b/tempest/api/compute/servers/test_delete_server.py
index 7e34213..451d08f 100644
--- a/tempest/api/compute/servers/test_delete_server.py
+++ b/tempest/api/compute/servers/test_delete_server.py
@@ -23,7 +23,6 @@
 
 
 class DeleteServersTestJSON(base.BaseV2ComputeTest):
-    pause_available = CONF.compute_feature_enabled.pause
 
     # NOTE: Server creations of each test class should be under 10
     # for preventing "Quota exceeded for instances"
@@ -59,7 +58,8 @@
         self.assertEqual('204', resp['status'])
         self.client.wait_for_server_termination(server['id'])
 
-    @testtools.skipIf(not pause_available, 'Pause is not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+                          'Pause is not available.')
     @test.attr(type='gate')
     def test_delete_server_while_in_pause_state(self):
         # Delete a server while it's VM state is Pause
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 72ccc71..cc2d1ee 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -29,9 +29,6 @@
 
 
 class ServerActionsTestJSON(base.BaseV2ComputeTest):
-    resize_available = CONF.compute_feature_enabled.resize
-    pause_available = CONF.compute_feature_enabled.pause
-    suspend_available = CONF.compute_feature_enabled.suspend
     run_ssh = CONF.compute.run_ssh
 
     def setUp(self):
@@ -186,7 +183,8 @@
             if current_flavor == self.flavor_ref else self.flavor_ref
         return current_flavor, new_flavor_ref
 
-    @testtools.skipIf(not resize_available, 'Resize not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+                          'Resize not available.')
     @test.attr(type='smoke')
     def test_resize_server_confirm(self):
         # The server's RAM and disk space should be modified to that of
@@ -205,7 +203,8 @@
         resp, server = self.client.get_server(self.server_id)
         self.assertEqual(new_flavor_ref, server['flavor']['id'])
 
-    @testtools.skipIf(not resize_available, 'Resize not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+                          'Resize not available.')
     @test.attr(type='gate')
     def test_resize_server_revert(self):
         # The server's RAM and disk space should return to its original
@@ -342,7 +341,8 @@
 
         self.wait_for(self._get_output)
 
-    @testtools.skipIf(not pause_available, 'Pause is not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+                          'Pause is not available.')
     @test.attr(type='gate')
     def test_pause_unpause_server(self):
         resp, server = self.client.pause_server(self.server_id)
@@ -352,7 +352,8 @@
         self.assertEqual(202, resp.status)
         self.client.wait_for_server_status(self.server_id, 'ACTIVE')
 
-    @testtools.skipIf(not suspend_available, 'Suspend is not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+                          'Suspend is not available.')
     @test.attr(type='gate')
     def test_suspend_resume_server(self):
         resp, server = self.client.suspend_server(self.server_id)
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index cbfec5c..cc801b5 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -29,8 +29,6 @@
 
 
 class ServersNegativeTestJSON(base.BaseV2ComputeTest):
-    pause_available = CONF.compute_feature_enabled.pause
-    suspend_available = CONF.compute_feature_enabled.suspend
 
     def setUp(self):
         super(ServersNegativeTestJSON, self).setUp()
@@ -129,7 +127,8 @@
         self.assertRaises(exceptions.NotFound, self.client.reboot,
                           nonexistent_server, 'SOFT')
 
-    @testtools.skipIf(not pause_available, 'Pause is not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+                          'Pause is not available.')
     @test.attr(type=['negative', 'gate'])
     def test_pause_paused_server(self):
         # Pause a paused server.
@@ -309,7 +308,8 @@
         self.assertRaises(exceptions.NotFound, self.servers_client.stop,
                           nonexistent_server)
 
-    @testtools.skipIf(not pause_available, 'Pause is not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+                          'Pause is not available.')
     @test.attr(type=['negative', 'gate'])
     def test_pause_non_existent_server(self):
         # pause a non existent server
@@ -317,7 +317,8 @@
         self.assertRaises(exceptions.NotFound, self.client.pause_server,
                           nonexistent_server)
 
-    @testtools.skipIf(not pause_available, 'Pause is not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+                          'Pause is not available.')
     @test.attr(type=['negative', 'gate'])
     def test_unpause_non_existent_server(self):
         # unpause a non existent server
@@ -325,7 +326,8 @@
         self.assertRaises(exceptions.NotFound, self.client.unpause_server,
                           nonexistent_server)
 
-    @testtools.skipIf(not pause_available, 'Pause is not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+                          'Pause is not available.')
     @test.attr(type=['negative', 'gate'])
     def test_unpause_server_invalid_state(self):
         # unpause an active server.
@@ -333,7 +335,8 @@
                           self.client.unpause_server,
                           self.server_id)
 
-    @testtools.skipIf(not suspend_available, 'Suspend is not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+                          'Suspend is not available.')
     @test.attr(type=['negative', 'gate'])
     def test_suspend_non_existent_server(self):
         # suspend a non existent server
@@ -341,7 +344,8 @@
         self.assertRaises(exceptions.NotFound, self.client.suspend_server,
                           nonexistent_server)
 
-    @testtools.skipIf(not suspend_available, 'Suspend is not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+                          'Suspend is not available.')
     @test.attr(type=['negative', 'gate'])
     def test_suspend_server_invalid_state(self):
         # suspend a suspended server.
@@ -354,7 +358,8 @@
                           self.client.suspend_server,
                           self.server_id)
 
-    @testtools.skipIf(not suspend_available, 'Suspend is not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+                          'Suspend is not available.')
     @test.attr(type=['negative', 'gate'])
     def test_resume_non_existent_server(self):
         # resume a non existent server
@@ -362,7 +367,8 @@
         self.assertRaises(exceptions.NotFound, self.client.resume_server,
                           nonexistent_server)
 
-    @testtools.skipIf(not suspend_available, 'Suspend is not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+                          'Suspend is not available.')
     @test.attr(type=['negative', 'gate'])
     def test_resume_server_invalid_state(self):
         # resume an active server.
diff --git a/tempest/api/compute/servers/test_servers_negative_new.py b/tempest/api/compute/servers/test_servers_negative_new.py
index f860ff9..43ddb3a 100644
--- a/tempest/api/compute/servers/test_servers_negative_new.py
+++ b/tempest/api/compute/servers/test_servers_negative_new.py
@@ -21,6 +21,7 @@
 load_tests = test.NegativeAutoTest.load_tests
 
 
+@test.SimpleNegativeAutoTest
 class GetConsoleOutputNegativeTestJSON(base.BaseV2ComputeTest,
                                        test.NegativeAutoTest):
     _service = 'compute'
@@ -31,7 +32,3 @@
         super(GetConsoleOutputNegativeTestJSON, cls).setUpClass()
         _resp, server = cls.create_test_server()
         cls.set_resource("server", server['id'])
-
-    @test.attr(type=['negative', 'gate'])
-    def test_get_console_output(self):
-        self.execute(self._schema_file)
diff --git a/tempest/api/compute/v3/admin/test_servers_negative.py b/tempest/api/compute/v3/admin/test_servers_negative.py
index cc1be4e..fba4cd1 100644
--- a/tempest/api/compute/v3/admin/test_servers_negative.py
+++ b/tempest/api/compute/v3/admin/test_servers_negative.py
@@ -14,11 +14,16 @@
 
 import uuid
 
+import testtools
+
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest import config
 from tempest import exceptions
 from tempest.test import attr
 
+CONF = config.CONF
+
 
 class ServersAdminNegativeV3Test(base.BaseV3ComputeAdminTest):
 
@@ -119,6 +124,8 @@
                           self.client.migrate_server,
                           str(uuid.uuid4()))
 
+    @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+                          'Suspend is not available.')
     @attr(type=['negative', 'gate'])
     def test_migrate_server_invalid_state(self):
         # create server.
diff --git a/tempest/api/compute/v3/certificates/test_certificates.py b/tempest/api/compute/v3/certificates/test_certificates.py
index ce025fc..0ba44cb 100644
--- a/tempest/api/compute/v3/certificates/test_certificates.py
+++ b/tempest/api/compute/v3/certificates/test_certificates.py
@@ -20,12 +20,15 @@
 class CertificatesV3Test(base.BaseV3ComputeTest):
 
     @attr(type='gate')
-    def test_create_and_get_root_certificate(self):
+    def test_create_root_certificate(self):
         # create certificates
-        resp, create_body = self.certificates_client.create_certificate()
+        resp, body = self.certificates_client.create_certificate()
         self.assertEqual(201, resp.status)
-        self.assertIn('data', create_body)
-        self.assertIn('private_key', create_body)
+        self.assertIn('data', body)
+        self.assertIn('private_key', body)
+
+    @attr(type='gate')
+    def test_get_root_certificate(self):
         # get the root certificate
         resp, body = self.certificates_client.get_certificate('root')
         self.assertEqual(200, resp.status)
diff --git a/tempest/api/compute/v3/flavors/test_flavors_negative.py b/tempest/api/compute/v3/flavors/test_flavors_negative.py
index 1c0e4fb..657e2cd 100644
--- a/tempest/api/compute/v3/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/v3/flavors/test_flavors_negative.py
@@ -20,16 +20,14 @@
 load_tests = test.NegativeAutoTest.load_tests
 
 
+@test.SimpleNegativeAutoTest
 class FlavorsListNegativeV3Test(base.BaseV3ComputeTest,
                                 test.NegativeAutoTest):
     _service = 'computev3'
     _schema_file = 'compute/flavors/flavors_list_v3.json'
 
-    @test.attr(type=['negative', 'gate'])
-    def test_list_flavors_with_detail(self):
-        self.execute(self._schema_file)
 
-
+@test.SimpleNegativeAutoTest
 class FlavorDetailsNegativeV3Test(base.BaseV3ComputeTest,
                                   test.NegativeAutoTest):
     _service = 'computev3'
@@ -39,8 +37,3 @@
     def setUpClass(cls):
         super(FlavorDetailsNegativeV3Test, cls).setUpClass()
         cls.set_resource("flavor", cls.flavor_ref)
-
-    @test.attr(type=['negative', 'gate'])
-    def test_get_flavor_details(self):
-        # flavor details are not returned for non-existent flavors
-        self.execute(self._schema_file)
diff --git a/tempest/api/compute/v3/servers/test_attach_interfaces.py b/tempest/api/compute/v3/servers/test_attach_interfaces.py
index e1c69d9..c848f8c 100644
--- a/tempest/api/compute/v3/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/v3/servers/test_attach_interfaces.py
@@ -127,7 +127,7 @@
         _ifs = self._test_delete_interface(server, ifs)
         self.assertEqual(len(ifs) - 1, len(_ifs))
 
-    @attr(type='gate')
+    @attr(type='smoke')
     def test_add_remove_fixed_ip(self):
         # Add and Remove the fixed IP to server.
         server, ifs = self._create_server_get_interfaces()
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index 2582fa8..c377c30 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -26,7 +26,6 @@
 
 
 class ServerActionsV3Test(base.BaseV3ComputeTest):
-    resize_available = CONF.compute_feature_enabled.resize
     run_ssh = CONF.compute.run_ssh
 
     def setUp(self):
@@ -175,7 +174,8 @@
             if current_flavor == self.flavor_ref else self.flavor_ref
         return current_flavor, new_flavor_ref
 
-    @testtools.skipIf(not resize_available, 'Resize not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+                          'Resize not available.')
     @test.attr(type='smoke')
     def test_resize_server_confirm(self):
         # The server's RAM and disk space should be modified to that of
@@ -194,7 +194,8 @@
         resp, server = self.client.get_server(self.server_id)
         self.assertEqual(new_flavor_ref, server['flavor']['id'])
 
-    @testtools.skipIf(not resize_available, 'Resize not available.')
+    @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+                          'Resize not available.')
     @test.attr(type='gate')
     def test_resize_server_revert(self):
         # The server's RAM and disk space should return to its original
@@ -337,6 +338,8 @@
         self.assertEqual(202, resp.status)
         self.client.wait_for_server_status(self.server_id, 'ACTIVE')
 
+    @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+                          'Suspend is not available.')
     @test.attr(type='gate')
     def test_suspend_resume_server(self):
         resp, server = self.client.suspend_server(self.server_id)
diff --git a/tempest/api/compute/v3/servers/test_servers_negative.py b/tempest/api/compute/v3/servers/test_servers_negative.py
index cb5e93d..586a52a 100644
--- a/tempest/api/compute/v3/servers/test_servers_negative.py
+++ b/tempest/api/compute/v3/servers/test_servers_negative.py
@@ -16,6 +16,8 @@
 import base64
 import sys
 
+import testtools
+
 from tempest.api.compute import base
 from tempest import clients
 from tempest.common.utils import data_utils
@@ -311,6 +313,8 @@
                           self.client.unpause_server,
                           self.server_id)
 
+    @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+                          'Suspend is not available.')
     @test.attr(type=['negative', 'gate'])
     def test_suspend_non_existent_server(self):
         # suspend a non existent server
@@ -318,6 +322,8 @@
         self.assertRaises(exceptions.NotFound, self.client.suspend_server,
                           nonexistent_server)
 
+    @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+                          'Suspend is not available.')
     @test.attr(type=['negative', 'gate'])
     def test_suspend_server_invalid_state(self):
         # suspend a suspended server.
@@ -337,6 +343,8 @@
         self.assertRaises(exceptions.NotFound, self.client.resume_server,
                           nonexistent_server)
 
+    @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+                          'Suspend is not available.')
     @test.attr(type=['negative', 'gate'])
     def test_resume_server_invalid_state(self):
         # resume an active server.
diff --git a/tempest/api/data_processing/base.py b/tempest/api/data_processing/base.py
index 73ad22b..84d5be6 100644
--- a/tempest/api/data_processing/base.py
+++ b/tempest/api/data_processing/base.py
@@ -26,9 +26,10 @@
     @classmethod
     def setUpClass(cls):
         super(BaseDataProcessingTest, cls).setUpClass()
-        os = cls.get_client_manager()
         if not CONF.service_available.sahara:
             raise cls.skipException("Sahara support is required")
+
+        os = cls.get_client_manager()
         cls.client = os.data_processing_client
 
         # set some constants
@@ -57,7 +58,7 @@
     @classmethod
     def tearDownClass(cls):
         # cleanup node group templates
-        for ngt_id in cls._node_group_templates:
+        for ngt_id in getattr(cls, '_node_group_templates', []):
             try:
                 cls.client.delete_node_group_template(ngt_id)
             except Exception:
diff --git a/tempest/api/identity/admin/test_tokens.py b/tempest/api/identity/admin/test_tokens.py
index c931bcf..7fec28d 100644
--- a/tempest/api/identity/admin/test_tokens.py
+++ b/tempest/api/identity/admin/test_tokens.py
@@ -72,11 +72,16 @@
         self.assertEqual(200, resp.status)
         self.data.users.append(user)
 
-        # Create a tenant.
-        tenant_name = data_utils.rand_name(name='tenant-')
-        resp, tenant = self.client.create_tenant(tenant_name)
+        # Create a couple tenants.
+        tenant1_name = data_utils.rand_name(name='tenant-')
+        resp, tenant1 = self.client.create_tenant(tenant1_name)
         self.assertEqual(200, resp.status)
-        self.data.tenants.append(tenant)
+        self.data.tenants.append(tenant1)
+
+        tenant2_name = data_utils.rand_name(name='tenant-')
+        resp, tenant2 = self.client.create_tenant(tenant2_name)
+        self.assertEqual(200, resp.status)
+        self.data.tenants.append(tenant2)
 
         # Create a role
         role_name = data_utils.rand_name(name='role-')
@@ -84,8 +89,12 @@
         self.assertEqual(200, resp.status)
         self.data.roles.append(role)
 
-        # Grant the user the role on the tenant.
-        resp, _ = self.client.assign_user_role(tenant['id'], user['id'],
+        # Grant the user the role on the tenants.
+        resp, _ = self.client.assign_user_role(tenant1['id'], user['id'],
+                                               role['id'])
+        self.assertEqual(200, resp.status)
+
+        resp, _ = self.client.assign_user_role(tenant2['id'], user['id'],
                                                role['id'])
         self.assertEqual(200, resp.status)
 
@@ -95,10 +104,20 @@
 
         token_id = body['token']['id']
 
-        # Use the unscoped token to get a scoped token.
-        rsp, body = self.token_client.auth_token(token_id, tenant=tenant_name)
+        # Use the unscoped token to get a token scoped to tenant1
+        rsp, body = self.token_client.auth_token(token_id, tenant=tenant1_name)
         self.assertEqual(200, resp.status)
 
+        scoped_token_id = body['token']['id']
+
+        # Revoke the scoped token
+        resp, body = self.client.delete_token(scoped_token_id)
+        self.assertEqual(204, resp.status)
+
+        # Use the unscoped token to get a token scoped to tenant2
+        rsp, body = self.token_client.auth_token(token_id, tenant=tenant2_name)
+        self.assertEqual(204, resp.status)
+
 
 class TokensTestXML(TokensTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index ab0fb7c..89dd87f 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -53,9 +53,7 @@
 
     @classmethod
     def _get_identity_admin_client(cls):
-        """
-        Returns an instance of the Identity Admin API client
-        """
+        """Returns an instance of the Identity Admin API client."""
         manager = clients.AdminManager(interface=cls._interface)
         admin_client = manager.identity_client
         return admin_client
@@ -119,6 +117,6 @@
 
     @staticmethod
     def stack_output(stack, output_key):
-        """Return a stack output value for a give key."""
+        """Return a stack output value for a given key."""
         return next((o['output_value'] for o in stack['outputs']
                     if o['output_key'] == output_key), None)
diff --git a/tempest/api/orchestration/stacks/test_non_empty_stack.py b/tempest/api/orchestration/stacks/test_non_empty_stack.py
index 70bf562..1da533b 100644
--- a/tempest/api/orchestration/stacks/test_non_empty_stack.py
+++ b/tempest/api/orchestration/stacks/test_non_empty_stack.py
@@ -47,7 +47,7 @@
 
     @attr(type='gate')
     def test_stack_list(self):
-        """Created stack should be on the list of existing stacks."""
+        """Created stack should be in the list of existing stacks."""
         resp, stacks = self.client.list_stacks()
         self.assertEqual('200', resp['status'])
         self.assertIsInstance(stacks, list)
@@ -76,7 +76,7 @@
 
     @attr(type='gate')
     def test_suspend_resume_stack(self):
-        """suspend and resume a stack."""
+        """Suspend and resume a stack."""
         resp, suspend_stack = self.client.suspend_stack(self.stack_identifier)
         self.assertEqual('200', resp['status'])
         self.client.wait_for_stack_status(self.stack_identifier,
@@ -121,7 +121,7 @@
 
     @attr(type='gate')
     def test_resource_metadata(self):
-        """Getting metadata for created resource should be possible."""
+        """Getting metadata for created resources should be possible."""
         resp, metadata = self.client.show_resource_metadata(
             self.stack_identifier,
             self.resource_name)
@@ -147,7 +147,7 @@
 
     @attr(type='gate')
     def test_show_event(self):
-        """Getting details about existing event should be possible."""
+        """Getting details about an event should be possible."""
         resp, events = self.client.list_resource_events(self.stack_identifier,
                                                         self.resource_name)
         self.assertNotEqual([], events)
diff --git a/tempest/api/orchestration/stacks/test_swift_resources.py b/tempest/api/orchestration/stacks/test_swift_resources.py
index b954128..5948e11 100644
--- a/tempest/api/orchestration/stacks/test_swift_resources.py
+++ b/tempest/api/orchestration/stacks/test_swift_resources.py
@@ -49,7 +49,7 @@
             cls.test_resources[resource['logical_resource_id']] = resource
 
     def test_created_resources(self):
-        """Created stack should be on the list of existing stacks."""
+        """Created stack should be in the list of existing stacks."""
         resources = [('SwiftContainer', 'OS::Swift::Container'),
                      ('SwiftContainerWebsite', 'OS::Swift::Container')]
         for resource_name, resource_type in resources:
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 2ce3a4f..6294cd9 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -29,6 +29,9 @@
         super(VolumesSnapshotTest, cls).setUpClass()
         cls.volume_origin = cls.create_volume()
 
+        if not CONF.volume_feature_enabled.snapshot:
+            raise cls.skipException("Cinder volume snapshots are disabled")
+
     @classmethod
     def tearDownClass(cls):
         super(VolumesSnapshotTest, cls).tearDownClass()
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index 9e47c03..61aa307 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -14,13 +14,23 @@
 
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
+from tempest import config
 from tempest import exceptions
 from tempest import test
 
+CONF = config.CONF
+
 
 class VolumesSnapshotNegativeTest(base.BaseVolumeV1Test):
     _interface = "json"
 
+    @classmethod
+    def setUpClass(cls):
+        super(VolumesSnapshotNegativeTest, cls).setUpClass()
+
+        if not CONF.volume_feature_enabled.snapshot:
+            raise cls.skipException("Cinder volume snapshots are disabled")
+
     @test.attr(type=['negative', 'gate'])
     def test_create_snapshot_with_nonexistent_volume_id(self):
         # Create a snapshot with nonexistent volume id
diff --git a/tempest/api_schema/compute/flavors.py b/tempest/api_schema/compute/flavors.py
new file mode 100644
index 0000000..a6367d4
--- /dev/null
+++ b/tempest/api_schema/compute/flavors.py
@@ -0,0 +1,37 @@
+# Copyright 2014 NEC Corporation.  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 tempest.api_schema.compute import parameter_types
+
+list_flavors = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'flavors': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'name': {'type': 'string'},
+                        'links': parameter_types.links,
+                        'id': {'type': 'string'}
+                    },
+                    'required': ['name', 'links', 'id']
+                }
+            }
+        },
+        'required': ['flavors']
+    }
+}
diff --git a/tempest/api_schema/compute/hosts.py b/tempest/api_schema/compute/hosts.py
index b9a3db9..a73e214 100644
--- a/tempest/api_schema/compute/hosts.py
+++ b/tempest/api_schema/compute/hosts.py
@@ -33,3 +33,34 @@
         'required': ['hosts']
     }
 }
+
+show_host_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'host': {
+                'type': 'array',
+                'item': {
+                    'type': 'object',
+                    'properties': {
+                        'resource': {
+                            'type': 'object',
+                            'properties': {
+                                'cpu': {'type': 'integer'},
+                                'disk_gb': {'type': 'integer'},
+                                'host': {'type': 'string'},
+                                'memory_mb': {'type': 'integer'},
+                                'project': {'type': 'string'}
+                            },
+                            'required': ['cpu', 'disk_gb', 'host',
+                                         'memory_mb', 'project']
+                        }
+                    },
+                    'required': ['resource']
+                }
+            }
+        },
+        'required': ['host']
+    }
+}
diff --git a/tempest/cli/simple_read_only/test_cinder.py b/tempest/cli/simple_read_only/test_cinder.py
index afbd732..723333b 100644
--- a/tempest/cli/simple_read_only/test_cinder.py
+++ b/tempest/cli/simple_read_only/test_cinder.py
@@ -16,6 +16,7 @@
 import logging
 import re
 import subprocess
+import testtools
 
 import tempest.cli
 from tempest import config
@@ -86,6 +87,8 @@
     def test_cinder_rate_limits(self):
         self.cinder('rate-limits')
 
+    @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+                          'Volume snapshot not available.')
     def test_cinder_snapshot_list(self):
         self.cinder('snapshot-list')
 
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 5d7779e..35d4ff2 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -549,7 +549,7 @@
         # code if it exists is something that we expect. This is explicitly
         # declared in the V3 API and so we should be able to export this in
         # the response schema. For now we'll ignore it.
-        if str(resp.status).startswith('2'):
+        if resp.status in HTTP_SUCCESS:
             response_code = schema['status_code']
             if resp.status not in response_code:
                 msg = ("The status code(%s) is different than the expected "
diff --git a/tempest/config.py b/tempest/config.py
index 778ac23..af96132 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -189,7 +189,7 @@
                help="IP version used for SSH connections."),
     cfg.BoolOpt('use_floatingip_for_ssh',
                 default=True,
-                help="Dose the SSH uses Floating IP?"),
+                help="Does SSH use Floating IPs?"),
     cfg.StrOpt('catalog_type',
                default='compute',
                help="Catalog type of the Compute service."),
@@ -453,6 +453,9 @@
     cfg.BoolOpt('backup',
                 default=True,
                 help='Runs Cinder volumes backup test'),
+    cfg.BoolOpt('snapshot',
+                default=True,
+                help='Runs Cinder volume snapshot test'),
     cfg.ListOpt('api_extensions',
                 default=['all'],
                 help='A list of enabled volume extensions with a special '
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 128ec17..5235871 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -50,6 +50,13 @@
     14. Check the existence of a file which created at 6. in volume2
     """
 
+    @classmethod
+    def setUpClass(cls):
+        super(TestStampPattern, cls).setUpClass()
+
+        if not CONF.volume_feature_enabled.snapshot:
+            raise cls.skipException("Cinder volume snapshots are disabled")
+
     def _wait_for_volume_snapshot_status(self, volume_snapshot, status):
         self.status_timeout(self.volume_client.volume_snapshots,
                             volume_snapshot.id, status)
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index e89ea70..faca31f 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -35,6 +35,12 @@
      * Boot an additional instance from the new snapshot based volume
      * Check written content in the instance booted from snapshot
     """
+    @classmethod
+    def setUpClass(cls):
+        super(TestVolumeBootPattern, cls).setUpClass()
+
+        if not CONF.volume_feature_enabled.snapshot:
+            raise cls.skipException("Cinder volume snapshots are disabled")
 
     def _create_volume_from_image(self):
         img_uuid = CONF.compute.image_ref
diff --git a/tempest/services/compute/json/flavors_client.py b/tempest/services/compute/json/flavors_client.py
index bc64117..bc4a64f 100644
--- a/tempest/services/compute/json/flavors_client.py
+++ b/tempest/services/compute/json/flavors_client.py
@@ -16,6 +16,7 @@
 import json
 import urllib
 
+from tempest.api_schema.compute import flavors as common_schema
 from tempest.api_schema.compute import flavors_access as schema_access
 from tempest.common import rest_client
 from tempest import config
@@ -36,6 +37,7 @@
 
         resp, body = self.get(url)
         body = json.loads(body)
+        self.validate_response(common_schema.list_flavors, resp, body)
         return resp, body['flavors']
 
     def list_flavors_with_detail(self, params=None):
diff --git a/tempest/services/compute/json/hosts_client.py b/tempest/services/compute/json/hosts_client.py
index 0130f27..eeb417a 100644
--- a/tempest/services/compute/json/hosts_client.py
+++ b/tempest/services/compute/json/hosts_client.py
@@ -45,6 +45,7 @@
 
         resp, body = self.get("os-hosts/%s" % str(hostname))
         body = json.loads(body)
+        self.validate_response(schema.show_host_detail, resp, body)
         return resp, body['host']
 
     def update_host(self, hostname, **kwargs):
diff --git a/tempest/services/compute/v3/json/flavors_client.py b/tempest/services/compute/v3/json/flavors_client.py
index 655e279..3fdb3ca 100644
--- a/tempest/services/compute/v3/json/flavors_client.py
+++ b/tempest/services/compute/v3/json/flavors_client.py
@@ -16,6 +16,7 @@
 import json
 import urllib
 
+from tempest.api_schema.compute import flavors as common_schema
 from tempest.api_schema.compute import flavors_access as schema_access
 from tempest.common import rest_client
 from tempest import config
@@ -36,6 +37,7 @@
 
         resp, body = self.get(url)
         body = json.loads(body)
+        self.validate_response(common_schema.list_flavors, resp, body)
         return resp, body['flavors']
 
     def list_flavors_with_detail(self, params=None):
diff --git a/tempest/services/compute/v3/json/hosts_client.py b/tempest/services/compute/v3/json/hosts_client.py
index bcb9d36..db7134c 100644
--- a/tempest/services/compute/v3/json/hosts_client.py
+++ b/tempest/services/compute/v3/json/hosts_client.py
@@ -45,6 +45,7 @@
 
         resp, body = self.get("os-hosts/%s" % str(hostname))
         body = json.loads(body)
+        self.validate_response(schema.show_host_detail, resp, body)
         return resp, body['host']
 
     def update_host(self, hostname, **kwargs):
diff --git a/tempest/test.py b/tempest/test.py
index abf42c0..e4019f9 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -17,6 +17,7 @@
 import functools
 import json
 import os
+import re
 import sys
 import time
 import urllib
@@ -581,6 +582,24 @@
         return None
 
 
+def SimpleNegativeAutoTest(klass):
+    """
+    This decorator registers a test function on basis of the class name.
+    """
+    @attr(type=['negative', 'gate'])
+    def generic_test(self):
+        self.execute(self._schema_file)
+
+    cn = klass.__name__
+    cn = cn.replace('JSON', '')
+    cn = cn.replace('Test', '')
+    # NOTE(mkoderer): replaces uppercase chars inside the class name with '_'
+    lower_cn = re.sub('(?<!^)(?=[A-Z])', '_', cn).lower()
+    func_name = 'test_%s' % lower_cn
+    setattr(klass, func_name, generic_test)
+    return klass
+
+
 def call_until_true(func, duration, sleep_for):
     """
     Call the given function until it returns True (and return True) or
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index e6a1638..e8610d3 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -220,7 +220,6 @@
 
     # NOTE(afazekas): doctored test case,
     # with normal validation it would fail
-    @test.skip_because(bug="1182679")
     @test.attr(type='smoke')
     def test_integration_1(self):
         # EC2 1. integration test (not strict)