Merge "Relax check for router interface in _verify_network_details"
diff --git a/etc/accounts.yaml.sample b/etc/accounts.yaml.sample
new file mode 100644
index 0000000..d191769
--- /dev/null
+++ b/etc/accounts.yaml.sample
@@ -0,0 +1,7 @@
+- username: 'user_1'
+  tenant_name: 'test_tenant_1'
+  password: 'test_password'
+
+- username: 'user_2'
+  tenant_name: 'test_tenant_2'
+  password: 'test_password'
diff --git a/etc/schemas/compute/flavors/flavor_details.json b/etc/schemas/compute/flavors/flavor_details.json
deleted file mode 100644
index c16075c..0000000
--- a/etc/schemas/compute/flavors/flavor_details.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-    "name": "get-flavor-details",
-    "http-method": "GET",
-    "url": "flavors/%s",
-    "resources": [
-        {"name": "flavor", "expected_result": 404}
-    ]
-}
diff --git a/etc/schemas/compute/flavors/flavor_details_v3.json b/etc/schemas/compute/flavors/flavor_details_v3.json
deleted file mode 100644
index d1c1077..0000000
--- a/etc/schemas/compute/flavors/flavor_details_v3.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-    "name": "get-flavor-details",
-    "http-method": "GET",
-    "url": "flavors/%s",
-    "resources": ["flavor"]
-}
diff --git a/etc/schemas/compute/flavors/flavors_list.json b/etc/schemas/compute/flavors/flavors_list.json
deleted file mode 100644
index eb8383b..0000000
--- a/etc/schemas/compute/flavors/flavors_list.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-    "name": "list-flavors-with-detail",
-    "http-method": "GET",
-    "url": "flavors/detail",
-    "json-schema": {
-        "type": "object",
-        "properties": {
-            "minRam": {
-                "type": "integer",
-                "results": {
-                    "gen_none": 400,
-                    "gen_string": 400
-                }
-            },
-            "minDisk": {
-                "type": "integer",
-                "results": {
-                    "gen_none": 400,
-                    "gen_string": 400
-                }
-            }
-        }
-    }
-}
diff --git a/etc/schemas/compute/flavors/flavors_list_v3.json b/etc/schemas/compute/flavors/flavors_list_v3.json
deleted file mode 100644
index d5388b3..0000000
--- a/etc/schemas/compute/flavors/flavors_list_v3.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-    "name": "list-flavors-with-detail",
-    "http-method": "GET",
-    "url": "flavors/detail",
-    "json-schema": {
-        "type": "object",
-        "properties": {
-            "min_ram": {
-                "type": "integer",
-                "results": {
-                    "gen_none": 400,
-                    "gen_string": 400
-                }
-            },
-            "min_disk": {
-                "type": "integer",
-                "results": {
-                    "gen_none": 400,
-                    "gen_string": 400
-                }
-            }
-        }
-    }
-}
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 96ef11a..9ace4ea 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -105,6 +105,17 @@
 #syslog_log_facility=LOG_USER
 
 
+[auth]
+
+#
+# Options defined in tempest.config
+#
+
+# Path to the yaml file that contains the list of credentials
+# to use for running tests (string value)
+#test_accounts_file=etc/accounts.yaml
+
+
 [baremetal]
 
 #
@@ -386,6 +397,9 @@
 # If false, skip all nova v3 tests. (boolean value)
 #api_v3=false
 
+# If false skip all v2 api tests with xml (boolean value)
+#xml_api_v2=true
+
 # If false, skip disk config tests (boolean value)
 #disk_config=true
 
@@ -405,6 +419,10 @@
 # password? (boolean value)
 #change_password=false
 
+# Does the test environment support obtaining instance serial
+# console output? (boolean value)
+#console_output=true
+
 # Does the test environment support resizing? (boolean value)
 #resize=false
 
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index c2376c9..3485943 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -145,7 +145,7 @@
         self.assertEqual(200, resp.status)
         self.assertIn((aggregate_id, new_aggregate_name, new_az_name),
                       map(lambda x:
-                         (x['id'], x['name'], x['availability_zone']),
+                          (x['id'], x['name'], x['availability_zone']),
                           aggregates))
 
     @test.attr(type='gate')
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index a3295eb..343a39a 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -37,6 +37,9 @@
     def setUpClass(cls):
         cls.set_network_resources()
         super(BaseComputeTest, cls).setUpClass()
+        if getattr(cls, '_interface', None) == 'xml' and cls._api_version == 2:
+            if not CONF.compute_feature_enabled.xml_api_v2:
+                raise cls.skipException('XML API is not enabled')
 
         # TODO(andreaf) WE should care also for the alt_manager here
         # but only once client lazy load in the manager is done
diff --git a/tempest/api/compute/flavors/test_flavors_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
index 1638f2d..7672fc6 100644
--- a/tempest/api/compute/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -13,8 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-
 from tempest.api.compute import base
+from tempest.api_schema.request.compute.v2 import flavors
 from tempest import test
 
 
@@ -25,14 +25,14 @@
 class FlavorsListWithDetailsNegativeTestJSON(base.BaseV2ComputeTest,
                                              test.NegativeAutoTest):
     _service = 'compute'
-    _schema_file = 'compute/flavors/flavors_list.json'
+    _schema = flavors.flavor_list
 
 
 @test.SimpleNegativeAutoTest
 class FlavorDetailsNegativeTestJSON(base.BaseV2ComputeTest,
                                     test.NegativeAutoTest):
     _service = 'compute'
-    _schema_file = 'compute/flavors/flavor_details.json'
+    _schema = flavors.flavors_details
 
     @classmethod
     def setUpClass(cls):
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index ee525e7..005f38a 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -15,9 +15,9 @@
 
 import base64
 import logging
+import urlparse
 
 import testtools
-import urlparse
 
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
@@ -348,6 +348,8 @@
         lines = len(output.split('\n'))
         self.assertEqual(lines, 10)
 
+    @testtools.skipUnless(CONF.compute_feature_enabled.console_output,
+                          'Console output not supported.')
     @test.attr(type='gate')
     def test_get_console_output(self):
         # Positive test:Should be able to GET the console output
@@ -364,6 +366,8 @@
 
         self.wait_for(self._get_output)
 
+    @testtools.skipUnless(CONF.compute_feature_enabled.console_output,
+                          'Console output not supported.')
     @test.attr(type='gate')
     def test_get_console_output_server_id_in_shutoff_status(self):
         # Positive test:Should be able to GET the console output
diff --git a/tempest/api/compute/test_authorization.py b/tempest/api/compute/test_authorization.py
index fb8ded3..3fa4a89 100644
--- a/tempest/api/compute/test_authorization.py
+++ b/tempest/api/compute/test_authorization.py
@@ -62,9 +62,9 @@
 
         name = data_utils.rand_name('image')
         resp, body = cls.glance_client.create_image(name=name,
-                                                   container_format='bare',
-                                                   disk_format='raw',
-                                                   is_public=False)
+                                                    container_format='bare',
+                                                    disk_format='raw',
+                                                    is_public=False)
         image_id = body['id']
         image_file = StringIO.StringIO(('*' * 1024))
         resp, body = cls.glance_client.update_image(image_id, data=image_file)
diff --git a/tempest/api/compute/v3/admin/test_aggregates.py b/tempest/api/compute/v3/admin/test_aggregates.py
index e5ec08b..886b6a7 100644
--- a/tempest/api/compute/v3/admin/test_aggregates.py
+++ b/tempest/api/compute/v3/admin/test_aggregates.py
@@ -135,7 +135,7 @@
         self.assertEqual(200, resp.status)
         self.assertIn((aggregate_id, new_aggregate_name, new_az_name),
                       map(lambda x:
-                         (x['id'], x['name'], x['availability_zone']),
+                          (x['id'], x['name'], x['availability_zone']),
                           aggregates))
 
     @test.attr(type='gate')
diff --git a/tempest/api/compute/v3/admin/test_flavors_access.py b/tempest/api/compute/v3/admin/test_flavors_access.py
index c641bf6..09b6ebd 100644
--- a/tempest/api/compute/v3/admin/test_flavors_access.py
+++ b/tempest/api/compute/v3/admin/test_flavors_access.py
@@ -38,7 +38,6 @@
         cls.vcpus = 1
         cls.disk = 10
 
-    @test.skip_because(bug='1265416')
     @test.attr(type='gate')
     def test_flavor_access_list_with_private_flavor(self):
         # Test to list flavor access successfully by querying private flavor
@@ -58,7 +57,6 @@
         self.assertEqual(str(new_flavor_id), str(first_flavor['flavor_id']))
         self.assertEqual(self.adm_tenant_id, first_flavor['tenant_id'])
 
-    @test.skip_because(bug='1265416')
     @test.attr(type='gate')
     def test_flavor_access_add_remove(self):
         # Test to add and remove flavor access to a given tenant.
diff --git a/tempest/api/compute/v3/admin/test_flavors_access_negative.py b/tempest/api/compute/v3/admin/test_flavors_access_negative.py
index 02ecb24..0fdfabd 100644
--- a/tempest/api/compute/v3/admin/test_flavors_access_negative.py
+++ b/tempest/api/compute/v3/admin/test_flavors_access_negative.py
@@ -55,7 +55,6 @@
                           self.client.list_flavor_access,
                           new_flavor_id)
 
-    @test.skip_because(bug='1265416')
     @test.attr(type=['negative', 'gate'])
     def test_flavor_non_admin_add(self):
         # Test to add flavor access as a user without admin privileges.
@@ -72,7 +71,6 @@
                           new_flavor['id'],
                           self.tenant_id)
 
-    @test.skip_because(bug='1265416')
     @test.attr(type=['negative', 'gate'])
     def test_flavor_non_admin_remove(self):
         # Test to remove flavor access as a user without admin privileges.
@@ -93,7 +91,6 @@
                           new_flavor['id'],
                           self.tenant_id)
 
-    @test.skip_because(bug='1265416')
     @test.attr(type=['negative', 'gate'])
     def test_add_flavor_access_duplicate(self):
         # Create a new flavor.
@@ -118,7 +115,6 @@
                           new_flavor['id'],
                           self.tenant_id)
 
-    @test.skip_because(bug='1265416')
     @test.attr(type=['negative', 'gate'])
     def test_remove_flavor_access_not_found(self):
         # Create a new flavor.
diff --git a/tempest/api/compute/v3/flavors/test_flavors_negative.py b/tempest/api/compute/v3/flavors/test_flavors_negative.py
index 657e2cd..cdf018f 100644
--- a/tempest/api/compute/v3/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/v3/flavors/test_flavors_negative.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.api_schema.request.compute.v3 import flavors
 from tempest import test
 
 
@@ -24,14 +25,14 @@
 class FlavorsListNegativeV3Test(base.BaseV3ComputeTest,
                                 test.NegativeAutoTest):
     _service = 'computev3'
-    _schema_file = 'compute/flavors/flavors_list_v3.json'
+    _schema = flavors.flavor_list
 
 
 @test.SimpleNegativeAutoTest
 class FlavorDetailsNegativeV3Test(base.BaseV3ComputeTest,
                                   test.NegativeAutoTest):
     _service = 'computev3'
-    _schema_file = 'compute/flavors/flavor_details_v3.json'
+    _schema = flavors.flavors_details
 
     @classmethod
     def setUpClass(cls):
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index 4404043..d05e158 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -14,9 +14,9 @@
 #    under the License.
 
 import logging
+import urlparse
 
 import testtools
