Merge "Remove the Glance HTTP client. Use the common Rest Client instead."
diff --git a/HACKING.rst b/HACKING.rst
index d3ac5c6..480650c 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -21,6 +21,7 @@
 - [T111] Check that service client names of DELETE should be consistent
 - [T112] Check that tempest.lib should not import local tempest code
 - [T113] Check that tests use data_utils.rand_uuid() instead of uuid.uuid4()
+- [T114] Check that tempest.lib does not use tempest config
 - [N322] Method's default argument shouldn't be mutable
 
 Test Data/Configuration
@@ -132,16 +133,18 @@
 
 Set-up is split in a series of steps (setup stages), which can be overwritten
 by test classes. Set-up stages are:
- - `skip_checks`
- - `setup_credentials`
- - `setup_clients`
- - `resource_setup`
+
+- `skip_checks`
+- `setup_credentials`
+- `setup_clients`
+- `resource_setup`
 
 Tear-down is also split in a series of steps (teardown stages), which are
 stacked for execution only if the corresponding setup stage had been
 reached during the setup phase. Tear-down stages are:
- - `clear_credentials` (defined in the base test class)
- - `resource_cleanup`
+
+- `clear_credentials` (defined in the base test class)
+- `resource_cleanup`
 
 Skipping Tests
 --------------
diff --git a/README.rst b/README.rst
index 650a1ed..725a890 100644
--- a/README.rst
+++ b/README.rst
@@ -25,8 +25,7 @@
   discover features of a cloud incorrectly, and give people an
   incorrect assessment of their cloud. Explicit is always better.
 - Tempest uses OpenStack public interfaces. Tests in Tempest should
-  only touch public interfaces, API calls (native or 3rd party),
-  or libraries.
+  only touch public OpenStack APIs.
 - Tempest should not touch private or implementation specific
   interfaces. This means not directly going to the database, not
   directly hitting the hypervisors, not testing extensions not
@@ -163,7 +162,7 @@
 Tempest also has a set of unit tests which test the Tempest code itself. These
 tests can be run by specifying the test discovery path::
 
-    $> OS_TEST_PATH=./tempest/tests testr run --parallel
+    $ OS_TEST_PATH=./tempest/tests testr run --parallel
 
 By setting OS_TEST_PATH to ./tempest/tests it specifies that test discover
 should only be run on the unit test directory. The default value of OS_TEST_PATH
@@ -214,8 +213,8 @@
 To start you need to create a configuration file. The easiest way to create a
 configuration file is to generate a sample in the ``etc/`` directory ::
 
-    $> cd $TEMPEST_ROOT_DIR
-    $> oslo-config-generator --config-file \
+    $ cd $TEMPEST_ROOT_DIR
+    $ oslo-config-generator --config-file \
         etc/config-generator.tempest.conf \
         --output-file etc/tempest.conf
 
@@ -237,21 +236,21 @@
 After setting up your configuration file, you can execute the set of Tempest
 tests by using ``testr`` ::
 
-    $> testr run --parallel
+    $ testr run --parallel
 
 To run one single test serially ::
 
-    $> testr run tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server
+    $ testr run tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server
 
 Alternatively, you can use the run_tempest.sh script which will create a venv
 and run the tests or use tox to do the same. Tox also contains several existing
 job configurations. For example::
 
-   $> tox -efull
+   $ tox -efull
 
 which will run the same set of tests as the OpenStack gate. (it's exactly how
 the gate invokes Tempest) Or::
 
-  $> tox -esmoke
+  $ tox -esmoke
 
 to run the tests tagged as smoke.
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index d610dc5..9a7ce15 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -348,11 +348,14 @@
     service catalog should be in a standard format (which is going to be
     standardized at the keystone level).
     Tempest expects URLs in the Service catalog in the following format:
-     * ``http://example.com:1234/<version-info>``
+
+    * ``http://example.com:1234/<version-info>``
+
     Examples:
-     * Good - ``http://example.com:1234/v2.0``
-     * Wouldn’t work -  ``http://example.com:1234/xyz/v2.0/``
-       (adding prefix/suffix around version etc)
+
+    * Good - ``http://example.com:1234/v2.0``
+    * Wouldn’t work -  ``http://example.com:1234/xyz/v2.0/``
+      (adding prefix/suffix around version etc)
 
 Service Feature Configuration
 -----------------------------
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index fc05b12..3568470 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -24,6 +24,7 @@
 
   Those should be defined under respective section of each service.
   For example::
+
       [compute]
       min_microversion = None
       max_microversion = latest
@@ -159,7 +160,8 @@
 
 
 Notes about Compute Microversion Tests
