Merge "Check only for added/deleted metadata entries"
diff --git a/.testr.conf b/.testr.conf
index 4f6e0b3..95a4fb4 100644
--- a/.testr.conf
+++ b/.testr.conf
@@ -3,7 +3,7 @@
              OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
              OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-500} \
              OS_TEST_LOCK_PATH=${OS_TEST_LOCK_PATH:-${TMPDIR:-'/tmp'}} \
-             ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./tempest/test_discover} $LISTOPT $IDOPTION
+             ${PYTHON:-python} -m subunit.run discover -t ${OS_TOP_LEVEL:-./} ${OS_TEST_PATH:-./tempest/test_discover} $LISTOPT $IDOPTION
 test_id_option=--load-list $IDFILE
 test_list_option=--list
 group_regex=([^\.]*\.)*
diff --git a/README.rst b/README.rst
index 94a5352..af24569 100644
--- a/README.rst
+++ b/README.rst
@@ -117,8 +117,8 @@
 Tempest suite.
 
 Alternatively, you can use the run_tests.sh script which will create a venv and
-run the unit tests. There are also the py26, py27, or py33 tox jobs which will
-run the unit tests with the corresponding version of python.
+run the unit tests. There are also the py27 and py34 tox jobs which will run
+the unit tests with the corresponding version of python.
 
 Python 2.6
 ----------
@@ -131,3 +131,16 @@
 on an earlier release with python 2.6 you can easily run Tempest against it
 from a remote system running python 2.7. (or deploy a cloud guest in your cloud
 that has python 2.7)
+
+Python 3.4
+----------
+
+Starting during the Liberty release development cycle work began on enabling
+Tempest to run under both Python 2.7 and Python 3.4. Tempest strives to fully
+support running with Python 3.4. A gating unit test job was added to also run
+Tempest's unit tests under Python 3.4. This means that the Tempest code at
+least imports under Python 3.4 and things that have unit test coverage will
+work on Python 3.4. However, because large parts of Tempest are self verifying
+there might be uncaught issues running on Python 3.4. So until there is a gating
+job which does a full Tempest run using Python 3.4 there isn't any guarantee
+that running Tempest under Python 3.4 is bug free.
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index b434fdc..eab7487 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -310,6 +310,12 @@
 will additionally be used as a fallback for server creation. However, unlike
 accounts.yaml this should never be triggered.
 
+However, there is an option *create_isolated_networks* to disable tenant
+isolation's automatic provisioning of network resources. If this option is
+used you will have to either rely on there only being a single/default network
+available for the server creation, or use *fixed_network_name* to inform
+Tempest which network to use.
+
 Configuring Available Services
 ------------------------------
 OpenStack is really a constellation of several different projects which
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 3f9e70e..b3aca42 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -40,13 +40,15 @@
 #log_dir = <None>
 
 # Use syslog for logging. Existing syslog format is DEPRECATED during
-# I, and will change in J to honor RFC5424. (boolean value)
+# I, and changed in J to honor RFC5424. (boolean value)
 #use_syslog = false
 
 # (Optional) Enables or disables syslog rfc5424 format for logging. If
 # enabled, prefixes the MSG part of the syslog message with APP-NAME
 # (RFC5424). The format without the APP-NAME is deprecated in K, and
-# will be removed in L, along with this option. (boolean value)
+# will be removed in M, along with this option. (boolean value)
+# This option is deprecated for removal.
+# Its value may be silently ignored in the future.
 #use_syslog_rfc_format = true
 
 # Syslog facility to receive log lines. (string value)
@@ -67,7 +69,7 @@
 
 # Prefix each line of exception output with this format. (string
 # value)
-#logging_exception_prefix = %(asctime)s.%(msecs)03d %(process)d TRACE %(name)s %(instance)s
+#logging_exception_prefix = %(asctime)s.%(msecs)03d %(process)d ERROR %(name)s %(instance)s
 
 # List of logger=LEVEL pairs. (list value)
 #default_log_levels = amqp=WARN,amqplib=WARN,boto=WARN,qpid=WARN,sqlalchemy=WARN,suds=INFO,oslo.messaging=INFO,iso8601=WARN,requests.packages.urllib3.connectionpool=WARN,urllib3.connectionpool=WARN,websocket=WARN,requests.packages.urllib3.util.retry=WARN,urllib3.util.retry=WARN,keystonemiddleware=WARN,routes.middleware=WARN,stevedore=WARN
@@ -86,6 +88,9 @@
 # (string value)
 #instance_uuid_format = "[instance: %(uuid)s] "
 
+# Enables or disables fatal status of deprecations. (boolean value)
+#fatal_deprecations = false
+
 
 [auth]
 
@@ -117,6 +122,14 @@
 # the domain from theadmin user is used instead. (string value)
 #tenant_isolation_domain_name = <None>
 
+# If allow_tenant_isolation is set to True and Neutron is enabled
+# Tempest will try to create a useable network, subnet, and router
+# when needed for each tenant it  creates. However in some neutron
+# configurations, like with VLAN provider networks, this doesn't work.
+# So if set to False the isolated networks will not be created
+# (boolean value)
+#create_isolated_networks = true
+
 
 [baremetal]
 
@@ -571,6 +584,9 @@
 # applies to user and project (string value)
 #admin_domain_name = <None>
 
+# ID of the default domain (string value)
+#default_domain_id = default
+
 
 [identity-feature-enabled]
 
@@ -935,6 +951,10 @@
 # Image container format (string value)
 #img_container_format = bare
 
+# Glance image properties. Use for custom images which require them
+# (dict value)
+#img_properties = <None>
+
 # AMI image file name (string value)
 #ami_img_file = cirros-0.3.1-x86_64-blank.img
 
@@ -1171,3 +1191,7 @@
 
 # Is the v2 volume API enabled (boolean value)
 #api_v2 = true
+
+# Update bootable status of a volume Not implemented on icehouse
+# (boolean value)
+#bootable = false
diff --git a/setup.cfg b/setup.cfg
index 2de9f34..3a14bba 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -15,6 +15,8 @@
     Programming Language :: Python
     Programming Language :: Python :: 2
     Programming Language :: Python :: 2.7
+    Programming Language :: Python :: 3
+    Programming Language :: Python :: 3.4
 
 [entry_points]
 console_scripts =
@@ -22,6 +24,7 @@
     javelin2 = tempest.cmd.javelin:main
     run-tempest-stress = tempest.cmd.run_stress:main
     tempest-cleanup = tempest.cmd.cleanup:main
+    tempest-account-generator = tempest.cmd.account_generator:main
 
 oslo.config.opts =
     tempest.config = tempest.config:list_opts
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index c4581e5..9aacfa5 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -303,7 +303,7 @@
         if 'wait_until' in kwargs:
             cls.images_client.wait_for_image_status(image_id,
                                                     kwargs['wait_until'])
-            image = cls.images_client.get_image(image_id)
+            image = cls.images_client.show_image(image_id)
 
             if kwargs['wait_until'] == 'ACTIVE':
                 if kwargs.get('wait_for_server', True):
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index a92048f..3771a43 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -84,11 +84,11 @@
         self.client.wait_for_image_status(image_id, 'ACTIVE')
 
         # Verify the image was created correctly
-        image = self.client.get_image(image_id)
+        image = self.client.show_image(image_id)
         self.assertEqual(name, image['name'])
         self.assertEqual('test', image['metadata']['image_type'])
 
-        original_image = self.client.get_image(self.image_ref)
+        original_image = self.client.show_image(self.image_ref)
 
         # Verify minRAM is the same as the original image
         self.assertEqual(image['minRam'], original_image['minRam'])
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index aa12044..8a39d90 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -140,4 +140,4 @@
         self.client.delete_image(image_id)
         self.image_ids.remove(image_id)
 
-        self.assertRaises(lib_exc.NotFound, self.client.get_image, image_id)
+        self.assertRaises(lib_exc.NotFound, self.client.show_image, image_id)
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index abe7be5..e2d4bd9 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -62,7 +62,7 @@
             image_file = six.StringIO(('*' * 1024))
             cls.glance_client.update_image(image_id, data=image_file)
             cls.client.wait_for_image_status(image_id, 'ACTIVE')
-            body = cls.client.get_image(image_id)
+            body = cls.client.show_image(image_id)
             return body
 
         # Create non-snapshot images via glance
@@ -249,7 +249,7 @@
         # The detailed list of servers should be filtered by image type
         params = {'type': 'snapshot'}
         images = self.client.list_images_with_detail(params)
-        self.client.get_image(self.image_ref)
+        self.client.show_image(self.image_ref)
 
         self.assertTrue(any([i for i in images
                              if i['id'] == self.snapshot1_id]))
diff --git a/tempest/api/compute/images/test_list_image_filters_negative.py b/tempest/api/compute/images/test_list_image_filters_negative.py
index 334516b..4110619 100644
--- a/tempest/api/compute/images/test_list_image_filters_negative.py
+++ b/tempest/api/compute/images/test_list_image_filters_negative.py
@@ -41,5 +41,5 @@
     def test_get_nonexistent_image(self):
         # Check raises a NotFound
         nonexistent_image = data_utils.rand_uuid()