-import urlparse
 
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
@@ -339,6 +339,8 @@
         lines = len(output.split('\n'))
         self.assertEqual(lines, 10)
 
+    @testtools.skipUnless(CONF.compute_feature_enabled.console_output,
+                          'Console output not supported.')
     @test.attr(type='gate')
     def test_get_console_output(self):
         # Positive test:Should be able to GET the console output
@@ -355,6 +357,8 @@
 
         self.wait_for(self._get_output)
 
+    @testtools.skipUnless(CONF.compute_feature_enabled.console_output,
+                          'Console output not supported.')
     @test.attr(type='gate')
     def test_get_console_output_server_id_in_shutoff_status(self):
         # Positive test:Should be able to GET the console output
diff --git a/tempest/api/compute/v3/servers/test_server_rescue_negative.py b/tempest/api/compute/v3/servers/test_server_rescue_negative.py
index 83fe128..6d192a3 100644
--- a/tempest/api/compute/v3/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/v3/servers/test_server_rescue_negative.py
@@ -56,7 +56,8 @@
 
     @classmethod
     def tearDownClass(cls):
-        cls.delete_volume(cls.volume['id'])
+        if hasattr(cls, 'volume'):
+            cls.delete_volume(cls.volume['id'])
         super(ServerRescueNegativeV3Test, cls).tearDownClass()
 
     def _detach(self, server_id, volume_id):
diff --git a/tempest/api/compute/volumes/test_volumes_get.py b/tempest/api/compute/volumes/test_volumes_get.py
index c3d6ba6..708524c 100644
--- a/tempest/api/compute/volumes/test_volumes_get.py
+++ b/tempest/api/compute/volumes/test_volumes_get.py
@@ -13,11 +13,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from testtools import matchers
+
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest import config
 from tempest import test
-from testtools import matchers
+
 
 CONF = config.CONF
 
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 1561a6e..886c808 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -12,6 +12,7 @@
 
 import datetime
 import re
+
 from tempest.api.identity import base
 from tempest import auth
 from tempest import clients
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 8eb7d33..3996cc1 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -18,9 +18,12 @@
 from tempest import clients
 from tempest.common.utils import data_utils
 from tempest import config
+from tempest import exceptions
+from tempest.openstack.common import log as logging
 import tempest.test
 
 CONF = config.CONF
+LOG = logging.getLogger(__name__)
 
 
 class BaseIdentityAdminTest(tempest.test.BaseTestCase):
@@ -195,19 +198,36 @@
                 description=self.test_description)
             self.domains.append(self.domain)
 
+        @staticmethod
+        def _try_wrapper(func, item, **kwargs):
+            try:
+                if kwargs:
+                    func(item['id'], kwargs)
+                else:
+                    func(item['id'])
+            except exceptions.NotFound:
+                pass
+            except Exception:
+                LOG.exception("Unexpected exception occurred in %s deletion."
+                              " But ignored here." % item['id'])
+
         def teardown_all(self):
+            # NOTE(masayukig): v3 client doesn't have v2 method.
+            # (e.g. delete_tenant) So we need to check resources existence
+            # before using client methods.
             for user in self.users:
-                self.client.delete_user(user['id'])
+                self._try_wrapper(self.client.delete_user, user)
             for tenant in self.tenants:
-                self.client.delete_tenant(tenant['id'])
+                self._try_wrapper(self.client.delete_tenant, tenant)
             for role in self.roles:
-                self.client.delete_role(role['id'])
+                self._try_wrapper(self.client.delete_role, role)
             for v3_user in self.v3_users:
-                self.client.delete_user(v3_user['id'])
+                self._try_wrapper(self.client.delete_user, v3_user)
             for v3_project in self.projects:
-                self.client.delete_project(v3_project['id'])
+                self._try_wrapper(self.client.delete_project, v3_project)
             for v3_role in self.v3_roles:
-                self.client.delete_role(v3_role['id'])
+                self._try_wrapper(self.client.delete_role, v3_role)
             for domain in self.domains:
-                self.client.update_domain(domain['id'], enabled=False)
-                self.client.delete_domain(domain['id'])
+                self._try_wrapper(self.client.update_domain, domain,
+                                  enabled=False)
+                self._try_wrapper(self.client.delete_domain, domain)
diff --git a/tempest/api/network/test_allowed_address_pair.py b/tempest/api/network/test_allowed_address_pair.py
index c897716..8d984d1 100644
--- a/tempest/api/network/test_allowed_address_pair.py
+++ b/tempest/api/network/test_allowed_address_pair.py
@@ -85,8 +85,8 @@
         # Update allowed address pair attribute of port
         allowed_address_pairs = [{'ip_address': self.ip_address,
                                   'mac_address': self.mac_address}]
-        resp, body = self.client.update_port(port_id,
-                          allowed_address_pairs=allowed_address_pairs)
+        resp, body = self.client.update_port(
+            port_id, allowed_address_pairs=allowed_address_pairs)
         self.assertEqual('200', resp['status'])
         newport = body['port']
         self._confirm_allowed_address_pair(newport, self.ip_address)
diff --git a/tempest/api/network/test_load_balancer.py b/tempest/api/network/test_load_balancer.py
index db24e0d..7a12ef6 100644
--- a/tempest/api/network/test_load_balancer.py
+++ b/tempest/api/network/test_load_balancer.py
@@ -290,8 +290,8 @@
         health_monitor = body['health_monitor']
         # Verification of health_monitor update
         resp, body = (self.client.update_health_monitor
-                     (health_monitor['id'],
-                      admin_state_up=False))
+                      (health_monitor['id'],
+                       admin_state_up=False))
         self.assertEqual('200', resp['status'])
         updated_health_monitor = body['health_monitor']
         self.assertFalse(updated_health_monitor['admin_state_up'])
@@ -323,10 +323,10 @@
         self.addCleanup(self.client.delete_health_monitor,
                         health_monitor['id'])
         resp, body = (self.client.update_health_monitor
-                     (health_monitor['id'],
-                      http_method="POST",
-                      url_path="/home/user",
-                      expected_codes="290"))
+                      (health_monitor['id'],
+                       http_method="POST",
+                       url_path="/home/user",
+                       expected_codes="290"))
         self.assertEqual('200', resp['status'])
         updated_health_monitor = body['health_monitor']
         self.assertEqual("POST", updated_health_monitor['http_method'])
@@ -348,7 +348,7 @@
     def test_associate_disassociate_health_monitor_with_pool(self):
         # Verify that a health monitor can be associated with a pool
         resp, body = (self.client.associate_health_monitor_with_pool
-                     (self.health_monitor['id'], self.pool['id']))
+                      (self.health_monitor['id'], self.pool['id']))
         self.assertEqual('201', resp['status'])
         resp, body = self.client.show_health_monitor(
             self.health_monitor['id'])
@@ -360,7 +360,7 @@
         self.assertIn(health_monitor['id'], pool['health_monitors'])
         # Verify that a health monitor can be disassociated from a pool
         resp, body = (self.client.disassociate_health_monitor_with_pool
-                     (self.health_monitor['id'], self.pool['id']))
+                      (self.health_monitor['id'], self.pool['id']))
         self.assertEqual('204', resp['status'])
         resp, body = self.client.show_pool(self.pool['id'])
         pool = body['pool']
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index dca4c17..878335d 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -115,11 +115,11 @@
     def test_create_router_with_default_snat_value(self):
         # Create a router with default snat rule
         name = data_utils.rand_name('router')
-        router = self._create_router(name,
-                 external_network_id=CONF.network.public_network_id)
-        self._verify_router_gateway(router['id'],
-              {'network_id': CONF.network.public_network_id,
-              'enable_snat': True})
+        router = self._create_router(
+            name, external_network_id=CONF.network.public_network_id)
+        self._verify_router_gateway(
+            router['id'], {'network_id': CONF.network.public_network_id,
+                           'enable_snat': True})
 
     @test.requires_ext(extension='ext-gw-mode', service='network')
     @test.attr(type='smoke')
@@ -135,10 +135,10 @@
                 name, external_gateway_info=external_gateway_info)
             self.assertEqual('201', resp['status'])
             self.addCleanup(self.admin_client.delete_router,
-                    create_body['router']['id'])
+                            create_body['router']['id'])
             # Verify snat attributes after router creation
             self._verify_router_gateway(create_body['router']['id'],
-                    exp_ext_gw_info=external_gateway_info)
+                                        exp_ext_gw_info=external_gateway_info)
 
     @test.attr(type='smoke')
     def test_add_remove_router_interface_with_subnet_id(self):
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index b21aa44..8b74b7e 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -17,10 +17,11 @@
 import hashlib
 import random
 import re
-import six
 import time
 import zlib
 
+import six
+
 from tempest.api.object_storage import base
 from tempest.common import custom_matchers
 from tempest.common.utils import data_utils
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index 531df2d..cfebc2c 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -11,6 +11,7 @@
 #    under the License.
 
 import os.path
+
 import yaml
 
 from tempest import clients
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
index 27c6196..26e3ac6 100644
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ b/tempest/api/orchestration/stacks/test_neutron_resources.py
@@ -12,6 +12,7 @@
 
 
 import logging
+
 import netaddr
 
 from tempest.api.orchestration import base
@@ -66,16 +67,17 @@
             cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
             _, resources = cls.client.list_resources(cls.stack_identifier)
         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 = cls.client.get_resource(cls.stack_identifier,
-                                                 'Server')
-            server_id = body['physical_resource_id']
-            LOG.debug('Console output for %s', server_id)
-            resp, output = cls.servers_client.get_console_output(
-                server_id, None)
-            LOG.debug(output)
+            if CONF.compute_feature_enabled.console_output:
+                # attempt to log the server console to help with debugging
+                # the cause of the server not signalling the waitcondition
+                # to heat.
+                resp, body = cls.client.get_resource(cls.stack_identifier,
+                                                     'Server')
+                server_id = body['physical_resource_id']
+                LOG.debug('Console output for %s', server_id)
+                resp, output = cls.servers_client.get_console_output(
+                    server_id, None)
+                LOG.debug(output)
             raise e
 
         cls.test_resources = {}
diff --git a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
index e3ffdaf..c6f880b 100644
--- a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
+++ b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
@@ -56,10 +56,10 @@
                                                    ext=self._tpl_type)
         resources = [('KeyPairSavePrivate',
                       nova_keypair_template[self._resource][
-                      'KeyPairSavePrivate'][self._type]),
+                          'KeyPairSavePrivate'][self._type]),
                      ('KeyPairDontSavePrivate',
                       nova_keypair_template[self._resource][
-                      'KeyPairDontSavePrivate'][self._type])]
+                          'KeyPairDontSavePrivate'][self._type])]
 
         for resource_name, resource_type in resources:
             resource = self.test_resources.get(resource_name, None)
diff --git a/tempest/api/queuing/test_queues.py b/tempest/api/queuing/test_queues.py
index e43178a..b340b60 100644
--- a/tempest/api/queuing/test_queues.py
+++ b/tempest/api/queuing/test_queues.py
@@ -14,6 +14,7 @@
 # limitations under the License.
 
 import logging
+
 from six import moves
 from testtools import matchers
 
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index b8a2faa..ff225a2 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -15,11 +15,12 @@
 #    under the License.
 import operator
 
+from testtools import matchers
+
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
 from tempest.openstack.common import log as logging
 from tempest import test
-from testtools import matchers
 
 LOG = logging.getLogger(__name__)
 