-"""""""""""""""""""""""""""""""""""
+""""""""""""""""""""""""""""""""""""""
+
 Some of the compute Microversion tests have been already implemented
 with the Microversion testing framework. So for further tests only
 step 4 is needed.
diff --git a/releasenotes/notes/remove-integrated-horizon-bb57551c1e5f5be3.yaml b/releasenotes/notes/remove-integrated-horizon-bb57551c1e5f5be3.yaml
new file mode 100644
index 0000000..294f6d9
--- /dev/null
+++ b/releasenotes/notes/remove-integrated-horizon-bb57551c1e5f5be3.yaml
@@ -0,0 +1,7 @@
+---
+upgrade:
+  - The integrated dashboard scenario test has been
+    removed and is now in a separate tempest plugin
+    tempest-horizon. The removed test coverage can be
+    used by installing tempest-horizon on the server
+    where you run tempest.
diff --git a/requirements.txt b/requirements.txt
index d567082..f41d2a7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,17 +9,17 @@
 netaddr!=0.7.16,>=0.7.12 # BSD
 testrepository>=0.0.18 # Apache-2.0/BSD
 pyOpenSSL>=0.14 # Apache-2.0
-oslo.concurrency>=3.5.0 # Apache-2.0
+oslo.concurrency>=3.8.0 # Apache-2.0
 oslo.config>=3.9.0 # Apache-2.0
 oslo.i18n>=2.1.0 # Apache-2.0
 oslo.log>=1.14.0 # Apache-2.0
 oslo.serialization>=1.10.0 # Apache-2.0
 oslo.utils>=3.5.0 # Apache-2.0
 six>=1.9.0 # MIT
-fixtures<2.0,>=1.3.1 # Apache-2.0/BSD
+fixtures>=3.0.0 # Apache-2.0/BSD
 testscenarios>=0.4 # Apache-2.0/BSD
 PyYAML>=3.1.0 # MIT
 stevedore>=1.10.0 # Apache-2.0
 PrettyTable<0.8,>=0.7 # BSD
-os-testr>=0.4.1 # Apache-2.0
+os-testr>=0.7.0 # Apache-2.0
 urllib3>=1.15.1 # MIT
diff --git a/setup.cfg b/setup.cfg
index 0ddb898..24e0214 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,7 +5,7 @@
     README.rst
 author = OpenStack
 author-email = openstack-dev@lists.openstack.org
-home-page = http://www.openstack.org/
+home-page = http://docs.openstack.org/developer/tempest/
 classifier =
     Intended Audience :: Information Technology
     Intended Audience :: System Administrators
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage.py b/tempest/api/compute/admin/test_simple_tenant_usage.py
index 8986db8..a4ed8dc 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage.py
@@ -59,7 +59,9 @@
                 return True
             except e.InvalidHTTPResponseBody:
                 return False
-        test.call_until_true(is_valid, duration, 1)
+        self.assertEqual(test.call_until_true(is_valid, duration, 1), True,
+                         "%s not return valid response in %s secs" % (
+                             func.__name__, duration))
         return self.resp
 
     @test.idempotent_id('062c8ae9-9912-4249-8b51-e38d664e926e')
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index c05045e..07423ff 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -204,10 +204,6 @@
     @test.idempotent_id('1678d144-ed74-43f8-8e57-ab10dbf9b3c2')
     @testtools.skipUnless(CONF.service_available.neutron,
                           'Neutron service must be available.')
-    # The below skipUnless should be removed once Kilo-eol happens.
-    @testtools.skipUnless(CONF.compute_feature_enabled.
-                          allow_duplicate_networks,
-                          'Duplicate networks must be allowed')
     def test_verify_duplicate_network_nics(self):
         # Verify that server creation does not fail when more than one nic
         # is created on the same network.
diff --git a/tempest/api/network/test_security_groups_negative.py b/tempest/api/network/test_security_groups_negative.py
index b9765c8..a3b0a82 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -153,6 +153,7 @@
 
         # Create rule for icmp protocol with invalid ports
         states = [(1, 256, 'Invalid value for ICMP code'),
+                  (-1, 25, 'Invalid value'),
                   (None, 6, 'ICMP type (port-range-min) is missing'),
                   (300, 1, 'Invalid value for ICMP type')]
         for pmin, pmax, msg in states:
diff --git a/tempest/api/volume/test_qos.py b/tempest/api/volume/admin/test_qos.py
similarity index 100%
rename from tempest/api/volume/test_qos.py
rename to tempest/api/volume/admin/test_qos.py
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index b2e52bb..cf05f5f 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -30,6 +30,11 @@
         super(BaseVolumeQuotasAdminV2TestJSON, cls).setup_credentials()
         cls.demo_tenant_id = cls.os.credentials.tenant_id
 
+    def _delete_volume(self, volume_id):
+        # Delete the specified volume using admin credentials
+        self.admin_volume_client.delete_volume(volume_id)
+        self.admin_volume_client.wait_for_resource_deletion(volume_id)
+
     @test.idempotent_id('59eada70-403c-4cef-a2a3-a8ce2f1b07a0')
     def test_list_quotas(self):
         quotas = (self.quotas_client.show_quota_set(self.demo_tenant_id)
@@ -83,8 +88,7 @@
             self.demo_tenant_id)['quota_set']
 
         volume = self.create_volume()
-        self.addCleanup(self.admin_volume_client.delete_volume,
-                        volume['id'])
+        self.addCleanup(self._delete_volume, volume['id'])
 
         new_quota_usage = self.quotas_client.show_quota_usage(
             self.demo_tenant_id)['quota_set']
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index b09cd2c..1289297 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -13,11 +13,15 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import base64
+import six
+
+from oslo_serialization import jsonutils as json
+
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
-from tempest.lib import decorators
 from tempest import test
 
 CONF = config.CONF
@@ -41,6 +45,20 @@
         self.backups_adm_client.delete_backup(backup_id)
         self.backups_adm_client.wait_for_backup_deletion(backup_id)
 
+    def _decode_url(self, backup_url):
+        return json.loads(base64.decodestring(backup_url))
+
+    def _encode_backup(self, backup):
+        retval = json.dumps(backup)
+        if six.PY3:
+            retval = retval.encode('utf-8')
+        return base64.encodestring(retval)
+
+    def _modify_backup_url(self, backup_url, changes):
+        backup = self._decode_url(backup_url)
+        backup.update(changes)
+        return self._encode_backup(backup)
+
     @test.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
     def test_volume_backup_create_get_detailed_list_restore_delete(self):
         # Create backup
@@ -78,9 +96,13 @@
         waiters.wait_for_volume_status(self.admin_volume_client,
                                        restore['volume_id'], 'available')
 
-    @decorators.skip_because(bug='1455043')
     @test.idempotent_id('a99c54a1-dd80-4724-8a13-13bf58d4068d')
     def test_volume_backup_export_import(self):
+        """Test backup export import functionality.
+
+        Cinder allows exporting DB backup information through its API so it can
+        be imported back in case of a DB loss.
+        """
         # Create backup
         backup_name = data_utils.rand_name('Backup')
         backup = (self.backups_adm_client.create_backup(
@@ -99,25 +121,40 @@
                         'cinder.backup.drivers'))
         self.assertIsNotNone(export_backup['backup_url'])
 
+        # NOTE(geguileo): Backups are imported with the same backup id
+        # (important for incremental backups among other things), so we cannot
+        # import the exported backup information as it is, because that Backup
+        # ID already exists.  So we'll fake the data by changing the backup id
+        # in the exported backup DB info we have retrieved before importing it
+        # back.
+        new_id = data_utils.rand_uuid()
+        new_url = self._modify_backup_url(
+            export_backup['backup_url'], {'id': new_id})
+
         # Import Backup
         import_backup = self.backups_adm_client.import_backup(
             backup_service=export_backup['backup_service'],
-            backup_url=export_backup['backup_url'])['backup']
-        self.addCleanup(self._delete_backup, import_backup['id'])
+            backup_url=new_url)['backup']
+
+        # NOTE(geguileo): We delete both backups, but only one of those
+        # deletions will delete data from the backup back-end because they
+        # were both pointing to the same backend data.
+        self.addCleanup(self._delete_backup, new_id)
         self.assertIn("id", import_backup)
+        self.assertEqual(new_id, import_backup['id'])
         self.backups_adm_client.wait_for_backup_status(import_backup['id'],
                                                        'available')
 
         # Verify Import Backup
         backups = self.backups_adm_client.list_backups(detail=True)['backups']
-        self.assertIn(import_backup['id'], [b['id'] for b in backups])
+        self.assertIn(new_id, [b['id'] for b in backups])
 
         # Restore backup
-        restore = (self.backups_adm_client.restore_backup(import_backup['id'])
-                   ['restore'])
+        restore = self.backups_adm_client.restore_backup(
+            backup['id'])['restore']
         self.addCleanup(self.admin_volume_client.delete_volume,
                         restore['volume_id'])
-        self.assertEqual(import_backup['id'], restore['backup_id'])
+        self.assertEqual(backup['id'], restore['backup_id'])
         waiters.wait_for_volume_status(self.admin_volume_client,
                                        restore['volume_id'], 'available')
 
diff --git a/tempest/common/cred_client.py b/tempest/common/cred_client.py
index 37c9727..b23bc6f 100644
--- a/tempest/common/cred_client.py
+++ b/tempest/common/cred_client.py
@@ -170,6 +170,29 @@
                                                       user['id'],
                                                       role['id'])
 
+    def assign_user_role_on_domain(self, user, role_name, domain=None):
+        """Assign the specified role on a domain
+
+        :param user: a user dict
+        :param role_name: name of the role to be assigned
+        :param domain: (optional) The domain to assign the role on. If not
+                                  specified the default domain of cred_client
+        """
+        # NOTE(andreaf) This method is very specific to the v3 case, and
+        # because of that it's not defined in the parent class.
+        if domain is None:
+            domain = self.creds_domain
+        role = self._check_role_exists(role_name)
+        if not role:
+            msg = 'No "%s" role found' % role_name
+            raise lib_exc.NotFound(msg)
+        try:
+            self.roles_client.assign_user_role_on_domain(
+                domain['id'], user['id'], role['id'])
+        except lib_exc.Conflict:
+            LOG.debug("Role %s already assigned on domain %s for user %s",
+                      role['id'], domain['id'], user['id'])
+
 
 def get_creds_client(identity_client,
                      projects_client,
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 82db9cc..286e01f 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -73,8 +73,8 @@
     # the test should be skipped else it would fail.
     identity_version = identity_version or CONF.identity.auth_version
     if CONF.auth.use_dynamic_credentials or force_tenant_isolation:
-        admin_creds = get_configured_credentials(
-            'identity_admin', fill_in=True, identity_version=identity_version)
+        admin_creds = get_configured_admin_credentials(
+            fill_in=True, identity_version=identity_version)
         return dynamic_creds.DynamicCredentialProvider(
             name=name,
             network_resources=network_resources,
@@ -111,8 +111,8 @@
             is_admin = False
     else:
         try:
-            get_configured_credentials('identity_admin', fill_in=False,
-                                       identity_version=identity_version)
+            get_configured_admin_credentials(fill_in=False,
+                                             identity_version=identity_version)
         except exceptions.InvalidConfiguration:
             is_admin = False
     return is_admin
@@ -163,39 +163,32 @@
 
 # Read credentials from configuration, builds a Credentials object
 # based on the specified or configured version
-def get_configured_credentials(credential_type, fill_in=True,
-                               identity_version=None):
+def get_configured_admin_credentials(fill_in=True, identity_version=None):
     identity_version = identity_version or CONF.identity.auth_version
 
     if identity_version not in ('v2', 'v3'):
         raise exceptions.InvalidConfiguration(
             'Unsupported auth version: %s' % identity_version)
 
-    if credential_type not in CREDENTIAL_TYPES:
-        raise exceptions.InvalidCredentials()
-    conf_attributes = ['username', 'password', 'project_name']
+    conf_attributes = ['username', 'password',
+                       'project_name']
 
     if identity_version == 'v3':
         conf_attributes.append('domain_name')
     # Read the parts of credentials from config
     params = DEFAULT_PARAMS.copy()
-    section, prefix = CREDENTIAL_TYPES[credential_type]
     for attr in conf_attributes:
-        _section = getattr(CONF, section)
-        if prefix is None:
-            params[attr] = getattr(_section, attr)
-        else:
-            params[attr] = getattr(_section, prefix + "_" + attr)
+        params[attr] = getattr(CONF.auth, 'admin_' + attr)
     # Build and validate credentials. We are reading configured credentials,
     # so validate them even if fill_in is False
     credentials = get_credentials(fill_in=fill_in,
                                   identity_version=identity_version, **params)
     if not fill_in:
         if not credentials.is_valid():
-            msg = ("The %s credentials are incorrectly set in the config file."
-                   " Double check that all required values are assigned" %
-                   credential_type)
-            raise exceptions.InvalidConfiguration(msg)
+            msg = ("The admin credentials are incorrectly set in the config "
+                   "file for identity version %s. Double check that all "
+                   "required values are assigned.")
+            raise exceptions.InvalidConfiguration(msg % identity_version)
     return credentials
 
 
@@ -223,19 +216,10 @@
 # === Credential / client managers
 
 
-class ConfiguredUserManager(clients.Manager):
-    """Manager that uses user credentials for its managed client objects"""
-
-    def __init__(self, service=None):
-        super(ConfiguredUserManager, self).__init__(
-            credentials=get_configured_credentials('user'),
-            service=service)
-
-
 class AdminManager(clients.Manager):
     """Manager that uses admin credentials for its managed client objects"""
 
     def __init__(self, service=None):
         super(AdminManager, self).__init__(
-            credentials=get_configured_credentials('identity_admin'),
+            credentials=get_configured_admin_credentials(),
             service=service)
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index a63af38..3a3e3c2 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -122,7 +122,9 @@
         project = self.creds_client.create_project(
             name=project_name, description=project_desc)
 
-        username = data_utils.rand_name(root) + suffix
+        # NOTE(andreaf) User and project can be distinguished from the context,
+        # having the same ID in both makes it easier to match them and debug.
+        username = project_name
         user_password = data_utils.rand_password()
         email = data_utils.rand_name(root) + suffix + "@example.com"
         user = self.creds_client.create_user(
@@ -134,6 +136,9 @@
             self.creds_client.assign_user_role(user, project,
                                                self.admin_role)
             role_assigned = True
+            if self.identity_version == 'v3':
+                self.creds_client.assign_user_role_on_domain(
+                    user, CONF.identity.admin_role)
         # Add roles specified in config file
         for conf_role in CONF.auth.tempest_roles:
             self.creds_client.assign_user_role(user, project, conf_role)
diff --git a/tempest/common/preprov_creds.py b/tempest/common/preprov_creds.py
index 7350222..42c69d5 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/common/preprov_creds.py
@@ -249,6 +249,9 @@
             raise lib_exc.InvalidCredentials(
                 "Account file %s doesn't exist" % self.test_accounts_file)
         useable_hashes = self._get_match_hash_list(roles)
+        if len(useable_hashes) == 0:
+            msg = 'No users configured for type/roles %s' % roles
+            raise lib_exc.InvalidCredentials(msg)
         free_hash = self._get_free_hash(useable_hashes)
         clean_creds = self._sanitize_creds(
             self.hash_dict['creds'][free_hash])
diff --git a/tempest/common/utils/file_utils.py b/tempest/common/utils/file_utils.py
deleted file mode 100644
index 43083f4..0000000
--- a/tempest/common/utils/file_utils.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# 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.
-
-
-def have_effective_read_access(path):
-    try:
-        fh = open(path, "rb")
-    except IOError:
-        return False
-    fh.close()
-    return True
diff --git a/tempest/config.py b/tempest/config.py
index f5125b5..3810ceb 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -370,14 +370,6 @@
                      'encrypted volume to a running server instance? This may '
                      'depend on the combination of compute_driver in nova and '
                      'the volume_driver(s) in cinder.'),
-    # TODO(mriedem): Remove allow_duplicate_networks once kilo-eol happens
-    # since the option was removed from nova in Liberty and is the default
-    # behavior starting in Liberty.
-    cfg.BoolOpt('allow_duplicate_networks',
-                default=False,
-                help='Does the test environment support creating instances '
-                     'with multiple ports on the same network? This is only '
-                     'valid when using Neutron.'),
     cfg.BoolOpt('config_drive',
                 default=True,
                 help='Enable special configuration drive with metadata.'),
@@ -849,21 +841,6 @@
                help="Value must match heat configuration of the same name."),
 ]
 
-
-dashboard_group = cfg.OptGroup(name="dashboard",
-                               title="Dashboard options")
-
-DashboardGroup = [
-    cfg.StrOpt('dashboard_url',
-               default='http://localhost/',
-               help="Where the dashboard can be found"),
-    cfg.StrOpt('login_url',
-               default='http://localhost/auth/login/',
-               help="Login page for the dashboard",
-               deprecated_for_removal=True),
-]
-
-
 data_processing_group = cfg.OptGroup(name="data-processing",
                                      title="Data Processing options")
 
@@ -994,9 +971,6 @@
     cfg.BoolOpt('heat',
                 default=False,
                 help="Whether or not Heat is expected to be available"),
-    cfg.BoolOpt('horizon',
-                default=True,
-                help="Whether or not Horizon is expected to be available"),
     cfg.BoolOpt('sahara',
                 default=False,
                 help="Whether or not Sahara is expected to be available"),
@@ -1139,7 +1113,6 @@
     (object_storage_feature_group, ObjectStoreFeaturesGroup),
     (database_group, DatabaseGroup),
     (orchestration_group, OrchestrationGroup),
-    (dashboard_group, DashboardGroup),
     (data_processing_group, DataProcessingGroup),
     (data_processing_feature_group, DataProcessingFeaturesGroup),
     (stress_group, StressGroup),
@@ -1207,7 +1180,6 @@
             'object-storage-feature-enabled']
         self.database = _CONF.database
         self.orchestration = _CONF.orchestration
-        self.dashboard = _CONF.dashboard
         self.data_processing = _CONF['data-processing']
         self.data_processing_feature_enabled = _CONF[
             'data-processing-feature-enabled']
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index aff9dee..09106d1 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -256,6 +256,23 @@
     yield (0, msg)
 
 
+def dont_use_config_in_tempest_lib(logical_line, filename):
+    """Check that tempest.lib doesn't use tempest config
+
+    T114
+    """
+
+    if 'tempest/lib/' not in filename:
+        return
+
+    if ('tempest.config' in logical_line
+        or 'from tempest import config' in logical_line
+        or 'oslo_config' in logical_line):
+        msg = ('T114: tempest.lib can not have any dependency on tempest '
+               'config.')
+        yield(0, msg)
+
+
 def factory(register):
     register(import_no_clients_in_api_and_scenario_tests)
     register(scenario_tests_need_service_tags)
@@ -268,4 +285,5 @@
     register(get_resources_on_service_clients)
     register(delete_resources_on_service_clients)
     register(dont_import_local_tempest_into_lib)
+    register(dont_use_config_in_tempest_lib)
     register(use_rand_uuid_instead_of_uuid4)
diff --git a/tempest/lib/api_schema/response/compute/v2_1/images.py b/tempest/lib/api_schema/response/compute/v2_1/images.py
index daab898..b0f1934 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/images.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/images.py
@@ -77,7 +77,7 @@
                     'properties': {
                         'id': {'type': 'string'},
                         'links': image_links,
-                        'name': {'type': 'string'}
+                        'name': {'type': ['string', 'null']}
                     },
                     'additionalProperties': False,
                     'required': ['id', 'links', 'name']
diff --git a/tempest/lib/cli/base.py b/tempest/lib/cli/base.py
index 9460bf4..a43d002 100644
--- a/tempest/lib/cli/base.py
+++ b/tempest/lib/cli/base.py
@@ -119,7 +119,7 @@
         :param merge_stderr: if True the stderr buffer is merged into stdout
         :type merge_stderr: boolean
         """