-        self.assertRaises(lib_exc.NotFound, self.client.get_image,
+        self.assertRaises(lib_exc.NotFound, self.client.show_image,
                           nonexistent_image)
diff --git a/tempest/api/compute/images/test_list_images.py b/tempest/api/compute/images/test_list_images.py
index 95f49c9..5b80c72 100644
--- a/tempest/api/compute/images/test_list_images.py
+++ b/tempest/api/compute/images/test_list_images.py
@@ -37,7 +37,7 @@
     @test.idempotent_id('490d0898-e12a-463f-aef0-c50156b9f789')
     def test_get_image(self):
         # Returns the correct details for a single image
-        image = self.client.get_image(self.image_ref)
+        image = self.client.show_image(self.image_ref)
         self.assertEqual(self.image_ref, image['id'])
 
     @test.idempotent_id('fd51b7f4-d4a3-4331-9885-866658112a6f')
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 5374af0..d7607ab 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -56,13 +56,13 @@
         # Do some sanity checks here. If one of the images does
         # not exist, fail early since the tests won't work...
         try:
-            cls.images_client.get_image(cls.image_ref)
+            cls.images_client.show_image(cls.image_ref)
         except lib_exc.NotFound:
             raise RuntimeError("Image %s (image_ref) was not found!" %
                                cls.image_ref)
 
         try:
-            cls.images_client.get_image(cls.image_ref_alt)
+            cls.images_client.show_image(cls.image_ref_alt)
         except lib_exc.NotFound:
             raise RuntimeError("Image %s (image_ref_alt) was not found!" %
                                cls.image_ref_alt)
@@ -275,8 +275,14 @@
             msg = 'fixed_network_name needs to be configured to run this test'
             raise self.skipException(msg)
         self.s1 = self.client.get_server(self.s1['id'])
-        ip = self.s1['addresses'][self.fixed_network_name][0]['addr']
-        params = {'ip': ip}
+        for addr_spec in self.s1['addresses'][self.fixed_network_name]:
+            ip = addr_spec['addr']
+            if addr_spec['version'] == 4:
+                params = {'ip': ip}
+                break
+        else:
+            msg = "Skipped until bug 1450859 is resolved"
+            raise self.skipException(msg)
         body = self.client.list_servers(params)
         servers = body['servers']
 
@@ -295,8 +301,12 @@
             msg = 'fixed_network_name needs to be configured to run this test'
             raise self.skipException(msg)
         self.s1 = self.client.get_server(self.s1['id'])
-        ip = self.s1['addresses'][self.fixed_network_name][0]['addr'][0:-3]
-        params = {'ip': ip}
+        addr_spec = self.s1['addresses'][self.fixed_network_name][0]
+        ip = addr_spec['addr'][0:-3]
+        if addr_spec['version'] == 4:
+            params = {'ip': ip}
+        else:
+            params = {'ip6': ip}
         body = self.client.list_servers(params)
         servers = body['servers']
 
diff --git a/tempest/api/compute/servers/test_multiple_create.py b/tempest/api/compute/servers/test_multiple_create.py
index eed3be8..11b457b 100644
--- a/tempest/api/compute/servers/test_multiple_create.py
+++ b/tempest/api/compute/servers/test_multiple_create.py
@@ -30,7 +30,9 @@
         This is the right way to create_multiple servers and manage to get the
         created servers into the servers list to be cleaned up after all.
         """
-        kwargs['name'] = kwargs.get('name', self._generate_name())
+        kwargs['name'] = name if name else self._generate_name()
+        if wait_until:
+            kwargs['wait_until'] = wait_until
         body = self.create_test_server(**kwargs)
 
         return body
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index c4cabaa..c2ed0ce 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -47,8 +47,6 @@
             self.__class__.server_id = self.rebuild_server(self.server_id)
 
     def tearDown(self):
-        server = self.client.get_server(self.server_id)
-        self.assertEqual(self.image_ref, server['image']['id'])
         self.server_check_teardown()
         super(ServerActionsTestJSON, self).tearDown()
 
@@ -110,6 +108,14 @@
         # The server should be signaled to reboot gracefully
         self._test_reboot_server('SOFT')
 
+    def _rebuild_server_and_check(self, image_ref):
+        rebuilt_server = self.client.rebuild(self.server_id, image_ref)
+        self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+        msg = ('Server was not rebuilt to the original image. '
+               'The original image: {0}. The current image: {1}'
+               .format(image_ref, rebuilt_server['image']['id']))
+        self.assertEqual(image_ref, rebuilt_server['image']['id'], msg)
+
     @test.idempotent_id('aaa6cdf3-55a7-461a-add9-1c8596b9a07c')
     def test_rebuild_server(self):
         # The server should be rebuilt using the provided image and data
@@ -129,8 +135,7 @@
         # If the server was rebuilt on a different image, restore it to the
         # original image once the test ends
         if self.image_ref_alt != self.image_ref:
-            self.addCleanup(self.client.rebuild,
-                            (self.server_id, self.image_ref))
+            self.addCleanup(self._rebuild_server_and_check, self.image_ref)
 
         # Verify the properties in the initial response are correct
         self.assertEqual(self.server_id, rebuilt_server['id'])
@@ -157,11 +162,15 @@
         # image and remain in SHUTOFF state
         server = self.client.get_server(self.server_id)
         old_image = server['image']['id']
-        new_image = self.image_ref_alt \
-            if old_image == self.image_ref else self.image_ref
+        new_image = (self.image_ref_alt
+                     if old_image == self.image_ref else self.image_ref)
         self.client.stop(self.server_id)
         self.client.wait_for_server_status(self.server_id, 'SHUTOFF')
         rebuilt_server = self.client.rebuild(self.server_id, new_image)
+        # If the server was rebuilt on a different image, restore it to the
+        # original image once the test ends
+        if self.image_ref_alt != self.image_ref:
+            self.addCleanup(self._rebuild_server_and_check, old_image)
 
         # Verify the properties in the initial response are correct
         self.assertEqual(self.server_id, rebuilt_server['id'])
@@ -175,10 +184,6 @@
         rebuilt_image_id = server['image']['id']
         self.assertEqual(new_image, rebuilt_image_id)
 
-        # Restore to the original image (The tearDown will test it again)
-        if self.image_ref_alt != self.image_ref:
-            self.client.rebuild(self.server_id, old_image)
-            self.client.wait_for_server_status(self.server_id, 'SHUTOFF')
         self.client.start(self.server_id)
 
     def _test_resize_server_confirm(self, stop=False):
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index b333122..31078e3 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -64,6 +64,7 @@
 
         key_name = data_utils.rand_name('key')
         self.keypairs_client.create_keypair(key_name)
+        self.addCleanup(self.keypairs_client.delete_keypair, key_name)
         self.keypairs_client.list_keypairs()
         server = self.create_test_server(key_name=key_name)
         self.client.wait_for_server_status(server['id'], 'ACTIVE')
diff --git a/tempest/api/compute/test_authorization.py b/tempest/api/compute/test_authorization.py
index 9f007bf..63b78a0 100644
--- a/tempest/api/compute/test_authorization.py
+++ b/tempest/api/compute/test_authorization.py
@@ -73,7 +73,7 @@
         image_file = six.StringIO(('*' * 1024))
         body = cls.glance_client.update_image(image_id, data=image_file)
         cls.glance_client.wait_for_image_status(image_id, 'active')
-        cls.image = cls.images_client.get_image(image_id)
+        cls.image = cls.images_client.show_image(image_id)
 
         cls.keypairname = data_utils.rand_name('keypair')
         cls.keypairs_client.create_keypair(cls.keypairname)
@@ -233,7 +233,7 @@
     def test_get_image_for_alt_account_fails(self):
         # A GET request for an image on another user's account should fail
         self.assertRaises(lib_exc.NotFound,
-                          self.alt_images_client.get_image, self.image['id'])
+                          self.alt_images_client.show_image, self.image['id'])
 
     @test.idempotent_id('9facb962-f043-4a9d-b9ee-166a32dea098')
     def test_delete_image_for_alt_account_fails(self):
diff --git a/tempest/api/identity/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index 79943bb..b775e91 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -13,10 +13,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.api.identity import base
+from tempest import config
+from tempest import test
+
 from tempest_lib.common.utils import data_utils
 
-from tempest.api.identity import base
-from tempest import test
+CONF = config.CONF
 
 
 class DomainsTestJSON(base.BaseIdentityV3AdminTest):
@@ -105,3 +108,18 @@
         expected_data = {'name': d_name, 'enabled': True}
         self.assertIsNone(domain['description'])
         self.assertDictContainsSubset(expected_data, domain)
+
+
+class DefaultDomainTestJSON(base.BaseIdentityV3AdminTest):
+
+    @classmethod
+    def resource_setup(cls):
+        cls.domain_id = CONF.identity.default_domain_id
+        super(DefaultDomainTestJSON, cls).resource_setup()
+
+    @test.attr(type='smoke')
+    @test.idempotent_id('17a5de24-e6a0-4e4a-a9ee-d85b6e5612b5')
+    def test_default_domain_exists(self):
+        domain = self.client.get_domain(self.domain_id)
+
+        self.assertTrue(domain['enabled'])
diff --git a/tempest/api/identity/admin/v3/test_domains_negative.py b/tempest/api/identity/admin/v3/test_domains_negative.py
new file mode 100644
index 0000000..e2f3ef5
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_domains_negative.py
@@ -0,0 +1,38 @@
+# Copyright 2015 Red Hat Inc.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.api.identity import base
+from tempest import test
+
+from tempest_lib.common.utils import data_utils
+from tempest_lib import exceptions as lib_exc
+
+
+class DomainsNegativeTestJSON(base.BaseIdentityV3AdminTest):
+    _interface = 'json'
+
+    @test.attr(type=['negative', 'gate'])
+    @test.idempotent_id('1f3fbff5-4e44-400d-9ca1-d953f05f609b')
+    def test_delete_active_domain(self):
+        d_name = data_utils.rand_name('domain')
+        d_desc = data_utils.rand_name('domain-desc')
+        domain = self.client.create_domain(d_name, description=d_desc)
+        domain_id = domain['id']
+
+        self.addCleanup(self.delete_domain, domain_id)
+
+        # domain need to be disabled before deleting
+        self.assertRaises(lib_exc.Forbidden, self.client.delete_domain,
+                          domain_id)
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 5d66b9c..913e807 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -66,16 +66,9 @@
 
     credentials = ['primary']
 
-    @classmethod
-    def setup_credentials(cls):
-        super(BaseIdentityV2Test, cls).setup_credentials()
-        cls.os = cls.get_client_manager(identity_version='v2')
-
-    @classmethod
-    def skip_checks(cls):
-        super(BaseIdentityV2Test, cls).skip_checks()
-        if not CONF.identity_feature_enabled.api_v2:
-            raise cls.skipException("Identity api v2 is not enabled")
+    # identity v2 tests should obtain tokens and create accounts via v2
+    # regardless of the configured CONF.identity.auth_version
+    identity_version = 'v2'
 
     @classmethod
     def setup_clients(cls):
@@ -94,7 +87,7 @@
 
 class BaseIdentityV2AdminTest(BaseIdentityV2Test):
 
-    credentials = ['admin']
+    credentials = ['primary', 'admin']
 
     @classmethod
     def setup_clients(cls):
@@ -117,16 +110,9 @@
 
     credentials = ['primary']
 
-    @classmethod
-    def setup_credentials(cls):
-        super(BaseIdentityV3Test, cls).setup_credentials()
-        cls.os = cls.get_client_manager(identity_version='v3')
-
-    @classmethod
-    def skip_checks(cls):
-        super(BaseIdentityV3Test, cls).skip_checks()
-        if not CONF.identity_feature_enabled.api_v3:
-            raise cls.skipException("Identity api v3 is not enabled")
+    # identity v3 tests should obtain tokens and create accounts via v3
+    # regardless of the configured CONF.identity.auth_version
+    identity_version = 'v3'
 
     @classmethod
     def setup_clients(cls):
@@ -146,7 +132,7 @@
 
 class BaseIdentityV3AdminTest(BaseIdentityV3Test):
 
-    credentials = ['admin']
+    credentials = ['primary', 'admin']
 
     @classmethod
     def setup_clients(cls):
@@ -186,6 +172,12 @@
         if len(role) > 0:
             return role[0]
 
+    def delete_domain(self, domain_id):
+        # NOTE(mpavlase) It is necessary to disable the domain before deleting
+        # otherwise it raises Forbidden exception
+        self.client.update_domain(domain_id, enabled=False)
+        self.client.delete_domain(domain_id)
+
 
 class DataGenerator(object):
 
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index acf8272..00959d9 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -146,7 +146,7 @@
         cls.alt_tenant_id = cls.alt_img_client.tenant_id
 
     def _list_image_ids_as_alt(self):
-        image_list = self.alt_img_client.image_list()
+        image_list = self.alt_img_client.list_images()
         image_ids = map(lambda x: x['id'], image_list)
         return image_ids
 
diff --git a/tempest/api/image/v1/test_image_members.py b/tempest/api/image/v1/test_image_members.py
index d57cb93..4969858 100644
--- a/tempest/api/image/v1/test_image_members.py
+++ b/tempest/api/image/v1/test_image_members.py
@@ -23,12 +23,12 @@
     def test_add_image_member(self):
         image = self._create_image()
         self.client.add_member(self.alt_tenant_id, image)
-        body = self.client.get_image_membership(image)
+        body = self.client.list_image_members(image)
         members = body['members']
         members = map(lambda x: x['member_id'], members)
         self.assertIn(self.alt_tenant_id, members)
         # get image as alt user
-        self.alt_img_cli.get_image(image)
+        self.alt_img_cli.show_image(image)
 
     @test.idempotent_id('6a5328a5-80e8-4b82-bd32-6c061f128da9')
     def test_get_shared_images(self):
@@ -47,6 +47,6 @@
         image_id = self._create_image()
         self.client.add_member(self.alt_tenant_id, image_id)
         self.client.delete_member(self.alt_tenant_id, image_id)
-        body = self.client.get_image_membership(image_id)
+        body = self.client.list_image_members(image_id)
         members = body['members']
         self.assertEqual(0, len(members), str(members))
diff --git a/tempest/api/image/v1/test_image_members_negative.py b/tempest/api/image/v1/test_image_members_negative.py
index b96cf71..df0e88a 100644
--- a/tempest/api/image/v1/test_image_members_negative.py
+++ b/tempest/api/image/v1/test_image_members_negative.py
@@ -52,5 +52,5 @@
         # Image is hidden from another tenants.
         image_id = self._create_image()
         self.assertRaises(lib_exc.NotFound,
-                          self.alt_img_cli.get_image,
+                          self.alt_img_cli.show_image,
                           image_id)
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index ea95059..6c25c93 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -77,7 +77,7 @@
         self.assertEqual('New Http Image', body.get('name'))
         self.assertFalse(body.get('is_public'))
         self.client.wait_for_image_status(image_id, 'active')
-        self.client.get_image(image_id)
+        self.client.show_image(image_id)
 
     @test.idempotent_id('05b19d55-140c-40d0-b36b-fafd774d421b')
     def test_register_image_with_min_ram(self):
@@ -168,14 +168,14 @@
     @test.idempotent_id('246178ab-3b33-4212-9a4b-a7fe8261794d')
     def test_index_no_params(self):
         # Simple test to see all fixture images returned
-        images_list = self.client.image_list()
+        images_list = self.client.list_images()
         image_list = map(lambda x: x['id'], images_list)
         for image_id in self.created_images:
             self.assertIn(image_id, image_list)
 
     @test.idempotent_id('f1755589-63d6-4468-b098-589820eb4031')
     def test_index_disk_format(self):
-        images_list = self.client.image_list(disk_format='ami')
+        images_list = self.client.list_images(disk_format='ami')
         for image in images_list:
             self.assertEqual(image['disk_format'], 'ami')
         result_set = set(map(lambda x: x['id'], images_list))
@@ -184,7 +184,7 @@
 
     @test.idempotent_id('2143655d-96d9-4bec-9188-8674206b4b3b')
     def test_index_container_format(self):
-        images_list = self.client.image_list(container_format='bare')
+        images_list = self.client.list_images(container_format='bare')
         for image in images_list:
             self.assertEqual(image['container_format'], 'bare')
         result_set = set(map(lambda x: x['id'], images_list))
@@ -193,7 +193,7 @@
 
     @test.idempotent_id('feb32ac6-22bb-4a16-afd8-9454bb714b14')
     def test_index_max_size(self):
-        images_list = self.client.image_list(size_max=42)
+        images_list = self.client.list_images(size_max=42)
         for image in images_list:
             self.assertTrue(image['size'] <= 42)
         result_set = set(map(lambda x: x['id'], images_list))
@@ -202,7 +202,7 @@
 
     @test.idempotent_id('6ffc16d0-4cbf-4401-95c8-4ac63eac34d8')
     def test_index_min_size(self):
-        images_list = self.client.image_list(size_min=142)
+        images_list = self.client.list_images(size_min=142)
         for image in images_list:
             self.assertTrue(image['size'] >= 142)
         result_set = set(map(lambda x: x['id'], images_list))
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 32f80a2..8cccddd 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -59,7 +59,7 @@
         self.client.store_image(image_id, image_file)
 
         # Now try to get image details
-        body = self.client.get_image(image_id)
+        body = self.client.show_image(image_id)
         self.assertEqual(image_id, body['id'])
         self.assertEqual(image_name, body['name'])
         self.assertEqual(uuid, body['ramdisk_id'])
@@ -88,7 +88,7 @@
         self.client.wait_for_resource_deletion(image_id)
 
         # Verifying deletion
-        images = self.client.image_list()
+        images = self.client.list_images()
         images_id = [item['id'] for item in images]
         self.assertNotIn(image_id, images_id)
 
@@ -118,7 +118,7 @@
 
         # Verifying updating
 
-        body = self.client.get_image(image_id)
+        body = self.client.show_image(image_id)
         self.assertEqual(image_id, body['id'])
         self.assertEqual(new_image_name, body['name'])
 
@@ -164,7 +164,7 @@
         """
         Perform list action with given params and validates result.
         """
-        images_list = self.client.image_list(params=params)
+        images_list = self.client.list_images(params=params)
         # Validating params of fetched images
         for image in images_list:
             for key in params:
@@ -174,7 +174,7 @@
     @test.idempotent_id('1e341d7a-90a9-494c-b143-2cdf2aeb6aee')
     def test_index_no_params(self):
         # Simple test to see all fixture images returned
-        images_list = self.client.image_list()
+        images_list = self.client.list_images()
         image_list = map(lambda x: x['id'], images_list)
 
         for image in self.created_images:
@@ -203,7 +203,7 @@
         # Test to get all images by size
         image_id = self.created_images[1]
         # Get image metadata
-        image = self.client.get_image(image_id)
+        image = self.client.show_image(image_id)
 
         params = {"size": image['size']}
         self._list_by_param_value_and_assert(params)
@@ -213,11 +213,11 @@
         # Test to get all images with size between 2000 to 3000
         image_id = self.created_images[1]
         # Get image metadata
-        image = self.client.get_image(image_id)
+        image = self.client.show_image(image_id)
 
         size = image['size']
         params = {"size_min": size - 500, "size_max": size + 500}
-        images_list = self.client.image_list(params=params)
+        images_list = self.client.list_images(params=params)
         image_size_list = map(lambda x: x['size'], images_list)
 
         for image_size in image_size_list:
@@ -235,7 +235,7 @@
     def test_list_images_param_limit(self):
         # Test to get images by limit
         params = {"limit": 2}
-        images_list = self.client.image_list(params=params)
+        images_list = self.client.list_images(params=params)
 
         self.assertEqual(len(images_list), params['limit'],
                          "Failed to get images by limit")
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
index 26d4197..07c2073 100644
--- a/tempest/api/image/v2/test_images_member.py
+++ b/tempest/api/image/v2/test_images_member.py
@@ -29,7 +29,7 @@
                                                  self.alt_tenant_id,
                                                  'accepted')
         self.assertIn(image_id, self._list_image_ids_as_alt())
-        body = self.os_img_client.get_image_membership(image_id)
+        body = self.os_img_client.list_image_members(image_id)
         members = body['members']
         member = members[0]
         self.assertEqual(len(members), 1, str(members))
diff --git a/tempest/api/image/v2/test_images_negative.py b/tempest/api/image/v2/test_images_negative.py
index 60a10a2..c5c5e8b 100644
--- a/tempest/api/image/v2/test_images_negative.py
+++ b/tempest/api/image/v2/test_images_negative.py
@@ -25,7 +25,7 @@
 class ImagesNegativeTest(base.BaseV2ImageTest):
 
     """
-    here we have -ve tests for get_image and delete_image api
+    here we have -ve tests for show_image and delete_image api
 
     Tests
         ** get non-existent image
@@ -41,7 +41,7 @@
     def test_get_non_existent_image(self):
         # get the non-existent image
         non_existent_id = str(uuid.uuid4())
-        self.assertRaises(lib_exc.NotFound, self.client.get_image,
+        self.assertRaises(lib_exc.NotFound, self.client.show_image,
                           non_existent_id)
 
     @test.attr(type=['negative'])
@@ -49,7 +49,7 @@
     def test_get_image_null_id(self):
         # get image with image_id = NULL
         image_id = ""
-        self.assertRaises(lib_exc.NotFound, self.client.get_image, image_id)
+        self.assertRaises(lib_exc.NotFound, self.client.show_image, image_id)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('e57fc127-7ba0-4693-92d7-1d8a05ebcba9')
@@ -64,7 +64,7 @@
         self.client.wait_for_resource_deletion(image_id)
 
         # get the deleted image
-        self.assertRaises(lib_exc.NotFound, self.client.get_image, image_id)
+        self.assertRaises(lib_exc.NotFound, self.client.show_image, image_id)
 
         # delete the deleted image
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
diff --git a/tempest/api/image/v2/test_images_tags.py b/tempest/api/image/v2/test_images_tags.py
index 24688dc..e38136e 100644
--- a/tempest/api/image/v2/test_images_tags.py
+++ b/tempest/api/image/v2/test_images_tags.py
@@ -31,10 +31,10 @@
 
         # Creating image tag and verify it.
         self.client.add_image_tag(image_id, tag)
-        body = self.client.get_image(image_id)
+        body = self.client.show_image(image_id)
         self.assertIn(tag, body['tags'])
 
         # Deleting image tag and verify it.
         self.client.delete_image_tag(image_id, tag)
-        body = self.client.get_image(image_id)
+        body = self.client.show_image(image_id)
         self.assertNotIn(tag, body['tags'])
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 5e3e374..7a96f34 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -393,7 +393,9 @@
                                       " in filtered list (%s)." % nonexternal)
         self.assertIn(CONF.network.public_network_id, networks)
 
-        subnets_iter = (network['subnets'] for network in body['networks'])
+        subnets_iter = (network['subnets']
+                        for network in body['networks']
+                        if not network['shared'])
         # subnets_iter is a list (iterator) of lists. This flattens it to a
         # list of UUIDs
         public_subnets_iter = itertools.chain(*subnets_iter)
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 0c7fa6b..41a7d65 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -24,6 +24,8 @@
 
 class BaseObjectTest(tempest.test.BaseTestCase):
 
+    credentials = [['operator', CONF.object_storage.operator_role]]
+
     @classmethod
     def skip_checks(cls):
         super(BaseObjectTest, cls).skip_checks()
@@ -35,8 +37,9 @@
     def setup_credentials(cls):
         cls.set_network_resources()
         super(BaseObjectTest, cls).setup_credentials()
-        operator_role = CONF.object_storage.operator_role
-        cls.os = cls.get_client_manager(roles=[operator_role])
+        # credentials may be overwritten by children classes
+        if hasattr(cls, 'os_roles_operator'):
+            cls.os = cls.os_roles_operator
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index 04bfee4..bbdf367 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -23,12 +23,14 @@
 
 class AccountQuotasTest(base.BaseObjectTest):
 
+    credentials = [['operator', CONF.object_storage.operator_role],
+                   ['reseller', CONF.object_storage.reseller_admin_role]]
+
     @classmethod
     def setup_credentials(cls):
         super(AccountQuotasTest, cls).setup_credentials()
-        reseller_admin_role = CONF.object_storage.reseller_admin_role
-        cls.os_reselleradmin = cls.get_client_manager(
-            roles=[reseller_admin_role])
+        cls.os = cls.os_roles_operator
+        cls.os_reselleradmin = cls.os_roles_reseller
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/object_storage/test_account_quotas_negative.py b/tempest/api/object_storage/test_account_quotas_negative.py
index a11b407..e945e1e 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -25,12 +25,14 @@
 
 class AccountQuotasNegativeTest(base.BaseObjectTest):
 
+    credentials = [['operator', CONF.object_storage.operator_role],
+                   ['reseller', CONF.object_storage.reseller_admin_role]]
+
     @classmethod
     def setup_credentials(cls):
         super(AccountQuotasNegativeTest, cls).setup_credentials()
-        reseller_admin_role = CONF.object_storage.reseller_admin_role
-        cls.os_reselleradmin = cls.get_client_manager(
-            roles=[reseller_admin_role])
+        cls.os = cls.os_roles_operator
+        cls.os_reselleradmin = cls.os_roles_reseller
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index c28a3e0..ac41148 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -29,13 +29,15 @@
 
 class AccountTest(base.BaseObjectTest):
 
+    credentials = [['operator', CONF.object_storage.operator_role],
+                   ['operator_alt', CONF.object_storage.operator_role]]
     containers = []
 
     @classmethod
     def setup_credentials(cls):
         super(AccountTest, cls).setup_credentials()
-        cls.os_operator = cls.get_client_manager(
-            roles=[CONF.object_storage.operator_role], force_new=True)
+        cls.os = cls.os_roles_operator
+        cls.os_operator = cls.os_roles_operator_alt
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/object_storage/test_account_services_negative.py b/tempest/api/object_storage/test_account_services_negative.py
index dfc5dfa..998c2bd 100644
--- a/tempest/api/object_storage/test_account_services_negative.py
+++ b/tempest/api/object_storage/test_account_services_negative.py
@@ -23,11 +23,14 @@
 
 class AccountNegativeTest(base.BaseObjectTest):
 
+    credentials = [['operator', CONF.object_storage.operator_role],
+                   ['operator_alt', CONF.object_storage.operator_role]]
+
     @classmethod
     def setup_credentials(cls):
         super(AccountNegativeTest, cls).setup_credentials()
-        cls.os_operator = cls.get_client_manager(
-            roles=[CONF.object_storage.operator_role], force_new=True)
+        cls.os = cls.os_roles_operator
+        cls.os_operator = cls.os_roles_operator_alt
 
     @test.attr(type=['negative'])
     @test.idempotent_id('070e6aca-6152-4867-868d-1118d68fb38c')
diff --git a/tempest/api/object_storage/test_container_acl.py b/tempest/api/object_storage/test_container_acl.py
index 25dac6b..4df813d 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -24,11 +24,14 @@
 
 class ObjectTestACLs(base.BaseObjectTest):
 
+    credentials = [['operator', CONF.object_storage.operator_role],
+                   ['operator_alt', CONF.object_storage.operator_role]]
+
     @classmethod
     def setup_credentials(cls):
         super(ObjectTestACLs, cls).setup_credentials()
-        cls.os_operator = cls.get_client_manager(
-            roles=[CONF.object_storage.operator_role], force_new=True)
+        cls.os = cls.os_roles_operator
+        cls.os_operator = cls.os_roles_operator_alt
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/object_storage/test_container_acl_negative.py b/tempest/api/object_storage/test_container_acl_negative.py
index 31c301a..1c42e97 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -24,11 +24,14 @@
 
 class ObjectACLsNegativeTest(base.BaseObjectTest):
 
+    credentials = [['operator', CONF.object_storage.operator_role],
+                   ['operator_alt', CONF.object_storage.operator_role]]
+
     @classmethod
     def setup_credentials(cls):
         super(ObjectACLsNegativeTest, cls).setup_credentials()
-        cls.os_operator = cls.get_client_manager(
-            roles=[CONF.object_storage.operator_role], force_new=True)
+        cls.os = cls.os_roles_operator
+        cls.os_operator = cls.os_roles_operator_alt
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 4c0723d..06e700b 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -37,11 +37,14 @@
 class ContainerSyncTest(base.BaseObjectTest):
     clients = {}
 
+    credentials = [['operator', CONF.object_storage.operator_role],
+                   ['operator_alt', CONF.object_storage.operator_role]]
+
     @classmethod
     def setup_credentials(cls):
         super(ContainerSyncTest, cls).setup_credentials()
-        cls.os_alt = cls.get_client_manager(
-            roles=[CONF.object_storage.operator_role], force_new=True)
+        cls.os = cls.os_roles_operator
+        cls.os_alt = cls.os_roles_operator_alt
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index b02f178..627895e 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -989,11 +989,14 @@
 
 class PublicObjectTest(base.BaseObjectTest):
 
+    credentials = [['operator', CONF.object_storage.operator_role],
+                   ['operator_alt', CONF.object_storage.operator_role]]
+
     @classmethod
     def setup_credentials(cls):
         super(PublicObjectTest, cls).setup_credentials()
-        cls.os_alt = cls.get_client_manager(
-            roles=[CONF.object_storage.operator_role], force_new=True)
+        cls.os = cls.os_roles_operator
+        cls.os_alt = cls.os_roles_operator_alt
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/orchestration/stacks/test_swift_resources.py b/tempest/api/orchestration/stacks/test_swift_resources.py
index d4fd3f9..dadabfa 100644
--- a/tempest/api/orchestration/stacks/test_swift_resources.py
+++ b/tempest/api/orchestration/stacks/test_swift_resources.py
@@ -31,6 +31,14 @@
             raise cls.skipException("Swift support is required")
 
     @classmethod
+    def setup_credentials(cls):
+        super(SwiftResourcesTestJSON, cls).setup_credentials()
+        stack_owner_role = CONF.orchestration.stack_owner_role
+        operator_role = CONF.object_storage.operator_role
+        cls.os = cls.get_client_manager(
+            roles=[stack_owner_role, operator_role])
+
+    @classmethod
     def setup_clients(cls):
         super(SwiftResourcesTestJSON, cls).setup_clients()
         cls.account_client = cls.os.account_client
diff --git a/tempest/api/volume/test_volume_metadata.py b/tempest/api/volume/test_volume_metadata.py
index e89ff9a..8529cfc 100644
--- a/tempest/api/volume/test_volume_metadata.py
+++ b/tempest/api/volume/test_volume_metadata.py
@@ -75,7 +75,7 @@
             self.volume_id, update)
         # Get the metadata of the volume
         body = self.volumes_client.show_volume_metadata(self.volume_id)
