Merge "Add unit basic unit tests for tenant_isolation"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 068a666..8ab3505 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -128,6 +128,9 @@
 # AWS Access Key (string value)
 #aws_access=<None>
 
+# AWS Zone for EC2 tests (string value)
+#aws_zone=nova
+
 # S3 Materials Path (string value)
 #s3_materials_path=/opt/stack/devstack/files/images/s3-materials/cirros-0.3.0
 
@@ -307,14 +310,14 @@
 
 # Administrative Username to use for Nova API requests.
 # (string value)
-#username=admin
+#username=<None>
 
 # Administrative Tenant name to use for Nova API requests.
 # (string value)
-#tenant_name=admin
+#tenant_name=<None>
 
 # API key to use when authenticating as admin. (string value)
-#password=pass
+#password=<None>
 
 
 [compute-feature-enabled]
@@ -341,9 +344,6 @@
 # password? (boolean value)
 #change_password=false
 
-# Does the test environment support snapshots? (boolean value)
-#create_image=false
-
 # Does the test environment support resizing? (boolean value)
 #resize=false
 
@@ -451,16 +451,16 @@
 #endpoint_type=publicURL
 
 # Username to use for Nova API requests. (string value)
-#username=demo
+#username=<None>
 
 # Tenant name to use for Nova API requests. (string value)
-#tenant_name=demo
+#tenant_name=<None>
 
 # Role required to administrate keystone. (string value)
 #admin_role=admin
 
 # API key to use when authenticating. (string value)
-#password=pass
+#password=<None>
 
 # Username of alternate user to use for Nova API requests.
 # (string value)
@@ -476,14 +476,14 @@
 
 # Administrative Username to use for Keystone API requests.
 # (string value)
-#admin_username=admin
+#admin_username=<None>
 
 # Administrative Tenant name to use for Keystone API requests.
 # (string value)
-#admin_tenant_name=admin
+#admin_tenant_name=<None>
 
 # API key to use when authenticating as admin. (string value)
-#admin_password=pass
+#admin_password=<None>
 
 
 [identity-feature-enabled]
@@ -617,6 +617,14 @@
 # (string value)
 #public_router_id=
 
+# Timeout in seconds to wait for network operation to
+# complete. (integer value)
+#build_timeout=300
+
+# Time in seconds between network operation status checks.
+# (integer value)
+#build_interval=10
+
 
 [network-feature-enabled]
 
@@ -799,9 +807,9 @@
 # value)
 #horizon=true
 
-# Whether or not Savanna is expected to be available (boolean
+# Whether or not Sahara is expected to be available (boolean
 # value)
-#savanna=false
+#sahara=false
 
 # Whether or not Ironic is expected to be available (boolean
 # value)
diff --git a/requirements.txt b/requirements.txt
index 48d1b12..434e12e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -20,6 +20,6 @@
 testrepository>=0.0.18
 oslo.config>=1.2.0
 six>=1.5.2
-iso8601>=0.1.8
+iso8601>=0.1.9
 fixtures>=0.3.14
 testscenarios>=0.4
diff --git a/tempest/api/compute/admin/test_instance_usage_audit_log.py b/tempest/api/compute/admin/test_instance_usage_audit_log.py
index 32c8656..055a177 100644
--- a/tempest/api/compute/admin/test_instance_usage_audit_log.py
+++ b/tempest/api/compute/admin/test_instance_usage_audit_log.py
@@ -14,10 +14,10 @@
 #    under the License.
 
 import datetime
+import urllib
 
 from tempest.api.compute import base
 from tempest import test
-import urllib
 
 
 class InstanceUsageAuditLogTestJSON(base.BaseV2ComputeAdminTest):
diff --git a/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py b/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
index fe4a184..6a5fc96 100644
--- a/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
+++ b/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
@@ -14,11 +14,11 @@
 #    under the License.
 
 import datetime
+import urllib
 
 from tempest.api.compute import base
 from tempest import exceptions
 from tempest import test
-import urllib
 
 
 class InstanceUsageAuditLogNegativeTestJSON(base.BaseV2ComputeAdminTest):
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 5af091e..09c7274 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -92,6 +92,29 @@
         self.assertEqual(200, resp.status)
         self.assertEqual(quota_set['ram'], 5120)
 
+    @test.attr(type='gate')
+    def test_delete_quota(self):
+        # Admin can delete the resource quota set for a tenant
+        tenant_name = data_utils.rand_name('ram_quota_tenant_')
+        tenant_desc = tenant_name + '-desc'
+        identity_client = self.os_adm.identity_client
+        _, tenant = identity_client.create_tenant(name=tenant_name,
+                                                  description=tenant_desc)
+        tenant_id = tenant['id']
+        self.addCleanup(identity_client.delete_tenant, tenant_id)
+        resp, quota_set_default = self.adm_client.get_quota_set(tenant_id)
+        ram_default = quota_set_default['ram']
+
+        resp, body = self.adm_client.update_quota_set(tenant_id, ram='5120')
+        self.assertEqual(200, resp.status)
+
+        resp, body = self.adm_client.delete_quota_set(tenant_id)
+        self.assertEqual(202, resp.status)
+
+        resp, quota_set_new = self.adm_client.get_quota_set(tenant_id)
+        self.assertEqual(200, resp.status)
+        self.assertEqual(ram_default, quota_set_new['ram'])
+
 
 class QuotasAdminTestXML(QuotasAdminTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/admin/test_services.py b/tempest/api/compute/admin/test_services.py
index 9dd429b..2feb825 100644
--- a/tempest/api/compute/admin/test_services.py
+++ b/tempest/api/compute/admin/test_services.py
@@ -14,7 +14,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api.compute.api_schema import services as schema
 from tempest.api.compute import base
 from tempest import test
 
@@ -33,7 +32,7 @@
     @test.attr(type='gate')
     def test_list_services(self):
         resp, services = self.client.list_services()
-        self.validate_response(schema.list_services, resp, services)
+        self.assertEqual(200, resp.status)
         self.assertNotEqual(0, len(services))
 
     @test.attr(type='gate')
@@ -41,7 +40,7 @@
         binary_name = 'nova-compute'
         params = {'binary': binary_name}
         resp, services = self.client.list_services(params)
-        self.validate_response(schema.list_services, resp, services)
+        self.assertEqual(200, resp.status)
         self.assertNotEqual(0, len(services))
         for service in services:
             self.assertEqual(binary_name, service['binary'])
@@ -49,14 +48,12 @@
     @test.attr(type='gate')
     def test_get_service_by_host_name(self):
         resp, services = self.client.list_services()
-        self.validate_response(schema.list_services, resp, services)
         host_name = services[0]['host']
         services_on_host = [service for service in services if
                             service['host'] == host_name]
         params = {'host': host_name}
 
         resp, services = self.client.list_services(params)
-        self.validate_response(schema.list_services, resp, services)
 
         # we could have a periodic job checkin between the 2 service
         # lookups, so only compare binary lists.
@@ -70,13 +67,12 @@
     @test.attr(type='gate')
     def test_get_service_by_service_and_host_name(self):
         resp, services = self.client.list_services()
-        self.validate_response(schema.list_services, resp, services)
         host_name = services[0]['host']
         binary_name = services[0]['binary']
         params = {'host': host_name, 'binary': binary_name}
 
         resp, services = self.client.list_services(params)
-        self.validate_response(schema.list_services, resp, services)
+        self.assertEqual(200, resp.status)
         self.assertEqual(1, len(services))
         self.assertEqual(host_name, services[0]['host'])
         self.assertEqual(binary_name, services[0]['binary'])
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage.py b/tempest/api/compute/admin/test_simple_tenant_usage.py
index cc8641f..33cd6f3 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage.py
@@ -14,10 +14,10 @@
 #    under the License.
 
 import datetime
+import time
 
 from tempest.api.compute import base
 from tempest import test
-import time
 
 
 class TenantUsagesTestJSON(base.BaseV2ComputeAdminTest):
diff --git a/tempest/api/compute/api_schema/services.py b/tempest/api/compute/api_schema/services.py
deleted file mode 100644
index ef5868c..0000000
--- a/tempest/api/compute/api_schema/services.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# 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.
-
-list_services = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'array',
-        'items': {
-            'type': 'object',
-            'properties': {
-                # NOTE: Now the type of 'id' is integer, but here allows
-                # 'string' also because we will be able to change it to
-                # 'uuid' in the future.
-                'id': {'type': ['integer', 'string']},
-                'zone': {'type': 'string'},
-                'host': {'type': 'string'},
-                'state': {'type': 'string'},
-                'binary': {'type': 'string'},
-                'status': {'type': 'string'},
-                'updated_at': {'type': 'string'},
-                'disabled_reason': {'type': ['string', 'null']},
-            },
-            'required': ['id', 'zone', 'host', 'state', 'binary', 'status',
-                         'updated_at', 'disabled_reason'],
-        },
-    }
-}
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index e9b9efa..abd36a6 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -15,8 +15,6 @@
 
 import time
 
-import jsonschema
-
 from tempest import clients
 from tempest.common.utils import data_utils
 from tempest import config
@@ -178,25 +176,6 @@
 
         return resp, body
 