-        flags += ' --endpoint-type %s' % endpoint_type
+        flags += ' --os-endpoint-type %s' % endpoint_type
         return self.cmd_with_auth(
             'nova', action, flags, params, fail_ok, merge_stderr)
 
@@ -247,7 +247,7 @@
         :param merge_stderr: if True the stderr buffer is merged into stdout
         :type merge_stderr: boolean
         """
-        flags += ' --os-endpoint-type %s' % endpoint_type
+        flags += ' --endpoint-type %s' % endpoint_type
         return self.cmd_with_auth(
             'cinder', action, flags, params, fail_ok, merge_stderr)
 
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 179db17..627143d 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -221,6 +221,10 @@
         :raises exceptions.InvalidHttpSuccessCode: if the read code isn't an
                                                    expected http success code
         """
+        if not isinstance(read_code, int):
+            raise TypeError("'read_code' must be an int instead of (%s)"
+                            % type(read_code))
+
         assert_msg = ("This function only allowed to use for HTTP status"
                       "codes which explicitly defined in the RFC 7231 & 4918."
                       "{0} is not a defined Success Code!"
diff --git a/tempest/lib/decorators.py b/tempest/lib/decorators.py
index e78e624..6ed99b4 100644
--- a/tempest/lib/decorators.py
+++ b/tempest/lib/decorators.py
@@ -69,12 +69,11 @@
                                "or False") % attr
 
     def __call__(self, func):