diff --git a/tempest/api_schema/request/__init__.py b/tempest/api_schema/request/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api_schema/request/__init__.py
diff --git a/tempest/api_schema/request/compute/__init__.py b/tempest/api_schema/request/compute/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api_schema/request/compute/__init__.py
diff --git a/tempest/api_schema/request/compute/flavors.py b/tempest/api_schema/request/compute/flavors.py
new file mode 100644
index 0000000..36e5a19
--- /dev/null
+++ b/tempest/api_schema/request/compute/flavors.py
@@ -0,0 +1,32 @@
+# (c) 2014 Deutsche Telekom AG
+#    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.
+
+common_flavor_details = {
+    "name": "get-flavor-details",
+    "http-method": "GET",
+    "url": "flavors/%s",
+    "resources": [
+        {"name": "flavor", "expected_result": 404}
+    ]
+}
+
+common_flavor_list = {
+    "name": "list-flavors-with-detail",
+    "http-method": "GET",
+    "url": "flavors/detail",
+    "json-schema": {
+        "type": "object",
+        "properties": {
+        }
+    }
+}
diff --git a/tempest/api_schema/request/compute/v2/__init__.py b/tempest/api_schema/request/compute/v2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api_schema/request/compute/v2/__init__.py
diff --git a/tempest/api_schema/request/compute/v2/flavors.py b/tempest/api_schema/request/compute/v2/flavors.py
new file mode 100644
index 0000000..08f6c28
--- /dev/null
+++ b/tempest/api_schema/request/compute/v2/flavors.py
@@ -0,0 +1,37 @@
+# (c) 2014 Deutsche Telekom AG
+#    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 copy
+
+from tempest.api_schema.request.compute import flavors
+
+flavors_details = copy.deepcopy(flavors.common_flavor_details)
+
+flavor_list = copy.deepcopy(flavors.common_flavor_list)
+
+flavor_list["json-schema"]["properties"] = {
+    "minRam": {
+        "type": "integer",
+        "results": {
+            "gen_none": 400,
+            "gen_string": 400
+        }
+    },
+    "minDisk": {
+        "type": "integer",
+        "results": {
+            "gen_none": 400,
+            "gen_string": 400
+        }
+    }
+}
diff --git a/tempest/api_schema/request/compute/v3/__init__.py b/tempest/api_schema/request/compute/v3/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/api_schema/request/compute/v3/__init__.py
diff --git a/tempest/api_schema/request/compute/v3/flavors.py b/tempest/api_schema/request/compute/v3/flavors.py
new file mode 100644
index 0000000..b913aca
--- /dev/null
+++ b/tempest/api_schema/request/compute/v3/flavors.py
@@ -0,0 +1,37 @@
+# (c) 2014 Deutsche Telekom AG
+#    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 copy
+
+from tempest.api_schema.request.compute import flavors
+
+flavors_details = copy.deepcopy(flavors.common_flavor_details)
+
+flavor_list = copy.deepcopy(flavors.common_flavor_list)
+
+flavor_list["json-schema"]["properties"] = {
+    "min_ram": {
+        "type": "integer",
+        "results": {
+            "gen_none": 400,
+            "gen_string": 400
+        }
+    },
+    "min_disk": {
+        "type": "integer",
+        "results": {
+            "gen_none": 400,
+            "gen_string": 400
+        }
+    }
+}
diff --git a/tempest/api_schema/response/compute/v2/hypervisors.py b/tempest/api_schema/response/compute/v2/hypervisors.py
index 7ad81c0..1878881 100644
--- a/tempest/api_schema/response/compute/v2/hypervisors.py
+++ b/tempest/api_schema/response/compute/v2/hypervisors.py
@@ -13,8 +13,10 @@
 #    under the License.
 
 import copy
+
 from tempest.api_schema.response.compute import hypervisors
 
+
 hypervisors_servers = copy.deepcopy(hypervisors.common_hypervisors_detail)
 
 # Defining extra attributes for V3 show hypervisor schema
diff --git a/tempest/api_schema/response/compute/v3/hypervisors.py b/tempest/api_schema/response/compute/v3/hypervisors.py
index 9a9a9f7..b36ae7e 100644
--- a/tempest/api_schema/response/compute/v3/hypervisors.py
+++ b/tempest/api_schema/response/compute/v3/hypervisors.py
@@ -13,8 +13,10 @@
 #    under the License.
 
 import copy
+
 from tempest.api_schema.response.compute import hypervisors
 
+
 list_hypervisors_detail = copy.deepcopy(
     hypervisors.common_list_hypervisors_detail)
 # Defining extra attributes for V3 show hypervisor schema