-        self.assertThat(body.items(), matchers.ContainsAll(update.items()))
+        self.assertEqual(update, body)
 
     @test.idempotent_id('862261c5-8df4-475a-8c21-946e50e36a20')
     def test_update_volume_metadata_item(self):
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index f571f2d..375d34a 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -18,6 +18,7 @@
 from tempest.api.volume import base
 from tempest import config
 from tempest import test
+import testtools
 
 CONF = config.CONF
 
@@ -69,6 +70,18 @@
         self.client.detach_volume(self.volume['id'])
         self.client.wait_for_volume_status(self.volume['id'], 'available')
 
+    @test.idempotent_id('63e21b4c-0a0c-41f6-bfc3-7c2816815599')
+    @testtools.skipUnless(CONF.volume_feature_enabled.bootable,
+                          'Update bootable status of a volume is not enabled.')
+    def test_volume_bootable(self):
+        # Verify that a volume bootable flag is retrieved
+        for bool_bootable in [True, False]:
+            self.client.set_bootable_volume(self.volume['id'], bool_bootable)
+            fetched_volume = self.client.show_volume(self.volume['id'])
+            # Get Volume information
+            bool_flag = self._is_true(fetched_volume['bootable'])
+            self.assertEqual(bool_bootable, bool_flag)
+
     @test.idempotent_id('9516a2c8-9135-488c-8dd6-5677a7e5f371')
     @test.stresstest(class_setup_per='process')
     @test.services('compute')