+        @functools.wraps(func)
         def _skipper(*args, **kw):
             """Wrapped skipper function."""
             testobj = args[0]
             if not getattr(testobj, self.attr, False):
                 raise testtools.TestCase.skipException(self.message)
             func(*args, **kw)
-        _skipper.__name__ = func.__name__
-        _skipper.__doc__ = func.__doc__
         return _skipper
diff --git a/tempest/lib/services/compute/base_compute_client.py b/tempest/lib/services/compute/base_compute_client.py
index a387b85..433c94c 100644
--- a/tempest/lib/services/compute/base_compute_client.py
+++ b/tempest/lib/services/compute/base_compute_client.py
@@ -67,11 +67,13 @@
 
         :param schema_versions_info: List of dict which provides schema
                                      information with range of valid versions.
-        Example -
-        schema_versions_info = [
-            {'min': None, 'max': '2.1', 'schema': schemav21},
-            {'min': '2.2', 'max': '2.9', 'schema': schemav22},
-            {'min': '2.10', 'max': None, 'schema': schemav210}]
+
+        Example::
+
+         schema_versions_info = [
+             {'min': None, 'max': '2.1', 'schema': schemav21},
+             {'min': '2.2', 'max': '2.9', 'schema': schemav22},
+             {'min': '2.10', 'max': None, 'schema': schemav210}]
         """
         schema = None
         version = api_version_request.APIVersionRequest(COMPUTE_MICROVERSION)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 65677a0..7389722 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -1069,10 +1069,11 @@
                                         security_groups_client=None):
         """Create loginable security group rule
 