diff --git a/tempest/api_schema/response/compute/v3/servers.py b/tempest/api_schema/response/compute/v3/servers.py
index 230de5f..d0edd44 100644
--- a/tempest/api_schema/response/compute/v3/servers.py
+++ b/tempest/api_schema/response/compute/v3/servers.py
@@ -55,9 +55,7 @@
         'mac_addr': {'type': 'string'}
     })
 addresses_v3['patternProperties']['^[a-zA-Z0-9-_.]+$']['items'][
-    'required'].extend(
-        ['type', 'mac_addr']
-    )
+    'required'].extend(['type', 'mac_addr'])
 
 update_server = copy.deepcopy(servers.base_update_get_server)
 update_server['response_body']['properties']['server']['properties'].update({
diff --git a/tempest/auth.py b/tempest/auth.py
index 5df6224..c84ad6b 100644
--- a/tempest/auth.py
+++ b/tempest/auth.py
@@ -18,16 +18,17 @@
 import datetime
 import exceptions
 import re
-import six
 import urlparse
 
+import six
+
 from tempest import config
+from tempest.openstack.common import log as logging
 from tempest.services.identity.json import identity_client as json_id
 from tempest.services.identity.v3.json import identity_client as json_v3id
 from tempest.services.identity.v3.xml import identity_client as xml_v3id
 from tempest.services.identity.xml import identity_client as xml_id
 
-from tempest.openstack.common import log as logging
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index 02f8c05..c33589a 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -13,14 +13,18 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import functools
 import os
 import shlex
 import subprocess
 
+import testtools
+
 import tempest.cli.output_parser
 from tempest import config
 from tempest import exceptions
 from tempest.openstack.common import log as logging
+from tempest.openstack.common import versionutils
 import tempest.test
 
 
@@ -29,6 +33,65 @@
 CONF = config.CONF
 
 
+def execute(cmd, action, flags='', params='', fail_ok=False,
+            merge_stderr=False):
+    """Executes specified command for the given action."""
+    cmd = ' '.join([os.path.join(CONF.cli.cli_dir, cmd),
+                    flags, action, params])
+    LOG.info("running: '%s'" % cmd)
+    cmd = shlex.split(cmd.encode('utf-8'))
+    result = ''
+    result_err = ''
+    stdout = subprocess.PIPE
+    stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE
+    proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
+    result, result_err = proc.communicate()
+    if not fail_ok and proc.returncode != 0:
+        raise exceptions.CommandFailed(proc.returncode,
+                                       cmd,
+                                       result,
+                                       result_err)
+    return result
+
+
+def check_client_version(client, version):
+    """Checks if the client's version is compatible with the given version
+
+    @param client: The client to check.
+    @param version: The version to compare against.
+    @return: True if the client version is compatible with the given version
+             parameter, False otherwise.
+    """
+    current_version = execute(client, '', params='--version',
+                              merge_stderr=True)
+
+    if not current_version.strip():
+        raise exceptions.TempestException('"%s --version" output was empty' %
+                                          client)
+
+    return versionutils.is_compatible(version, current_version,
+                                      same_major=False)
+
+
+def min_client_version(*args, **kwargs):
+    """A decorator to skip tests if the client used isn't of the right version.
+
+    @param client: The client command to run. For python-novaclient, this is
+                   'nova', for python-cinderclient this is 'cinder', etc.
+    @param version: The minimum version required to run the CLI test.
+    """
+    def decorator(func):
+        @functools.wraps(func)
+        def wrapper(*func_args, **func_kwargs):
+            if not check_client_version(kwargs['client'], kwargs['version']):
+                msg = "requires %s client version >= %s" % (kwargs['client'],
+                                                            kwargs['version'])
+                raise testtools.TestCase.skipException(msg)
+            return func(*func_args, **func_kwargs)
+        return wrapper
+    return decorator
+
+
 class ClientTestBase(tempest.test.BaseTestCase):
     @classmethod
     def setUpClass(cls):
@@ -50,7 +113,7 @@
     def nova_manage(self, action, flags='', params='', fail_ok=False,
                     merge_stderr=False):
         """Executes nova-manage command for the given action."""
-        return self.cmd(
+        return execute(
             'nova-manage', action, flags, params, fail_ok, merge_stderr)
 
     def keystone(self, action, flags='', params='', admin=True, fail_ok=False):
@@ -114,28 +177,7 @@
                   CONF.identity.admin_password,
                   CONF.identity.uri))
         flags = creds + ' ' + flags
-        return self.cmd(cmd, action, flags, params, fail_ok, merge_stderr)
-
-    def cmd(self, cmd, action, flags='', params='', fail_ok=False,
-            merge_stderr=False):
-        """Executes specified command for the given action."""
-        cmd = ' '.join([os.path.join(CONF.cli.cli_dir, cmd),
-                        flags, action, params])
-        LOG.info("running: '%s'" % cmd)
-        cmd = shlex.split(cmd.encode('utf-8'))
-        result = ''
-        result_err = ''
-        stdout = subprocess.PIPE
-        stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE
-        proc = subprocess.Popen(
-            cmd, stdout=stdout, stderr=stderr)
-        result, result_err = proc.communicate()
-        if not fail_ok and proc.returncode != 0:
-            raise exceptions.CommandFailed(proc.returncode,
-                                           cmd,
-                                           result,
-                                           result_err)
-        return result
+        return execute(cmd, action, flags, params, fail_ok, merge_stderr)
 
     def assertTableStruct(self, items, field_names):
         """Verify that all items has keys listed in field_names."""
diff --git a/tempest/cli/simple_read_only/test_cinder.py b/tempest/cli/simple_read_only/test_cinder.py
index 04971c1..3a9a7a6 100644
--- a/tempest/cli/simple_read_only/test_cinder.py
+++ b/tempest/cli/simple_read_only/test_cinder.py
@@ -15,6 +15,7 @@
 
 import logging
 import re
+
 import testtools
 
 from tempest import cli
diff --git a/tempest/cli/simple_read_only/test_heat.py b/tempest/cli/simple_read_only/test_heat.py
index 7a952fc..8e413a9 100644
--- a/tempest/cli/simple_read_only/test_heat.py
+++ b/tempest/cli/simple_read_only/test_heat.py
@@ -12,6 +12,7 @@
 
 import json
 import os
+
 import yaml
 
 import tempest.cli
@@ -85,6 +86,7 @@
     def test_heat_help(self):
         self.heat('help')
 
+    @tempest.cli.min_client_version(client='heat', version='0.2.7')
     def test_heat_bash_completion(self):
         self.heat('bash-completion')
 
diff --git a/tempest/cli/simple_read_only/test_nova.py b/tempest/cli/simple_read_only/test_nova.py
index 7085cc9..9bac7a6 100644
--- a/tempest/cli/simple_read_only/test_nova.py
+++ b/tempest/cli/simple_read_only/test_nova.py
@@ -144,6 +144,7 @@
     def test_admin_secgroup_list_rules(self):
         self.nova('secgroup-list-rules')
 
+    @tempest.cli.min_client_version(client='nova', version='2.18')
     def test_admin_server_group_list(self):
         self.nova('server-group-list')
 
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 3616a82..6761a69 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -19,13 +19,13 @@
 
 """
 
+import argparse
 import logging
 import os
 import sys
 import unittest
-import yaml
 
-import argparse
+import yaml
 
 import tempest.auth
 from tempest import config
@@ -249,8 +249,8 @@
                 if return_code is 0:
                     break
             self.assertNotEqual(count, 59,
-                               "Server %s is not pingable at %s" % (
-                               server['name'], addr))
+                                "Server %s is not pingable at %s" % (
+                                    server['name'], addr))
 
     def check_volumes(self):
         """Check that the volumes are still there and attached."""
@@ -401,7 +401,7 @@
         image_id = _get_image_by_name(client, server['image'])['id']
         flavor_id = _get_flavor_by_name(client, server['flavor'])['id']
         resp, body = client.servers.create_server(server['name'], image_id,
-                                                 flavor_id)
+                                                  flavor_id)
         server_id = body['id']
         client.servers.wait_for_server_status(server_id, 'ACTIVE')
 
@@ -420,7 +420,7 @@
 
         client.servers.delete_server(response['id'])
         client.servers.wait_for_server_termination(response['id'],
-                ignore_error=True)
+                                                   ignore_error=True)
 
 
 #######################
diff --git a/tempest/cmd/run_stress.py b/tempest/cmd/run_stress.py
index 07f3f66..a3f185c 100755
--- a/tempest/cmd/run_stress.py
+++ b/tempest/cmd/run_stress.py
@@ -18,13 +18,14 @@
 import inspect
 import json
 import sys
-from testtools import testsuite
 try:
     from unittest import loader
 except ImportError:
     # unittest in python 2.6 does not contain loader, so uses unittest2
     from unittest2 import loader
 
+from testtools import testsuite
+
 from tempest.openstack.common import log as logging
 from tempest.stress import driver
 
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 673da4f..70fd27b 100755
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -269,7 +269,7 @@
                 if getattr(CONF.service_available, codename_match[cfgname]):
                     print('Endpoint type %s not found either disable service '
                           '%s or fix the catalog_type in the config file' % (
-                          catalog_type, codename_match[cfgname]))
+                              catalog_type, codename_match[cfgname]))
                     if update:
                         change_option(codename_match[cfgname],
                                       'service_available', False)
@@ -278,7 +278,7 @@
                                codename_match[cfgname]):
                     print('Endpoint type %s is available, service %s should be'
                           ' set as available in the config file.' % (
-                          catalog_type, codename_match[cfgname]))
+                              catalog_type, codename_match[cfgname]))
                     if update:
                         change_option(codename_match[cfgname],
                                       'service_available', True)
diff --git a/tempest/common/accounts.py b/tempest/common/accounts.py
new file mode 100644
index 0000000..ad88ea2
--- /dev/null
+++ b/tempest/common/accounts.py
@@ -0,0 +1,131 @@
+# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
+#
+#    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 hashlib
+import os
+
+import yaml
+
+from tempest import auth
+from tempest.common import cred_provider
+from tempest import config
+from tempest import exceptions
+from tempest.openstack.common import lockutils
+from tempest.openstack.common import log as logging
+
+CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
+
+def read_accounts_yaml(path):
+    yaml_file = open(path, 'r')
+    accounts = yaml.load(yaml_file)
+    return accounts
+
+
+class Accounts(cred_provider.CredentialProvider):
+
+    def __init__(self, name):
+        super(Accounts, self).__init__(name)
+        accounts = read_accounts_yaml(CONF.auth.test_accounts_file)
+        self.hash_dict = self.get_hash_dict(accounts)
+        self.accounts_dir = os.path.join(CONF.lock_path, 'test_accounts')
+        self.isolated_creds = {}
+
+    @classmethod
+    def get_hash_dict(cls, accounts):
+        hash_dict = {}
+        for account in accounts:
+            temp_hash = hashlib.md5()
+            temp_hash.update(str(account))
+            hash_dict[temp_hash.hexdigest()] = account
+        return hash_dict
+
+    def _create_hash_file(self, hash):
+        path = os.path.join(os.path.join(self.accounts_dir, hash))
+        if not os.path.isfile(path):
+            open(path, 'w').close()
+            return True
+        return False
+
+    @lockutils.synchronized('test_accounts_io', external=True)
+    def _get_free_hash(self, hashes):
+        if not os.path.isdir(self.accounts_dir):
+            os.mkdir(self.accounts_dir)
+            # Create File from first hash (since none are in use)
+            self._create_hash_file(hashes[0])
+            return hashes[0]
+        for hash in hashes:
+            res = self._create_hash_file(hash)
+            if res:
+                return hash
+        msg = 'Insufficient number of users provided'
+        raise exceptions.InvalidConfiguration(msg)
+
+    def _get_creds(self):
+        free_hash = self._get_free_hash(self.hashes.keys())
+        return self.hash_dict[free_hash]
+
+    @lockutils.synchronized('test_accounts_io', external=True)
+    def remove_hash(self, hash):
+        hash_path = os.path.join(self.accounts_dir, hash)
+        if not os.path.isfile(hash_path):
+            LOG.warning('Expected an account lock file %s to remove, but '
+                        'one did not exist')
+        else:
+            os.remove(hash_path)
+            if not os.listdir(self.accounts_dir):
+                os.rmdir(self.accounts_dir)
+
+    def get_hash(self, creds):
+        for hash in self.hash_dict:
+            # NOTE(mtreinish) Assuming with v3 that username, tenant, password
+            # is unique enough
+            cred_dict = {
+                'username': creds.username,
+                'tenant_name': creds.tenant_name,
+                'password': creds.password
+            }
+            if self.hash_dict[hash] == cred_dict:
+                return hash
+        raise AttributeError('Invalid credentials %s' % creds)
+
+    def remove_credentials(self, creds):
+        hash = self.get_hash(creds)
+        self.remove_hash(hash, self.accounts_dir)
+
+    def get_primary_creds(self):
+        if self.credentials.get('primary'):
+            return self.credentials.get('primary')
+        creds = self._get_creds()
+        primary_credential = auth.get_credentials(**creds)
+        self.credentials['primary'] = primary_credential
+        return primary_credential
+
+    def get_alt_creds(self):
+        if self.credentials.get('alt'):
+            return self.credentials.get('alt')
+        creds = self._get_creds()
+        alt_credential = auth.get_credentials(**creds)
+        self.credentials['alt'] = alt_credential
+        return alt_credential
+
+    def clear_isolated_creds(self):
+        for creds in self.credentials.values():
+            self.remove_credentials(creds)
+
+    def get_admin_creds(self):
+        msg = ('If admin credentials are available tenant_isolation should be'
+               ' used instead')
+        raise NotImplementedError(msg)
diff --git a/tempest/common/cred_provider.py b/tempest/common/cred_provider.py
index dc4f049..9808ed1 100644
--- a/tempest/common/cred_provider.py
+++ b/tempest/common/cred_provider.py
@@ -12,6 +12,7 @@
 #    limitations under the License.
 
 import abc
+
 import six
 
 from tempest import config
diff --git a/tempest/common/debug.py b/tempest/common/debug.py
index 228be7a..16e5ffe 100644
--- a/tempest/common/debug.py
+++ b/tempest/common/debug.py
@@ -14,7 +14,6 @@
 
 from tempest.common import commands
 from tempest import config
-
 from tempest.openstack.common import log as logging
 
 CONF = config.CONF
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index 55aca5a..5f35c85 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -19,15 +19,17 @@
 import hashlib
 import httplib
 import json
-import OpenSSL
 import posixpath
 import re
-from six import moves
 import socket
 import StringIO
 import struct
 import urlparse
 
+
+import OpenSSL
+from six import moves
+
 from tempest import exceptions as exc
 from tempest.openstack.common import log as logging
 
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 9e0f4d3..ff92b67 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -16,12 +16,12 @@
 
 import collections
 import json
-from lxml import etree
 import re
 import string
 import time
 
 import jsonschema
+from lxml import etree
 
 from tempest.common import http
 from tempest.common.utils import misc as misc_utils
@@ -209,8 +209,9 @@
             pattern = """Unexpected http success status code {0},
                          The expected status code is {1}"""
             if ((not isinstance(expected_code, list) and
-                (read_code != expected_code)) or (isinstance(expected_code,
-                list) and (read_code not in expected_code))):
+                 (read_code != expected_code)) or
+                (isinstance(expected_code, list) and
+                 (read_code not in expected_code))):
                 details = pattern.format(read_code, expected_code)
                 raise exceptions.InvalidHttpSuccessCode(details)
 
diff --git a/tempest/common/ssh.py b/tempest/common/ssh.py
index 531887c..c06ce3b 100644
--- a/tempest/common/ssh.py
+++ b/tempest/common/ssh.py
@@ -16,11 +16,12 @@
 
 import cStringIO
 import select
-import six
 import socket
 import time
 import warnings
 
+import six
+
 from tempest import exceptions
 from tempest.openstack.common import log as logging
 
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 57a14a2..89904b2 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -11,9 +11,10 @@
 #    under the License.
 
 import re
-import six
 import time
 
+import six
+
 from tempest.common import ssh
 from tempest import config
 from tempest import exceptions
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index d242c14..c4f1214 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -74,7 +74,7 @@
         if (server_status == 'ERROR') and raise_on_error:
             if 'fault' in body:
                 raise exceptions.BuildErrorException(body['fault'],
-                                                    server_id=server_id)
+                                                     server_id=server_id)
             else:
                 raise exceptions.BuildErrorException(server_id=server_id)
 
diff --git a/tempest/config.py b/tempest/config.py
index 7d195bf..af6dd7a 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -29,6 +29,18 @@
         conf.register_opt(opt, group=opt_group.name)
 
 
+auth_group = cfg.OptGroup(name='auth',
+                          title="Options for authentication and credentials")
+
+
+AuthGroup = [
+    cfg.StrOpt('test_accounts_file',
+               default='etc/accounts.yaml',
+               help="Path to the yaml file that contains the list of "
+                    "credentials to use for running tests"),
+]
+
+
 identity_group = cfg.OptGroup(name='identity',
                               title="Keystone Configuration Options")
 
@@ -261,6 +273,9 @@
     cfg.BoolOpt('api_v3',
                 default=False,
                 help="If false, skip all nova v3 tests."),
+    cfg.BoolOpt('xml_api_v2',
+                default=True,
+                help="If false skip all v2 api tests with xml"),
     cfg.BoolOpt('disk_config',
                 default=True,
                 help="If false, skip disk config tests"),
@@ -280,6 +295,10 @@
                 default=False,
                 help="Does the test environment support changing the admin "
                      "password?"),
+    cfg.BoolOpt('console_output',
+                default=True,
+                help="Does the test environment support obtaining instance "
+                     "serial console output?"),
     cfg.BoolOpt('resize',
                 default=False,
                 help="Does the test environment support resizing?"),
@@ -1012,6 +1031,7 @@
 
 
 def register_opts():
+    register_opt_group(cfg.CONF, auth_group, AuthGroup)
     register_opt_group(cfg.CONF, compute_group, ComputeGroup)
     register_opt_group(cfg.CONF, compute_features_group,
                        ComputeFeaturesGroup)
@@ -1060,6 +1080,7 @@
     DEFAULT_CONFIG_FILE = "tempest.conf"
 
     def _set_attrs(self):
+        self.auth = cfg.CONF.auth
         self.compute = cfg.CONF.compute
         self.compute_feature_enabled = cfg.CONF['compute-feature-enabled']
         self.identity = cfg.CONF.identity
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 9d443cc..cc31fad 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -223,5 +223,8 @@
 
     def __str__(self):
         return ("Command '%s' returned non-zero exit status %d.\n"
-        "stdout:\n%s\n"
-        "stderr:\n%s" % (self.cmd, self.returncode, self.stdout, self.stderr))
+                "stdout:\n%s\n"
+                "stderr:\n%s" % (self.cmd,
+                                 self.returncode,
+                                 self.stdout,
+                                 self.stderr))
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 606208e..57c07d1 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -17,7 +17,6 @@
 import logging
 import os
 import re
-import six
 import subprocess
 import time
 
@@ -27,6 +26,7 @@
 import netaddr
 from neutronclient.common import exceptions as exc
 from novaclient import exceptions as nova_exceptions
+import six
 
 from tempest.api.network import common as net_common
 from tempest import auth
@@ -355,6 +355,22 @@
 
         return secgroup
 
+    def rebuild_server(self, server, client=None, image=None,
+                       preserve_ephemeral=False, wait=True,
+                       rebuild_kwargs=None):
+        if client is None:
+            client = self.compute_client
+        if image is None:
+            image = CONF.compute.image_ref
+        rebuild_kwargs = rebuild_kwargs or {}
+
+        LOG.debug("Rebuilding server (name: %s, image: %s, preserve eph: %s)",
+                  server.name, image, preserve_ephemeral)
+        server.rebuild(image, preserve_ephemeral=preserve_ephemeral,
+                       **rebuild_kwargs)
+        if wait:
+            self.status_timeout(client.servers, server.id, 'ACTIVE')
+
     def create_server(self, client=None, name=None, image=None, flavor=None,
                       wait_on_boot=True, wait_on_delete=True,
                       create_kwargs={}):
@@ -490,6 +506,9 @@
         return linux_client
 
     def _log_console_output(self, servers=None):
+        if not CONF.compute_feature_enabled.console_output:
+            LOG.debug('Console output not supported, cannot log')
+            return
         if not servers:
             servers = self.compute_client.servers.list()
         for server in servers:
diff --git a/tempest/scenario/orchestration/test_autoscaling.py b/tempest/scenario/orchestration/test_autoscaling.py
index aa7b6f8..8894106 100644
--- a/tempest/scenario/orchestration/test_autoscaling.py
+++ b/tempest/scenario/orchestration/test_autoscaling.py
@@ -10,9 +10,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import heatclient.exc as heat_exceptions
 import time
 
+import heatclient.exc as heat_exceptions
+
 from tempest import config
 from tempest.scenario import manager
 from tempest import test
diff --git a/tempest/scenario/test_baremetal_basic_ops.py b/tempest/scenario/test_baremetal_basic_ops.py
index f197c15..9ad6bc4 100644
--- a/tempest/scenario/test_baremetal_basic_ops.py
+++ b/tempest/scenario/test_baremetal_basic_ops.py
@@ -23,7 +23,7 @@
 LOG = logging.getLogger(__name__)
 
 
-class BaremetalBasicOpsPXESSH(manager.BaremetalScenarioTest):
+class BaremetalBasicOps(manager.BaremetalScenarioTest):
     """
     This smoke test tests the pxe_ssh Ironic driver.  It follows this basic
     set of operations:
@@ -35,10 +35,76 @@
         * Verifies SSH connectivity using created keypair via fixed IP
         * Associates a floating ip
         * Verifies SSH connectivity using created keypair via floating IP
+        * Verifies instance rebuild with ephemeral partition preservation
         * Deletes instance
         * Monitors the associated Ironic node for power and
           expected state transitions
     """
+    def rebuild_instance(self, preserve_ephemeral=False):
+        self.rebuild_server(self.instance,
+                            preserve_ephemeral=preserve_ephemeral,
+                            wait=False)
+
+        node = self.get_node(instance_id=self.instance.id)
+        self.instance = self.compute_client.servers.get(self.instance.id)
+
+        self.addCleanup_with_wait(self.compute_client.servers,
+                                  self.instance.id,
+                                  cleanup_callable=self.delete_wrapper,
+                                  cleanup_args=[self.instance])
+
+        # We should remain on the same node
+        self.assertEqual(self.node.uuid, node.uuid)
+        self.node = node
+
+        self.status_timeout(self.compute_client.servers, self.instance.id,
+                            'REBUILD')
+        self.status_timeout(self.compute_client.servers, self.instance.id,
+                            'ACTIVE')
+
+    def create_remote_file(self, client, filename):
+        """Create a file on the remote client connection.
+
+        After creating the file, force a filesystem sync. Otherwise,
+        if we issue a rebuild too quickly, the file may not exist.
+        """
+        client.exec_command('sudo touch ' + filename)
+        client.exec_command('sync')
+
+    def verify_partition(self, client, label, mount, gib_size):
+        """Verify a labeled partition's mount point and size."""
+        LOG.info("Looking for partition %s mounted on %s" % (label, mount))
+
+        # Validate we have a device with the given partition label
+        cmd = "/sbin/blkid | grep '%s' | cut -d':' -f1" % label
+        device = client.exec_command(cmd).rstrip('\n')
+        LOG.debug("Partition device is %s" % device)
+        self.assertNotEqual('', device)
+
+        # Validate the mount point for the device
+        cmd = "mount | grep '%s' | cut -d' ' -f3" % device
+        actual_mount = client.exec_command(cmd).rstrip('\n')
+        LOG.debug("Partition mount point is %s" % actual_mount)
+        self.assertEqual(actual_mount, mount)
+
+        # Validate the partition size matches what we expect
+        numbers = '0123456789'
+        devnum = device.replace('/dev/', '')
+        cmd = "cat /sys/block/%s/%s/size" % (devnum.rstrip(numbers), devnum)
+        num_bytes = client.exec_command(cmd).rstrip('\n')
+        num_bytes = int(num_bytes) * 512
+        actual_gib_size = num_bytes / (1024 * 1024 * 1024)
+        LOG.debug("Partition size is %d GiB" % actual_gib_size)
+        self.assertEqual(actual_gib_size, gib_size)
+
+    def get_flavor_ephemeral_size(self):
+        """Returns size of the ephemeral partition in GiB."""
+        f_id = self.instance.flavor['id']
+        ephemeral = self.compute_client.flavors.get(f_id).ephemeral
+        if ephemeral != 'N/A':
+            return int(ephemeral)
+        return None
+
     def add_floating_ip(self):
         floating_ip = self.compute_client.floating_ips.create()
         self.instance.add_floating_ip(floating_ip)
@@ -53,10 +119,32 @@
 
     @test.services('baremetal', 'compute', 'image', 'network')
     def test_baremetal_server_ops(self):
+        test_filename = '/mnt/rebuild_test.txt'
         self.add_keypair()
         self.boot_instance()
         self.validate_ports()
         self.verify_connectivity()
         floating_ip = self.add_floating_ip()
         self.verify_connectivity(ip=floating_ip)
+
+        vm_client = self.get_remote_client(self.instance)
+
+        # We expect the ephemeral partition to be mounted on /mnt and to have
+        # the same size as our flavor definition.
+        eph_size = self.get_flavor_ephemeral_size()
+        self.assertIsNotNone(eph_size)
+        self.verify_partition(vm_client, 'ephemeral0', '/mnt', eph_size)
+
+        # Create the test file
+        self.create_remote_file(vm_client, test_filename)
+
+        # Rebuild and preserve the ephemeral partition
+        self.rebuild_instance(True)
+        self.verify_connectivity()
+
+        # Check that we maintained our data
+        vm_client = self.get_remote_client(self.instance)
+        self.verify_partition(vm_client, 'ephemeral0', '/mnt', eph_size)
+        vm_client.exec_command('ls ' + test_filename)
+
         self.terminate_instance()
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 8c7af3d..fc6c66c 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -15,6 +15,7 @@
 #    under the License.
 import collections
 import re
+
 import testtools
 
 from tempest.api.network import common as net_common
diff --git a/tempest/services/compute/xml/floating_ips_client.py b/tempest/services/compute/xml/floating_ips_client.py
index fa4aa07..730e870 100644
--- a/tempest/services/compute/xml/floating_ips_client.py
+++ b/tempest/services/compute/xml/floating_ips_client.py
@@ -13,9 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from lxml import etree
 import urllib
 
+from lxml import etree
+
 from tempest.common import rest_client
 from tempest.common import xml_utils
 from tempest import config
diff --git a/tempest/services/compute/xml/hosts_client.py b/tempest/services/compute/xml/hosts_client.py
index 23a7dd6..ddb92b6 100644
--- a/tempest/services/compute/xml/hosts_client.py
+++ b/tempest/services/compute/xml/hosts_client.py
@@ -15,6 +15,7 @@
 import urllib
 
 from lxml import etree
+
 from tempest.common import rest_client
 from tempest.common import xml_utils
 from tempest import config
diff --git a/tempest/services/compute/xml/security_groups_client.py b/tempest/services/compute/xml/security_groups_client.py
index 9eccb90..56ac7ba 100644
--- a/tempest/services/compute/xml/security_groups_client.py
+++ b/tempest/services/compute/xml/security_groups_client.py
@@ -13,9 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from lxml import etree
 import urllib
 
+from lxml import etree
+
 from tempest.common import rest_client
 from tempest.common import xml_utils
 from tempest import config
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index 8e53b8d..2e28bfe 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -110,7 +110,7 @@
 
     def add_router_interface_with_subnet_id(self, router_id, subnet_id):
         uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
-              router_id)
+                                                      router_id)
         update_body = {"subnet_id": subnet_id}
         update_body = json.dumps(update_body)
         resp, body = self.put(uri, update_body)