diff --git a/tempest/api_schema/response/compute/baremetal_nodes.py b/tempest/api_schema/response/compute/v2_1/baremetal_nodes.py
similarity index 100%
rename from tempest/api_schema/response/compute/baremetal_nodes.py
rename to tempest/api_schema/response/compute/v2_1/baremetal_nodes.py
diff --git a/tempest/api_schema/response/compute/v2_1/flavors.py b/tempest/api_schema/response/compute/v2_1/flavors.py
index 725d17a..26760ac 100644
--- a/tempest/api_schema/response/compute/v2_1/flavors.py
+++ b/tempest/api_schema/response/compute/v2_1/flavors.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api_schema.response.compute import parameter_types
+from tempest.api_schema.response.compute.v2_1 import parameter_types
 
 list_flavors = {
     'status_code': [200],
diff --git a/tempest/api_schema/response/compute/flavors_access.py b/tempest/api_schema/response/compute/v2_1/flavors_access.py
similarity index 100%
rename from tempest/api_schema/response/compute/flavors_access.py
rename to tempest/api_schema/response/compute/v2_1/flavors_access.py
diff --git a/tempest/api_schema/response/compute/flavors_extra_specs.py b/tempest/api_schema/response/compute/v2_1/flavors_extra_specs.py
similarity index 94%
rename from tempest/api_schema/response/compute/flavors_extra_specs.py
rename to tempest/api_schema/response/compute/v2_1/flavors_extra_specs.py
index 4003d36..faa25d0 100644
--- a/tempest/api_schema/response/compute/flavors_extra_specs.py
+++ b/tempest/api_schema/response/compute/v2_1/flavors_extra_specs.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-flavor_extra_specs = {
+set_get_flavor_extra_specs = {
     'status_code': [200],
     'response_body': {
         'type': 'object',
@@ -28,7 +28,7 @@
     }
 }
 
-flavor_extra_specs_key = {
+set_get_flavor_extra_specs_key = {
     'status_code': [200],
     'response_body': {
         'type': 'object',
diff --git a/tempest/api_schema/response/compute/v2_1/floating_ips.py b/tempest/api_schema/response/compute/v2_1/floating_ips.py
index 7369bec..ad1c531 100644
--- a/tempest/api_schema/response/compute/v2_1/floating_ips.py
+++ b/tempest/api_schema/response/compute/v2_1/floating_ips.py
@@ -48,7 +48,7 @@
     }
 }
 
-floating_ip = {
+create_get_floating_ip = {
     'status_code': [200],
     'response_body': {
         'type': 'object',
@@ -59,7 +59,7 @@
     }
 }
 
-floating_ip_pools = {
+list_floating_ip_pools = {
     'status_code': [200],
     'response_body': {
         'type': 'object',
diff --git a/tempest/api_schema/response/compute/v2_1/images.py b/tempest/api_schema/response/compute/v2_1/images.py
index 3c0b80e..e6f8db6 100644
--- a/tempest/api_schema/response/compute/v2_1/images.py
+++ b/tempest/api_schema/response/compute/v2_1/images.py
@@ -14,7 +14,7 @@
 
 import copy
 
-from tempest.api_schema.response.compute import parameter_types
+from tempest.api_schema.response.compute.v2_1 import parameter_types
 
 image_links = copy.deepcopy(parameter_types.links)
 image_links['items']['properties'].update({'type': {'type': 'string'}})
diff --git a/tempest/api_schema/response/compute/v2_1/interfaces.py b/tempest/api_schema/response/compute/v2_1/interfaces.py
index 4de3309..033f816 100644
--- a/tempest/api_schema/response/compute/v2_1/interfaces.py
+++ b/tempest/api_schema/response/compute/v2_1/interfaces.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api_schema.response.compute import parameter_types
+from tempest.api_schema.response.compute.v2_1 import parameter_types
 
 interface_common_info = {
     'type': 'object',
diff --git a/tempest/api_schema/response/compute/migrations.py b/tempest/api_schema/response/compute/v2_1/migrations.py
similarity index 100%
rename from tempest/api_schema/response/compute/migrations.py
rename to tempest/api_schema/response/compute/v2_1/migrations.py
diff --git a/tempest/api_schema/response/compute/parameter_types.py b/tempest/api_schema/response/compute/v2_1/parameter_types.py
similarity index 100%
rename from tempest/api_schema/response/compute/parameter_types.py
rename to tempest/api_schema/response/compute/v2_1/parameter_types.py
diff --git a/tempest/api_schema/response/compute/v2_1/servers.py b/tempest/api_schema/response/compute/v2_1/servers.py
index 726f9b1..875f607 100644
--- a/tempest/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/api_schema/response/compute/v2_1/servers.py
@@ -14,7 +14,7 @@
 
 import copy
 
-from tempest.api_schema.response.compute import parameter_types
+from tempest.api_schema.response.compute.v2_1 import parameter_types
 
 create_server = {
     'status_code': [202],
diff --git a/tempest/api_schema/response/compute/services.py b/tempest/api_schema/response/compute/v2_1/services.py
similarity index 100%
rename from tempest/api_schema/response/compute/services.py
rename to tempest/api_schema/response/compute/v2_1/services.py
diff --git a/tempest/api_schema/response/compute/v2_1/tenant_usages.py b/tempest/api_schema/response/compute/v2_1/tenant_usages.py
index 0b824a1..d51ef12 100644
--- a/tempest/api_schema/response/compute/v2_1/tenant_usages.py
+++ b/tempest/api_schema/response/compute/v2_1/tenant_usages.py
@@ -66,7 +66,7 @@
                                  'total_hours', 'total_local_gb_usage',
                                  'total_memory_mb_usage', 'total_vcpus_usage']
 
-list_tenant = {
+list_tenant_usage = {
     'status_code': [200],
     'response_body': {
         'type': 'object',
@@ -80,7 +80,7 @@
     }
 }
 
-get_tenant = {
+get_tenant_usage = {
     'status_code': [200],
     'response_body': {
         'type': 'object',
diff --git a/tempest/api_schema/response/compute/version.py b/tempest/api_schema/response/compute/v2_1/version.py
similarity index 100%
rename from tempest/api_schema/response/compute/version.py
rename to tempest/api_schema/response/compute/v2_1/version.py
diff --git a/tempest/clients.py b/tempest/clients.py
index a0c9471..9f6a9bb 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -22,6 +22,7 @@
 from tempest.common import cred_provider
 from tempest.common import negative_rest_client
 from tempest import config
+from tempest import exceptions
 from tempest import manager
 from tempest.services.baremetal.v1.json.baremetal_client import \
     BaremetalClientJSON
@@ -343,11 +344,22 @@
         self.credentials_client = CredentialsClientJSON(self.auth_provider,
                                                         **params)
         # Token clients do not use the catalog. They only need default_params.
-        self.token_client = TokenClientJSON(CONF.identity.uri,
-                                            **self.default_params)
+        # They read auth_url, so they should only be set if the corresponding
+        # API version is marked as enabled
+        if CONF.identity_feature_enabled.api_v2:
+            if CONF.identity.uri:
+                self.token_client = TokenClientJSON(
+                    CONF.identity.uri, **self.default_params)
+            else:
+                msg = 'Identity v2 API enabled, but no identity.uri set'
+                raise exceptions.InvalidConfiguration(msg)
         if CONF.identity_feature_enabled.api_v3:
-            self.token_v3_client = V3TokenClientJSON(CONF.identity.uri_v3,
-                                                     **self.default_params)
+            if CONF.identity.uri_v3:
+                self.token_v3_client = V3TokenClientJSON(
+                    CONF.identity.uri_v3, **self.default_params)
+            else:
+                msg = 'Identity v3 API enabled, but no identity.uri_v3 set'
+                raise exceptions.InvalidConfiguration(msg)
 
     def _set_volume_clients(self):
         params = {
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
new file mode 100755
index 0000000..0a48b8d
--- /dev/null
+++ b/tempest/cmd/account_generator.py
@@ -0,0 +1,266 @@
+#!/usr/bin/env python
+
+# Copyright 2015 Mirantis, Inc.
+#
+# 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 argparse
+import os
+
+from oslo_log import log as logging
+import yaml
+
+from tempest import config
+from tempest import exceptions
+from tempest.services.identity.v2.json import identity_client
+import tempest_lib.auth
+from tempest_lib.common.utils import data_utils
+import tempest_lib.exceptions
+
+LOG = None
+CONF = config.CONF
+
+
+def setup_logging():
+    global LOG
+    logging.setup(CONF, __name__)
+    LOG = logging.getLogger(__name__)
+
+
+def keystone_admin(opts):
+    _creds = tempest_lib.auth.KeystoneV2Credentials(
+        username=opts.os_username,
+        password=opts.os_password,
+        tenant_name=opts.os_tenant_name)
+    auth_params = {
+        'disable_ssl_certificate_validation':
+            CONF.identity.disable_ssl_certificate_validation,
+        'ca_certs': CONF.identity.ca_certificates_file,
+        'trace_requests': CONF.debug.trace_requests
+    }
+    _auth = tempest_lib.auth.KeystoneV2AuthProvider(
+        _creds, CONF.identity.uri, **auth_params)
+    params = {
+        'disable_ssl_certificate_validation':
+            CONF.identity.disable_ssl_certificate_validation,
+        'ca_certs': CONF.identity.ca_certificates_file,
+        'trace_requests': CONF.debug.trace_requests,
+        'build_interval': CONF.compute.build_interval,
+        'build_timeout': CONF.compute.build_timeout
+    }
+    return identity_client.IdentityClientJSON(
+        _auth,
+        CONF.identity.catalog_type,
+        CONF.identity.region,
+        endpoint_type='adminURL',
+        **params
+    )
+
+
+def create_resources(opts, resources):
+    admin = keystone_admin(opts)
+    roles = admin.list_roles()
+    for u in resources['users']:
+        u['role_ids'] = []
+        for r in u.get('roles', ()):
+            try:
+                role = filter(lambda r_: r_['name'] == r, roles)[0]
+                u['role_ids'] += [role['id']]
+            except IndexError:
+                raise exceptions.TempestException(
+                    "Role: %s - doesn't exist" % r
+                )
+    existing = [x['name'] for x in admin.list_tenants()]
+    for tenant in resources['tenants']:
+        if tenant not in existing:
+            admin.create_tenant(tenant)
+        else:
+            LOG.warn("Tenant '%s' already exists in this environment" % tenant)
+    LOG.info('Tenants created')
+    for u in resources['users']:
+        try:
+            tenant = admin.get_tenant_by_name(u['tenant'])
+        except tempest_lib.exceptions.NotFound:
+            LOG.error("Tenant: %s - not found" % u['tenant'])
+            continue
+        while True:
+            try:
+                admin.get_user_by_username(tenant['id'], u['name'])
+            except tempest_lib.exceptions.NotFound:
+                admin.create_user(
+                    u['name'], u['pass'], tenant['id'],
+                    "%s@%s" % (u['name'], tenant['id']),
+                    enabled=True)
+                break
+            else:
+                LOG.warn("User '%s' already exists in this environment. "
+                         "New name generated" % u['name'])
+                u['name'] = random_user_name(opts.tag, u['prefix'])
+
+    LOG.info('Users created')
+    for u in resources['users']:
+        try:
+            tenant = admin.get_tenant_by_name(u['tenant'])
+        except tempest_lib.exceptions.NotFound:
+            LOG.error("Tenant: %s - not found" % u['tenant'])
+            continue
+        try:
+            user = admin.get_user_by_username(tenant['id'],
+                                              u['name'])
+        except tempest_lib.exceptions.NotFound:
+            LOG.error("User: %s - not found" % u['user'])
+            continue
+        for r in u['role_ids']:
+            try:
+                admin.assign_user_role(tenant['id'], user['id'], r)
+            except tempest_lib.exceptions.Conflict:
+                # don't care if it's already assigned
+                pass
+    LOG.info('Roles assigned')
+    LOG.info('Resources deployed successfully!')
+
+
+def random_user_name(tag, prefix):
+    if tag:
+        return data_utils.rand_name('-'.join((tag, prefix)))
+    else:
+        return data_utils.rand_name(prefix)
+
+
+def generate_resources(opts):
+    spec = [{'number': 1,
+             'prefix': 'primary',
+             'roles': (CONF.auth.tempest_roles +
+                       [CONF.object_storage.operator_role])},
+            {'number': 1,
+             'prefix': 'alt',
+             'roles': (CONF.auth.tempest_roles +
+                       [CONF.object_storage.operator_role])},
+            {'number': 1,
+             'prefix': 'swift_admin',
+             'roles': (CONF.auth.tempest_roles +
+                       [CONF.object_storage.operator_role,
+                        CONF.object_storage.reseller_admin_role])},
+            {'number': 1,
+             'prefix': 'stack_owner',
+             'roles': (CONF.auth.tempest_roles +
+                       [CONF.orchestration.stack_owner_role])},
+            ]
+    if opts.admin:
+        spec.append({
+            'number': 1,
+            'prefix': 'admin',
+            'roles': (CONF.auth.tempest_roles +
+                      [CONF.identity.admin_role])
+        })
+    resources = {'tenants': [],
+                 'users': []}
+    for count in range(opts.concurrency):
+        for user_group in spec:
+            users = [random_user_name(opts.tag, user_group['prefix'])
+                     for _ in range(user_group['number'])]
+            for user in users:
+                tenant = '-'.join((user, 'tenant'))
+                resources['tenants'].append(tenant)
+                resources['users'].append({
+                    'tenant': tenant,
+                    'name': user,
+                    'pass': data_utils.rand_name(),
+                    'prefix': user_group['prefix'],
+                    'roles': user_group['roles']
+                })
+    return resources
+
+
+def dump_accounts(opts, resources):
+    accounts = []
+    for user in resources['users']:
+        accounts.append({
+            'username': user['name'],
+            'tenant_name': user['tenant'],
+            'password': user['pass'],
+            'roles': user['roles']
+        })
+    if os.path.exists(opts.accounts):
+        os.rename(opts.accounts, '.'.join((opts.accounts, 'bak')))
+    with open(opts.accounts, 'w') as f:
+        yaml.dump(accounts, f, default_flow_style=False)
+    LOG.info('%s generated successfully!' % opts.accounts)
+
+
+def get_options():
+    usage_string = ('account_generator [-h] <ARG> ...\n\n'
+                    'To see help on specific argument, do:\n'
+                    'account_generator <ARG> -h')
+    parser = argparse.ArgumentParser(
+        description='Create accounts.yaml file for concurrent test runs. '
+                    'One primary user, one alt user, '
+                    'one swift admin, one stack owner '
+                    'and one admin (optionally) will be created '
+                    'for each concurrent thread.',
+        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+        usage=usage_string
+    )
+
+    parser.add_argument('-c', '--config-file',
+                        metavar='/etc/tempest.conf',
+                        help='path to tempest config file')
+    parser.add_argument('--os-username',
+                        metavar='<auth-user-name>',
+                        default=os.environ.get('OS_USERNAME'),
+                        help='User should have permitions '
+                             'to create new user accounts and '
+                             'tenants. Defaults to env[OS_USERNAME].')
+    parser.add_argument('--os-password',
+                        metavar='<auth-password>',
+                        default=os.environ.get('OS_PASSWORD'),
+                        help='Defaults to env[OS_PASSWORD].')
+    parser.add_argument('--os-tenant-name',
+                        metavar='<auth-tenant-name>',
+                        default=os.environ.get('OS_TENANT_NAME'),
+                        help='Defaults to env[OS_TENANT_NAME].')
+    parser.add_argument('--tag',
+                        default='',
+                        required=False,
+                        dest='tag',
+                        help='Resources tag')
+    parser.add_argument('-r', '--concurrency',
+                        default=1,
+                        type=int,
+                        required=True,
+                        dest='concurrency',
+                        help='Concurrency count')
+    parser.add_argument('--with-admin',
+                        action='store_true',
+                        dest='admin',
+                        help='Create admin in every tenant')
+    parser.add_argument('accounts',
+                        metavar='accounts_file.yaml',
+                        help='Output accounts yaml file')
+
+    opts = parser.parse_args()
+    if opts.config_file:
+        config.CONF.set_config_path(opts.config_file)
+    return opts
+
+
+def main(opts=None):
+    if not opts:
+        opts = get_options()
+    setup_logging()
+    resources = generate_resources(opts)
+    create_resources(opts, resources)
+    dump_accounts(opts, resources)
+
+if __name__ == "__main__":
+    main()
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 1ad12eb..eb6f143 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -343,6 +343,44 @@
         self.data['volumes'] = vols
 
 
+class VolumeQuotaService(BaseService):
+    def __init__(self, manager, **kwargs):
+        super(VolumeQuotaService, self).__init__(kwargs)
+        self.client = manager.volume_quotas_client
+
+    def delete(self):
+        client = self.client
+        try:
+            client.delete_quota_set(self.tenant_id)
+        except Exception as e:
+            LOG.exception("Delete Volume Quotas exception: %s" % e)
+            pass
+
+    def dry_run(self):
+        quotas = self.client.show_quota_usage(self.tenant_id)
+        self.data['volume_quotas'] = quotas
+
+
+class NovaQuotaService(BaseService):
+    def __init__(self, manager, **kwargs):
+        super(NovaQuotaService, self).__init__(kwargs)
+        self.client = manager.quotas_client
+        self.limits_client = manager.limits_client
+
+    def delete(self):
+        client = self.client
+        try:
+            client.delete_quota_set(self.tenant_id)
+        except Exception as e:
+            LOG.exception("Delete Quotas exception: %s" % e)
+            pass
+
+    def dry_run(self):
+        client = self.limits_client
+        quotas = client.get_absolute_limits()
+        self.data['compute_quotas'] = quotas
+
+
 # Begin network service classes
 class NetworkService(BaseService):
     def __init__(self, manager, **kwargs):
@@ -667,7 +705,7 @@
         self.data['pools'] = pools
 
 
-class NetworMeteringLabelRuleService(NetworkService):
+class NetworkMeteringLabelRuleService(NetworkService):
 
     def list(self):
         client = self.client
@@ -692,7 +730,7 @@
         self.data['rules'] = rules
 
 
-class NetworMeteringLabelService(NetworkService):
+class NetworkMeteringLabelService(NetworkService):
 
     def list(self):
         client = self.client
@@ -1052,6 +1090,7 @@
         tenant_services.append(ServerGroupService)
         if not IS_NEUTRON:
             tenant_services.append(FloatingIpService)
+        tenant_services.append(NovaQuotaService)
     if IS_HEAT:
         tenant_services.append(StackService)
     if IS_NEUTRON:
@@ -1068,8 +1107,8 @@
             tenant_services.append(NetworkVipService)
             tenant_services.append(NetworkPoolService)
         if test.is_extension_enabled('metering', 'network'):
-            tenant_services.append(NetworMeteringLabelRuleService)
-            tenant_services.append(NetworMeteringLabelService)
+            tenant_services.append(NetworkMeteringLabelRuleService)
+            tenant_services.append(NetworkMeteringLabelService)
         tenant_services.append(NetworkRouterService)
         tenant_services.append(NetworkFloatingIpService)
         tenant_services.append(NetworkPortService)
@@ -1078,6 +1117,7 @@
     if IS_CINDER:
         tenant_services.append(SnapshotService)
         tenant_services.append(VolumeService)
+        tenant_services.append(VolumeQuotaService)
     return tenant_services
 
 
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 4e2af76..d3426c6 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -617,7 +617,7 @@
 
 
 def _get_image_by_name(client, name):
-    body = client.images.image_list()
+    body = client.images.list_images()
     for image in body:
         if name == image['name']:
             return image
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
index fee1467..5ded3ee 100644
--- a/tempest/common/isolated_creds.py
+++ b/tempest/common/isolated_creds.py
@@ -311,7 +311,8 @@
             LOG.info("Acquired isolated creds:\n credentials: %s"
                      % credentials)
             if (CONF.service_available.neutron and
-                not CONF.baremetal.driver_enabled):
+                not CONF.baremetal.driver_enabled and
+                CONF.auth.create_isolated_networks):
                 network, subnet, router = self._create_network_resources(
                     credentials.tenant_id)
                 credentials.set_resources(network=network, subnet=subnet,
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index bedff1f..4b3995b 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -46,6 +46,9 @@
                                      channel_timeout=ssh_channel_timeout)
 
     def exec_command(self, cmd):
+        # Shell options below add more clearness on failures,
+        # path is extended for some non-cirros guest oses (centos7)
+        cmd = "set -eu -o pipefail; PATH=$PATH:/sbin; " + cmd
         return self.ssh_client.exec_command(cmd)
 
     def validate_authentication(self):
@@ -95,26 +98,26 @@
         return self.exec_command(cmd)
 
     def get_mac_address(self):
-        cmd = "/bin/ip addr | awk '/ether/ {print $2}'"
+        cmd = "ip addr | awk '/ether/ {print $2}'"
         return self.exec_command(cmd)
 
     def get_nic_name(self, address):
-        cmd = "/bin/ip -o addr | awk '/%s/ {print $2}'" % address
+        cmd = "ip -o addr | awk '/%s/ {print $2}'" % address
         return self.exec_command(cmd)
 
     def get_ip_list(self):
-        cmd = "/bin/ip address"
+        cmd = "ip address"
         return self.exec_command(cmd)
 
     def assign_static_ip(self, nic, addr):
-        cmd = "sudo /bin/ip addr add {ip}/{mask} dev {nic}".format(
+        cmd = "sudo ip addr add {ip}/{mask} dev {nic}".format(
             ip=addr, mask=CONF.network.tenant_network_mask_bits,
             nic=nic
         )
         return self.exec_command(cmd)
 
     def turn_nic_on(self, nic):
-        cmd = "sudo /bin/ip link set {nic} up".format(nic=nic)
+        cmd = "sudo ip link set {nic} up".format(nic=nic)
         return self.exec_command(cmd)
 
     def get_pids(self, pr_name):
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 64ff7f2..249bac9 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -99,15 +99,15 @@
 def wait_for_image_status(client, image_id, status):
     """Waits for an image to reach a given status.
 
-    The client should have a get_image(image_id) method to get the image.
+    The client should have a show_image(image_id) method to get the image.
     The client should also have build_interval and build_timeout attributes.
     """
-    image = client.get_image(image_id)
+    image = client.show_image(image_id)
     start = int(time.time())
 
     while image['status'] != status:
         time.sleep(client.build_interval)
-        image = client.get_image(image_id)
+        image = client.show_image(image_id)
         status_curr = image['status']
         if status_curr == 'ERROR':
             raise exceptions.AddImageException(image_id=image_id)
diff --git a/tempest/config.py b/tempest/config.py
index 3f3e7e7..603ccd2 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -69,7 +69,15 @@
                help="Only applicable when identity.auth_version is v3."
                     "Domain within which isolated credentials are provisioned."
                     "The default \"None\" means that the domain from the"
-                    "admin user is used instead.")
+                    "admin user is used instead."),
+    cfg.BoolOpt('create_isolated_networks',
+                default=True,
+                help="If allow_tenant_isolation is set to True and Neutron is "
+                     "enabled Tempest will try to create a useable network, "
+                     "subnet, and router when needed for each tenant it  "
+                     "creates. However in some neutron configurations, like "
+                     "with VLAN provider networks, this doesn't work. So if "
+                     "set to False the isolated networks will not be created"),
 ]
 
 identity_group = cfg.OptGroup(name='identity',
@@ -142,6 +150,9 @@
     cfg.StrOpt('admin_domain_name',
                help="Admin domain name for authentication (Keystone V3)."
                     "The same domain applies to user and project"),
+    cfg.StrOpt('default_domain_id',
+               default='default',
+               help="ID of the default domain"),
 ]
 
 identity_feature_group = cfg.OptGroup(name='identity-feature-enabled',
@@ -657,6 +668,10 @@
     cfg.BoolOpt('api_v2',
                 default=True,
                 help="Is the v2 volume API enabled"),
+    cfg.BoolOpt('bootable',
+                default=False,
+                help='Update bootable status of a volume '
+                     'Not implemented on icehouse ')
 ]
 
 
@@ -943,6 +958,8 @@
     cfg.StrOpt('img_container_format',
                default='bare',
                help='Image container format'),
+    cfg.DictOpt('img_properties', help='Glance image properties. '
+                'Use for custom images which require them'),
     cfg.StrOpt('ami_img_file',
                default='cirros-0.3.1-x86_64-blank.img',
                help='AMI image file name'),
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 50aa261..5f8d605 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -20,6 +20,7 @@
 from oslo_log import log
 import six
 from tempest_lib.common.utils import data_utils
+from tempest_lib.common.utils import misc as misc_utils
 from tempest_lib import exceptions as lib_exc
 
 from tempest.common import fixed_network
@@ -307,8 +308,13 @@
                                                   password=password)
         try:
             linux_client.validate_authentication()
-        except Exception:
-            LOG.exception('Initializing SSH connection to %s failed' % ip)
+        except Exception as e:
+            message = ('Initializing SSH connection to %(ip)s failed. '
+                       'Error: %(error)s' % {'ip': ip, 'error': e})
+            caller = misc_utils.find_test_caller()
+            if caller:
+                message = '(%s) %s' % (caller, message)
+            LOG.exception(message)
             # If we don't explicitly set for which servers we want to
             # log the console output then all the servers will be logged.
             # See the definition of _log_console_output()
@@ -317,7 +323,8 @@
 
         return linux_client
 
-    def _image_create(self, name, fmt, path, properties=None):
+    def _image_create(self, name, fmt, path,
+                      disk_format=None, properties=None):
         if properties is None:
             properties = {}
         name = data_utils.rand_name('%s-' % name)
@@ -326,10 +333,10 @@
         params = {
             'name': name,
             'container_format': fmt,
-            'disk_format': fmt,
+            'disk_format': disk_format or fmt,
             'is_public': 'False',
         }
-        params.update(properties)
+        params['properties'] = properties
         image = self.image_client.create_image(**params)
         self.addCleanup(self.image_client.delete_image, image['id'])
         self.assertEqual("queued", image['status'])
@@ -343,23 +350,22 @@
         ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
         img_container_format = CONF.scenario.img_container_format
         img_disk_format = CONF.scenario.img_disk_format
+        img_properties = CONF.scenario.img_properties
         LOG.debug("paths: img: %s, container_fomat: %s, disk_format: %s, "
-                  "ami: %s, ari: %s, aki: %s" %
+                  "properties: %s, ami: %s, ari: %s, aki: %s" %
                   (img_path, img_container_format, img_disk_format,
-                   ami_img_path, ari_img_path, aki_img_path))
+                   img_properties, ami_img_path, ari_img_path, aki_img_path))
         try:
             self.image = self._image_create('scenario-img',
                                             img_container_format,
                                             img_path,
-                                            properties={'disk_format':
-                                                        img_disk_format})
+                                            disk_format=img_disk_format,
+                                            properties=img_properties)
         except IOError:
             LOG.debug("A qcow2 image was not found. Try to get a uec image.")
             kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
             ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
-            properties = {
-                'properties': {'kernel_id': kernel, 'ramdisk_id': ramdisk}
-            }
+            properties = {'kernel_id': kernel, 'ramdisk_id': ramdisk}
             self.image = self._image_create('scenario-ami', 'ami',
                                             path=ami_img_path,
                                             properties=properties)
@@ -407,9 +413,9 @@
         return snapshot_image
 
     def nova_volume_attach(self):
-        # TODO(andreaf) Device should be here CONF.compute.volume_device_name
         volume = self.servers_client.attach_volume(
-            self.server['id'], self.volume['id'], '/dev/vdb')
+            self.server['id'], self.volume['id'], '/dev/%s'
+            % CONF.compute.volume_device_name)
         self.assertEqual(self.volume['id'], volume['id'])
         self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
         # Refresh the volume after the attachment
@@ -985,7 +991,7 @@
         router_id = CONF.network.public_router_id
         network_id = CONF.network.public_network_id
         if router_id:
-            resp, body = client.show_router(router_id)
+            body = client.show_router(router_id)
             return net_resources.AttributeDict(**body['router'])
         elif network_id:
             router = self._create_router(client, tenant_id)
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index b795775..d9918f3 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -48,7 +48,7 @@
             self.image_ref = CONF.compute.image_ref
         if not hasattr(self, 'flavor_ref'):
             self.flavor_ref = CONF.compute.flavor_ref
-        self.image_utils = test_utils.ImageUtils()
+        self.image_utils = test_utils.ImageUtils(self.manager)
         if not self.image_utils.is_flavor_enough(self.flavor_ref,
                                                  self.image_ref):
             raise self.skipException(
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 53b471a..51c4c59 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -103,9 +103,9 @@
         return self.create_volume(snapshot_id=snapshot_id)
 
     def _attach_volume(self, server, volume):
-        # TODO(andreaf) we should use device from config instead if vdb
         attached_volume = self.servers_client.attach_volume(
-            server['id'], volume['id'], device='/dev/vdb')
+            server['id'], volume['id'], device='/dev/%s'
+            % CONF.compute.volume_device_name)
         self.assertEqual(volume['id'], attached_volume['id'])
         self._wait_for_volume_status(attached_volume, 'in-use')
 
@@ -119,7 +119,7 @@
         def _func():
             part = ssh.get_partitions()
             LOG.debug("Partitions:%s" % part)
-            return 'vdb' in part
+            return CONF.compute.volume_device_name in part
 
         if not tempest.test.call_until_true(_func,
                                             CONF.compute.build_timeout,
@@ -128,15 +128,18 @@
 
     def _create_timestamp(self, server_or_ip):
         ssh_client = self._ssh_to_server(server_or_ip)
-        ssh_client.exec_command('sudo /usr/sbin/mkfs.ext4 /dev/vdb')
-        ssh_client.exec_command('sudo mount /dev/vdb /mnt')
+        ssh_client.exec_command('sudo /usr/sbin/mkfs.ext4 /dev/%s'
+                                % CONF.compute.volume_device_name)
+        ssh_client.exec_command('sudo mount /dev/%s /mnt'
+                                % CONF.compute.volume_device_name)
         ssh_client.exec_command('sudo sh -c "date > /mnt/timestamp;sync"')
         self.timestamp = ssh_client.exec_command('sudo cat /mnt/timestamp')
         ssh_client.exec_command('sudo umount /mnt')
 
     def _check_timestamp(self, server_or_ip):
         ssh_client = self._ssh_to_server(server_or_ip)
-        ssh_client.exec_command('sudo mount /dev/vdb /mnt')
+        ssh_client.exec_command('sudo mount /dev/%s /mnt'
+                                % CONF.compute.volume_device_name)
         got_timestamp = ssh_client.exec_command('sudo cat /mnt/timestamp')
         self.assertEqual(self.timestamp, got_timestamp)
 
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 177697b..1731c48 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -41,8 +41,6 @@
         super(TestVolumeBootPattern, cls).skip_checks()
         if not CONF.volume_feature_enabled.snapshot:
             raise cls.skipException("Cinder volume snapshots are disabled")
-        if CONF.volume.storage_protocol == 'ceph':
-            raise cls.skipException('Skip until bug 1439371 is fixed.')
 
     def _create_volume_from_image(self):
         img_uuid = CONF.compute.image_ref
diff --git a/tempest/scenario/utils.py b/tempest/scenario/utils.py
index e5613d6..f8d9dd4 100644
--- a/tempest/scenario/utils.py
+++ b/tempest/scenario/utils.py
@@ -30,33 +30,21 @@
 CONF = config.CONF
 
 
-@misc.singleton
 class ImageUtils(object):
 
     default_ssh_user = 'root'
 
-    def __init__(self):
+    def __init__(self, os):
         # Load configuration items
         self.ssh_users = json.loads(CONF.input_scenario.ssh_user_regex)
         self.non_ssh_image_pattern = \
             CONF.input_scenario.non_ssh_image_regex
         # Setup clients
-        network_resources = {
-            'network': False,
-            'router': False,
-            'subnet': False,
-            'dhcp': False,
-        }
-        self.isolated_creds = credentials.get_isolated_credentials(
-            name='ScenarioImageUtils',
-            identity_version=CONF.identity.auth_version,
-            network_resources=network_resources)
-        os = clients.Manager(self.isolated_creds.get_primary_creds())
         self.images_client = os.images_client
         self.flavors_client = os.flavors_client
 
     def ssh_user(self, image_id):
-        _image = self.images_client.get_image(image_id)
+        _image = self.images_client.show_image(image_id)
         for regex, user in self.ssh_users:
             # First match wins
             if re.match(regex, _image['name']) is not None:
@@ -69,14 +57,14 @@
                              string=str(image['name']))
 
     def is_sshable_image(self, image_id):
-        _image = self.images_client.get_image(image_id)
+        _image = self.images_client.show_image(image_id)
         return self._is_sshable_image(_image)
 
     def _is_flavor_enough(self, flavor, image):
         return image['minDisk'] <= flavor['disk']
 
     def is_flavor_enough(self, flavor_id, image_id):
-        _image = self.images_client.get_image(image_id)
+        _image = self.images_client.show_image(image_id)
         _flavor = self.flavors_client.get_flavor_details(flavor_id)
         return self._is_flavor_enough(_flavor, _image)
 
@@ -131,6 +119,9 @@
         nname = ''.join(c for c in nname if c in self.validchars)
         return nname
 
+    def clear_creds(self):
+        self.isolated_creds.clear_isolated_creds()
+
     @property
     def scenario_images(self):
         """
@@ -177,12 +168,19 @@
         loader, standard_tests, pattern = args
     else:
         standard_tests, module, loader = args
+    output = None
+    scenario_utils = None
     try:
         scenario_utils = InputScenarioUtils()
         scenario_flavor = scenario_utils.scenario_flavors
         scenario_image = scenario_utils.scenario_images
     except (exceptions.InvalidConfiguration, TypeError):
-        return standard_tests
+        output = standard_tests
+    finally:
+        if scenario_utils:
+            scenario_utils.clear_creds()
+    if output is not None:
+        return output
     for test in testtools.iterate_tests(standard_tests):
         setattr(test, 'scenarios', testscenarios.multiply_scenarios(
             scenario_image,
diff --git a/tempest/services/compute/json/baremetal_nodes_client.py b/tempest/services/compute/json/baremetal_nodes_client.py
index fa2d7f4..d8f13c4 100644
--- a/tempest/services/compute/json/baremetal_nodes_client.py
+++ b/tempest/services/compute/json/baremetal_nodes_client.py
@@ -16,7 +16,8 @@
 
 from six.moves.urllib import parse as urllib
 
-from tempest.api_schema.response.compute import baremetal_nodes as schema
+from tempest.api_schema.response.compute.v2_1 import baremetal_nodes \
+    as schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/flavors_client.py b/tempest/services/compute/json/flavors_client.py
index 80cbe4d..7938d8e 100644
--- a/tempest/services/compute/json/flavors_client.py
+++ b/tempest/services/compute/json/flavors_client.py
@@ -17,10 +17,11 @@
 
 from six.moves.urllib import parse as urllib
 
-from tempest.api_schema.response.compute import flavors_access as schema_access
-from tempest.api_schema.response.compute import flavors_extra_specs \
-    as schema_extra_specs
 from tempest.api_schema.response.compute.v2_1 import flavors as schema
+from tempest.api_schema.response.compute.v2_1 import flavors_access \
+    as schema_access
+from tempest.api_schema.response.compute.v2_1 import flavors_extra_specs \
+    as schema_extra_specs
 from tempest.common import service_client
 
 
@@ -103,7 +104,7 @@
         resp, body = self.post('flavors/%s/os-extra_specs' % flavor_id,
                                post_body)
         body = json.loads(body)
-        self.validate_response(schema_extra_specs.flavor_extra_specs,
+        self.validate_response(schema_extra_specs.set_get_flavor_extra_specs,
                                resp, body)
         return service_client.ResponseBody(resp, body['extra_specs'])
 
@@ -111,7 +112,7 @@
         """Gets extra Specs details of the mentioned flavor."""
         resp, body = self.get('flavors/%s/os-extra_specs' % flavor_id)
         body = json.loads(body)
-        self.validate_response(schema_extra_specs.flavor_extra_specs,
+        self.validate_response(schema_extra_specs.set_get_flavor_extra_specs,
                                resp, body)
         return service_client.ResponseBody(resp, body['extra_specs'])
 
@@ -120,8 +121,9 @@
         resp, body = self.get('flavors/%s/os-extra_specs/%s' % (str(flavor_id),
                               key))
         body = json.loads(body)
-        self.validate_response(schema_extra_specs.flavor_extra_specs_key,
-                               resp, body)
+        self.validate_response(
+            schema_extra_specs.set_get_flavor_extra_specs_key,
+            resp, body)
         return service_client.ResponseBody(resp, body)
 
     def update_flavor_extra_spec(self, flavor_id, key, **kwargs):
@@ -129,8 +131,9 @@
         resp, body = self.put('flavors/%s/os-extra_specs/%s' %
                               (flavor_id, key), json.dumps(kwargs))
         body = json.loads(body)
-        self.validate_response(schema_extra_specs.flavor_extra_specs_key,
-                               resp, body)
+        self.validate_response(
+            schema_extra_specs.set_get_flavor_extra_specs_key,
+            resp, body)
         return service_client.ResponseBody(resp, body)
 
     def unset_flavor_extra_spec(self, flavor_id, key):
diff --git a/tempest/services/compute/json/floating_ips_client.py b/tempest/services/compute/json/floating_ips_client.py
index 9568a5e..f30bfdb 100644
--- a/tempest/services/compute/json/floating_ips_client.py
+++ b/tempest/services/compute/json/floating_ips_client.py
@@ -40,7 +40,7 @@
         url = "os-floating-ips/%s" % str(floating_ip_id)
         resp, body = self.get(url)
         body = json.loads(body)
-        self.validate_response(schema.floating_ip, resp, body)
+        self.validate_response(schema.create_get_floating_ip, resp, body)
         return service_client.ResponseBody(resp, body['floating_ip'])
 
     def create_floating_ip(self, pool_name=None):
@@ -50,7 +50,7 @@
         post_body = json.dumps(post_body)
         resp, body = self.post(url, post_body)
         body = json.loads(body)
-        self.validate_response(schema.floating_ip, resp, body)
+        self.validate_response(schema.create_get_floating_ip, resp, body)
         return service_client.ResponseBody(resp, body['floating_ip'])
 
     def delete_floating_ip(self, floating_ip_id):
@@ -108,7 +108,7 @@
 
         resp, body = self.get(url)
         body = json.loads(body)
-        self.validate_response(schema.floating_ip_pools, resp, body)
+        self.validate_response(schema.list_floating_ip_pools, resp, body)
         return service_client.ResponseBodyList(resp, body['floating_ip_pools'])
 
     def create_floating_ips_bulk(self, ip_range, pool, interface):
diff --git a/tempest/services/compute/json/images_client.py b/tempest/services/compute/json/images_client.py
index 5462efc..30aa962 100644
--- a/tempest/services/compute/json/images_client.py
+++ b/tempest/services/compute/json/images_client.py
@@ -65,7 +65,7 @@
         self.validate_response(schema.list_images_details, resp, body)
         return service_client.ResponseBodyList(resp, body['images'])
 
-    def get_image(self, image_id):
+    def show_image(self, image_id):
         """Returns the details of a single image."""
         resp, body = self.get("images/%s" % str(image_id))
         self.expected_success(200, resp.status)
@@ -131,7 +131,7 @@
 
     def is_resource_deleted(self, id):
         try:
-            self.get_image(id)
+            self.show_image(id)
         except lib_exc.NotFound:
             return True
         return False
diff --git a/tempest/services/compute/json/migrations_client.py b/tempest/services/compute/json/migrations_client.py
index 009992c..f708a07 100644
--- a/tempest/services/compute/json/migrations_client.py
+++ b/tempest/services/compute/json/migrations_client.py
@@ -16,7 +16,7 @@
 
 from six.moves.urllib import parse as urllib
 
-from tempest.api_schema.response.compute import migrations as schema
+from tempest.api_schema.response.compute.v2_1 import migrations as schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/services_client.py b/tempest/services/compute/json/services_client.py
index e2d959b..156ad8d 100644
--- a/tempest/services/compute/json/services_client.py
+++ b/tempest/services/compute/json/services_client.py
@@ -18,7 +18,7 @@
 
 from six.moves.urllib import parse as urllib
 
-from tempest.api_schema.response.compute import services as schema
+from tempest.api_schema.response.compute.v2_1 import services as schema
 from tempest.common import service_client
 
 
diff --git a/tempest/services/compute/json/tenant_usages_client.py b/tempest/services/compute/json/tenant_usages_client.py
index b7e2b2a..52f46e2 100644
--- a/tempest/services/compute/json/tenant_usages_client.py
+++ b/tempest/services/compute/json/tenant_usages_client.py
@@ -30,7 +30,7 @@
 
         resp, body = self.get(url)
         body = json.loads(body)
-        self.validate_response(schema.list_tenant, resp, body)
+        self.validate_response(schema.list_tenant_usage, resp, body)
         return service_client.ResponseBodyList(resp, body['tenant_usages'][0])
 
     def get_tenant_usage(self, tenant_id, params=None):
@@ -40,5 +40,5 @@
 
         resp, body = self.get(url)
         body = json.loads(body)
-        self.validate_response(schema.get_tenant, resp, body)
+        self.validate_response(schema.get_tenant_usage, resp, body)
         return service_client.ResponseBodyList(resp, body['tenant_usage'])
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index 5e442fa..7c58f01 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -201,7 +201,7 @@
         self.expected_success(200, resp.status)
         return service_client.ResponseBody(resp, body)
 
-    def image_list(self, **kwargs):
+    def list_images(self, **kwargs):
         url = 'v1/images'
 
         if len(kwargs) > 0:
@@ -240,7 +240,7 @@
         body = self._image_meta_from_headers(resp)
         return service_client.ResponseBody(resp, body)
 
-    def get_image(self, image_id):
+    def show_image(self, image_id):
         url = 'v1/images/%s' % image_id
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
@@ -258,7 +258,7 @@
         """Returns the primary type of resource this client works with."""
         return 'image_meta'
 
-    def get_image_membership(self, image_id):
+    def list_image_members(self, image_id):
         url = 'v1/images/%s/members' % image_id
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index aff8e85..a4cb48c 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -102,7 +102,7 @@
         self.expected_success(204, resp.status)
         return service_client.ResponseBody(resp)
 
-    def image_list(self, params=None):
+    def list_images(self, params=None):
         url = 'v2/images'
 
         if params:
@@ -114,7 +114,7 @@
         self._validate_schema(body, type='images')
         return service_client.ResponseBodyList(resp, body['images'])
 
-    def get_image(self, image_id):
+    def show_image(self, image_id):
         url = 'v2/images/%s' % image_id
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
@@ -123,7 +123,7 @@
 
     def is_resource_deleted(self, id):
         try:
-            self.get_image(id)
+            self.show_image(id)
         except lib_exc.NotFound:
             return True
         return False
@@ -159,7 +159,7 @@
         self.expected_success(204, resp.status)
         return service_client.ResponseBody(resp)
 
-    def get_image_membership(self, image_id):
+    def list_image_members(self, image_id):
         url = 'v2/images/%s/members' % image_id
         resp, body = self.get(url)
         self.expected_success(200, resp.status)
diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
index 9a08bbd..65aa0f4 100644
--- a/tempest/services/volume/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -123,6 +123,15 @@
         self.expected_success(202, resp.status)
         return service_client.ResponseBody(resp, body)
 
+    def set_bootable_volume(self, volume_id, bootable):
+        """set a bootable flag for a volume - true or false."""
+        post_body = {"bootable": bootable}
+        post_body = json.dumps({'os-set_bootable': post_body})
+        url = 'volumes/%s/action' % (volume_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, body)
+
     def detach_volume(self, volume_id):
         """Detaches a volume from an instance."""
         post_body = {}
diff --git a/tempest/test.py b/tempest/test.py
index d80e478..0d709f6c 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -230,7 +230,9 @@
     _service = None
 
     # NOTE(andreaf) credentials holds a list of the credentials to be allocated
-    # at class setup time. Credential types can be 'primary', 'alt' or 'admin'
+    # at class setup time. Credential types can be 'primary', 'alt', 'admin' or
+    # a list of roles - the first element of the list being a label, and the
+    # rest the actual roles
     credentials = []
     # Resources required to validate a server using ssh
     validation_resources = {}
@@ -322,9 +324,16 @@
         if 'admin' in cls.credentials and not credentials.is_admin_available():
             msg = "Missing Identity Admin API credentials in configuration."
             raise cls.skipException(msg)
-        if 'alt' is cls.credentials and not credentials.is_alt_available():
+        if 'alt' in cls.credentials and not credentials.is_alt_available():
             msg = "Missing a 2nd set of API credentials in configuration."
             raise cls.skipException(msg)
+        if hasattr(cls, 'identity_version'):
+            if cls.identity_version == 'v2':
+                if not CONF.identity_feature_enabled.api_v2:
+                    raise cls.skipException("Identity api v2 is not enabled")
+            elif cls.identity_version == 'v3':
+                if not CONF.identity_feature_enabled.api_v3:
+                    raise cls.skipException("Identity api v3 is not enabled")
 
     @classmethod
     def setup_credentials(cls):
@@ -337,19 +346,24 @@
             # This may raise an exception in case credentials are not available
             # In that case we want to let the exception through and the test
             # fail accordingly
-            manager = cls.get_client_manager(
-                credential_type=credentials_type)
-            setattr(cls, 'os_%s' % credentials_type, manager)
-            # Setup some common aliases
-            # TODO(andreaf) The aliases below are a temporary hack
-            # to avoid changing too much code in one patch. They should
-            # be removed eventually
-            if credentials_type == 'primary':
-                cls.os = cls.manager = cls.os_primary
-            if credentials_type == 'admin':
-                cls.os_adm = cls.admin_manager = cls.os_admin
-            if credentials_type == 'alt':
-                cls.alt_manager = cls.os_alt
+            if isinstance(credentials_type, six.string_types):
+                manager = cls.get_client_manager(
+                    credential_type=credentials_type)
+                setattr(cls, 'os_%s' % credentials_type, manager)
+                # Setup some common aliases
+                # TODO(andreaf) The aliases below are a temporary hack
+                # to avoid changing too much code in one patch. They should
+                # be removed eventually
+                if credentials_type == 'primary':
+                    cls.os = cls.manager = cls.os_primary
+                if credentials_type == 'admin':
+                    cls.os_adm = cls.admin_manager = cls.os_admin
+                if credentials_type == 'alt':
+                    cls.alt_manager = cls.os_alt
+            elif isinstance(credentials_type, list):
+                manager = cls.get_client_manager(roles=credentials_type[1:],
+                                                 force_new=True)
+                setattr(cls, 'os_roles_%s' % credentials_type[0], manager)
 
     @classmethod
     def setup_clients(cls):
@@ -439,14 +453,13 @@
         return cls._creds_provider
 
     @classmethod
-    def get_client_manager(cls, identity_version=None,
-                           credential_type=None, roles=None, force_new=None):
+    def get_client_manager(cls, credential_type=None, roles=None,
+                           force_new=None):
         """Returns an OpenStack client manager
 
         Returns an OpenStack client manager based on either credential_type
         or a list of roles. If neither is specified, it defaults to
         credential_type 'primary'
-        :param identity_version: string - v2 or v3
         :param credential_type: string - primary, alt or admin
         :param roles: list of roles
 
@@ -458,7 +471,6 @@
             raise ValueError(msg)
         if not any([roles, credential_type]):
             credential_type = 'primary'
-        cls.identity_version = identity_version
         cred_provider = cls._get_credentials_provider()
         if roles:
             for role in roles:
@@ -486,7 +498,7 @@
         """
         Clears isolated creds if set
         """
-        if hasattr(cls, '_cred_provider'):
+        if hasattr(cls, '_creds_provider'):
             cls._creds_provider.clear_isolated_creds()
 
     @classmethod
diff --git a/tempest/tests/cmd/test_javelin.py b/tempest/tests/cmd/test_javelin.py
new file mode 100644
index 0000000..860599b
--- /dev/null
+++ b/tempest/tests/cmd/test_javelin.py
@@ -0,0 +1,233 @@
+#!/usr/bin/env python
+#
+#    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
+from oslotest import mockpatch
+from tempest_lib import exceptions as lib_exc
+
+from tempest.cmd import javelin
+from tempest.tests import base
+
+
+class JavelinUnitTest(base.TestCase):
+
+    def setUp(self):
+        super(JavelinUnitTest, self).setUp()
+        javelin.setup_logging()
+        self.fake_client = mock.MagicMock()
+        self.fake_object = mock.MagicMock()
+
+
+class TestCreateResources(JavelinUnitTest):
+    def test_create_tenants(self):
+
+        self.fake_client.identity.list_tenants.return_value = []
+        self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
+                                              return_value=self.fake_client))
+
+        javelin.create_tenants([self.fake_object['name']])
+
+        mocked_function = self.fake_client.identity.create_tenant
+        mocked_function.assert_called_once_with(self.fake_object['name'])
+
+    def test_create_duplicate_tenant(self):
+        self.fake_client.identity.list_tenants.return_value = [
+            {'name': self.fake_object['name']}]
+        self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
+                                              return_value=self.fake_client))
+
+        javelin.create_tenants([self.fake_object['name']])
+
+        mocked_function = self.fake_client.identity.create_tenant
+        self.assertFalse(mocked_function.called)
+
+    def test_create_users(self):
+        self.fake_client.identity.get_tenant_by_name.return_value = \
+            self.fake_object['tenant']
+        self.fake_client.identity.get_user_by_username.side_effect = \
+            lib_exc.NotFound()
+        self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
+                                              return_value=self.fake_client))
+
+        javelin.create_users([self.fake_object])
+
+        fake_tenant_id = self.fake_object['tenant']['id']
+        fake_email = "%s@%s" % (self.fake_object['user'], fake_tenant_id)
+        mocked_function = self.fake_client.identity.create_user
+        mocked_function.assert_called_once_with(self.fake_object['name'],
+                                                self.fake_object['password'],
+                                                fake_tenant_id,
+                                                fake_email,
+                                                enabled=True)
+
+    def test_create_user_missing_tenant(self):
+        self.fake_client.identity.get_tenant_by_name.side_effect = \
+            lib_exc.NotFound()
+        self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
+                                              return_value=self.fake_client))
+
+        javelin.create_users([self.fake_object])
+
+        mocked_function = self.fake_client.identity.create_user
+        self.assertFalse(mocked_function.called)
+
+    def test_create_objects(self):
+
+        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+                                              return_value=self.fake_client))
+        self.useFixture(mockpatch.PatchObject(javelin, "_assign_swift_role"))
+        self.useFixture(mockpatch.PatchObject(javelin, "_file_contents",
+                        return_value=self.fake_object.content))
+
+        javelin.create_objects([self.fake_object])
+
+        mocked_function = self.fake_client.containers.create_container
+        mocked_function.assert_called_once_with(self.fake_object['container'])
+        mocked_function = self.fake_client.objects.create_object
+        mocked_function.assert_called_once_with(self.fake_object['container'],
+                                                self.fake_object['name'],
+                                                self.fake_object.content)
+
+    def test_create_images(self):
+        self.fake_client.images.create_image.return_value = \
+            self.fake_object['body']
+
+        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+                                              return_value=self.fake_client))
+        self.useFixture(mockpatch.PatchObject(javelin, "_get_image_by_name",
+                                              return_value=[]))
+        self.useFixture(mockpatch.PatchObject(javelin, "_resolve_image",
+                                              return_value=(None, None)))
+
+        with mock.patch('six.moves.builtins.open', mock.mock_open(),
+                        create=True) as open_mock:
+            javelin.create_images([self.fake_object])
+
+        mocked_function = self.fake_client.images.create_image
+        mocked_function.assert_called_once_with(self.fake_object['name'],
+                                                self.fake_object['format'],
+                                                self.fake_object['format'])
+
+        mocked_function = self.fake_client.images.store_image
+        fake_image_id = self.fake_object['body'].get('id')
+        mocked_function.assert_called_once_with(fake_image_id, open_mock())
+
+    def test_create_networks(self):
+        self.fake_client.networks.list_networks.return_value = {
+            'networks': []}
+
+        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+                                              return_value=self.fake_client))
+
+        javelin.create_networks([self.fake_object])
+
+        mocked_function = self.fake_client.networks.create_network
+        mocked_function.assert_called_once_with(name=self.fake_object['name'])
+
+    def test_create_subnet(self):
+
+        fake_network = self.fake_object['network']
+
+        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+                                              return_value=self.fake_client))
+        self.useFixture(mockpatch.PatchObject(javelin, "_get_resource_by_name",
+                                              return_value=fake_network))
+
+        fake_netaddr = mock.MagicMock()
+        self.useFixture(mockpatch.PatchObject(javelin, "netaddr",
+                                              return_value=fake_netaddr))
+        fake_version = javelin.netaddr.IPNetwork().version
+
+        javelin.create_subnets([self.fake_object])
+
+        mocked_function = self.fake_client.networks.create_subnet
+        mocked_function.assert_called_once_with(network_id=fake_network['id'],
+                                                cidr=self.fake_object['range'],
+                                                name=self.fake_object['name'],
+                                                ip_version=fake_version)
+
+
+class TestDestroyResources(JavelinUnitTest):
+
+    def test_destroy_tenants(self):
+
+        fake_tenant = self.fake_object['tenant']
+
+        fake_auth = mock.MagicMock()
+        fake_auth.identity.get_tenant_by_name.return_value = fake_tenant
+
+        self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
+                                              return_value=fake_auth))
+        javelin.destroy_tenants([fake_tenant])
+
+        mocked_function = fake_auth.identity.delete_tenant
+        mocked_function.assert_called_once_with(fake_tenant['id'])
+
+    def test_destroy_users(self):
+
+        fake_user = self.fake_object['user']
+        fake_tenant = self.fake_object['tenant']
+
+        fake_auth = mock.MagicMock()
+        fake_auth.identity.get_tenant_by_name.return_value = fake_tenant
+        fake_auth.identity.get_user_by_username.return_value = fake_user
+
+        self.useFixture(mockpatch.PatchObject(javelin, "keystone_admin",
+                                              return_value=fake_auth))
+
+        javelin.destroy_users([fake_user])
+
+        mocked_function = fake_auth.identity.delete_user
+        mocked_function.assert_called_once_with(fake_user['id'])
+
+    def test_destroy_objects(self):
+
+        fake_client = mock.MagicMock()
+        fake_client.objects.delete_object.return_value = {'status': "200"}, ""
+        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+                                              return_value=fake_client))
+        javelin.destroy_objects([self.fake_object])
+
+        mocked_function = fake_client.objects.delete_object
+        mocked_function.asswert_called_once(self.fake_object['container'],
+                                            self.fake_object['name'])
+
+    def test_destroy_images(self):
+
+        fake_client = mock.MagicMock()
+        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+                                              return_value=fake_client))
+        self.useFixture(mockpatch.PatchObject(javelin, "_get_image_by_name",
+                        return_value=self.fake_object['image']))
+
+        javelin.destroy_images([self.fake_object])
+
+        mocked_function = fake_client.images.delete_image
+        mocked_function.assert_called_once_with(
+            self.fake_object['image']['id'])
+
+    def test_destroy_networks(self):
+
+        fake_client = mock.MagicMock()
+        self.useFixture(mockpatch.PatchObject(javelin, "client_for_user",
+                                              return_value=fake_client))
+        self.useFixture(mockpatch.PatchObject(
+            javelin, "_get_resource_by_name",
+            return_value=self.fake_object['resource']))
+
+        javelin.destroy_networks([self.fake_object])
+
+        mocked_function = fake_client.networks.delete_network
+        mocked_function.assert_called_once_with(
+            self.fake_object['resource']['id'])
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index d6377e6..3506856 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -73,6 +73,7 @@
     # the information using gnu/linux tools.
 
     def _assert_exec_called_with(self, cmd):