-        These rules are intended to permit inbound ssh and icmp
-        traffic from all sources, so no group_id is provided.
-        Setting a group_id would only permit traffic from ports
-        belonging to the same security group.
+        This function will create:
+        1. egress and ingress tcp port 22 allow rule in order to allow ssh
+        access for ipv4.
+        2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
+        3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
         """
 
         if security_group_rules_client is None:
diff --git a/tempest/scenario/test_dashboard_basic_ops.py b/tempest/scenario/test_dashboard_basic_ops.py
deleted file mode 100644
index 5d4f7b3..0000000
--- a/tempest/scenario/test_dashboard_basic_ops.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# 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 six.moves import html_parser as HTMLParser
-from six.moves.urllib import parse
-from six.moves.urllib import request
-
-from tempest import config
-from tempest.scenario import manager
-from tempest import test
-
-CONF = config.CONF
-
-
-class HorizonHTMLParser(HTMLParser.HTMLParser):
-    csrf_token = None
-    region = None
-    login = None
-
-    def _find_name(self, attrs, name):
-        for attrpair in attrs:
-            if attrpair[0] == 'name' and attrpair[1] == name:
-                return True
-        return False
-
-    def _find_value(self, attrs):
-        for attrpair in attrs:
-            if attrpair[0] == 'value':
-                return attrpair[1]
-        return None
-
-    def _find_attr_value(self, attrs, attr_name):
-        for attrpair in attrs:
-            if attrpair[0] == attr_name:
-                return attrpair[1]
-        return None
-
-    def handle_starttag(self, tag, attrs):
-        if tag == 'input':
-            if self._find_name(attrs, 'csrfmiddlewaretoken'):
-                self.csrf_token = self._find_value(attrs)
-            if self._find_name(attrs, 'region'):
-                self.region = self._find_value(attrs)
-        if tag == 'form':
-            self.login = self._find_attr_value(attrs, 'action')
-
-
-class TestDashboardBasicOps(manager.ScenarioTest):
-
-    """The test suite for dashboard basic operations
-
-    This is a basic scenario test:
-    * checks that the login page is available
-    * logs in as a regular user
-    * checks that the user home page loads without error
-    """
-
-    @classmethod
-    def skip_checks(cls):
-        super(TestDashboardBasicOps, cls).skip_checks()
-        if not CONF.service_available.horizon:
-            raise cls.skipException("Horizon support is required")
-
-    @classmethod
-    def setup_credentials(cls):
-        cls.set_network_resources()
-        super(TestDashboardBasicOps, cls).setup_credentials()
-
-    def check_login_page(self):
-        response = request.urlopen(CONF.dashboard.dashboard_url)
-        self.assertIn("id_username", response.read())
-
-    def user_login(self, username, password):
-        self.opener = request.build_opener(request.HTTPCookieProcessor())
-        response = self.opener.open(CONF.dashboard.dashboard_url).read()
-
-        # Grab the CSRF token and default region
-        parser = HorizonHTMLParser()
-        parser.feed(response)
-
-        # construct login url for dashboard, discovery accommodates non-/ web
-        # root for dashboard
-        login_url = parse.urljoin(CONF.dashboard.dashboard_url, parser.login)
-
-        # Prepare login form request
-        req = request.Request(login_url)
-        req.add_header('Content-type', 'application/x-www-form-urlencoded')
-        req.add_header('Referer', CONF.dashboard.dashboard_url)
-
-        # Pass the default domain name regardless of the auth version in order
-        # to test the scenario of when horizon is running with keystone v3
-        params = {'username': username,
-                  'password': password,
-                  'region': parser.region,
-                  'domain': CONF.auth.default_credentials_domain_name,
-                  'csrfmiddlewaretoken': parser.csrf_token}
-        self.opener.open(req, parse.urlencode(params))
-
-    def check_home_page(self):
-        response = self.opener.open(CONF.dashboard.dashboard_url)
-        self.assertIn('Overview', response.read())
-
-    @test.idempotent_id('4f8851b1-0e69-482b-b63b-84c6e76f6c80')
-    @test.services('dashboard')
-    def test_basic_scenario(self):
-        creds = self.os.credentials
-        self.check_login_page()
-        self.user_login(creds.username, creds.password)
-        self.check_home_page()
diff --git a/tempest/services/baremetal/base.py b/tempest/services/baremetal/base.py
index 6e24801..2bdd092 100644
--- a/tempest/services/baremetal/base.py
+++ b/tempest/services/baremetal/base.py
@@ -111,7 +111,7 @@
             uri += "?%s" % urllib.urlencode(kwargs)
 
         resp, body = self.get(uri)
-        self.expected_success(200, resp['status'])
+        self.expected_success(200, resp.status)
 
         return resp, self.deserialize(body)
 
@@ -127,7 +127,7 @@
         else:
             uri = self._get_uri(resource, uuid=uuid, permanent=permanent)
         resp, body = self.get(uri)
-        self.expected_success(200, resp['status'])
+        self.expected_success(200, resp.status)
 
         return resp, self.deserialize(body)
 
@@ -145,7 +145,7 @@
         uri = self._get_uri(resource)
 
         resp, body = self.post(uri, body=body)
-        self.expected_success(201, resp['status'])
+        self.expected_success(201, resp.status)
 
         return resp, self.deserialize(body)
 
@@ -160,7 +160,7 @@
         uri = self._get_uri(resource, uuid)
 
         resp, body = self.delete(uri)
-        self.expected_success(204, resp['status'])
+        self.expected_success(204, resp.status)
         return resp, body
 
     def _patch_request(self, resource, uuid, patch_object):
@@ -176,7 +176,7 @@
         patch_body = json.dumps(patch_object)
 
         resp, body = self.patch(uri, body=patch_body)
-        self.expected_success(200, resp['status'])
+        self.expected_success(200, resp.status)
         return resp, self.deserialize(body)
 
     @handle_errors
@@ -201,5 +201,5 @@
         put_body = json.dumps(put_object)
 
         resp, body = self.put(uri, body=put_body)
-        self.expected_success(202, resp['status'])
+        self.expected_success([202, 204], resp.status)
         return resp, body
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
index 382b851..2beaaa9 100644
--- a/tempest/stress/driver.py
+++ b/tempest/stress/driver.py
@@ -135,10 +135,13 @@
                 break
         if skip:
             break
+        # TODO(andreaf) This has to be reworked to use the credential
+        # provider interface. For now only tests marked as 'use_admin' will
+        # work.
         if test.get('use_admin', False):
             manager = admin_manager
         else:
-            manager = credentials.ConfiguredUserManager()
+            raise NotImplemented('Non admin tests are not supported')
         for p_number in moves.xrange(test.get('threads', default_thread_num)):
             if test.get('use_isolated_tenants', False):
                 username = data_utils.rand_name("stress_user")
diff --git a/tempest/test.py b/tempest/test.py
index aefe1a9..d31c509 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -77,7 +77,6 @@
         'network': True,
         'identity': True,
         'object_storage': CONF.service_available.swift,
-        'dashboard': CONF.service_available.horizon,
         'data_processing': CONF.service_available.sahara,
         'database': CONF.service_available.trove
     }
@@ -92,8 +91,8 @@
     """
     def decorator(f):
         services = ['compute', 'image', 'baremetal', 'volume', 'orchestration',
-                    'network', 'identity', 'object_storage', 'dashboard',
-                    'data_processing', 'database']
+                    'network', 'identity', 'object_storage', 'data_processing',
+                    'database']
         for service in args:
             if service not in services:
                 raise exceptions.InvalidServiceTag('%s is not a valid '
diff --git a/tempest/tests/common/test_dynamic_creds.py b/tempest/tests/common/test_dynamic_creds.py
index 8d4f33b..f025418 100644
--- a/tempest/tests/common/test_dynamic_creds.py
+++ b/tempest/tests/common/test_dynamic_creds.py
@@ -21,15 +21,19 @@
 from tempest import config
 from tempest import exceptions
 from tempest.lib.common import rest_client
-from tempest.lib.services.identity.v2 import token_client as json_token_client
-from tempest.services.identity.v2.json import identity_client as \
-    json_iden_client
-from tempest.services.identity.v2.json import roles_client as \
-    json_roles_client
+from tempest.lib.services.identity.v2 import token_client as v2_token_client
+from tempest.lib.services.identity.v3 import token_client as v3_token_client
+from tempest.services.identity.v2.json import identity_client as v2_iden_client
+from tempest.services.identity.v2.json import roles_client as v2_roles_client
 from tempest.services.identity.v2.json import tenants_client as \
-    json_tenants_client
-from tempest.services.identity.v2.json import users_client as \
-    json_users_client
+    v2_tenants_client
+from tempest.services.identity.v2.json import users_client as v2_users_client
+from tempest.services.identity.v3.json import domains_client
+from tempest.services.identity.v3.json import identity_client as v3_iden_client
+from tempest.services.identity.v3.json import projects_client as \
+    v3_projects_client
+from tempest.services.identity.v3.json import roles_client as v3_roles_client
+from tempest.services.identity.v3.json import users_clients as v3_users_client
 from tempest.services.network.json import routers_client
 from tempest.tests import base
 from tempest.tests import fake_config
@@ -43,13 +47,24 @@
                     'identity_version': 'v2',
                     'admin_role': 'admin'}
 