-    @classmethod
-    def validate_response(cls, schema, resp, body):
-        response_code = schema['status_code']
-        if resp.status not in response_code:
-            msg = ("The status code(%s) is different than the expected "
-                   "one(%s)") % (resp.status, response_code)
-            raise exceptions.InvalidHttpSuccessCode(msg)
-        response_schema = schema.get('response_body')
-        if response_schema:
-            try:
-                jsonschema.validate(body, response_schema)
-            except jsonschema.ValidationError as ex:
-                msg = ("HTTP response body is invalid (%s)") % ex
-                raise exceptions.InvalidHTTPResponseBody(msg)
-        else:
-            if body:
-                msg = ("HTTP response body should not exist (%s)") % body
-                raise exceptions.InvalidHTTPResponseBody(msg)
-
     def wait_for(self, condition):
         """Repeatedly calls condition() until a timeout."""
         start_time = int(time.time())
@@ -432,3 +411,4 @@
         cls.aggregates_admin_client = cls.os_adm.aggregates_v3_client
         cls.hosts_admin_client = cls.os_adm.hosts_v3_client
         cls.quotas_admin_client = cls.os_adm.quotas_v3_client
+        cls.agents_admin_client = cls.os_adm.agents_v3_client
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index c0f7af0..abd8a4c 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -24,10 +24,11 @@
     floating_ip = None
 
     @classmethod
+    @test.safe_setup
     def setUpClass(cls):
         super(FloatingIPsTestJSON, cls).setUpClass()
         cls.client = cls.floating_ips_client
-        #cls.servers_client = cls.servers_client
+        cls.floating_ip_id = None
 
         # Server creation
         resp, server = cls.create_test_server(wait_until='ACTIVE')
@@ -40,7 +41,8 @@
     @classmethod
     def tearDownClass(cls):
         # Deleting the floating IP which is created in this method
-        resp, body = cls.client.delete_floating_ip(cls.floating_ip_id)
+        if cls.floating_ip_id:
+            resp, body = cls.client.delete_floating_ip(cls.floating_ip_id)
         super(FloatingIPsTestJSON, cls).tearDownClass()
 
     @test.attr(type='gate')
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index 195a018..91eb4c5 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -24,14 +24,15 @@
 class ImagesMetadataTestJSON(base.BaseV2ComputeTest):
 
     @classmethod