+        cmd = "set -eu -o pipefail; PATH=$PATH:/sbin; " + cmd
         self.ssh_mock.mock.exec_command.assert_called_with(cmd)
 
     def test_get_number_of_vcpus(self):
@@ -119,7 +120,7 @@
 
         self.assertEqual(self.conn.get_mac_address(), macs)
         self._assert_exec_called_with(
-            "/bin/ip addr | awk '/ether/ {print $2}'")
+            "ip addr | awk '/ether/ {print $2}'")
 
     def test_get_ip_list(self):
         ips = """1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue
@@ -136,7 +137,7 @@
        valid_lft forever preferred_lft forever"""
         self.ssh_mock.mock.exec_command.return_value = ips
         self.assertEqual(self.conn.get_ip_list(), ips)
-        self._assert_exec_called_with('/bin/ip address')
+        self._assert_exec_called_with('ip address')
 
     def test_assign_static_ip(self):
         self.ssh_mock.mock.exec_command.return_value = ''
@@ -144,9 +145,10 @@
         nic = 'eth0'
         self.assertEqual(self.conn.assign_static_ip(nic, ip), '')
         self._assert_exec_called_with(
-            "sudo /bin/ip addr add %s/%s dev %s" % (ip, '28', nic))
+            "sudo ip addr add %s/%s dev %s" % (ip, '28', nic))
 
     def test_turn_nic_on(self):
         nic = 'eth0'
         self.conn.turn_nic_on(nic)