+    token_client = v2_token_client
+    iden_client = v2_iden_client
+    roles_client = v2_roles_client
+    tenants_client = v2_tenants_client
+    users_client = v2_users_client
+    token_client_class = token_client.TokenClient
+    fake_response = fake_identity._fake_v2_response
+    assign_role_on_project = 'assign_user_role'
+    tenants_client_class = tenants_client.TenantsClient
+    delete_tenant = 'delete_tenant'
+
     def setUp(self):
         super(TestDynamicCredentialProvider, self).setUp()
         self.useFixture(fake_config.ConfigFixture())
         self.patchobject(config, 'TempestConfigPrivate',
                          fake_config.FakePrivate)
-        self.patchobject(json_token_client.TokenClient, 'raw_request',
-                         fake_identity._fake_v2_response)
+        self.patchobject(self.token_client_class, 'raw_request',
+                         self.fake_response)
         cfg.CONF.set_default('operator_role', 'FakeRole',
                              group='object-storage')
         self._mock_list_ec2_credentials('fake_user_id', 'fake_tenant_id')
@@ -59,7 +74,7 @@
     def test_tempest_client(self):
         creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
         self.assertIsInstance(creds.identity_admin_client,
-                              json_iden_client.IdentityClient)
+                              self.iden_client.IdentityClient)
 
     def _get_fake_admin_creds(self):
         return credentials.get_credentials(
@@ -70,7 +85,7 @@
 
     def _mock_user_create(self, id, name):
         user_fix = self.useFixture(mockpatch.PatchObject(
-            json_users_client.UsersClient,
+            self.users_client.UsersClient,
             'create_user',
             return_value=(rest_client.ResponseBody
                           (200, {'user': {'id': id, 'name': name}}))))
@@ -78,7 +93,7 @@
 
     def _mock_tenant_create(self, id, name):
         tenant_fix = self.useFixture(mockpatch.PatchObject(
-            json_tenants_client.TenantsClient,
+            self.tenants_client.TenantsClient,
             'create_tenant',
             return_value=(rest_client.ResponseBody
                           (200, {'tenant': {'id': id, 'name': name}}))))
@@ -86,7 +101,7 @@
 
     def _mock_list_roles(self, id, name):
         roles_fix = self.useFixture(mockpatch.PatchObject(
-            json_roles_client.RolesClient,
+            self.roles_client.RolesClient,
             'list_roles',
             return_value=(rest_client.ResponseBody
                           (200,
@@ -97,7 +112,7 @@
 
     def _mock_list_2_roles(self):
         roles_fix = self.useFixture(mockpatch.PatchObject(
-            json_roles_client.RolesClient,
+            self.roles_client.RolesClient,
             'list_roles',
             return_value=(rest_client.ResponseBody
                           (200,
@@ -108,24 +123,25 @@
 
     def _mock_assign_user_role(self):
         tenant_fix = self.useFixture(mockpatch.PatchObject(
-            json_roles_client.RolesClient,
-            'assign_user_role',
+            self.roles_client.RolesClient,
+            self.assign_role_on_project,
             return_value=(rest_client.ResponseBody
                           (200, {}))))
         return tenant_fix
 
     def _mock_list_role(self):
         roles_fix = self.useFixture(mockpatch.PatchObject(
-            json_roles_client.RolesClient,
+            self.roles_client.RolesClient,
             'list_roles',
             return_value=(rest_client.ResponseBody
-                          (200, {'roles': [{'id': '1',
-                                 'name': 'FakeRole'}]}))))
+                          (200, {'roles': [
+                              {'id': '1', 'name': 'FakeRole'},
+                              {'id': '2', 'name': 'Member'}]}))))
         return roles_fix
 
     def _mock_list_ec2_credentials(self, user_id, tenant_id):
         ec2_creds_fix = self.useFixture(mockpatch.PatchObject(
-            json_users_client.UsersClient,
+            self.users_client.UsersClient,
             'list_user_ec2_credentials',
             return_value=(rest_client.ResponseBody
                           (200, {'credentials': [{
@@ -180,12 +196,12 @@
         self._mock_user_create('1234', 'fake_admin_user')
         self._mock_tenant_create('1234', 'fake_admin_tenant')
 
-        user_mock = mock.patch.object(json_roles_client.RolesClient,
-                                      'assign_user_role')
+        user_mock = mock.patch.object(self.roles_client.RolesClient,
+                                      self.assign_role_on_project)
         user_mock.start()
         self.addCleanup(user_mock.stop)
-        with mock.patch.object(json_roles_client.RolesClient,
-                               'assign_user_role') as user_mock:
+        with mock.patch.object(self.roles_client.RolesClient,
+                               self.assign_role_on_project) as user_mock:
             admin_creds = creds.get_admin_creds()
         user_mock.assert_has_calls([
             mock.call('1234', '1234', '1234')])
@@ -203,12 +219,12 @@
         self._mock_user_create('1234', 'fake_role_user')
         self._mock_tenant_create('1234', 'fake_role_tenant')
 
-        user_mock = mock.patch.object(json_roles_client.RolesClient,
-                                      'assign_user_role')
+        user_mock = mock.patch.object(self.roles_client.RolesClient,
+                                      self.assign_role_on_project)
         user_mock.start()
         self.addCleanup(user_mock.stop)
-        with mock.patch.object(json_roles_client.RolesClient,
-                               'assign_user_role') as user_mock:
+        with mock.patch.object(self.roles_client.RolesClient,
+                               self.assign_role_on_project) as user_mock:
             role_creds = creds.get_creds_by_roles(
                 roles=['role1', 'role2'])
         calls = user_mock.mock_calls
@@ -240,12 +256,10 @@
         self._mock_user_create('123456', 'fake_admin_user')
         self._mock_list_roles('123456', 'admin')
         creds.get_admin_creds()
-        user_mock = self.patch(
-            'tempest.services.identity.v2.json.users_client.'
-            'UsersClient.delete_user')
-        tenant_mock = self.patch(
-            'tempest.services.identity.v2.json.tenants_client.'
-            'TenantsClient.delete_tenant')
+        user_mock = self.patchobject(self.users_client.UsersClient,
+                                     'delete_user')
+        tenant_mock = self.patchobject(self.tenants_client_class,
+                                       self.delete_tenant)
         creds.clear_creds()
         # Verify user delete calls
         calls = user_mock.mock_calls
@@ -374,18 +388,13 @@
         self._mock_router_create('123456', 'fake_admin_router')
         self._mock_list_roles('123456', 'admin')
         creds.get_admin_creds()
-        self.patch('tempest.services.identity.v2.json.users_client.'
-                   'UsersClient.delete_user')
-        self.patch('tempest.services.identity.v2.json.tenants_client.'
-                   'TenantsClient.delete_tenant')
-        net = mock.patch.object(creds.networks_admin_client,
-                                'delete_network')
+        self.patchobject(self.users_client.UsersClient, 'delete_user')
+        self.patchobject(self.tenants_client_class, self.delete_tenant)
+        net = mock.patch.object(creds.networks_admin_client, 'delete_network')
         net_mock = net.start()
-        subnet = mock.patch.object(creds.subnets_admin_client,
-                                   'delete_subnet')
+        subnet = mock.patch.object(creds.subnets_admin_client, 'delete_subnet')
         subnet_mock = subnet.start()
-        router = mock.patch.object(creds.routers_admin_client,
-                                   'delete_router')
+        router = mock.patch.object(creds.routers_admin_client, 'delete_router')
         router_mock = router.start()
         remove_router_interface_mock = self.patch(
             'tempest.services.network.json.routers_client.RoutersClient.'
@@ -587,3 +596,42 @@
         self._mock_tenant_create('1234', 'fake_prim_tenant')
         self.assertRaises(exceptions.InvalidConfiguration,
                           creds.get_primary_creds)
+
+
+class TestDynamicCredentialProviderV3(TestDynamicCredentialProvider):
+
+    fixed_params = {'name': 'test class',
+                    'identity_version': 'v3',
+                    'admin_role': 'admin'}
+
+    token_client = v3_token_client
+    iden_client = v3_iden_client
+    roles_client = v3_roles_client
+    tenants_client = v3_projects_client
+    users_client = v3_users_client
+    token_client_class = token_client.V3TokenClient
+    fake_response = fake_identity._fake_v3_response
+    assign_role_on_project = 'assign_user_role_on_project'
+    tenants_client_class = tenants_client.ProjectsClient
+    delete_tenant = 'delete_project'
+
+    def setUp(self):
+        super(TestDynamicCredentialProviderV3, self).setUp()
+        self.useFixture(fake_config.ConfigFixture())
+        self.useFixture(mockpatch.PatchObject(
+            domains_client.DomainsClient, 'list_domains',
+            return_value=dict(domains=[dict(id='default',
+                                            name='Default')])))
+        self.patchobject(self.roles_client.RolesClient,
+                         'assign_user_role_on_domain')
+
+    def _mock_list_ec2_credentials(self, user_id, tenant_id):
+        pass
+
+    def _mock_tenant_create(self, id, name):
+        project_fix = self.useFixture(mockpatch.PatchObject(
+            self.tenants_client.ProjectsClient,
+            'create_project',
+            return_value=(rest_client.ResponseBody
+                          (200, {'project': {'id': id, 'name': name}}))))
+        return project_fix
diff --git a/tempest/tests/common/test_preprov_creds.py b/tempest/tests/common/test_preprov_creds.py
index 22bbdd3..13d4713 100644
--- a/tempest/tests/common/test_preprov_creds.py
+++ b/tempest/tests/common/test_preprov_creds.py
@@ -14,6 +14,7 @@
 
 import hashlib
 import os
+import testtools
 
 import mock
 from oslo_concurrency.fixture import lockutils as lockutils_fixtures
@@ -69,10 +70,12 @@
              'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
             {'username': 'test_user10', 'project_name': 'test_tenant10',
              'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
-            {'username': 'test_user11', 'tenant_name': 'test_tenant11',
+            {'username': 'test_admin1', 'tenant_name': 'test_tenant11',
              'password': 'p', 'roles': [admin_role]},
-            {'username': 'test_user12', 'project_name': 'test_tenant12',
-             'password': 'p', 'roles': [admin_role]}]
+            {'username': 'test_admin2', 'project_name': 'test_tenant12',
+             'password': 'p', 'roles': [admin_role]},
+            {'username': 'test_admin3', 'project_name': 'test_tenant13',
+             'password': 'p', 'types': ['admin']}]
 
     def setUp(self):
         super(TestPreProvisionedCredentials, self).setUp()
@@ -262,9 +265,6 @@
         self.assertFalse(test_accounts_class.is_multi_user())
 
     def test__get_creds_by_roles_one_role(self):
-        self.useFixture(mockpatch.Patch(
-            'tempest.common.preprov_creds.read_accounts_yaml',
-            return_value=self.test_accounts))
         test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
         hashes = test_accounts_class.hash_dict['roles']['role4']
@@ -280,9 +280,6 @@
             self.assertIn(i, args)
 
     def test__get_creds_by_roles_list_role(self):
-        self.useFixture(mockpatch.Patch(
-            'tempest.common.preprov_creds.read_accounts_yaml',
-            return_value=self.test_accounts))
         test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
         hashes = test_accounts_class.hash_dict['roles']['role4']
@@ -300,9 +297,6 @@
             self.assertIn(i, args)
 
     def test__get_creds_by_roles_no_admin(self):
-        self.useFixture(mockpatch.Patch(
-            'tempest.common.preprov_creds.read_accounts_yaml',
-            return_value=self.test_accounts))
         test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
             **self.fixed_params)
         hashes = list(test_accounts_class.hash_dict['creds'].keys())
@@ -346,13 +340,95 @@
         self.assertEqual('fake-id', network['id'])
         self.assertEqual('network-2', network['name'])
 
+    def test_get_primary_creds(self):
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        primary_creds = test_accounts_class.get_primary_creds()
+        self.assertNotIn('test_admin', primary_creds.username)
+
+    def test_get_primary_creds_none_available(self):
+        admin_accounts = [x for x in self.test_accounts if 'test_admin'
+                          in x['username']]
+        self.useFixture(mockpatch.Patch(
+            'tempest.common.preprov_creds.read_accounts_yaml',
+            return_value=admin_accounts))
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        with testtools.ExpectedException(lib_exc.InvalidCredentials):
+            # Get one more
+            test_accounts_class.get_primary_creds()
+
+    def test_get_alt_creds(self):
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        alt_creds = test_accounts_class.get_alt_creds()
+        self.assertNotIn('test_admin', alt_creds.username)
+
+    def test_get_alt_creds_none_available(self):
+        admin_accounts = [x for x in self.test_accounts if 'test_admin'
+                          in x['username']]
+        self.useFixture(mockpatch.Patch(
+            'tempest.common.preprov_creds.read_accounts_yaml',
+            return_value=admin_accounts))
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        with testtools.ExpectedException(lib_exc.InvalidCredentials):
+            # Get one more
+            test_accounts_class.get_alt_creds()
+
+    def test_get_admin_creds(self):
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        admin_creds = test_accounts_class.get_admin_creds()
+        self.assertIn('test_admin', admin_creds.username)
+
+    def test_get_admin_creds_by_type(self):
+        test_accounts = [
+            {'username': 'test_user10', 'project_name': 'test_tenant10',
+             'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
+            {'username': 'test_admin1', 'tenant_name': 'test_tenant11',
+             'password': 'p', 'types': ['admin']}]
+        self.useFixture(mockpatch.Patch(
+            'tempest.common.preprov_creds.read_accounts_yaml',
+            return_value=test_accounts))
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        admin_creds = test_accounts_class.get_admin_creds()
+        self.assertIn('test_admin', admin_creds.username)
+
+    def test_get_admin_creds_by_role(self):
+        test_accounts = [
+            {'username': 'test_user10', 'project_name': 'test_tenant10',
+             'password': 'p', 'roles': ['role1', 'role2', 'role3', 'role4']},
+            {'username': 'test_admin1', 'tenant_name': 'test_tenant11',
+             'password': 'p', 'roles': [cfg.CONF.identity.admin_role]}]
+        self.useFixture(mockpatch.Patch(
+            'tempest.common.preprov_creds.read_accounts_yaml',
+            return_value=test_accounts))
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        admin_creds = test_accounts_class.get_admin_creds()
+        self.assertIn('test_admin', admin_creds.username)
+
+    def test_get_admin_creds_none_available(self):
+        non_admin_accounts = [x for x in self.test_accounts if 'test_admin'
+                              not in x['username']]
+        self.useFixture(mockpatch.Patch(
+            'tempest.common.preprov_creds.read_accounts_yaml',
+            return_value=non_admin_accounts))
+        test_accounts_class = preprov_creds.PreProvisionedCredentialProvider(
+            **self.fixed_params)
+        with testtools.ExpectedException(lib_exc.InvalidCredentials):
+            # Get one more
+            test_accounts_class.get_admin_creds()
+
 
 class TestPreProvisionedCredentialsV3(TestPreProvisionedCredentials):
 
     fixed_params = {'name': 'test class',
                     'identity_version': 'v3',
                     'test_accounts_file': 'fake_accounts_file',
-                    'accounts_lock_dir': 'fake_locks_dir',
+                    'accounts_lock_dir': 'fake_locks_dir_v3',
                     'admin_role': 'admin',
                     'object_storage_operator_role': 'operator',
                     'object_storage_reseller_admin_role': 'reseller'}
@@ -389,9 +465,9 @@
             {'username': 'test_user10', 'project_name': 'test_project10',
              'domain_name': 'domain', 'password': 'p',
              'roles': ['role1', 'role2', 'role3', 'role4']},
-            {'username': 'test_user11', 'project_name': 'test_project11',
-             'domain_name': 'domain', 'password': 'p',
-             'roles': [admin_role]},
-            {'username': 'test_user12', 'project_name': 'test_project12',
-             'domain_name': 'domain', 'password': 'p',
-             'roles': [admin_role]}]
+            {'username': 'test_admin1', 'project_name': 'test_project11',
+             'domain_name': 'domain', 'password': 'p', 'roles': [admin_role]},
+            {'username': 'test_admin2', 'project_name': 'test_project12',
+             'domain_name': 'domain', 'password': 'p', 'roles': [admin_role]},
+            {'username': 'test_admin3', 'project_name': 'test_tenant13',
+             'domain_name': 'domain', 'password': 'p', 'types': ['admin']}]
diff --git a/tempest/tests/common/utils/test_file_utils.py b/tempest/tests/common/utils/test_file_utils.py
deleted file mode 100644
index 937aefa..0000000
--- a/tempest/tests/common/utils/test_file_utils.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright 2014 IBM Corp.
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import mock
-
-from tempest.common.utils import file_utils
-from tempest.tests import base
-
-
-class TestFileUtils(base.TestCase):
-
-    def test_have_effective_read_path(self):
-        with mock.patch('six.moves.builtins.open', mock.mock_open(),
-                        create=True):
-            result = file_utils.have_effective_read_access('fake_path')
-        self.assertTrue(result)
-
-    def test_not_effective_read_path(self):
-        result = file_utils.have_effective_read_access('fake_path')
-        self.assertFalse(result)
diff --git a/tempest/tests/lib/test_rest_client.py b/tempest/tests/lib/test_rest_client.py
index 2a6fad5..106a1e5 100644
--- a/tempest/tests/lib/test_rest_client.py
+++ b/tempest/tests/lib/test_rest_client.py
@@ -693,6 +693,24 @@
         self.assertRaises(AssertionError, self.rest_client.expected_success,
                           expected_code, read_code)
 
+    def test_non_success_read_code_as_string(self):
+        expected_code = 202
+        read_code = '202'
+        self.assertRaises(TypeError, self.rest_client.expected_success,
+                          expected_code, read_code)
+
+    def test_non_success_read_code_as_list(self):
+        expected_code = 202
+        read_code = [202]
+        self.assertRaises(TypeError, self.rest_client.expected_success,
+                          expected_code, read_code)
+
+    def test_non_success_expected_code_as_non_int(self):
+        expected_code = ['201', 202]
+        read_code = 202
+        self.assertRaises(AssertionError, self.rest_client.expected_success,
+                          expected_code, read_code)
+
 
 class TestResponseBody(base.TestCase):
 
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index aba2aab..f005c21 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -167,3 +167,16 @@
         self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
             "import tempest.exception",
             './tempest/lib/common/compute.py'))))
+
+    def test_dont_use_config_in_tempest_lib(self):
+        self.assertFalse(list(checks.dont_use_config_in_tempest_lib(
+            'from tempest import config', './tempest/common/compute.py')))
+        self.assertFalse(list(checks.dont_use_config_in_tempest_lib(
+            'from oslo_concurrency import lockutils',
+            './tempest/lib/auth.py')))
+        self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
+            'from tempest import config', './tempest/lib/auth.py')))
+        self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
+            'from oslo_config import cfg', './tempest/lib/decorators.py')))
+        self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
+            'import tempest.config', './tempest/lib/common/rest_client.py')))
diff --git a/test-requirements.txt b/test-requirements.txt
index 9ef956a..763f0ba 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -7,6 +7,6 @@
 python-subunit>=0.0.18 # Apache-2.0/BSD
 oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
 reno>=1.6.2 # Apache2