@@ -119,7 +119,7 @@
 
     def add_router_interface_with_port_id(self, router_id, port_id):
         uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
-              router_id)
+                                                      router_id)
         update_body = {"port_id": port_id}
         update_body = json.dumps(update_body)
         resp, body = self.put(uri, update_body)
@@ -128,7 +128,7 @@
 
     def remove_router_interface_with_subnet_id(self, router_id, subnet_id):
         uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
-              router_id)
+                                                         router_id)
         update_body = {"subnet_id": subnet_id}
         update_body = json.dumps(update_body)
         resp, body = self.put(uri, update_body)
@@ -137,7 +137,7 @@
 
     def remove_router_interface_with_port_id(self, router_id, port_id):
         uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
-              router_id)
+                                                         router_id)
         update_body = {"port_id": port_id}
         update_body = json.dumps(update_body)
         resp, body = self.put(uri, update_body)
diff --git a/tempest/services/network/xml/network_client.py b/tempest/services/network/xml/network_client.py
index 22cc948..ea9dc77 100644
--- a/tempest/services/network/xml/network_client.py
+++ b/tempest/services/network/xml/network_client.py
@@ -10,9 +10,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from lxml import etree
 import xml.etree.ElementTree as ET
 
+from lxml import etree
+
 from tempest.common import rest_client
 from tempest.common import xml_utils as common
 from tempest.services.network import network_client_base as client_base
@@ -136,7 +137,7 @@
 
     def add_router_interface_with_subnet_id(self, router_id, subnet_id):
         uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
-              router_id)
+                                                      router_id)
         subnet = common.Element("subnet_id", subnet_id)
         resp, body = self.put(uri, str(common.Document(subnet)))
         body = _root_tag_fetcher_and_xml_to_json_parse(body)
@@ -144,7 +145,7 @@
 
     def add_router_interface_with_port_id(self, router_id, port_id):
         uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