-        self._assert_exec_called_with('sudo /bin/ip link set %s up' % nic)
+        self._assert_exec_called_with(
+            'sudo ip link set %s up' % nic)
diff --git a/tempest/tests/test_tenant_isolation.py b/tempest/tests/test_tenant_isolation.py
index 47e6abd..72a63c3 100644
--- a/tempest/tests/test_tenant_isolation.py
+++ b/tempest/tests/test_tenant_isolation.py
@@ -268,6 +268,36 @@
         self.assertEqual(alt_creds.user_id, '1234')
 
     @mock.patch('tempest_lib.common.rest_client.RestClient')
+    def test_no_network_creation_with_config_set(self, MockRestClient):
+        cfg.CONF.set_default('create_isolated_networks', False, group='auth')
+        iso_creds = isolated_creds.IsolatedCreds(name='test class',
+                                                 password='fake_password')
+        self._mock_assign_user_role()
+        self._mock_list_role()
+        self._mock_user_create('1234', 'fake_prim_user')
+        self._mock_tenant_create('1234', 'fake_prim_tenant')
+        net = mock.patch.object(iso_creds.network_admin_client,
+                                'delete_network')
+        net_mock = net.start()
+        subnet = mock.patch.object(iso_creds.network_admin_client,
+                                   'delete_subnet')
+        subnet_mock = subnet.start()
+        router = mock.patch.object(iso_creds.network_admin_client,
+                                   'delete_router')
+        router_mock = router.start()
+
+        primary_creds = iso_creds.get_primary_creds()
+        self.assertEqual(net_mock.mock_calls, [])
+        self.assertEqual(subnet_mock.mock_calls, [])
+        self.assertEqual(router_mock.mock_calls, [])
+        network = primary_creds.network
+        subnet = primary_creds.subnet
+        router = primary_creds.router
+        self.assertIsNone(network)
+        self.assertIsNone(subnet)
+        self.assertIsNone(router)
+
+    @mock.patch('tempest_lib.common.rest_client.RestClient')
     def test_network_creation(self, MockRestClient):
         iso_creds = isolated_creds.IsolatedCreds(name='test class',
                                                  password='fake_password')