-mock>=1.2 # BSD
+mock>=2.0 # BSD
 coverage>=3.6 # Apache-2.0
 oslotest>=1.10.0 # Apache-2.0
diff --git a/tools/check_logs.py b/tools/check_logs.py
index e34dec3..caad85c 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -20,8 +20,8 @@
 import os
 import re
 import six
+import six.moves.urllib.request as urlreq
 import sys
-import urllib2
 
 import yaml
 
@@ -67,9 +67,9 @@
                 logs_with_errors.append(name)
     for (name, url) in url_specs:
         whitelist = whitelists.get(name, [])
-        req = urllib2.Request(url)
+        req = urlreq.Request(url)
         req.add_header('Accept-Encoding', 'gzip')
-        page = urllib2.urlopen(req)
+        page = urlreq.urlopen(req)
         buf = six.StringIO(page.read())
         f = gzip.GzipFile(fileobj=buf)
         if scan_content(name, f.read().splitlines(), regexp, whitelist):
@@ -95,7 +95,7 @@
 
 
 def collect_url_logs(url):
-    page = urllib2.urlopen(url)
+    page = urlreq.urlopen(url)
     content = page.read()
     logs = re.findall('(screen-[\w-]+\.txt\.gz)</a>', content)
     return logs
diff --git a/tools/find_stack_traces.py b/tools/find_stack_traces.py
index 49a42fe..f2da27a 100755
--- a/tools/find_stack_traces.py
+++ b/tools/find_stack_traces.py
@@ -19,8 +19,8 @@
 import pprint
 import re
 import six
+import six.moves.urllib.request as urlreq
 import sys
-import urllib2
 
 
 pp = pprint.PrettyPrinter()
@@ -65,9 +65,9 @@
 
 def hunt_for_stacktrace(url):
     """Return TRACE or ERROR lines out of logs."""
-    req = urllib2.Request(url)
+    req = urlreq.Request(url)
     req.add_header('Accept-Encoding', 'gzip')
-    page = urllib2.urlopen(req)
+    page = urlreq.urlopen(req)
     buf = six.StringIO(page.read())
     f = gzip.GzipFile(fileobj=buf)
     content = f.read()
@@ -105,7 +105,7 @@
 
 
 def collect_logs(url):
-    page = urllib2.urlopen(url)
+    page = urlreq.urlopen(url)
     content = page.read()
     logs = re.findall('(screen-[\w-]+\.txt\.gz)</a>', content)
     return logs