+    @test.safe_setup
     def setUpClass(cls):
         super(ImagesMetadataTestJSON, cls).setUpClass()
         if not CONF.service_available.glance:
             skip_msg = ("%s skipped as glance is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
 
-        cls.servers_client = cls.servers_client
         cls.client = cls.images_client
+        cls.image_id = None
 
         resp, server = cls.create_test_server(wait_until='ACTIVE')
         cls.server_id = server['id']
@@ -45,7 +46,8 @@
 
     @classmethod
     def tearDownClass(cls):
-        cls.client.delete_image(cls.image_id)
+        if cls.image_id:
+            cls.client.delete_image(cls.image_id)
         super(ImagesMetadataTestJSON, cls).tearDownClass()
 
     def setUp(self):
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index b152c3c..d2fd970 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -13,7 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
 
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
@@ -61,8 +60,6 @@
         resp, flavor = self.flavors_client.get_flavor_details(flavor_id)
         return flavor['disk']
 
-    @testtools.skipUnless(CONF.compute_feature_enabled.create_image,
-                          'Environment unable to create images.')
     @test.attr(type='smoke')
     def test_create_delete_image(self):
 
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index ddf37ce..778294e 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -186,8 +186,7 @@
 
         admin_pass = self.image_ssh_password
 
-        resp, server_no_eph_disk = (self.
-                                    create_test_server(
+        resp, server_no_eph_disk = (self.create_test_server(
                                     wait_until='ACTIVE',
                                     adminPass=admin_pass,
                                     flavor=flavor_no_eph_disk_id))
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 837114c..f0913f1 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -26,6 +26,7 @@
 class ListServerFiltersTestJSON(base.BaseV2ComputeTest):
 
     @classmethod
+    @test.safe_setup
     def setUpClass(cls):
         super(ListServerFiltersTestJSON, cls).setUpClass()
         cls.client = cls.servers_client
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 71d6018..21465d8 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -17,6 +17,7 @@
 import time
 
 import testtools
+import urlparse
 
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
@@ -427,6 +428,29 @@
         self.assertEqual(202, resp.status)
         self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
 
+    def _validate_url(self, url):
+        valid_scheme = ['http', 'https']
+        parsed_url = urlparse.urlparse(url)
+        self.assertNotEqual('None', parsed_url.port)
+        self.assertNotEqual('None', parsed_url.hostname)
+        self.assertIn(parsed_url.scheme, valid_scheme)
+
+    @testtools.skipUnless(CONF.compute_feature_enabled.vnc_console,
+                          'VNC Console feature is disabled.')
+    @test.attr(type='gate')
+    def test_get_vnc_console(self):
+        # Get the VNC console of type 'novnc' and 'xvpvnc'
+        console_types = ['novnc', 'xvpvnc']
+        for console_type in console_types:
+            resp, body = self.servers_client.get_vnc_console(self.server_id,
+                                                             console_type)
+            self.assertEqual(
+                200, resp.status,
+                "Failed to get Console Type: %s" % (console_types))
+            self.assertEqual(console_type, body['type'])
+            self.assertNotEqual('', body['url'])
+            self._validate_url(body['url'])
+
 
 class ServerActionsTestXML(ServerActionsTestJSON):
     _interface = 'xml'
diff --git a/tempest/api/compute/servers/test_server_rescue_negative.py b/tempest/api/compute/servers/test_server_rescue_negative.py
index 277f28f..e027567 100644
--- a/tempest/api/compute/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/servers/test_server_rescue_negative.py
@@ -45,6 +45,11 @@
             cls.rescue_id, adminPass=rescue_password)
         cls.servers_client.wait_for_server_status(cls.rescue_id, 'RESCUE')
 
+    @classmethod
+    def tearDownClass(cls):
+        cls.delete_volume(cls.volume['id'])
+        super(ServerRescueNegativeTestJSON, cls).tearDownClass()
+
     def _detach(self, server_id, volume_id):
         self.servers_client.detach_volume(server_id, volume_id)
         self.volumes_extensions_client.wait_for_volume_status(volume_id,
diff --git a/tempest/api/compute/v3/admin/test_agents.py b/tempest/api/compute/v3/admin/test_agents.py
new file mode 100644
index 0000000..9d01b71
--- /dev/null
+++ b/tempest/api/compute/v3/admin/test_agents.py
@@ -0,0 +1,91 @@
+# 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.compute import base
+from tempest import test
+
+
+class AgentsAdminV3Test(base.BaseV3ComputeAdminTest):
+
+    """
+    Tests Agents API that require admin privileges
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        super(AgentsAdminV3Test, cls).setUpClass()
+        cls.client = cls.agents_admin_client
+
+    @test.attr(type='gate')
+    def test_create_update_list_delete_agents(self):
+
+        """
+        1. Create 2 agents.
+        2. Update one of the agents.
+        3. List all agent builds.
+        4. List the agent builds by the filter.
+        5. Delete agents.
+        """
+        params_kvm = expected_kvm = {'hypervisor': 'kvm',
+                                     'os': 'win',
+                                     'architecture': 'x86',
+                                     'version': '7.0',
+                                     'url': 'xxx://xxxx/xxx/xxx',
+                                     'md5hash': ("""add6bb58e139be103324d04d"""
+                                                 """82d8f545""")}
+
+        resp, agent_kvm = self.client.create_agent(**params_kvm)
+        self.assertEqual(201, resp.status)
+        for expected_item, value in expected_kvm.items():
+            self.assertEqual(value, agent_kvm[expected_item])
+
+        params_xen = expected_xen = {'hypervisor': 'xen',
+                                     'os': 'linux',
+                                     'architecture': 'x86',
+                                     'version': '7.0',
+                                     'url': 'xxx://xxxx/xxx/xxx1',
+                                     'md5hash': """add6bb58e139be103324d04d8"""
+                                                """2d8f546"""}
+
+        resp, agent_xen = self.client.create_agent(**params_xen)
+        self.assertEqual(201, resp.status)
+
+        for expected_item, value in expected_xen.items():
+            self.assertEqual(value, agent_xen[expected_item])
+
+        params_kvm_new = expected_kvm_new = {'version': '8.0',
+                                             'url': 'xxx://xxxx/xxx/xxx2',
+                                             'md5hash': """add6bb58e139be103"""
+                                                        """324d04d82d8f547"""}
+
+        resp, resp_agent_kvm = self.client.update_agent(agent_kvm['agent_id'],
+                                                        **params_kvm_new)
+        self.assertEqual(200, resp.status)
+        for expected_item, value in expected_kvm_new.items():
+            self.assertEqual(value, resp_agent_kvm[expected_item])
+
+        resp, agents = self.client.list_agents()
+        self.assertEqual(200, resp.status)
+        self.assertTrue(len(agents) > 1)
+
+        params_filter = {'hypervisor': 'kvm'}
+        resp, agent = self.client.list_agents(params_filter)
+        self.assertEqual(200, resp.status)
+        self.assertTrue(len(agent) > 0)
+        self.assertEqual('kvm', agent[0]['hypervisor'])
+
+        resp, _ = self.client.delete_agent(agent_kvm['agent_id'])
+        self.assertEqual(204, resp.status)
+        resp, _ = self.client.delete_agent(agent_xen['agent_id'])
+        self.assertEqual(204, resp.status)
diff --git a/tempest/api/compute/v3/admin/test_quotas.py b/tempest/api/compute/v3/admin/test_quotas.py
index 0c138bb..536891c 100644
--- a/tempest/api/compute/v3/admin/test_quotas.py
+++ b/tempest/api/compute/v3/admin/test_quotas.py
@@ -56,7 +56,7 @@
     def test_get_quota_set_detail(self):
         # Admin can get the detail of resource quota set for a tenant
         expected_quota_set = self.default_quota_set | set(['id'])
-        expected_detail = {'reserved', 'limit', 'in_use'}
+        expected_detail = ['reserved', 'limit', 'in_use']
         resp, quota_set = self.adm_client.get_quota_set_detail(
             self.demo_tenant_id)
         self.assertEqual(200, resp.status)
diff --git a/tempest/api/compute/v3/admin/test_services.py b/tempest/api/compute/v3/admin/test_services.py
index 0a7c7f1..b367dad 100644
--- a/tempest/api/compute/v3/admin/test_services.py
+++ b/tempest/api/compute/v3/admin/test_services.py
@@ -14,7 +14,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api.compute.api_schema import services as schema
 from tempest.api.compute import base
 from tempest.test import attr
 
@@ -33,7 +32,7 @@
     @attr(type='gate')
     def test_list_services(self):
         resp, services = self.client.list_services()
-        self.validate_response(schema.list_services, resp, services)
+        self.assertEqual(200, resp.status)
         self.assertNotEqual(0, len(services))
 
     @attr(type='gate')
@@ -41,7 +40,7 @@
         binary_name = 'nova-compute'
         params = {'binary': binary_name}
         resp, services = self.client.list_services(params)
-        self.validate_response(schema.list_services, resp, services)
+        self.assertEqual(200, resp.status)
         self.assertNotEqual(0, len(services))
         for service in services:
             self.assertEqual(binary_name, service['binary'])
@@ -49,14 +48,13 @@
     @attr(type='gate')
     def test_get_service_by_host_name(self):
         resp, services = self.client.list_services()
-        self.validate_response(schema.list_services, resp, services)
+        self.assertEqual(200, resp.status)
         host_name = services[0]['host']
         services_on_host = [service for service in services if
                             service['host'] == host_name]
         params = {'host': host_name}
 
         resp, services = self.client.list_services(params)
-        self.validate_response(schema.list_services, resp, services)
 
         # we could have a periodic job checkin between the 2 service
         # lookups, so only compare binary lists.
@@ -70,13 +68,12 @@
     @attr(type='gate')
     def test_get_service_by_service_and_host_name(self):
         resp, services = self.client.list_services()
-        self.validate_response(schema.list_services, resp, services)
         host_name = services[0]['host']
         binary_name = services[0]['binary']
         params = {'host': host_name, 'binary': binary_name}
 
         resp, services = self.client.list_services(params)
-        self.validate_response(schema.list_services, resp, services)
+        self.assertEqual(200, resp.status)
         self.assertEqual(1, len(services))
         self.assertEqual(host_name, services[0]['host'])
         self.assertEqual(binary_name, services[0]['binary'])
diff --git a/tempest/api/compute/v3/images/test_images_negative.py b/tempest/api/compute/v3/images/test_images_negative.py
index c38373f..0705bdc 100644
--- a/tempest/api/compute/v3/images/test_images_negative.py
+++ b/tempest/api/compute/v3/images/test_images_negative.py
@@ -35,7 +35,7 @@
         resp, body = self.servers_client.create_image(server_id, name, meta)
         image_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(self.client.delete_image, image_id)
-        self.client.wait_for_image_status(image_id, 'ACTIVE')
+        self.client.wait_for_image_status(image_id, 'active')
         return resp, body
 
     @test.attr(type=['negative', 'gate'])
diff --git a/tempest/api/compute/v3/images/test_images_oneserver.py b/tempest/api/compute/v3/images/test_images_oneserver.py
index 48a885e..3aab1e1 100644
--- a/tempest/api/compute/v3/images/test_images_oneserver.py
+++ b/tempest/api/compute/v3/images/test_images_oneserver.py
@@ -13,7 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
 
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
@@ -61,8 +60,6 @@
         resp, flavor = self.flavors_client.get_flavor_details(flavor_id)
         return flavor['disk']
 
-    @testtools.skipUnless(CONF.compute_feature_enabled.create_image,
-                          'Environment unable to create images.')
     @test.attr(type='smoke')
     def test_create_delete_image(self):
 
@@ -73,26 +70,26 @@
                                                       name, meta)
         self.assertEqual(202, resp.status)
         image_id = data_utils.parse_image_id(resp['location'])
-        self.client.wait_for_image_status(image_id, 'ACTIVE')
+        self.client.wait_for_image_status(image_id, 'active')
 
         # Verify the image was created correctly
-        resp, image = self.client.get_image(image_id)
+        resp, image = self.client.get_image_meta(image_id)
         self.assertEqual(name, image['name'])
-        self.assertEqual('test', image['metadata']['image_type'])
+        self.assertEqual('test', image['properties']['image_type'])
 
-        resp, original_image = self.client.get_image(self.image_ref)
+        resp, original_image = self.client.get_image_meta(self.image_ref)
 
         # Verify minRAM is the same as the original image
-        self.assertEqual(image['minRam'], original_image['minRam'])
+        self.assertEqual(image['min_ram'], original_image['min_ram'])
 
         # Verify minDisk is the same as the original image or the flavor size
         flavor_disk_size = self._get_default_flavor_disk_size(self.flavor_ref)
-        self.assertIn(str(image['minDisk']),
-                      (str(original_image['minDisk']), str(flavor_disk_size)))
+        self.assertIn(str(image['min_disk']),
+                      (str(original_image['min_disk']), str(flavor_disk_size)))
 
         # Verify the image was deleted correctly
         resp, body = self.client.delete_image(image_id)
-        self.assertEqual('204', resp['status'])
+        self.assertEqual('200', resp['status'])
         self.client.wait_for_resource_deletion(image_id)
 
     @test.attr(type=['gate'])
diff --git a/tempest/api/compute/v3/servers/test_create_server.py b/tempest/api/compute/v3/servers/test_create_server.py
index a212ca5..7e9aaf2 100644
--- a/tempest/api/compute/v3/servers/test_create_server.py
+++ b/tempest/api/compute/v3/servers/test_create_server.py
@@ -195,8 +195,7 @@
 
         admin_pass = self.image_ssh_password
 
-        resp, server_no_eph_disk = (self.
-                                    create_test_server(
+        resp, server_no_eph_disk = (self.create_test_server(
                                     wait_until='ACTIVE',
                                     adminPass=admin_pass,
                                     flavor=flavor_no_eph_disk_id))
diff --git a/tempest/api/compute/v3/servers/test_list_server_filters.py b/tempest/api/compute/v3/servers/test_list_server_filters.py
index ec31e8e..2cb176c 100644
--- a/tempest/api/compute/v3/servers/test_list_server_filters.py
+++ b/tempest/api/compute/v3/servers/test_list_server_filters.py
@@ -26,6 +26,7 @@
 class ListServerFiltersV3Test(base.BaseV3ComputeTest):
 
     @classmethod
+    @test.safe_setup
     def setUpClass(cls):
         super(ListServerFiltersV3Test, cls).setUpClass()
         cls.client = cls.servers_client
diff --git a/tempest/api/data_processing/base.py b/tempest/api/data_processing/base.py
index 5b272ef..73ad22b 100644
--- a/tempest/api/data_processing/base.py
+++ b/tempest/api/data_processing/base.py
@@ -27,8 +27,8 @@
     def setUpClass(cls):
         super(BaseDataProcessingTest, cls).setUpClass()
         os = cls.get_client_manager()
-        if not CONF.service_available.savanna:
-            raise cls.skipException("Savanna support is required")
+        if not CONF.service_available.sahara:
+            raise cls.skipException("Sahara support is required")
         cls.client = os.data_processing_client
 
         # set some constants
diff --git a/tempest/api/data_processing/test_node_group_templates.py b/tempest/api/data_processing/test_node_group_templates.py
index ff4fa6a..a64c345 100644
--- a/tempest/api/data_processing/test_node_group_templates.py
+++ b/tempest/api/data_processing/test_node_group_templates.py
@@ -28,7 +28,7 @@
 
         if template_name is None:
             # generate random name if it's not specified
-            template_name = data_utils.rand_name('savanna')
+            template_name = data_utils.rand_name('sahara')
 
         # create simple node group template
         resp, body, template_id = self.create_node_group_template(
diff --git a/tempest/api/identity/admin/test_users_negative.py b/tempest/api/identity/admin/test_users_negative.py
index 1188325..4e8ebe5 100644
--- a/tempest/api/identity/admin/test_users_negative.py
+++ b/tempest/api/identity/admin/test_users_negative.py
@@ -13,11 +13,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import uuid
+
 from tempest.api.identity import base
 from tempest.common.utils import data_utils
 from tempest import exceptions
 from tempest.test import attr
-import uuid
 
 
 class UsersNegativeTestJSON(base.BaseIdentityV2AdminTest):
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index ce11911..abde8f7 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -101,6 +101,7 @@
                                               disk_format='iso',
                                               visibility='public')
         self.assertEqual(201, resp.status)
+        self.addCleanup(self.client.delete_image, body['id'])
         self.assertEqual('queued', body['status'])
         image_id = body['id']
 
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index eb397ba..f4050c5 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -76,11 +76,8 @@
         resp, body = self.admin_client.remove_router_from_l3_agent(
             self.agent['id'], router['router']['id'])
         self.assertEqual('204', resp['status'])
-        resp, body = self.admin_client.list_l3_agents_hosting_router(
-            router['router']['id'])
-        for agent in body['agents']:
-            l3_agent_ids.append(agent['id'])
-        self.assertNotIn(self.agent['id'], l3_agent_ids)
+        # NOTE(afazekas): The deletion not asserted, because neutron
+        # is not forbidden to reschedule the router to the same agent
 
 
 class L3AgentSchedulerTestXML(L3AgentSchedulerTestJSON):
diff --git a/tempest/api/network/test_load_balancer.py b/tempest/api/network/test_load_balancer.py
index ba92199..695dbf8 100644
--- a/tempest/api/network/test_load_balancer.py
+++ b/tempest/api/network/test_load_balancer.py
@@ -131,6 +131,7 @@
         # Verification of vip delete
         resp, body = self.client.delete_vip(vip['id'])
         self.assertEqual('204', resp['status'])
+        self.client.wait_for_resource_deletion('vip', vip['id'])
         # Verification of pool update
         new_name = "New_pool"
         resp, body = self.client.update_pool(pool['id'],
diff --git a/tempest/api/orchestration/stacks/test_server_cfn_init.py b/tempest/api/orchestration/stacks/test_server_cfn_init.py
index 7e8bc2d..95deaf5 100644
--- a/tempest/api/orchestration/stacks/test_server_cfn_init.py
+++ b/tempest/api/orchestration/stacks/test_server_cfn_init.py
@@ -17,6 +17,7 @@
 from tempest.common.utils import data_utils
 from tempest.common.utils.linux import remote_client
 from tempest import config
+from tempest import exceptions
 from tempest.openstack.common import log as logging
 from tempest import test
 
@@ -66,18 +67,13 @@
               content: smoke test complete
             /etc/cfn/cfn-credentials:
               content:
-                Fn::Join:
-                - ''
-                - - AWSAccessKeyId=
-                  - {Ref: SmokeKeys}
-                  - '
-
-                    '
-                  - AWSSecretKey=
-                  - Fn::GetAtt: [SmokeKeys, SecretAccessKey]
-                  - '
-
-                    '
+                Fn::Replace:
+                - SmokeKeys: {Ref: SmokeKeys}
+                  SecretAccessKey:
+                    'Fn::GetAtt': [SmokeKeys, SecretAccessKey]
+                - |
+                  AWSAccessKeyId=SmokeKeys
+                  AWSSecretKey=SecretAccessKey
               mode: '000400'
               owner: root
               group: root
@@ -90,19 +86,13 @@
       networks:
       - uuid: {Ref: network}
       user_data:
-        Fn::Base64:
-          Fn::Join:
-          - ''
-          - - |-
-                #!/bin/bash -v
-                /opt/aws/bin/cfn-init
-            - |-
-                || error_exit ''Failed to run cfn-init''
-                /opt/aws/bin/cfn-signal -e 0 --data "`cat /tmp/smoke-status`" '
-            - {Ref: WaitHandle}
-            - '''
-
-              '
+        Fn::Replace:
+        - WaitHandle: {Ref: WaitHandle}
+        - |
+          #!/bin/bash -v
+          /opt/aws/bin/cfn-init
+          /opt/aws/bin/cfn-signal -e 0 --data "`cat /tmp/smoke-status`" \
+              "WaitHandle"
   WaitHandle:
     Type: AWS::CloudFormation::WaitConditionHandle
   WaitCondition:
@@ -172,9 +162,32 @@
         linux_client.validate_authentication()
 
     @test.attr(type='slow')
-    def test_stack_wait_condition_data(self):
-
+    def test_all_resources_created(self):
         sid = self.stack_identifier
+        self.client.wait_for_resource_status(
+            sid, 'WaitHandle', 'CREATE_COMPLETE')
+        self.client.wait_for_resource_status(
+            sid, 'SmokeSecurityGroup', 'CREATE_COMPLETE')
+        self.client.wait_for_resource_status(
+            sid, 'SmokeKeys', 'CREATE_COMPLETE')
+        self.client.wait_for_resource_status(
+            sid, 'CfnUser', 'CREATE_COMPLETE')
+        self.client.wait_for_resource_status(
+            sid, 'SmokeServer', 'CREATE_COMPLETE')
+        try:
+            self.client.wait_for_resource_status(
+                sid, 'WaitCondition', 'CREATE_COMPLETE')
+        except exceptions.TimeoutException as e:
+            # attempt to log the server console to help with debugging
+            # the cause of the server not signalling the waitcondition
+            # to heat.
+            resp, body = self.client.get_resource(sid, 'SmokeServer')
+            server_id = body['physical_resource_id']
+            LOG.debug('Console output for %s', server_id)
+            resp, output = self.servers_client.get_console_output(
+                server_id, None)
+            LOG.debug(output)
+            raise e
 
         # wait for create to complete.
         self.client.wait_for_stack_status(sid, 'CREATE_COMPLETE')
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index 31f6730..742f7e1 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -23,6 +23,7 @@
 
 class VolumeQuotasAdminTestJSON(base.BaseVolumeV1AdminTest):
     _interface = "json"
+    force_tenant_isolation = True
 
     @classmethod
     def setUpClass(cls):
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 175da01..be5d76b 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -124,8 +124,8 @@
         resp, new_volume = \
             self.client.create_volume(size=1,
                                       display_description=new_v_desc,
-                                      availability_zone=volume[
-                                      'availability_zone'])
+                                      availability_zone=
+                                      volume['availability_zone'])
         self.assertEqual(200, resp.status)
         self.assertIn('id', new_volume)
         self.addCleanup(self._delete_volume, new_volume['id'])
@@ -133,8 +133,8 @@
         resp, update_volume = \
             self.client.update_volume(new_volume['id'],
                                       display_name=volume['display_name'],
-                                      display_description=volume[
-                                      'display_description'])
+                                      display_description=
+                                      volume['display_description'])
         self.assertEqual(200, resp.status)
 
         # NOTE(jdg): Revert back to strict true/false checking
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index 0e91371..fff40ed 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -116,8 +116,8 @@
                           ('details' if with_detail else '', key)
                     if key == 'metadata':
                         self.assertThat(volume[key].items(),
-                                        matchers.ContainsAll(params[key]
-                                        .items()), msg)
+                                        matchers.ContainsAll(
+                                            params[key].items()), msg)
                     else:
                         self.assertEqual(params[key], volume[key], msg)
 
diff --git a/tempest/api/compute/api_schema/__init__.py b/tempest/api_schema/__init__.py
similarity index 100%
rename from tempest/api/compute/api_schema/__init__.py
rename to tempest/api_schema/__init__.py
diff --git a/tempest/api/compute/api_schema/__init__.py b/tempest/api_schema/compute/__init__.py
similarity index 100%
copy from tempest/api/compute/api_schema/__init__.py
copy to tempest/api_schema/compute/__init__.py
diff --git a/tempest/api_schema/compute/services.py b/tempest/api_schema/compute/services.py
new file mode 100644
index 0000000..4793f5a
--- /dev/null
+++ b/tempest/api_schema/compute/services.py
@@ -0,0 +1,44 @@
+# 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.
+
+list_services = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'services': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        # NOTE: Now the type of 'id' is integer, but here
+                        # allows 'string' also because we will be able to
+                        # change it to 'uuid' in the future.
+                        'id': {'type': ['integer', 'string']},
+                        'zone': {'type': 'string'},
+                        'host': {'type': 'string'},
+                        'state': {'type': 'string'},
+                        'binary': {'type': 'string'},
+                        'status': {'type': 'string'},
+                        'updated_at': {'type': 'string'},
+                        'disabled_reason': {'type': ['string', 'null']}
+                    },
+                    'required': ['id', 'zone', 'host', 'state', 'binary',
+                                 'status', 'updated_at', 'disabled_reason']
+                }
+            }
+        },
+        'required': ['services']
+    }
+}
diff --git a/tempest/api/compute/api_schema/__init__.py b/tempest/api_schema/compute/v2/__init__.py
similarity index 100%
copy from tempest/api/compute/api_schema/__init__.py
copy to tempest/api_schema/compute/v2/__init__.py
diff --git a/tempest/api_schema/compute/v2/volumes.py b/tempest/api_schema/compute/v2/volumes.py
new file mode 100644
index 0000000..16ed7c2
--- /dev/null
+++ b/tempest/api_schema/compute/v2/volumes.py
@@ -0,0 +1,56 @@
+# 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.
+
+get_volume = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'volume': {
+                'type': 'object',
+                'properties': {
+                    # NOTE: Now the type of 'id' is integer, but here allows
+                    # 'string' also because we will be able to change it to
+                    # 'uuid' in the future.
+                    'id': {'type': ['integer', 'string']},
+                    'status': {'type': 'string'},
+                    'displayName': {'type': ['string', 'null']},
+                    'availabilityZone': {'type': 'string'},
+                    'createdAt': {'type': 'string'},
+                    'displayDescription': {'type': ['string', 'null']},
+                    'volumeType': {'type': 'string'},
+                    'snapshotId': {'type': ['string', 'null']},
+                    'metadata': {'type': 'object'},
+                    'size': {'type': 'integer'},
+                    'attachments': {
+                        'type': 'array',
+                        'items': {
+                            'type': 'object',
+                            'properties': {
+                                'id': {'type': ['integer', 'string']},
+                                'device': {'type': 'string'},
+                                'volumeId': {'type': ['integer', 'string']},
+                                'serverId': {'type': ['integer', 'string']}
+                            }
+                        }
+                    }
+                },
+                'required': ['id', 'status', 'displayName', 'availabilityZone',
+                             'createdAt', 'displayDescription', 'volumeType',
+                             'snapshotId', 'metadata', 'size', 'attachments']
+            }
+        },
+        'required': ['volume']
+    }
+}
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index 8c4ec45..810e6b2 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -83,9 +83,10 @@
         return self.cmd_with_auth(
             'neutron', action, flags, params, admin, fail_ok)
 
-    def savanna(self, action, flags='', params='', admin=True, fail_ok=False):
-        """Executes savanna command for the given action."""
+    def sahara(self, action, flags='', params='', admin=True, fail_ok=False):
+        """Executes sahara command for the given action."""
         return self.cmd_with_auth(
+            # TODO (slukjanov): replace with sahara when new client released
             'savanna', action, flags, params, admin, fail_ok)
 
     def cmd_with_auth(self, cmd, action, flags='', params='',
diff --git a/tempest/cli/simple_read_only/test_savanna.py b/tempest/cli/simple_read_only/test_sahara.py
similarity index 61%
rename from tempest/cli/simple_read_only/test_savanna.py
rename to tempest/cli/simple_read_only/test_sahara.py
index 1e30978..cd819a4 100644
--- a/tempest/cli/simple_read_only/test_savanna.py
+++ b/tempest/cli/simple_read_only/test_sahara.py
@@ -25,8 +25,8 @@
 LOG = logging.getLogger(__name__)
 
 
-class SimpleReadOnlySavannaClientTest(cli.ClientTestBase):
-    """Basic, read-only tests for Savanna CLI client.
+class SimpleReadOnlySaharaClientTest(cli.ClientTestBase):
+    """Basic, read-only tests for Sahara CLI client.
 
     Checks return values and output of read-only commands.
     These tests do not presume any content, nor do they create
@@ -35,36 +35,36 @@
 
     @classmethod
     def setUpClass(cls):
-        if not CONF.service_available.savanna:
-            msg = "Skipping all Savanna cli tests because it is not available"
+        if not CONF.service_available.sahara:
+            msg = "Skipping all Sahara cli tests because it is not available"
             raise cls.skipException(msg)
-        super(SimpleReadOnlySavannaClientTest, cls).setUpClass()
+        super(SimpleReadOnlySaharaClientTest, cls).setUpClass()
 
     @test.attr(type='negative')
-    def test_savanna_fake_action(self):
+    def test_sahara_fake_action(self):
         self.assertRaises(subprocess.CalledProcessError,
-                          self.savanna,
+                          self.sahara,
                           'this-does-not-exist')
 
-    def test_savanna_plugins_list(self):
-        plugins = self.parser.listing(self.savanna('plugin-list'))
+    def test_sahara_plugins_list(self):
+        plugins = self.parser.listing(self.sahara('plugin-list'))
         self.assertTableStruct(plugins, ['name', 'versions', 'title'])
 
-    def test_savanna_plugins_show(self):
-        plugin = self.parser.listing(self.savanna('plugin-show',
-                                                  params='--name vanilla'))
+    def test_sahara_plugins_show(self):
+        plugin = self.parser.listing(self.sahara('plugin-show',
+                                                 params='--name vanilla'))
         self.assertTableStruct(plugin, ['Property', 'Value'])
 
-    def test_savanna_node_group_template_list(self):
-        plugins = self.parser.listing(self.savanna('node-group-template-list'))
+    def test_sahara_node_group_template_list(self):
+        plugins = self.parser.listing(self.sahara('node-group-template-list'))
         self.assertTableStruct(plugins, ['name', 'id', 'plugin_name',
                                          'node_processes', 'description'])
 
-    def test_savanna_cluster_template_list(self):
-        plugins = self.parser.listing(self.savanna('cluster-template-list'))
+    def test_sahara_cluster_template_list(self):
+        plugins = self.parser.listing(self.sahara('cluster-template-list'))
         self.assertTableStruct(plugins, ['name', 'id', 'plugin_name',
                                          'node_groups', 'description'])
 
-    def test_savanna_cluster_list(self):
-        plugins = self.parser.listing(self.savanna('cluster-list'))
+    def test_sahara_cluster_list(self):
+        plugins = self.parser.listing(self.sahara('cluster-list'))
         self.assertTableStruct(plugins, ['name', 'id', 'status', 'node_count'])
diff --git a/tempest/clients.py b/tempest/clients.py
index ab7deb0..7ebd983 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -61,6 +61,7 @@
     TenantUsagesClientJSON
 from tempest.services.compute.json.volumes_extensions_client import \
     VolumesExtensionsClientJSON
+from tempest.services.compute.v3.json.agents_client import AgentsV3ClientJSON
 from tempest.services.compute.v3.json.aggregates_client import \
     AggregatesV3ClientJSON
 from tempest.services.compute.v3.json.availability_zone_client import \
@@ -315,6 +316,7 @@
             self.services_v3_client = ServicesV3ClientJSON(
                 self.auth_provider)
             self.service_client = ServiceClientJSON(self.auth_provider)
+            self.agents_v3_client = AgentsV3ClientJSON(self.auth_provider)
             self.aggregates_v3_client = AggregatesV3ClientJSON(
                 self.auth_provider)
             self.aggregates_client = AggregatesClientJSON(
diff --git a/tempest/common/commands.py b/tempest/common/commands.py
index 6405eaa..c31a038 100644
--- a/tempest/common/commands.py
+++ b/tempest/common/commands.py
@@ -73,3 +73,7 @@
 
 def iptables_ns(ns, table):
     return ip_ns_exec(ns, "iptables -v -S -t " + table)
+
+
+def ovs_db_dump():
+    return sudo_cmd_call("ovsdb-client dump")
diff --git a/tempest/common/debug.py b/tempest/common/debug.py
index 8325d4d..6a496c2 100644
--- a/tempest/common/debug.py
+++ b/tempest/common/debug.py
@@ -38,3 +38,15 @@
         for table in ['filter', 'nat', 'mangle']:
             LOG.info('ns(%s) table(%s):\n%s', ns, table,
                      commands.iptables_ns(ns, table))
+
+
+def log_ovs_db():
+    if not CONF.debug.enable or not CONF.service_available.neutron:
+        return
+    db_dump = commands.ovs_db_dump()
+    LOG.info("OVS DB:\n" + db_dump)
+
+
+def log_net_debug():
+    log_ip_ns()
+    log_ovs_db()
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 36ddb40..88dbe58 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -21,6 +21,8 @@
 import re
 import time
 
+import jsonschema
+
 from tempest.common import http
 from tempest import config
 from tempest import exceptions
@@ -502,6 +504,31 @@
                    % self.__class__.__name__)
         raise NotImplementedError(message)
 
+    @classmethod
+    def validate_response(cls, schema, resp, body):
+        # Only check the response if the status code is a success code
+        # TODO(cyeoh): Eventually we should be able to verify that a failure
+        # 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'):
+            response_code = schema['status_code']
+            if resp.status not in response_code:
+                msg = ("The status code(%s) is different than the expected "
+                       "one(%s)") % (resp.status, response_code)
+                raise exceptions.InvalidHttpSuccessCode(msg)
+            response_schema = schema.get('response_body')
+            if response_schema:
+                try:
+                    jsonschema.validate(body, response_schema)
+                except jsonschema.ValidationError as ex:
+                    msg = ("HTTP response body is invalid (%s)") % ex
+                    raise exceptions.InvalidHTTPResponseBody(msg)
+            else:
+                if body:
+                    msg = ("HTTP response body should not exist (%s)") % body
+                    raise exceptions.InvalidHTTPResponseBody(msg)
+
 
 class NegativeRestClient(RestClient):
     """
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 8420ad0..00e5e0d 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -43,6 +43,9 @@
                                      ssh_timeout, pkey=pkey,
                                      channel_timeout=ssh_channel_timeout)
 
+    def exec_command(self, cmd):
+        return self.ssh_client.exec_command(cmd)
+
     def validate_authentication(self):
         """Validate ssh connection and authentication
            This method raises an Exception when the validation fails.
@@ -51,33 +54,33 @@
 
     def hostname_equals_servername(self, expected_hostname):
         # Get host name using command "hostname"
-        actual_hostname = self.ssh_client.exec_command("hostname").rstrip()
+        actual_hostname = self.exec_command("hostname").rstrip()
         return expected_hostname == actual_hostname
 
     def get_files(self, path):
         # Return a list of comma separated files
         command = "ls -m " + path
-        return self.ssh_client.exec_command(command).rstrip('\n').split(', ')
+        return self.exec_command(command).rstrip('\n').split(', ')
 
     def get_ram_size_in_mb(self):
-        output = self.ssh_client.exec_command('free -m | grep Mem')
+        output = self.exec_command('free -m | grep Mem')
         if output:
             return output.split()[1]
 
     def get_number_of_vcpus(self):
         command = 'cat /proc/cpuinfo | grep processor | wc -l'
-        output = self.ssh_client.exec_command(command)
+        output = self.exec_command(command)
         return int(output)
 
     def get_partitions(self):
         # Return the contents of /proc/partitions
         command = 'cat /proc/partitions'
-        output = self.ssh_client.exec_command(command)
+        output = self.exec_command(command)
         return output
 
     def get_boot_time(self):
         cmd = 'cut -f1 -d. /proc/uptime'
-        boot_secs = self.ssh_client.exec_command(cmd)
+        boot_secs = self.exec_command(cmd)
         boot_time = time.time() - int(boot_secs)
         return time.localtime(boot_time)
 
@@ -85,27 +88,27 @@
         message = re.sub("([$\\`])", "\\\\\\\\\\1", message)
         # usually to /dev/ttyS0
         cmd = 'sudo sh -c "echo \\"%s\\" >/dev/console"' % message
-        return self.ssh_client.exec_command(cmd)
+        return self.exec_command(cmd)
 
     def ping_host(self, host):
         cmd = 'ping -c1 -w1 %s' % host
-        return self.ssh_client.exec_command(cmd)
+        return self.exec_command(cmd)
 
     def get_mac_address(self):
         cmd = "/sbin/ifconfig | awk '/HWaddr/ {print $5}'"
-        return self.ssh_client.exec_command(cmd)
+        return self.exec_command(cmd)
 
     def get_ip_list(self):
         cmd = "/bin/ip address"
-        return self.ssh_client.exec_command(cmd)
+        return self.exec_command(cmd)
 
     def assign_static_ip(self, nic, addr):
         cmd = "sudo /bin/ip addr add {ip}/{mask} dev {nic}".format(
             ip=addr, mask=CONF.network.tenant_network_mask_bits,
             nic=nic
         )
-        return self.ssh_client.exec_command(cmd)
+        return self.exec_command(cmd)
 
     def turn_nic_on(self, nic):
         cmd = "sudo /bin/ip link set {nic} up".format(nic=nic)
-        return self.ssh_client.exec_command(cmd)
+        return self.exec_command(cmd)
diff --git a/tempest/config.py b/tempest/config.py
index 46dcbcc..471a0de 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -60,16 +60,16 @@
                         'publicURL', 'adminURL', 'internalURL'],
                help="The endpoint type to use for the identity service."),
     cfg.StrOpt('username',
-               default='demo',
+               default=None,
                help="Username to use for Nova API requests."),
     cfg.StrOpt('tenant_name',
-               default='demo',
+               default=None,
                help="Tenant name to use for Nova API requests."),
     cfg.StrOpt('admin_role',
                default='admin',
                help="Role required to administrate keystone."),
     cfg.StrOpt('password',
-               default='pass',
+               default=None,
                help="API key to use when authenticating.",
                secret=True),
     cfg.StrOpt('alt_username',
@@ -85,15 +85,15 @@
                help="API key to use when authenticating as alternate user.",
                secret=True),
     cfg.StrOpt('admin_username',
-               default='admin',
+               default=None,
                help="Administrative Username to use for "
                     "Keystone API requests."),
     cfg.StrOpt('admin_tenant_name',
-               default='admin',
+               default=None,
                help="Administrative Tenant name to use for Keystone API "
                     "requests."),
     cfg.StrOpt('admin_password',
-               default='pass',
+               default=None,
                help="API key to use when authenticating as admin.",
                secret=True),
 ]
@@ -246,9 +246,6 @@
                 default=False,
                 help="Does the test environment support changing the admin "
                      "password?"),
-    cfg.BoolOpt('create_image',
-                default=False,
-                help="Does the test environment support snapshots?"),
     cfg.BoolOpt('resize',
                 default=False,
                 help="Does the test environment support resizing?"),
@@ -276,14 +273,14 @@
 
 ComputeAdminGroup = [
     cfg.StrOpt('username',
-               default='admin',
+               default=None,
                help="Administrative Username to use for Nova API requests."),
     cfg.StrOpt('tenant_name',
-               default='admin',
+               default=None,
                help="Administrative Tenant name to use for Nova API "
                     "requests."),
     cfg.StrOpt('password',
-               default='pass',
+               default=None,
                help="API key to use when authenticating as admin.",
                secret=True),
 ]
@@ -366,6 +363,14 @@
                default="",
                help="Id of the public router that provides external "
                     "connectivity"),
+    cfg.IntOpt('build_timeout',
+               default=300,
+               help="Timeout in seconds to wait for network operation to "
+                    "complete."),
+    cfg.IntOpt('build_interval',
+               default=10,
+               help="Time in seconds between network operation status "
+                    "checks."),
 ]
 
 network_feature_group = cfg.OptGroup(name='network-feature-enabled',
@@ -621,6 +626,9 @@
     cfg.StrOpt('aws_access',
                default=None,
                help="AWS Access Key"),
+    cfg.StrOpt('aws_zone',
+               default="nova",
+               help="AWS Zone for EC2 tests"),
     cfg.StrOpt('s3_materials_path',
                default="/opt/stack/devstack/files/images/"
                        "s3-materials/cirros-0.3.0",
@@ -753,9 +761,9 @@
     cfg.BoolOpt('horizon',
                 default=True,
                 help="Whether or not Horizon is expected to be available"),
-    cfg.BoolOpt('savanna',
+    cfg.BoolOpt('sahara',
                 default=False,
-                help="Whether or not Savanna is expected to be available"),
+                help="Whether or not Sahara is expected to be available"),
     cfg.BoolOpt('ironic',
                 default=False,
                 help="Whether or not Ironic is expected to be available"),
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 39b7760..24d2677 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -97,7 +97,7 @@
         except Exception:
             LOG.exception('ssh to server failed')
             self._log_console_output()
-            debug.log_ip_ns()
+            debug.log_net_debug()
             raise
 
     def check_partitions(self):
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 489b271..d5ab3d3 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -172,7 +172,7 @@
         except Exception:
             LOG.exception('Tenant connectivity check failed')
             self._log_console_output(servers=self.servers.keys())
-            debug.log_ip_ns()
+            debug.log_net_debug()
             raise
 
     def _create_and_associate_floating_ips(self):
@@ -204,7 +204,7 @@
                 ex_msg += ": " + msg
             LOG.exception(ex_msg)
             self._log_console_output(servers=self.servers.keys())
-            debug.log_ip_ns()
+            debug.log_net_debug()
             raise
 
     def _disassociate_floating_ips(self):
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index d404dd1..b9ee040 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -343,7 +343,7 @@
                                                             should_succeed),
                             msg)
         except Exception:
-            debug.log_ip_ns()
+            debug.log_net_debug()
             raise
 
     def _test_in_tenant_block(self, tenant):
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 37beb07..562020a 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -45,11 +45,10 @@
 
     def _ssh_to_server(self, server_or_ip):
         try:
-            linux_client = self.get_remote_client(server_or_ip)
+            return self.get_remote_client(server_or_ip)
         except Exception:
             LOG.exception()
             self._log_console_output()
-        return linux_client.ssh_client
 
     def _write_timestamp(self, server_or_ip):
         ssh_client = self._ssh_to_server(server_or_ip)
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 841f9e1..128ec17 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -72,8 +72,7 @@
         server.add_floating_ip(floating_ip)
 
     def _ssh_to_server(self, server_or_ip):
-        linux_client = self.get_remote_client(server_or_ip)
-        return linux_client.ssh_client
+        return self.get_remote_client(server_or_ip)
 
     def _create_volume_snapshot(self, volume):
         snapshot_name = data_utils.rand_name('scenario-snapshot-')
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 9a250d7..4046cbd 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -53,7 +53,7 @@
             'block_device_mapping': bd_map,
             'key_name': keypair.name
         }
-        return self.create_server(create_kwargs=create_kwargs)
+        return self.create_server(image='', create_kwargs=create_kwargs)
 
     def _create_snapshot_from_volume(self, vol_id):
         volume_snapshots = self.volume_client.volume_snapshots
@@ -101,14 +101,13 @@
             ip = server.networks[network_name_for_ssh][0]
 
         try:
-            client = self.get_remote_client(
+            return self.get_remote_client(
                 ip,
                 private_key=keypair.private_key)
         except Exception:
             LOG.exception('ssh to server failed')
             self._log_console_output()
             raise
-        return client.ssh_client
 
     def _get_content(self, ssh_client):
         return ssh_client.exec_command('cat /tmp/text')
diff --git a/tempest/services/botoclients.py b/tempest/services/botoclients.py
index b52d48c..7616a99 100644
--- a/tempest/services/botoclients.py
+++ b/tempest/services/botoclients.py
@@ -179,19 +179,6 @@
                            'revoke_security_group',
                            'revoke_security_group_egress'))
 
-    def get_good_zone(self):
-        """
-        :rtype: BaseString
-        :return: Returns with the first available zone name
-        """
-        for zone in self.get_all_zones():
-            # NOTE(afazekas): zone.region_name was None
-            if (zone.state == "available" and
-                zone.region.name == self.connection_data["region"].name):
-                return zone.name
-        else:
-            raise IndexError("Don't have a good zone")
-
 
 class ObjectClientS3(BotoClientBase):
 
diff --git a/tempest/services/compute/json/quotas_client.py b/tempest/services/compute/json/quotas_client.py
index 459ab6d..c1ac3db 100644
--- a/tempest/services/compute/json/quotas_client.py
+++ b/tempest/services/compute/json/quotas_client.py
@@ -100,3 +100,7 @@
 
         body = json.loads(body)
         return resp, body['quota_set']
+
+    def delete_quota_set(self, tenant_id):
+        """Delete the tenant's quota set."""
+        return self.delete('os-quota-sets/%s' % str(tenant_id))
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index 70d075a..ca0f114 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -440,3 +440,8 @@
     def inject_network_info(self, server_id, **kwargs):
         """Inject the Network Info into server"""
         return self.action(server_id, 'injectNetworkInfo', None, **kwargs)
+
+    def get_vnc_console(self, server_id, console_type):
+        """Get URL of VNC console."""
+        return self.action(server_id, "os-getVNCConsole",
+                           "console", type=console_type)
diff --git a/tempest/services/compute/json/services_client.py b/tempest/services/compute/json/services_client.py
index 1ab25ec..0f7d4cb 100644
--- a/tempest/services/compute/json/services_client.py
+++ b/tempest/services/compute/json/services_client.py
@@ -17,6 +17,7 @@
 import json
 import urllib
 
+from tempest.api_schema.compute import services as schema
 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(schema.list_services, resp, body)
         return resp, body['services']
 
     def enable_service(self, host_name, binary):
diff --git a/tempest/services/compute/json/volumes_extensions_client.py b/tempest/services/compute/json/volumes_extensions_client.py
index 5ef11ed..451dbac 100644
--- a/tempest/services/compute/json/volumes_extensions_client.py
+++ b/tempest/services/compute/json/volumes_extensions_client.py
@@ -17,6 +17,7 @@
 import time
 import urllib
 
+from tempest.api_schema.compute.v2 import volumes as schema
 from tempest.common import rest_client
 from tempest import config
 from tempest import exceptions
@@ -58,6 +59,7 @@
         url = "os-volumes/%s" % str(volume_id)
         resp, body = self.get(url)
         body = json.loads(body)
+        self.validate_response(schema.get_volume, resp, body)
         return resp, body['volume']
 
     def create_volume(self, size, **kwargs):
diff --git a/tempest/services/compute/v3/json/agents_client.py b/tempest/services/compute/v3/json/agents_client.py
new file mode 100644
index 0000000..6893af2
--- /dev/null
+++ b/tempest/services/compute/v3/json/agents_client.py
@@ -0,0 +1,52 @@
+# 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.
+
+import json
+import urllib
+
+from tempest.common import rest_client
+from tempest import config
+
+CONF = config.CONF
+
+
+class AgentsV3ClientJSON(rest_client.RestClient):
+
+    def __init__(self, auth_provider):
+        super(AgentsV3ClientJSON, self).__init__(auth_provider)
+        self.service = CONF.compute.catalog_v3_type
+
+    def list_agents(self, params=None):
+        """List all agent builds."""
+        url = 'os-agents'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        return resp, self._parse_resp(body)
+
+    def create_agent(self, **kwargs):
+        """Create an agent build."""
+        post_body = json.dumps({'agent': kwargs})
+        resp, body = self.post('os-agents', post_body)
+        return resp, self._parse_resp(body)
+
+    def delete_agent(self, agent_id):
+        """Delete an existing agent build."""
+        return self.delete('os-agents/%s' % str(agent_id))
+
+    def update_agent(self, agent_id, **kwargs):
+        """Update an agent build."""
+        put_body = json.dumps({'agent': kwargs})
+        resp, body = self.put('os-agents/%s' % str(agent_id), put_body)
+        return resp, self._parse_resp(body)
diff --git a/tempest/services/compute/v3/json/services_client.py b/tempest/services/compute/v3/json/services_client.py
index b4e65a0..88c4d16 100644
--- a/tempest/services/compute/v3/json/services_client.py
+++ b/tempest/services/compute/v3/json/services_client.py
@@ -17,6 +17,7 @@
 import json
 import urllib
 
+from tempest.api_schema.compute import services as schema
 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(schema.list_services, resp, body)
         return resp, body['services']
 
     def enable_service(self, host_name, binary):
diff --git a/tempest/services/compute/xml/quotas_client.py b/tempest/services/compute/xml/quotas_client.py
index b8b759f..85e481c 100644
--- a/tempest/services/compute/xml/quotas_client.py
+++ b/tempest/services/compute/xml/quotas_client.py
@@ -121,3 +121,7 @@
         body = xml_to_json(etree.fromstring(body))
         body = self._format_quota(body)
         return resp, body
+
+    def delete_quota_set(self, tenant_id):
+        """Delete the tenant's quota set."""
+        return self.delete('os-quota-sets/%s' % str(tenant_id))
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index 4d3646c..1215b80 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -645,3 +645,8 @@
     def inject_network_info(self, server_id, **kwargs):
         """Inject the Network Info into server"""
         return self.action(server_id, 'injectNetworkInfo', None, **kwargs)
+
+    def get_vnc_console(self, server_id, console_type):
+        """Get URL of VNC console."""
+        return self.action(server_id, "os-getVNCConsole",
+                           "console", type=console_type)
diff --git a/tempest/services/database/json/flavors_client.py b/tempest/services/database/json/flavors_client.py
index 1a8a4c1..2ec0405 100644
--- a/tempest/services/database/json/flavors_client.py
+++ b/tempest/services/database/json/flavors_client.py
@@ -13,9 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import urllib
+
 from tempest.common import rest_client
 from tempest import config
-import urllib
 
 CONF = config.CONF
 
diff --git a/tempest/services/network/network_client_base.py b/tempest/services/network/network_client_base.py
index f1bf548..41a7aa4 100644
--- a/tempest/services/network/network_client_base.py
+++ b/tempest/services/network/network_client_base.py
@@ -10,9 +10,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import time
 import urllib
 
 from tempest import config
+from tempest import exceptions
 
 CONF = config.CONF
 
@@ -54,6 +56,8 @@
         self.rest_client.service = CONF.network.catalog_type
         self.version = '2.0'
         self.uri_prefix = "v%s" % (self.version)
+        self.build_timeout = CONF.network.build_timeout
+        self.build_interval = CONF.network.build_interval
 
     def get_rest_client(self, auth_provider):
         raise NotImplementedError
@@ -189,3 +193,23 @@
         resp, body = self.post(uri, body)
         body = {'ports': self.deserialize_list(body)}
         return resp, body
+
+    def wait_for_resource_deletion(self, resource_type, id):
+        """Waits for a resource to be deleted."""
+        start_time = int(time.time())
+        while True:
+            if self.is_resource_deleted(resource_type, id):
+                return
+            if int(time.time()) - start_time >= self.build_timeout:
+                raise exceptions.TimeoutException
+            time.sleep(self.build_interval)
+
+    def is_resource_deleted(self, resource_type, id):
+        method = 'show_' + resource_type
+        try:
+            getattr(self, method)(id)
+        except AttributeError:
+            raise Exception("Unknown resource type %s " % resource_type)
+        except exceptions.NotFound:
+            return True
+        return False
diff --git a/tempest/stress/actions/ssh_floating.py b/tempest/stress/actions/ssh_floating.py
index a34a20d..c330165 100644
--- a/tempest/stress/actions/ssh_floating.py
+++ b/tempest/stress/actions/ssh_floating.py
@@ -69,7 +69,7 @@
         servers_client = self.manager.servers_client
         self.logger.info("creating %s" % name)
         vm_args = self.vm_extra_args.copy()
-        vm_args['security_groups'] = [{'name': self.sec_grp}]
+        vm_args['security_groups'] = [self.sec_grp]
         resp, server = servers_client.create_server(name, self.image,
                                                     self.flavor,
                                                     **vm_args)
@@ -90,16 +90,15 @@
         sec_grp_cli = self.manager.security_groups_client
         s_name = data_utils.rand_name('sec_grp-')
         s_description = data_utils.rand_name('desc-')
-        _, _sec_grp = sec_grp_cli.create_security_group(s_name,
-                                                        s_description)
-        self.sec_grp = _sec_grp['id']
+        _, self.sec_grp = sec_grp_cli.create_security_group(s_name,
+                                                            s_description)
         create_rule = sec_grp_cli.create_security_group_rule
-        create_rule(self.sec_grp, 'tcp', 22, 22)
-        create_rule(self.sec_grp, 'icmp', -1, -1)
+        create_rule(self.sec_grp['id'], 'tcp', 22, 22)
+        create_rule(self.sec_grp['id'], 'icmp', -1, -1)
 
     def _destroy_sec_grp(self):
         sec_grp_cli = self.manager.security_groups_client
-        sec_grp_cli.delete_security_group(self.sec_grp)
+        sec_grp_cli.delete_security_group(self.sec_grp['id'])
 
     def _create_floating_ip(self):
         floating_cli = self.manager.floating_ips_client
diff --git a/tempest/stress/cleanup.py b/tempest/stress/cleanup.py
index b46de35..2587331 100644
--- a/tempest/stress/cleanup.py
+++ b/tempest/stress/cleanup.py
@@ -45,6 +45,16 @@
         except Exception:
             pass
 
+    secgrp_client = admin_manager.security_groups_client
+    _, secgrp = secgrp_client.list_security_groups({"all_tenants": True})
+    secgrp_del = [grp for grp in secgrp if grp['name'] != 'default']
+    LOG.info("Cleanup::remove %s Security Group" % len(secgrp_del))
+    for g in secgrp_del:
+        try:
+            secgrp_client.delete_security_group(g['id'])
+        except Exception:
+            pass
+
     _, floating_ips = admin_manager.floating_ips_client.list_floating_ips()
     LOG.info("Cleanup::remove %s floating ips" % len(floating_ips))
     for f in floating_ips:
diff --git a/tempest/test.py b/tempest/test.py
index d358510..6290761 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -26,6 +26,8 @@
 import testresources
 import testtools
 
+from oslo.config import cfg
+
 from tempest import clients
 import tempest.common.generator.valid_generator as valid
 from tempest.common import isolated_creds
@@ -64,6 +66,25 @@
     return decorator
 
 
+def safe_setup(f):
+    """A decorator used to wrap the setUpClass for cleaning up resources
+       when setUpClass failed.
+    """
+
+    def decorator(cls):
+            try:
+                f(cls)
+            except Exception as se:
+                LOG.exception("setUpClass failed: %s" % se)
+                try:
+                    cls.tearDownClass()
+                except Exception as te:
+                    LOG.exception("tearDownClass failed: %s" % te)
+                raise se
+
+    return decorator
+
+
 def services(*args, **kwargs):
     """A decorator used to set an attr for each service used in a test case
 
@@ -410,7 +431,17 @@
         """
         description = NegativeAutoTest.load_schema(description_file)
         LOG.debug(description)
-        generator = importutils.import_class(CONF.negative.test_generator)()
+
+        # NOTE(mkoderer): since this will be executed on import level the
+        # config doesn't have to be in place (e.g. for the pep8 job).
+        # In this case simply return.
+        try:
+            generator = importutils.import_class(
+                CONF.negative.test_generator)()
+        except cfg.ConfigFilesNotFoundError:
+            LOG.critical(
+                "Tempest config not found. Test scenarios aren't created")
+            return
         generator.validate_schema(description)
         schema = description.get("json-schema", None)
         resources = description.get("resources", [])
diff --git a/tempest/api/compute/api_schema/__init__.py b/tempest/tests/common/__init__.py
similarity index 100%
copy from tempest/api/compute/api_schema/__init__.py
copy to tempest/tests/common/__init__.py
diff --git a/tempest/api/compute/api_schema/__init__.py b/tempest/tests/common/utils/__init__.py
similarity index 100%
copy from tempest/api/compute/api_schema/__init__.py
copy to tempest/tests/common/utils/__init__.py
diff --git a/tempest/tests/common/utils/test_data_utils.py b/tempest/tests/common/utils/test_data_utils.py
new file mode 100644
index 0000000..7aafdb2
--- /dev/null
+++ b/tempest/tests/common/utils/test_data_utils.py
@@ -0,0 +1,77 @@
+# 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.common.utils import data_utils
+from tempest.tests import base
+
+
+class TestDataUtils(base.TestCase):
+
+    def test_rand_uuid(self):
+        actual = data_utils.rand_uuid()
+        self.assertIsInstance(actual, str)
+        self.assertRegexpMatches(actual, "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]"
+                                         "{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
+        actual2 = data_utils.rand_uuid()
+        self.assertNotEqual(actual, actual2)
+
+    def test_rand_uuid_hex(self):
+        actual = data_utils.rand_uuid_hex()
+        self.assertIsInstance(actual, str)
+        self.assertRegexpMatches(actual, "^[0-9a-f]{32}$")
+
+        actual2 = data_utils.rand_uuid_hex()
+        self.assertNotEqual(actual, actual2)
+
+    def test_rand_name(self):
+        actual = data_utils.rand_name()
+        self.assertIsInstance(actual, str)
+        actual2 = data_utils.rand_name()
+        self.assertNotEqual(actual, actual2)
+
+        actual = data_utils.rand_name('foo')
+        self.assertTrue(actual.startswith('foo'))
+        actual2 = data_utils.rand_name('foo')
+        self.assertTrue(actual.startswith('foo'))
+        self.assertNotEqual(actual, actual2)
+
+    def test_rand_int(self):
+        actual = data_utils.rand_int_id()
+        self.assertIsInstance(actual, int)
+
+        actual2 = data_utils.rand_int_id()
+        self.assertNotEqual(actual, actual2)
+
+    def test_rand_mac_address(self):
+        actual = data_utils.rand_mac_address()
+        self.assertIsInstance(actual, str)
+        self.assertRegexpMatches(actual, "^([0-9a-f][0-9a-f]:){5}"
+                                         "[0-9a-f][0-9a-f]$")
+
+        actual2 = data_utils.rand_mac_address()
+        self.assertNotEqual(actual, actual2)
+
+    def test_parse_image_id(self):
+        actual = data_utils.parse_image_id("/foo/bar/deadbeaf")
+        self.assertEqual("deadbeaf", actual)
+
+    def test_arbitrary_string(self):
+        actual = data_utils.arbitrary_string()
+        self.assertEqual(actual, "test")
+        actual = data_utils.arbitrary_string(size=30, base_text="abc")
+        self.assertEqual(actual, "abc" * (30 / len("abc")))
+        actual = data_utils.arbitrary_string(size=5, base_text="deadbeaf")
+        self.assertEqual(actual, "deadb")
diff --git a/tempest/tests/test_waiters.py b/tempest/tests/test_waiters.py
new file mode 100644
index 0000000..1f9825e
--- /dev/null
+++ b/tempest/tests/test_waiters.py
@@ -0,0 +1,49 @@
+# Copyright 2014 IBM Corp.
+#
+#    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 mock
+
+from tempest.common import waiters
+from tempest import exceptions
+from tempest.tests import base
+
+
+class TestImageWaiters(base.TestCase):
+    def setUp(self):
+        super(TestImageWaiters, self).setUp()
+        self.client = mock.MagicMock()
+        self.client.build_timeout = 1
+        self.client.build_interval = 1
+
+    def test_wait_for_image_status(self):
+        self.client.get_image.return_value = (None, {'status': 'active'})
+        start_time = int(time.time())
+        waiters.wait_for_image_status(self.client, 'fake_image_id', 'active')
+        end_time = int(time.time())
+        # Ensure waiter returns before build_timeout
+        self.assertTrue((end_time - start_time) < 10)
+
+    def test_wait_for_image_status_timeout(self):
+        self.client.get_image.return_value = (None, {'status': 'saving'})
+        self.assertRaises(exceptions.TimeoutException,
+                          waiters.wait_for_image_status,
+                          self.client, 'fake_image_id', 'active')
+
+    def test_wait_for_image_status_error_on_image_create(self):
+        self.client.get_image.return_value = (None, {'status': 'ERROR'})
+        self.assertRaises(exceptions.AddImageException,
+                          waiters.wait_for_image_status,
+                          self.client, 'fake_image_id', 'active')
diff --git a/tempest/thirdparty/boto/test.py b/tempest/thirdparty/boto/test.py
index 10d421e..4c39f78 100644
--- a/tempest/thirdparty/boto/test.py
+++ b/tempest/thirdparty/boto/test.py
@@ -108,6 +108,9 @@
     CODE_RE = '.*'  # regexp makes sense in group match
 
     def match(self, exc):
+        """:returns: Retruns with an error string if not matches,
+               returns with None when matches.
+        """
         if not isinstance(exc, exception.BotoServerError):
             return "%r not an BotoServerError instance" % exc
         LOG.info("Status: %s , error_code: %s", exc.status, exc.error_code)
@@ -119,6 +122,7 @@
             return ("Error code (%s) does not match" +
                     "the expected re pattern \"%s\"") %\
                    (exc.error_code, self.CODE_RE)
+        return None
 
 
 class ClientError(BotoExceptionMatcher):
@@ -313,7 +317,7 @@
             except ValueError:
                 return "_GONE"
             except exception.EC2ResponseError as exc:
-                if colusure_matcher.match(exc):
+                if colusure_matcher.match(exc) is None:
                     return "_GONE"
                 else:
                     raise
@@ -449,7 +453,7 @@
                 return "_GONE"
             except exception.EC2ResponseError as exc:
                 if cls.ec2_error_code.\
-                        client.InvalidInstanceID.NotFound.match(exc):
+                        client.InvalidInstanceID.NotFound.match(exc) is None:
                     return "_GONE"
                 # NOTE(afazekas): incorrect code,
                 # but the resource must be destoreyd
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index bbfbb79..e6a1638 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -40,7 +40,7 @@
                                     ": requires ami/aki/ari manifest")))
         cls.s3_client = cls.os.s3_client
         cls.ec2_client = cls.os.ec2api_client
-        cls.zone = cls.ec2_client.get_good_zone()
+        cls.zone = CONF.boto.aws_zone
         cls.materials_path = CONF.boto.s3_materials_path
         ami_manifest = CONF.boto.ami_manifest
         aki_manifest = CONF.boto.aki_manifest
diff --git a/tempest/thirdparty/boto/test_ec2_volumes.py b/tempest/thirdparty/boto/test_ec2_volumes.py
index 6a771e5..12dea18 100644
--- a/tempest/thirdparty/boto/test_ec2_volumes.py
+++ b/tempest/thirdparty/boto/test_ec2_volumes.py
@@ -38,7 +38,7 @@
             raise cls.skipException(skip_msg)
 
         cls.client = cls.os.ec2api_client
-        cls.zone = cls.client.get_good_zone()
+        cls.zone = CONF.boto.aws_zone
 
     @test.attr(type='smoke')
     def test_create_get_delete(self):