diff --git a/tempest/tests/test_waiters.py b/tempest/tests/test_waiters.py
index cdf5362..329d610 100644
--- a/tempest/tests/test_waiters.py
+++ b/tempest/tests/test_waiters.py
@@ -29,7 +29,7 @@
         self.client.build_interval = 1
 
     def test_wait_for_image_status(self):
-        self.client.get_image.return_value = ({'status': 'active'})
+        self.client.show_image.return_value = ({'status': 'active'})
         start_time = int(time.time())
         waiters.wait_for_image_status(self.client, 'fake_image_id', 'active')
         end_time = int(time.time())
@@ -37,13 +37,13 @@
         self.assertTrue((end_time - start_time) < 10)
 
     def test_wait_for_image_status_timeout(self):
-        self.client.get_image.return_value = ({'status': 'saving'})
+        self.client.show_image.return_value = ({'status': 'saving'})
         self.assertRaises(exceptions.TimeoutException,
                           waiters.wait_for_image_status,
                           self.client, 'fake_image_id', 'active')
 
     def test_wait_for_image_status_error_on_image_create(self):
-        self.client.get_image.return_value = ({'status': 'ERROR'})
+        self.client.show_image.return_value = ({'status': 'ERROR'})
         self.assertRaises(exceptions.AddImageException,
                           waiters.wait_for_image_status,
                           self.client, 'fake_image_id', 'active')
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index 19a77dc..b7f0e81 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -283,7 +283,17 @@
                                          CONF.compute.ssh_user,
                                          pkey=self.keypair.material)
         text = data_utils.rand_name("Pattern text for console output")