-              router_id)
+                                                      router_id)
         port = common.Element("port_id", port_id)
         resp, body = self.put(uri, str(common.Document(port)))
         body = _root_tag_fetcher_and_xml_to_json_parse(body)
@@ -152,7 +153,7 @@
 
     def remove_router_interface_with_subnet_id(self, router_id, subnet_id):
         uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
-              router_id)
+                                                         router_id)
         subnet = common.Element("subnet_id", subnet_id)
         resp, body = self.put(uri, str(common.Document(subnet)))
         body = _root_tag_fetcher_and_xml_to_json_parse(body)
@@ -160,7 +161,7 @@
 
     def remove_router_interface_with_port_id(self, router_id, port_id):
         uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
-              router_id)
+                                                         router_id)
         port = common.Element("port_id", port_id)
         resp, body = self.put(uri, str(common.Document(port)))
         body = _root_tag_fetcher_and_xml_to_json_parse(body)
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index a0506f2..be0f888 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -15,12 +15,12 @@
 
 import json
 import urllib
+from xml.etree import ElementTree as etree
 
 from tempest.common import http
 from tempest.common import rest_client
 from tempest import config
 from tempest import exceptions
-from xml.etree import ElementTree as etree
 
 CONF = config.CONF
 
diff --git a/tempest/services/object_storage/container_client.py b/tempest/services/object_storage/container_client.py
index 546b557..ffc1326 100644
--- a/tempest/services/object_storage/container_client.py
+++ b/tempest/services/object_storage/container_client.py
@@ -15,10 +15,10 @@
 
 import json
 import urllib
+from xml.etree import ElementTree as etree
 
 from tempest.common import rest_client
 from tempest import config
-from xml.etree import ElementTree as etree
 
 CONF = config.CONF
 
diff --git a/tempest/services/telemetry/telemetry_client_base.py b/tempest/services/telemetry/telemetry_client_base.py
index b45c239..a184a77 100644
--- a/tempest/services/telemetry/telemetry_client_base.py
+++ b/tempest/services/telemetry/telemetry_client_base.py
@@ -14,9 +14,10 @@
 #    under the License.
 
 import abc
-import six
 import urllib
 
+import six
+
 from tempest import config
 
 CONF = config.CONF
diff --git a/tempest/services/volume/json/admin/volume_quotas_client.py b/tempest/services/volume/json/admin/volume_quotas_client.py
index 961c7da..7b3e5de 100644
--- a/tempest/services/volume/json/admin/volume_quotas_client.py
+++ b/tempest/services/volume/json/admin/volume_quotas_client.py
@@ -16,10 +16,9 @@
 
 import urllib
 
-from tempest.openstack.common import jsonutils
-
 from tempest.common import rest_client
 from tempest import config
+from tempest.openstack.common import jsonutils
 
 CONF = config.CONF
 
diff --git a/tempest/services/volume/xml/admin/volume_quotas_client.py b/tempest/services/volume/xml/admin/volume_quotas_client.py
index a38410b..a8c92fc 100644
--- a/tempest/services/volume/xml/admin/volume_quotas_client.py
+++ b/tempest/services/volume/xml/admin/volume_quotas_client.py
@@ -15,6 +15,7 @@
 #    under the License.
 
 import ast
+
 from lxml import etree
 
 from tempest.common import xml_utils as xml
diff --git a/tempest/services/volume/xml/volumes_client.py b/tempest/services/volume/xml/volumes_client.py
index 2d4a9e9..89947fb 100644
--- a/tempest/services/volume/xml/volumes_client.py
+++ b/tempest/services/volume/xml/volumes_client.py
@@ -15,9 +15,9 @@
 
 import time
 import urllib
+from xml.sax import saxutils
 
 from lxml import etree
-from xml.sax import saxutils
 
 from tempest.common import rest_client
 from tempest.common import xml_utils as common
diff --git a/tempest/stress/actions/volume_attach_delete.py b/tempest/stress/actions/volume_attach_delete.py
index b438f52..e0238d3 100644
--- a/tempest/stress/actions/volume_attach_delete.py
+++ b/tempest/stress/actions/volume_attach_delete.py
@@ -48,7 +48,7 @@
 
         # Step 3: attach volume to vm
         self.logger.info("attach volume (%s) to vm %s" %
-                        (volume['id'], server_id))
+                         (volume['id'], server_id))
         resp, body = self.manager.servers_client.attach_volume(server_id,
                                                                volume['id'],
                                                                '/dev/vdc')
diff --git a/tempest/stress/actions/volume_attach_verify.py b/tempest/stress/actions/volume_attach_verify.py
index a3ca0b7..0d3cb23 100644
--- a/tempest/stress/actions/volume_attach_verify.py
+++ b/tempest/stress/actions/volume_attach_verify.py
@@ -192,7 +192,7 @@
             self._create_volume()
         servers_client = self.manager.servers_client
         self.logger.info("attach volume (%s) to vm %s" %
-                        (self.volume['id'], self.server_id))
+                         (self.volume['id'], self.server_id))
         resp, body = servers_client.attach_volume(self.server_id,
                                                   self.volume['id'],
                                                   self.part_name)
diff --git a/tempest/test.py b/tempest/test.py
index 95ae23f..1e67d18 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -399,16 +399,21 @@
         cls.admin_client = os_admin.negative_client
 
     @staticmethod
-    def load_schema(file):
+    def load_schema(file_or_dict):
         """
-        Loads a schema from a file on a specified location.
+        Loads a schema from a file_or_dict on a specified location.
 
-        :param file: the file name
+        :param file_or_dict: just a dict or filename
         """
+        # NOTE(mkoderer): we will get rid of this function when all test are
+        # ported to dicts
+        if isinstance(file_or_dict, dict):
+            return file_or_dict
+
         # NOTE(mkoderer): must be extended for xml support
         fn = os.path.join(
             os.path.abspath(os.path.dirname(os.path.dirname(__file__))),
-            "etc", "schemas", file)
+            "etc", "schemas", file_or_dict)
         LOG.debug("Open schema file: %s" % (fn))
         return json.load(open(fn))
 
@@ -425,17 +430,21 @@
             standard_tests, module, loader = args
         for test in testtools.iterate_tests(standard_tests):
             schema_file = getattr(test, '_schema_file', None)
+            schema = getattr(test, '_schema', None)
             if schema_file is not None:
                 setattr(test, 'scenarios',
                         NegativeAutoTest.generate_scenario(schema_file))
+            elif schema is not None:
+                setattr(test, 'scenarios',
+                        NegativeAutoTest.generate_scenario(schema))
         return testscenarios.load_tests_apply_scenarios(*args)
 
     @staticmethod
-    def generate_scenario(description_file):
+    def generate_scenario(description):
         """
         Generates the test scenario list for a given description.
 
-        :param description: A dictionary with the following entries:
+        :param description: A file or dictionary with the following entries:
             name (required) name for the api
             http-method (required) one of HEAD,GET,PUT,POST,PATCH,DELETE
             url (required) the url to be appended to the catalog url with '%s'
@@ -451,7 +460,7 @@
                 the data is used to generate query strings appended to the url,
                 otherwise for the body of the http call.
         """
-        description = NegativeAutoTest.load_schema(description_file)
+        description = NegativeAutoTest.load_schema(description)
         LOG.debug(description)
         generator = importutils.import_class(
             CONF.negative.test_generator)()
@@ -481,13 +490,14 @@
         LOG.debug(scenario_list)
         return scenario_list
 
-    def execute(self, description_file):
+    def execute(self, description):
         """
         Execute a http call on an api that are expected to
         result in client errors. First it uses invalid resources that are part
         of the url, and then invalid data for queries and http request bodies.
 
-        :param description: A dictionary with the following entries:
+        :param description: A json file or dictionary with the following
+        entries:
             name (required) name for the api
             http-method (required) one of HEAD,GET,PUT,POST,PATCH,DELETE
             url (required) the url to be appended to the catalog url with '%s'
@@ -504,7 +514,7 @@
                 otherwise for the body of the http call.
 
         """
-        description = NegativeAutoTest.load_schema(description_file)
+        description = NegativeAutoTest.load_schema(description)
         LOG.info("Executing %s" % description["name"])
         LOG.debug(description)
         method = description["http-method"]
@@ -594,7 +604,10 @@
     """
     @attr(type=['negative', 'gate'])
     def generic_test(self):
-        self.execute(self._schema_file)
+        if hasattr(self, '_schema_file'):
+            self.execute(self._schema_file)
+        if hasattr(self, '_schema'):
+            self.execute(self._schema)
 
     cn = klass.__name__
     cn = cn.replace('JSON', '')
diff --git a/tempest/tests/base.py b/tempest/tests/base.py
index f4df3b9..27eb2c4 100644
--- a/tempest/tests/base.py
+++ b/tempest/tests/base.py
@@ -13,7 +13,6 @@
 #    under the License.
 
 import mock
-
 from oslotest import base
 from oslotest import moxstubout
 
diff --git a/tempest/tests/cli/test_cli.py b/tempest/tests/cli/test_cli.py
new file mode 100644
index 0000000..1fd5ccb
--- /dev/null
+++ b/tempest/tests/cli/test_cli.py
@@ -0,0 +1,59 @@
+# 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 mock
+import testtools
+
+from tempest import cli
+from tempest import exceptions
+from tempest.tests import base
+
+
+class TestMinClientVersion(base.TestCase):
+    """Tests for the min_client_version decorator.
+    """
+
+    def _test_min_version(self, required, installed, expect_skip):
+
+        @cli.min_client_version(client='nova', version=required)
+        def fake(self, expect_skip):
+            if expect_skip:
+                # If we got here, the decorator didn't raise a skipException as
+                # expected so we need to fail.
+                self.fail('Should not have gotten past the decorator.')
+
+        with mock.patch.object(cli, 'execute',
+                               return_value=installed) as mock_cmd:
+            if expect_skip:
+                self.assertRaises(testtools.TestCase.skipException, fake,
+                                  self, expect_skip)
+            else:
+                fake(self, expect_skip)
+            mock_cmd.assert_called_once_with('nova', '', params='--version',
+                                             merge_stderr=True)
+
+    def test_min_client_version(self):
+        # required, installed, expect_skip
+        cases = (('2.17.0', '2.17.0', False),
+                 ('2.17.0', '2.18.0', False),
+                 ('2.18.0', '2.17.0', True))
+
+        for case in cases:
+            self._test_min_version(*case)
+
+    @mock.patch.object(cli, 'execute', return_value=' ')
+    def test_check_client_version_empty_output(self, mock_execute):
+        # Tests that an exception is raised if the command output is empty.
+        self.assertRaises(exceptions.TempestException,
+                          cli.check_client_version, 'nova', '2.18.0')
diff --git a/tempest/tests/common/test_accounts.py b/tempest/tests/common/test_accounts.py
new file mode 100644
index 0000000..c24bfb6
--- /dev/null
+++ b/tempest/tests/common/test_accounts.py
@@ -0,0 +1,187 @@
+# Copyright 2014 Hewlett-Packard Development Company, L.P.
+#
+#    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 hashlib
+import os
+import tempfile
+
+import mock
+from oslo.config import cfg
+from oslotest import mockpatch
+
+from tempest import auth
+from tempest.common import accounts
+from tempest.common import http
+from tempest import config
+from tempest import exceptions
+from tempest.tests import base
+from tempest.tests import fake_config
+from tempest.tests import fake_identity
+
+
+class TestAccount(base.TestCase):
+
+    def setUp(self):
+        super(TestAccount, self).setUp()
+        self.useFixture(fake_config.ConfigFixture())
+        self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+        self.temp_dir = tempfile.mkdtemp()
+        cfg.CONF.set_default('lock_path', self.temp_dir)
+        self.addCleanup(os.rmdir, self.temp_dir)
+        self.test_accounts = [
+            {'username': 'test_user1', 'tenant_name': 'test_tenant1',
+             'password': 'p'},
+            {'username': 'test_user2', 'tenant_name': 'test_tenant2',
+             'password': 'p'},
+            {'username': 'test_user3', 'tenant_name': 'test_tenant3',
+             'password': 'p'},
+            {'username': 'test_user4', 'tenant_name': 'test_tenant4',
+             'password': 'p'},
+            {'username': 'test_user5', 'tenant_name': 'test_tenant5',
+             'password': 'p'},
+            {'username': 'test_user6', 'tenant_name': 'test_tenant6',
+             'password': 'p'},
+        ]
+        self.useFixture(mockpatch.Patch(
+            'tempest.common.accounts.read_accounts_yaml',
+            return_value=self.test_accounts))
+        cfg.CONF.set_default('test_accounts_file', '', group='auth')
+
+    def _get_hash_list(self, accounts_list):
+        hash_list = []
+        for account in accounts_list:
+            hash = hashlib.md5()
+            hash.update(str(account))
+            hash_list.append(hash.hexdigest())
+        return hash_list
+
+    def test_get_hash(self):
+        self.stubs.Set(http.ClosingHttp, 'request',
+                       fake_identity._fake_v2_response)
+        test_account_class = accounts.Accounts('test_name')
+        hash_list = self._get_hash_list(self.test_accounts)
+        test_cred_dict = self.test_accounts[3]
+        test_creds = auth.get_credentials(**test_cred_dict)
+        results = test_account_class.get_hash(test_creds)
+        self.assertEqual(hash_list[3], results)
+
+    def test_get_hash_dict(self):
+        test_account_class = accounts.Accounts('test_name')
+        hash_dict = test_account_class.get_hash_dict(self.test_accounts)
+        hash_list = self._get_hash_list(self.test_accounts)
+        for hash in hash_list:
+            self.assertIn(hash, hash_dict.keys())
+            self.assertIn(hash_dict[hash], self.test_accounts)
+
+    def test_create_hash_file_previous_file(self):
+        # Emulate the lock existing on the filesystem
+        self.useFixture(mockpatch.Patch('os.path.isfile', return_value=True))
+        with mock.patch('__builtin__.open', mock.mock_open(), create=True):
+            test_account_class = accounts.Accounts('test_name')
+            res = test_account_class._create_hash_file('12345')
+        self.assertFalse(res, "_create_hash_file should return False if the "
+                         "pseudo-lock file already exists")
+
+    def test_create_hash_file_no_previous_file(self):
+        # Emulate the lock not existing on the filesystem
+        self.useFixture(mockpatch.Patch('os.path.isfile', return_value=False))
+        with mock.patch('__builtin__.open', mock.mock_open(), create=True):
+            test_account_class = accounts.Accounts('test_name')
+            res = test_account_class._create_hash_file('12345')
+        self.assertTrue(res, "_create_hash_file should return True if the "
+                        "pseudo-lock doesn't already exist")
+
+    @mock.patch('tempest.openstack.common.lockutils.lock')
+    def test_get_free_hash_no_previous_accounts(self, lock_mock):
+        # Emulate no pre-existing lock
+        self.useFixture(mockpatch.Patch('os.path.isdir', return_value=False))
+        hash_list = self._get_hash_list(self.test_accounts)
+        mkdir_mock = self.useFixture(mockpatch.Patch('os.mkdir'))
+        self.useFixture(mockpatch.Patch('os.path.isfile', return_value=False))
+        test_account_class = accounts.Accounts('test_name')
+        with mock.patch('__builtin__.open', mock.mock_open(),
+                        create=True) as open_mock:
+            test_account_class._get_free_hash(hash_list)
+            lock_path = os.path.join(accounts.CONF.lock_path, 'test_accounts',
+                                     hash_list[0])
+            open_mock.assert_called_once_with(lock_path, 'w')
+        mkdir_path = os.path.join(accounts.CONF.lock_path, 'test_accounts')
+        mkdir_mock.mock.assert_called_once_with(mkdir_path)
+
+    @mock.patch('tempest.openstack.common.lockutils.lock')
+    def test_get_free_hash_no_free_accounts(self, lock_mock):
+        hash_list = self._get_hash_list(self.test_accounts)
+        # Emulate pre-existing lock dir
+        self.useFixture(mockpatch.Patch('os.path.isdir', return_value=True))
+        # Emulate all lcoks in list are in use
+        self.useFixture(mockpatch.Patch('os.path.isfile', return_value=True))
+        test_account_class = accounts.Accounts('test_name')
+        self.assertRaises(exceptions.InvalidConfiguration,
+                          test_account_class._get_free_hash, hash_list)
+
+    @mock.patch('tempest.openstack.common.lockutils.lock')
+    def test_get_free_hash_some_in_use_accounts(self, lock_mock):
+        # Emulate no pre-existing lock
+        self.useFixture(mockpatch.Patch('os.path.isdir', return_value=True))
+        hash_list = self._get_hash_list(self.test_accounts)
+        test_account_class = accounts.Accounts('test_name')
+
+        def _fake_is_file(path):
+            # Fake isfile() to return that the path exists unless a specific
+            # hash is in the path
+            if hash_list[3] in path:
+                return False
+            return True
+
+        self.stubs.Set(os.path, 'isfile', _fake_is_file)
+        with mock.patch('__builtin__.open', mock.mock_open(),
+                        create=True) as open_mock:
+            test_account_class._get_free_hash(hash_list)
+            lock_path = os.path.join(accounts.CONF.lock_path, 'test_accounts',
+                                     hash_list[3])
+            open_mock.assert_called_once_with(lock_path, 'w')
+
+    @mock.patch('tempest.openstack.common.lockutils.lock')
+    def test_remove_hash_last_account(self, lock_mock):
+        hash_list = self._get_hash_list(self.test_accounts)
+        # Pretend the pseudo-lock is there
+        self.useFixture(mockpatch.Patch('os.path.isfile', return_value=True))
+        # Pretend the lock dir is empty
+        self.useFixture(mockpatch.Patch('os.listdir', return_value=[]))
+        test_account_class = accounts.Accounts('test_name')
+        remove_mock = self.useFixture(mockpatch.Patch('os.remove'))
+        rmdir_mock = self.useFixture(mockpatch.Patch('os.rmdir'))
+        test_account_class.remove_hash(hash_list[2])
+        hash_path = os.path.join(accounts.CONF.lock_path, 'test_accounts',
+                                 hash_list[2])
+        lock_path = os.path.join(accounts.CONF.lock_path, 'test_accounts')
+        remove_mock.mock.assert_called_once_with(hash_path)
+        rmdir_mock.mock.assert_called_once_with(lock_path)
+
+    @mock.patch('tempest.openstack.common.lockutils.lock')
+    def test_remove_hash_not_last_account(self, lock_mock):
+        hash_list = self._get_hash_list(self.test_accounts)
+        # Pretend the pseudo-lock is there
+        self.useFixture(mockpatch.Patch('os.path.isfile', return_value=True))
+        # Pretend the lock dir is empty
+        self.useFixture(mockpatch.Patch('os.listdir', return_value=[
+            hash_list[1], hash_list[4]]))
+        test_account_class = accounts.Accounts('test_name')
+        remove_mock = self.useFixture(mockpatch.Patch('os.remove'))
+        rmdir_mock = self.useFixture(mockpatch.Patch('os.rmdir'))
+        test_account_class.remove_hash(hash_list[2])
+        hash_path = os.path.join(accounts.CONF.lock_path, 'test_accounts',
+                                 hash_list[2])
+        remove_mock.mock.assert_called_once_with(hash_path)
+        rmdir_mock.mock.assert_not_called()
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index 536cbcf..9e56916 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -61,3 +61,4 @@
     def __init__(self, parse_conf=True, config_path=None):
         cfg.CONF([], default_config_files=[])
         self._set_attrs()
+        self.lock_path = cfg.CONF.lock_path
diff --git a/tempest/tests/fake_http.py b/tempest/tests/fake_http.py
index ce2b2c0..7d77484 100644
--- a/tempest/tests/fake_http.py
+++ b/tempest/tests/fake_http.py
@@ -13,6 +13,7 @@
 #    under the License.
 
 import copy
+
 import httplib2
 
 
diff --git a/tempest/tests/fake_identity.py b/tempest/tests/fake_identity.py
index 1900fc9..97098e1 100644
--- a/tempest/tests/fake_identity.py
+++ b/tempest/tests/fake_identity.py
@@ -13,9 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import json
 
 import httplib2
-import json
 
 
 TOKEN = "fake_token"
diff --git a/tempest/tests/negative/test_negative_auto_test.py b/tempest/tests/negative/test_negative_auto_test.py
index 7a1909a..edff3a8 100644
--- a/tempest/tests/negative/test_negative_auto_test.py
+++ b/tempest/tests/negative/test_negative_auto_test.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import json
+
 import mock
 
 from tempest import config
@@ -30,9 +32,9 @@
                        "http-method": "GET",
                        "url": "flavors/detail",
                        "json-schema": {"type": "object",
-                                      "properties":
-                                      {"minRam": {"type": "integer"},
-                                       "minDisk": {"type": "integer"}}
+                                       "properties":
+                                       {"minRam": {"type": "integer"},
+                                        "minDisk": {"type": "integer"}}
                                        },
                        "resources": ["flavor", "volume", "image"]
                        }
@@ -70,3 +72,13 @@
         self._check_prop_entries(scenarios, "prop_minRam")
         self._check_prop_entries(scenarios, "prop_minDisk")
         self._check_resource_entries(scenarios, "inv_res")
+
+    def test_load_schema(self):
+        json_schema = json.dumps(self.fake_input_desc)
+        with mock.patch('tempest.test.open',
+                        mock.mock_open(read_data=json_schema),
+                        create=True):
+            return_file = test.NegativeAutoTest.load_schema('filename')
+            self.assertEqual(return_file, self.fake_input_desc)
+        return_dict = test.NegativeAutoTest.load_schema(self.fake_input_desc)
+        self.assertEqual(return_file, return_dict)
diff --git a/tempest/tests/test_commands.py b/tempest/tests/test_commands.py
index 1e2925b..2379741 100644
--- a/tempest/tests/test_commands.py
+++ b/tempest/tests/test_commands.py
@@ -12,9 +12,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import mock
 import subprocess
 
+import mock
+
 from tempest.common import commands
 from tempest.tests import base
 
diff --git a/tempest/tests/test_glance_http.py b/tempest/tests/test_glance_http.py
index 88b8129..9a6c9de 100644
--- a/tempest/tests/test_glance_http.py
+++ b/tempest/tests/test_glance_http.py
@@ -15,9 +15,10 @@
 
 import httplib
 import json
+import socket
+
 import mock
 import six
-import socket
 
 from tempest.common import glance_http
 from tempest import exceptions
@@ -41,17 +42,17 @@
 
         self.fake_auth.base_url = mock.MagicMock(return_value=self.endpoint)
 
-        self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
-                        'request',
-                        side_effect=self.fake_http.request(self.endpoint)[1]))
+        self.useFixture(mockpatch.PatchObject(
+            httplib.HTTPConnection,
+            'request',
+            side_effect=self.fake_http.request(self.endpoint)[1]))
         self.client = glance_http.HTTPClient(self.fake_auth, {})
 
     def _set_response_fixture(self, header, status, resp_body):
         resp = fake_http.fake_httplib(header, status=status,
                                       body=six.StringIO(resp_body))
         self.useFixture(mockpatch.PatchObject(httplib.HTTPConnection,
-                        'getresponse',
-                        return_value=resp))
+                        'getresponse', return_value=resp))
         return resp
 
     def test_json_request_without_content_type_header_in_response(self):
diff --git a/tempest/tests/test_rest_client.py b/tempest/tests/test_rest_client.py
index a351bd5..5f55ca2 100644
--- a/tempest/tests/test_rest_client.py
+++ b/tempest/tests/test_rest_client.py
@@ -12,9 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import httplib2
 import json
 
+import httplib2
 from oslotest import mockpatch
 
 from tempest.common import rest_client
diff --git a/tempest/tests/test_tenant_isolation.py b/tempest/tests/test_tenant_isolation.py
index bbc3d15..eddbb1d 100644
--- a/tempest/tests/test_tenant_isolation.py
+++ b/tempest/tests/test_tenant_isolation.py
@@ -335,9 +335,11 @@
         remove_router_interface_mock = self.patch(
             'tempest.services.network.json.network_client.NetworkClientJSON.'
             'remove_router_interface_with_subnet_id')
+        return_values = ({'status': 200}, {'ports': []})
         port_list_mock = mock.patch.object(iso_creds.network_admin_client,
-                                           'list_ports', return_value=(
-                                           {'status': 200}, {'ports': []}))
+                                           'list_ports',
+                                           return_value=return_values)
+
         port_list_mock.start()
         iso_creds.clear_isolated_creds()
         # Verify remove router interface calls
diff --git a/tempest/thirdparty/boto/test.py b/tempest/thirdparty/boto/test.py
index d3cbc4b..4bf71f3 100644
--- a/tempest/thirdparty/boto/test.py
+++ b/tempest/thirdparty/boto/test.py
@@ -17,7 +17,6 @@
 import logging as orig_logging
 import os
 import re
-import six
 import urlparse
 
 import boto
@@ -25,6 +24,7 @@
 from boto import exception
 from boto import s3
 import keystoneclient.exceptions
+import six
 
 import tempest.clients
 from tempest.common.utils import file_utils
@@ -187,7 +187,7 @@
         if len(args):
             string += ", "
     string += ", ".join("=".join(map(str, (key, value)))
-              for (key, value) in kwargs.items())
+                        for (key, value) in kwargs.items())
     return string + ")"
 
 
@@ -592,83 +592,83 @@
 
 
 for code in (('AccessDenied', 403),
-            ('AccountProblem', 403),
-            ('AmbiguousGrantByEmailAddress', 400),
-            ('BadDigest', 400),
-            ('BucketAlreadyExists', 409),
-            ('BucketAlreadyOwnedByYou', 409),
-            ('BucketNotEmpty', 409),
-            ('CredentialsNotSupported', 400),
-            ('CrossLocationLoggingProhibited', 403),
-            ('EntityTooSmall', 400),
-            ('EntityTooLarge', 400),
-            ('ExpiredToken', 400),
-            ('IllegalVersioningConfigurationException', 400),
-            ('IncompleteBody', 400),
-            ('IncorrectNumberOfFilesInPostRequest', 400),
-            ('InlineDataTooLarge', 400),
-            ('InvalidAccessKeyId', 403),
+             ('AccountProblem', 403),
+             ('AmbiguousGrantByEmailAddress', 400),
+             ('BadDigest', 400),
+             ('BucketAlreadyExists', 409),
+             ('BucketAlreadyOwnedByYou', 409),
+             ('BucketNotEmpty', 409),
+             ('CredentialsNotSupported', 400),
+             ('CrossLocationLoggingProhibited', 403),
+             ('EntityTooSmall', 400),
+             ('EntityTooLarge', 400),
+             ('ExpiredToken', 400),
+             ('IllegalVersioningConfigurationException', 400),
+             ('IncompleteBody', 400),
+             ('IncorrectNumberOfFilesInPostRequest', 400),
+             ('InlineDataTooLarge', 400),
+             ('InvalidAccessKeyId', 403),
              'InvalidAddressingHeader',
-            ('InvalidArgument', 400),
-            ('InvalidBucketName', 400),
-            ('InvalidBucketState', 409),
-            ('InvalidDigest', 400),
-            ('InvalidLocationConstraint', 400),
-            ('InvalidPart', 400),
-            ('InvalidPartOrder', 400),
-            ('InvalidPayer', 403),
-            ('InvalidPolicyDocument', 400),
-            ('InvalidRange', 416),
-            ('InvalidRequest', 400),
-            ('InvalidSecurity', 403),
-            ('InvalidSOAPRequest', 400),
-            ('InvalidStorageClass', 400),
-            ('InvalidTargetBucketForLogging', 400),
-            ('InvalidToken', 400),
-            ('InvalidURI', 400),
-            ('KeyTooLong', 400),
-            ('MalformedACLError', 400),
-            ('MalformedPOSTRequest', 400),
-            ('MalformedXML', 400),
-            ('MaxMessageLengthExceeded', 400),
-            ('MaxPostPreDataLengthExceededError', 400),
-            ('MetadataTooLarge', 400),
-            ('MethodNotAllowed', 405),
-            ('MissingAttachment'),
-            ('MissingContentLength', 411),
-            ('MissingRequestBodyError', 400),
-            ('MissingSecurityElement', 400),
-            ('MissingSecurityHeader', 400),
-            ('NoLoggingStatusForKey', 400),
-            ('NoSuchBucket', 404),
-            ('NoSuchKey', 404),
-            ('NoSuchLifecycleConfiguration', 404),
-            ('NoSuchUpload', 404),
-            ('NoSuchVersion', 404),
-            ('NotSignedUp', 403),
-            ('NotSuchBucketPolicy', 404),
-            ('OperationAborted', 409),
-            ('PermanentRedirect', 301),
-            ('PreconditionFailed', 412),
-            ('Redirect', 307),
-            ('RequestIsNotMultiPartContent', 400),
-            ('RequestTimeout', 400),
-            ('RequestTimeTooSkewed', 403),
-            ('RequestTorrentOfBucketError', 400),
-            ('SignatureDoesNotMatch', 403),
-            ('TemporaryRedirect', 307),
-            ('TokenRefreshRequired', 400),
-            ('TooManyBuckets', 400),
-            ('UnexpectedContent', 400),
-            ('UnresolvableGrantByEmailAddress', 400),
-            ('UserKeyMustBeSpecified', 400)):
+             ('InvalidArgument', 400),
+             ('InvalidBucketName', 400),
+             ('InvalidBucketState', 409),
+             ('InvalidDigest', 400),
+             ('InvalidLocationConstraint', 400),
+             ('InvalidPart', 400),
+             ('InvalidPartOrder', 400),
+             ('InvalidPayer', 403),
+             ('InvalidPolicyDocument', 400),
+             ('InvalidRange', 416),
+             ('InvalidRequest', 400),
+             ('InvalidSecurity', 403),
+             ('InvalidSOAPRequest', 400),
+             ('InvalidStorageClass', 400),
+             ('InvalidTargetBucketForLogging', 400),
+             ('InvalidToken', 400),
+             ('InvalidURI', 400),
+             ('KeyTooLong', 400),
+             ('MalformedACLError', 400),
+             ('MalformedPOSTRequest', 400),
+             ('MalformedXML', 400),
+             ('MaxMessageLengthExceeded', 400),
+             ('MaxPostPreDataLengthExceededError', 400),
+             ('MetadataTooLarge', 400),
+             ('MethodNotAllowed', 405),
+             ('MissingAttachment'),
+             ('MissingContentLength', 411),
+             ('MissingRequestBodyError', 400),
+             ('MissingSecurityElement', 400),
+             ('MissingSecurityHeader', 400),
+             ('NoLoggingStatusForKey', 400),
+             ('NoSuchBucket', 404),
+             ('NoSuchKey', 404),
+             ('NoSuchLifecycleConfiguration', 404),
+             ('NoSuchUpload', 404),
+             ('NoSuchVersion', 404),
+             ('NotSignedUp', 403),
+             ('NotSuchBucketPolicy', 404),
+             ('OperationAborted', 409),
+             ('PermanentRedirect', 301),
+             ('PreconditionFailed', 412),
+             ('Redirect', 307),
+             ('RequestIsNotMultiPartContent', 400),
+             ('RequestTimeout', 400),
+             ('RequestTimeTooSkewed', 403),
+             ('RequestTorrentOfBucketError', 400),
+             ('SignatureDoesNotMatch', 403),
+             ('TemporaryRedirect', 307),
+             ('TokenRefreshRequired', 400),
+             ('TooManyBuckets', 400),
+             ('UnexpectedContent', 400),
+             ('UnresolvableGrantByEmailAddress', 400),
+             ('UserKeyMustBeSpecified', 400)):
     _add_matcher_class(BotoTestCase.s3_error_code.client,
                        code, base=ClientError)
 
 
 for code in (('InternalError', 500),
-            ('NotImplemented', 501),
-            ('ServiceUnavailable', 503),
-            ('SlowDown', 503)):
+             ('NotImplemented', 501),
+             ('ServiceUnavailable', 503),
+             ('SlowDown', 503)):
     _add_matcher_class(BotoTestCase.s3_error_code.server,
                        code, base=ServerError)
diff --git a/tools/check_logs.py b/tools/check_logs.py
index bc4eaca..eab9f73 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -22,6 +22,7 @@
 import StringIO
 import sys
 import urllib2
+
 import yaml
 
 
diff --git a/tools/colorizer.py b/tools/colorizer.py
index a3a0616..e7152f2 100755
--- a/tools/colorizer.py
+++ b/tools/colorizer.py
@@ -42,10 +42,10 @@
 """Display a subunit stream through a colorized unittest test runner."""
 
 import heapq
-import subunit
 import sys
 import unittest
 
+import subunit
 import testtools
 
 
diff --git a/tools/find_stack_traces.py b/tools/find_stack_traces.py
index c905976..4862d01 100755
--- a/tools/find_stack_traces.py
+++ b/tools/find_stack_traces.py
@@ -16,12 +16,13 @@
 #    under the License.
 
 import gzip
+import pprint
 import re
 import StringIO
 import sys
 import urllib2
 
-import pprint
+
 pp = pprint.PrettyPrinter()
 
 NOVA_TIMESTAMP = r"\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d"
diff --git a/tox.ini b/tox.ini
index 9c32121..b3d4397 100644
--- a/tox.ini
+++ b/tox.ini
@@ -109,7 +109,9 @@
 [flake8]
 # E125 is a won't fix until https://github.com/jcrocholl/pep8/issues/126 is resolved.  For further detail see https://review.openstack.org/#/c/36788/
 # H402 skipped because some docstrings aren't sentences
-# Skipped because of new hacking 0.9: H407,H405,H904,H305,E123,H307,E122,E129,E128
-ignore = E125,H402,H404,H407,H405,H904,H305,E123,H307,E122,E129,E128
+# E123 skipped because it is ignored by default in the default pep8
+# E129 skipped because it is too limiting when combined with other rules
+# Skipped because of new hacking 0.9: H405,H904
+ignore = E125,H402,E123,E129,H404,H405,H904
 show-source = True
 exclude = .git,.venv,.tox,dist,doc,openstack,*egg