-        resp = ssh.write_to_console(text)
+        try:
+            resp = ssh.write_to_console(text)
+        except Exception:
+            if not CONF.compute_feature_enabled.console_output:
+                LOG.debug('Console output not supported, cannot log')
+            else:
+                console_output = instance.get_console_output().output
+                LOG.debug('Console output for %s\nbody=\n%s',
+                          instance.id, console_output)
+            raise
+
         self.assertFalse(resp)
 
         def _output():
diff --git a/tox.ini b/tox.ini
index 88d1302..4bb5df6 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = pep8,py27
+envlist = pep8,py27,py34
 minversion = 1.6
 skipsdist = True
 
@@ -59,16 +59,6 @@
   find . -type f -name "*.pyc" -delete
   bash tools/pretty_tox_serial.sh '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario|thirdparty)) {posargs}'
 
-[testenv:heat-slow]
-sitepackages = {[tempestenv]sitepackages}
-setenv = {[tempestenv]setenv}
-         OS_TEST_TIMEOUT=1200
-deps = {[tempestenv]deps}
-# The regex below is used to select heat api/scenario tests tagged as slow.
-commands =
-  find . -type f -name "*.pyc" -delete
-  bash tools/pretty_tox.sh '(?=.*\[.*\bslow\b.*\])(^tempest\.(api|scenario)\.orchestration) {posargs}'
-
 [testenv:large-ops]
 sitepackages = {[tempestenv]sitepackages}
 setenv = {[tempestenv]setenv}