Merge "Define v2 images_client as library"
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 32cd3ef..e4b104f 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -26,6 +26,11 @@
 - Run tests for admin APIs
 - Generate test credentials on the fly (see `Dynamic Credentials`_)
 
+When keystone uses a policy that requires domain scoped tokens for admin
+actions, the flag ``admin_domain_scope`` must be set to ``True``.
+The admin user configured, if any, must have a role assigned to the domain to
+be usable.
+
 Tempest allows for configuring pre-provisioned test credentials as well.
 This can be done using the accounts.yaml file (see
 `Pre-Provisioned Credentials`_). This file is used to specify an arbitrary
@@ -87,6 +92,14 @@
 by dynamic credentials. This option will not have any effect when Tempest is not
 configured to use dynamic credentials.
 
+When the ``admin_domain_scope`` option is set to ``True``, provisioned admin
+accounts will be assigned a role on domain configured in
+``default_credentials_domain_name``. This will make the accounts provisioned
+usable in a cloud where domain scoped tokens are required by keystone for
+admin operations. Note that the the initial pre-provision admin accounts,
+configured in tempest.conf, must have a role on the same domain as well, for
+Dynamic Credentials to work.
+
 
 Pre-Provisioned Credentials
 """""""""""""""""""""""""""
@@ -124,6 +137,18 @@
 to the tests using the credentials, and failure to do this will likely cause
 unexpected failures in some tests.
 
+When the keystone in the target cloud requires domain scoped tokens to
+perform admin actions, all pre-provisioned admin users must have a role
+assigned on the domain where test accounts a provisioned.
+The option ``admin_domain_scope`` is used to tell tempest that domain scoped
+tokens shall be used. ``default_credentials_domain_name`` is the domain where
+test accounts are expected to be provisioned if no domain is specified.
+
+Note that if credentials are pre-provisioned via ``tempest account-generator``
+the role on the domain will be assigned automatically for you, as long as
+``admin_domain_scope`` as ``default_credentials_domain_name`` are configured
+properly in tempest.conf.
+
 Pre-Provisioned Credentials are also know as accounts.yaml or accounts file.
 
 Compute
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 09ae468..9c8f1f6 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -98,7 +98,8 @@
             password=self.trustor_password,
             user_domain_id='default',
             tenant_name=self.trustor_project_name,
-            project_domain_id='default')
+            project_domain_id='default',
+            domain_id='default')
         os = clients.Manager(credentials=creds)
         self.trustor_client = os.trusts_client
 
@@ -266,7 +267,18 @@
     @test.attr(type='smoke')
     @test.idempotent_id('4773ebd5-ecbf-4255-b8d8-b63e6f72b65d')
     def test_get_trusts_all(self):
+
+        # Simple function that can be used for cleanup
+        def set_scope(auth_provider, scope):
+            auth_provider.scope = scope
+
         self.create_trust()
+        # Listing trusts can be done by trustor, by trustee, or without
+        # any filter if scoped to a project, so we must ensure token scope is
+        # project for this test.
+        original_scope = self.os_adm.auth_provider.scope
+        set_scope(self.os_adm.auth_provider, 'project')
+        self.addCleanup(set_scope, self.os_adm.auth_provider, original_scope)
         trusts_get = self.trusts_client.list_trusts()['trusts']
         trusts = [t for t in trusts_get
                   if t['id'] == self.trust_id]
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 2d4ff17..31420d1 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -162,6 +162,12 @@
         cls.creds_client = cls.os_adm.credentials_client
         cls.groups_client = cls.os_adm.groups_client
         cls.projects_client = cls.os_adm.projects_client
+        if CONF.identity.admin_domain_scope:
+            # NOTE(andreaf) When keystone policy requires it, the identity
+            # admin clients for these tests shall use 'domain' scoped tokens.
+            # As the client manager is already created by the base class,
+            # we set the scope for the inner auth provider.
+            cls.os_adm.auth_provider.scope = 'domain'
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/identity/v2/test_ec2_credentials.py b/tempest/api/identity/v2/test_ec2_credentials.py
index 8600980..5902196 100644
--- a/tempest/api/identity/v2/test_ec2_credentials.py
+++ b/tempest/api/identity/v2/test_ec2_credentials.py
@@ -36,16 +36,16 @@
     def test_create_ec2_credentials(self):
         """Create user ec2 credentials."""
         resp = self.non_admin_users_client.create_user_ec2_credentials(
-            self.creds.credentials.user_id,
-            tenant_id=self.creds.credentials.tenant_id)["credential"]
+            self.creds.user_id,
+            tenant_id=self.creds.tenant_id)["credential"]
         access = resp['access']
         self.addCleanup(
             self.non_admin_users_client.delete_user_ec2_credentials,
-            self.creds.credentials.user_id, access)
+            self.creds.user_id, access)
         self.assertNotEmpty(resp['access'])
         self.assertNotEmpty(resp['secret'])
-        self.assertEqual(self.creds.credentials.user_id, resp['user_id'])
-        self.assertEqual(self.creds.credentials.tenant_id, resp['tenant_id'])
+        self.assertEqual(self.creds.user_id, resp['user_id'])
+        self.assertEqual(self.creds.tenant_id, resp['tenant_id'])
 
     @test.idempotent_id('9e2ea42f-0a4f-468c-a768-51859ce492e0')
     def test_list_ec2_credentials(self):
@@ -54,24 +54,24 @@
         fetched_creds = []
         # create first ec2 credentials
         creds1 = self.non_admin_users_client.create_user_ec2_credentials(
-            self.creds.credentials.user_id,
-            tenant_id=self.creds.credentials.tenant_id)["credential"]
+            self.creds.user_id,
+            tenant_id=self.creds.tenant_id)["credential"]
         created_creds.append(creds1['access'])
         # create second ec2 credentials
         creds2 = self.non_admin_users_client.create_user_ec2_credentials(
-            self.creds.credentials.user_id,
-            tenant_id=self.creds.credentials.tenant_id)["credential"]
+            self.creds.user_id,
+            tenant_id=self.creds.tenant_id)["credential"]
         created_creds.append(creds2['access'])
         # add credentials to be cleaned up
         self.addCleanup(
             self.non_admin_users_client.delete_user_ec2_credentials,
-            self.creds.credentials.user_id, creds1['access'])
+            self.creds.user_id, creds1['access'])
         self.addCleanup(
             self.non_admin_users_client.delete_user_ec2_credentials,
-            self.creds.credentials.user_id, creds2['access'])
+            self.creds.user_id, creds2['access'])
         # get the list of user ec2 credentials
         resp = self.non_admin_users_client.list_user_ec2_credentials(
-            self.creds.credentials.user_id)["credentials"]
+            self.creds.user_id)["credentials"]
         fetched_creds = [cred['access'] for cred in resp]
         # created credentials should be in a fetched list
         missing = [cred for cred in created_creds
@@ -84,14 +84,14 @@
     def test_show_ec2_credentials(self):
         """Get the definite user ec2 credentials."""
         resp = self.non_admin_users_client.create_user_ec2_credentials(
-            self.creds.credentials.user_id,
-            tenant_id=self.creds.credentials.tenant_id)["credential"]
+            self.creds.user_id,
+            tenant_id=self.creds.tenant_id)["credential"]
         self.addCleanup(
             self.non_admin_users_client.delete_user_ec2_credentials,
-            self.creds.credentials.user_id, resp['access'])
+            self.creds.user_id, resp['access'])
 
         ec2_creds = self.non_admin_users_client.show_user_ec2_credentials(
-            self.creds.credentials.user_id, resp['access']
+            self.creds.user_id, resp['access']
         )["credential"]
         for key in ['access', 'secret', 'user_id', 'tenant_id']:
             self.assertEqual(ec2_creds[key], resp[key])
@@ -100,13 +100,13 @@
     def test_delete_ec2_credentials(self):
         """Delete user ec2 credentials."""
         resp = self.non_admin_users_client.create_user_ec2_credentials(
-            self.creds.credentials.user_id,
-            tenant_id=self.creds.credentials.tenant_id)["credential"]
+            self.creds.user_id,
+            tenant_id=self.creds.tenant_id)["credential"]
         access = resp['access']
         self.non_admin_users_client.delete_user_ec2_credentials(
-            self.creds.credentials.user_id, access)
+            self.creds.user_id, access)
         self.assertRaises(
             lib_exc.NotFound,
             self.non_admin_users_client.show_user_ec2_credentials,
-            self.creds.credentials.user_id,
+            self.creds.user_id,
             access)
diff --git a/tempest/api/identity/v2/test_tenants.py b/tempest/api/identity/v2/test_tenants.py
index b742e69..cc6de47 100644
--- a/tempest/api/identity/v2/test_tenants.py
+++ b/tempest/api/identity/v2/test_tenants.py
@@ -24,7 +24,7 @@
 
     @test.idempotent_id('ecae2459-243d-4ba1-ad02-65f15dc82b78')
     def test_list_tenants_returns_only_authorized_tenants(self):
-        alt_tenant_name = self.alt_manager.credentials.credentials.tenant_name
+        alt_tenant_name = self.alt_manager.credentials.tenant_name
         resp = self.non_admin_tenants_client.list_tenants()
 
         # check that user can see only that tenants that he presents in so user
diff --git a/tempest/api/identity/v2/test_tokens.py b/tempest/api/identity/v2/test_tokens.py
index 3b508f4..bdca1e0 100644
--- a/tempest/api/identity/v2/test_tokens.py
+++ b/tempest/api/identity/v2/test_tokens.py
@@ -43,8 +43,8 @@
         self.assertGreater(expires_at, now)
 
         self.assertEqual(body['token']['tenant']['id'],
-                         creds.credentials.tenant_id)
+                         creds.tenant_id)
         self.assertEqual(body['token']['tenant']['name'],
                          tenant_name)
 
-        self.assertEqual(body['user']['id'], creds.credentials.user_id)
+        self.assertEqual(body['user']['id'], creds.user_id)
diff --git a/tempest/api/identity/v2/test_users.py b/tempest/api/identity/v2/test_users.py
index 79f2576..4833f9e 100644
--- a/tempest/api/identity/v2/test_users.py
+++ b/tempest/api/identity/v2/test_users.py
@@ -46,9 +46,9 @@
             client.auth_provider.clear_auth()
             client.auth_provider.set_auth()
 
-        old_pass = self.creds.credentials.password
+        old_pass = self.creds.password
         new_pass = data_utils.rand_password()
-        user_id = self.creds.credentials.user_id
+        user_id = self.creds.user_id
         # to change password back. important for allow_tenant_isolation = false
         self.addCleanup(_restore_password, self.non_admin_users_client,
                         user_id, old_pass=old_pass, new_pass=new_pass)
diff --git a/tempest/api/identity/v3/test_projects.py b/tempest/api/identity/v3/test_projects.py
index 1574ab7..26cb90b 100644
--- a/tempest/api/identity/v3/test_projects.py
+++ b/tempest/api/identity/v3/test_projects.py
@@ -25,7 +25,7 @@
     @test.idempotent_id('86128d46-e170-4644-866a-cc487f699e1d')
     def test_list_projects_returns_only_authorized_projects(self):
         alt_project_name =\
-            self.alt_manager.credentials.credentials.project_name
+            self.alt_manager.credentials.project_name
         resp = self.non_admin_users_client.list_user_projects(
             self.os.credentials.user_id)
 
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
index 76b46c0..c92e750 100644
--- a/tempest/api/identity/v3/test_users.py
+++ b/tempest/api/identity/v3/test_users.py
@@ -46,9 +46,9 @@
             client.auth_provider.clear_auth()
             client.auth_provider.set_auth()
 
-        old_pass = self.creds.credentials.password
+        old_pass = self.creds.password
         new_pass = data_utils.rand_password()
-        user_id = self.creds.credentials.user_id
+        user_id = self.creds.user_id
         # to change password back. important for allow_tenant_isolation = false
         self.addCleanup(_restore_password, self.non_admin_users_client,
                         user_id, old_pass=old_pass, new_pass=new_pass)
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 10f753c..4b12ecb 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -384,7 +384,7 @@
     icreds = credentials.get_credentials_provider(
         'verify_tempest_config', network_resources=net_resources)
     try:
-        os = clients.Manager(icreds.get_primary_creds())
+        os = clients.Manager(icreds.get_primary_creds().credentials)
         services = check_service_availability(os, update)
         results = {}
         for service in ['nova', 'cinder', 'neutron', 'swift']:
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 4873fcf..c22afc1 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -193,17 +193,21 @@
 
 
 # Wrapper around auth.get_credentials to use the configured identity version
-# is none is specified
+# if none is specified
 def get_credentials(fill_in=True, identity_version=None, **kwargs):
     params = dict(DEFAULT_PARAMS, **kwargs)
     identity_version = identity_version or CONF.identity.auth_version
     # In case of "v3" add the domain from config if not specified
+    # To honour the "default_credentials_domain_name", if not domain
+    # field is specified at all, add it the credential dict.
     if identity_version == 'v3':
         domain_fields = set(x for x in auth.KeystoneV3Credentials.ATTRIBUTES
                             if 'domain' in x)
         if not domain_fields.intersection(kwargs.keys()):
             domain_name = CONF.auth.default_credentials_domain_name
-            params['user_domain_name'] = domain_name
+            # NOTE(andreaf) Setting domain_name implicitly sets user and
+            # project domain names, if they are None
+            params['domain_name'] = domain_name
 
         auth_url = CONF.identity.uri_v3
     else:
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index 3a3e3c2..0b92c15 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -96,8 +96,15 @@
                     os.networks_client, os.routers_client, os.subnets_client,
                     os.ports_client, os.security_groups_client)
         else:
-            return (os.identity_v3_client, os.projects_client,
-                    os.users_v3_client, os.roles_v3_client, os.domains_client,
+            # We use a dedicated client manager for identity client in case we
+            # need a different token scope for them.
+            scope = 'domain' if CONF.identity.admin_domain_scope else 'project'
+            identity_os = clients.Manager(self.default_admin_creds,
+                                          scope=scope)
+            return (identity_os.identity_v3_client,
+                    identity_os.projects_client,
+                    identity_os.users_v3_client, identity_os.roles_v3_client,
+                    identity_os.domains_client,
                     os.networks_client, os.routers_client,
                     os.subnets_client, os.ports_client,
                     os.security_groups_client)
@@ -136,7 +143,8 @@
             self.creds_client.assign_user_role(user, project,
                                                self.admin_role)
             role_assigned = True
-            if self.identity_version == 'v3':
+            if (self.identity_version == 'v3' and
+                    CONF.identity.admin_domain_scope):
                 self.creds_client.assign_user_role_on_domain(
                     user, CONF.identity.admin_role)
         # Add roles specified in config file
diff --git a/tempest/config.py b/tempest/config.py
index 3810ceb..1f88871 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -166,6 +166,10 @@
     cfg.StrOpt('default_domain_id',
                default='default',
                help="ID of the default domain"),
+    cfg.BoolOpt('admin_domain_scope',
+                default=False,
+                help="Whether keystone identity v3 policy required "
+                     "a domain scoped token to use admin APIs")
 ]
 
 identity_feature_group = cfg.OptGroup(name='identity-feature-enabled',
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 217c1ce..425758f 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -262,7 +262,7 @@
                  disable_ssl_certificate_validation=None,
                  ca_certs=None, trace_requests=None, scope='project'):
         super(KeystoneAuthProvider, self).__init__(credentials, scope)
-        self.dsvm = disable_ssl_certificate_validation
+        self.dscv = disable_ssl_certificate_validation
         self.ca_certs = ca_certs
         self.trace_requests = trace_requests
         self.auth_url = auth_url
@@ -341,7 +341,7 @@
 
     def _auth_client(self, auth_url):
         return json_v2id.TokenClient(
-            auth_url, disable_ssl_certificate_validation=self.dsvm,
+            auth_url, disable_ssl_certificate_validation=self.dscv,
             ca_certs=self.ca_certs, trace_requests=self.trace_requests)
 
     def _auth_params(self):
@@ -428,7 +428,7 @@
 
     def _auth_client(self, auth_url):
         return json_v3id.V3TokenClient(
-            auth_url, disable_ssl_certificate_validation=self.dsvm,
+            auth_url, disable_ssl_certificate_validation=self.dscv,
             ca_certs=self.ca_certs, trace_requests=self.trace_requests)
 
     def _auth_params(self):
@@ -532,6 +532,7 @@
             endpoint_type = endpoint_type.replace('URL', '')
         _base_url = None
         catalog = _auth_data.get('catalog', [])
+
         # Select entries with matching service type
         service_catalog = [ep for ep in catalog if ep['type'] == service]
         if len(service_catalog) > 0:
@@ -549,10 +550,20 @@
                 # NOTE(andreaf) If there's no catalog at all and the service
                 # is identity, it's a valid use case. Having a non-empty
                 # catalog with no identity in it is not valid instead.
+                msg = ('Got an empty catalog. Scope: %s. '
+                       'Falling back to configured URL for %s: %s')
+                LOG.debug(msg, self.scope, service, self.auth_url)
                 return apply_url_filters(self.auth_url, filters)
             else:
                 # No matching service
-                raise exceptions.EndpointNotFound(service)
+                msg = ('No matching service found in the catalog.\n'
+                       'Scope: %s, Credentials: %s\n'
+                       'Auth data: %s\n'
+                       'Service: %s, Region: %s, endpoint_type: %s\n'
+                       'Catalog: %s')
+                raise exceptions.EndpointNotFound(msg % (
+                    self.scope, self.credentials, _auth_data, service, region,
+                    endpoint_type, catalog))
         # Filter by endpoint type (interface)
         filtered_catalog = [ep for ep in service_catalog if
                             ep['interface'] == endpoint_type]
@@ -620,9 +631,9 @@
     creds = credential_class(**kwargs)
     # Fill in the credentials fields that were not specified
     if fill_in:
-        dsvm = disable_ssl_certificate_validation
+        dscv = disable_ssl_certificate_validation
         auth_provider = auth_provider_class(
-            creds, auth_url, disable_ssl_certificate_validation=dsvm,
+            creds, auth_url, disable_ssl_certificate_validation=dscv,
             ca_certs=ca_certs, trace_requests=trace_requests)
         creds = auth_provider.fill_credentials()
     return creds
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
index 45e5067..f9f0c83 100644
--- a/tempest/lib/common/utils/data_utils.py
+++ b/tempest/lib/common/utils/data_utils.py
@@ -19,6 +19,7 @@
 import string
 import uuid
 
+from oslo_utils import netutils
 import six.moves
 
 
@@ -183,7 +184,7 @@
     :rtype: netaddr.IPAddress
     """
     # Check if the prefix is IPv4 address
-    is_ipv4 = netaddr.valid_ipv4(cidr)
+    is_ipv4 = netutils.is_valid_ipv4(cidr)
     if is_ipv4:
         msg = "Unable to generate IP address by EUI64 for IPv4 prefix"
         raise TypeError(msg)
diff --git a/tempest/manager.py b/tempest/manager.py
index cafa5b9..72762ab 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -13,7 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common import cred_provider
 from tempest import config
 from tempest import exceptions
 from tempest.lib import auth
@@ -34,7 +33,7 @@
         Credentials to be used within the various client classes managed by the
         Manager object must be defined.
 
-        :param credentials: type Credentials or TestResources
+        :param credentials: An instance of `auth.Credentials`
         :param scope: default scope for tokens produced by the auth provider
         """
         self.credentials = credentials
@@ -42,15 +41,9 @@
         if not self.credentials.is_valid():
             raise exceptions.InvalidCredentials()
         self.auth_version = CONF.identity.auth_version
-        # Tenant isolation creates TestResources, but
-        # PreProvisionedCredentialProvider and some tests create Credentials
-        if isinstance(credentials, cred_provider.TestResources):
-            creds = self.credentials.credentials
-        else:
-            creds = self.credentials
         # Creates an auth provider for the credentials
-        self.auth_provider = get_auth_provider(creds, pre_auth=True,
-                                               scope=scope)
+        self.auth_provider = get_auth_provider(
+            self.credentials, pre_auth=True, scope=scope)
 
 
 def get_auth_provider_class(credentials):
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 1dfa86d..6994b9c 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -19,6 +19,7 @@
 import netaddr
 from oslo_log import log
 from oslo_serialization import jsonutils as json
+from oslo_utils import netutils
 import six
 
 from tempest.common import compute
@@ -834,7 +835,7 @@
         port_map = [(p["id"], fxip["ip_address"])
                     for p in ports
                     for fxip in p["fixed_ips"]
-                    if netaddr.valid_ipv4(fxip["ip_address"])
+                    if netutils.is_valid_ipv4(fxip["ip_address"])
                     and p['status'] in p_status]
         inactive = [p for p in ports if p['status'] != 'ACTIVE']
         if inactive:
diff --git a/tempest/scenario/utils.py b/tempest/scenario/utils.py
index 75fd000..c7ba659 100644
--- a/tempest/scenario/utils.py
+++ b/tempest/scenario/utils.py
@@ -107,7 +107,8 @@
             name='InputScenarioUtils',
             identity_version=CONF.identity.auth_version,
             network_resources=network_resources)
-        os = clients.Manager(self.cred_provider.get_primary_creds())
+        os = clients.Manager(
+            self.cred_provider.get_primary_creds().credentials)
         self.compute_images_client = os.compute_images_client
         self.flavors_client = os.flavors_client
         self.image_pattern = CONF.input_scenario.image_regex
diff --git a/tempest/test.py b/tempest/test.py
index d31c509..4c0d0bb 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -541,7 +541,8 @@
             else:
                 raise exceptions.InvalidCredentials(
                     "Invalid credentials type %s" % credential_type)
-        return cls.client_manager(credentials=creds, service=cls._service)
+        return cls.client_manager(credentials=creds.credentials,
+                                  service=cls._service)
 
     @classmethod
     def clear_credentials(cls):
@@ -640,7 +641,7 @@
                 credentials.is_admin_available(
                     identity_version=cls.get_identity_version())):
             admin_creds = cred_provider.get_admin_creds()
-            admin_manager = clients.Manager(admin_creds)
+            admin_manager = clients.Manager(admin_creds.credentials)
             networks_client = admin_manager.compute_networks_client
         return fixed_network.get_tenant_network(
             cred_provider, networks_client, CONF.compute.fixed_network_name)
diff --git a/tempest/tests/cmd/test_account_generator.py b/tempest/tests/cmd/test_account_generator.py
new file mode 100755
index 0000000..f253802
--- /dev/null
+++ b/tempest/tests/cmd/test_account_generator.py
@@ -0,0 +1,346 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import fixtures
+import mock
+from oslo_config import cfg
+
+from tempest.cmd import account_generator
+from tempest import config
+from tempest.tests import base
+from tempest.tests import fake_config
+from tempest.tests.lib import fake_identity
+
+
+class FakeOpts(object):
+
+    def __init__(self, version=3):
+        self.os_username = 'fake_user'
+        self.os_password = 'fake_password'
+        self.os_project_name = 'fake_project_name'
+        self.os_tenant_name = None
+        self.os_domain_name = 'fake_domain'
+        self.tag = 'fake'
+        self.concurrency = 2
+        self.with_admin = True
+        self.identity_version = version
+        self.accounts = 'fake_accounts.yml'
+
+
+class MockHelpersMixin(object):
+
+    def mock_config_and_opts(self, identity_version):
+        self.useFixture(fake_config.ConfigFixture())
+        self.patchobject(config, 'TempestConfigPrivate',
+                         fake_config.FakePrivate)
+        self.opts = FakeOpts(version=identity_version)
+
+    def mock_resource_creation(self):
+        fake_resource = dict(id='id', name='name')
+        self.user_create_fixture = self.useFixture(fixtures.MockPatch(
+            self.cred_client + '.create_user', return_value=fake_resource))
+        self.useFixture(fixtures.MockPatch(
+            self.cred_client + '.create_project',
+            return_value=fake_resource))
+        self.useFixture(fixtures.MockPatch(
+            self.cred_client + '.assign_user_role'))
+        self.useFixture(fixtures.MockPatch(
+            self.cred_client + '._check_role_exists',
+            return_value=fake_resource))
+        self.useFixture(fixtures.MockPatch(
+            self.dynamic_creds + '._create_network',
+            return_value=fake_resource))
+        self.useFixture(fixtures.MockPatch(
+            self.dynamic_creds + '._create_subnet',
+            return_value=fake_resource))
+        self.useFixture(fixtures.MockPatch(
+            self.dynamic_creds + '._create_router',
+            return_value=fake_resource))
+        self.useFixture(fixtures.MockPatch(
+            self.dynamic_creds + '._add_router_interface',
+            return_value=fake_resource))
+
+    def mock_domains(self):
+        fake_domain_list = {'domains': [{'id': 'fake_domain',
+                                         'name': 'Fake_Domain'}]}
+        self.useFixture(fixtures.MockPatch(''.join([
+            'tempest.services.identity.v3.json.domains_client.'
+            'DomainsClient.list_domains']),
+            return_value=fake_domain_list))
+        self.useFixture(fixtures.MockPatch(
+            self.cred_client + '.assign_user_role_on_domain'))
+
+
+class TestAccountGeneratorV2(base.TestCase, MockHelpersMixin):
+
+    identity_version = 2
+    identity_response = fake_identity._fake_v2_response
+
+    def setUp(self):
+        super(TestAccountGeneratorV2, self).setUp()
+        self.mock_config_and_opts(self.identity_version)
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.auth.AuthProvider.set_auth',
+            return_value=self.identity_response))
+
+    def test_get_credential_provider(self):
+        cp = account_generator.get_credential_provider(self.opts)
+        admin_creds = cp.default_admin_creds
+        self.assertEqual(self.opts.tag, cp.name)
+        self.assertIn(str(self.opts.identity_version), cp.identity_version)
+        self.assertEqual(self.opts.os_username, admin_creds.username)
+        self.assertEqual(self.opts.os_project_name, admin_creds.tenant_name)
+        self.assertEqual(self.opts.os_password, admin_creds.password)
+        self.assertFalse(hasattr(admin_creds, 'domain_name'))
+
+    def test_get_credential_provider_with_tenant(self):
+        self.opts.os_project_name = None
+        self.opts.os_tenant_name = 'fake_tenant'
+        cp = account_generator.get_credential_provider(self.opts)
+        admin_creds = cp.default_admin_creds
+        self.assertEqual(self.opts.os_tenant_name, admin_creds.tenant_name)
+
+
+class TestAccountGeneratorV3(TestAccountGeneratorV2):
+
+    identity_version = 3
+    identity_response = fake_identity._fake_v3_response
+
+    def setUp(self):
+        super(TestAccountGeneratorV3, self).setUp()
+        fake_domain_list = {'domains': [{'id': 'fake_domain'}]}
+        self.useFixture(fixtures.MockPatch(''.join([
+            'tempest.services.identity.v3.json.domains_client.'
+            'DomainsClient.list_domains']),
+            return_value=fake_domain_list))
+
+    def test_get_credential_provider(self):
+        cp = account_generator.get_credential_provider(self.opts)
+        admin_creds = cp.default_admin_creds
+        self.assertEqual(self.opts.tag, cp.name)
+        self.assertIn(str(self.opts.identity_version), cp.identity_version)
+        self.assertEqual(self.opts.os_username, admin_creds.username)
+        self.assertEqual(self.opts.os_project_name, admin_creds.tenant_name)
+        self.assertEqual(self.opts.os_password, admin_creds.password)
+        self.assertEqual(self.opts.os_domain_name, admin_creds.domain_name)
+
+    def test_get_credential_provider_without_domain(self):
+        self.opts.os_domain_name = None
+        cp = account_generator.get_credential_provider(self.opts)
+        admin_creds = cp.default_admin_creds
+        self.assertIsNotNone(admin_creds.domain_name)
+
+
+class TestGenerateResourcesV2(base.TestCase, MockHelpersMixin):
+
+    identity_version = 2
+    identity_response = fake_identity._fake_v2_response
+    cred_client = 'tempest.common.cred_client.V2CredsClient'
+    dynamic_creds = 'tempest.common.dynamic_creds.DynamicCredentialProvider'
+
+    def setUp(self):
+        super(TestGenerateResourcesV2, self).setUp()
+        self.mock_config_and_opts(self.identity_version)
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.auth.AuthProvider.set_auth',
+            return_value=self.identity_response))
+        self.cred_provider = account_generator.get_credential_provider(
+            self.opts)
+        self.mock_resource_creation()
+
+    def test_generate_resources_no_admin(self):
+        cfg.CONF.set_default('swift', False, group='service_available')
+        cfg.CONF.set_default('heat', False, group='service_available')
+        cfg.CONF.set_default('operator_role', 'fake_operator',
+                             group='object-storage')
+        cfg.CONF.set_default('reseller_admin_role', 'fake_reseller',
+                             group='object-storage')
+        cfg.CONF.set_default('stack_owner_role', 'fake_owner',
+                             group='orchestration')
+        resources = account_generator.generate_resources(
+            self.cred_provider, admin=False)
+        resource_types = [k for k, _ in resources]
+        # No admin, no heat, no swift, expect two credentials only
+        self.assertEqual(2, len(resources))
+        # Ensure create_user was invoked twice (two distinct users)
+        self.assertEqual(2, self.user_create_fixture.mock.call_count)
+        self.assertIn('primary', resource_types)
+        self.assertIn('alt', resource_types)
+        self.assertNotIn('admin', resource_types)
+        self.assertNotIn(['fake_operator'], resource_types)
+        self.assertNotIn(['fake_reseller'], resource_types)
+        self.assertNotIn(['fake_owner'], resource_types)
+        for resource in resources:
+            self.assertIsNotNone(resource[1].network)
+            self.assertIsNotNone(resource[1].router)
+            self.assertIsNotNone(resource[1].subnet)
+
+    def test_generate_resources_admin(self):
+        cfg.CONF.set_default('swift', False, group='service_available')
+        cfg.CONF.set_default('heat', False, group='service_available')
+        cfg.CONF.set_default('operator_role', 'fake_operator',
+                             group='object-storage')
+        cfg.CONF.set_default('reseller_admin_role', 'fake_reseller',
+                             group='object-storage')
+        cfg.CONF.set_default('stack_owner_role', 'fake_owner',
+                             group='orchestration')
+        resources = account_generator.generate_resources(
+            self.cred_provider, admin=True)
+        resource_types = [k for k, _ in resources]
+        # Admin, no heat, no swift, expect three credentials only
+        self.assertEqual(3, len(resources))
+        # Ensure create_user was invoked 3 times (3 distinct users)
+        self.assertEqual(3, self.user_create_fixture.mock.call_count)
+        self.assertIn('primary', resource_types)
+        self.assertIn('alt', resource_types)
+        self.assertIn('admin', resource_types)
+        self.assertNotIn(['fake_operator'], resource_types)
+        self.assertNotIn(['fake_reseller'], resource_types)
+        self.assertNotIn(['fake_owner'], resource_types)
+        for resource in resources:
+            self.assertIsNotNone(resource[1].network)
+            self.assertIsNotNone(resource[1].router)
+            self.assertIsNotNone(resource[1].subnet)
+
+    def test_generate_resources_swift_heat_admin(self):
+        cfg.CONF.set_default('swift', True, group='service_available')
+        cfg.CONF.set_default('heat', True, group='service_available')
+        cfg.CONF.set_default('operator_role', 'fake_operator',
+                             group='object-storage')
+        cfg.CONF.set_default('reseller_admin_role', 'fake_reseller',
+                             group='object-storage')
+        cfg.CONF.set_default('stack_owner_role', 'fake_owner',
+                             group='orchestration')
+        resources = account_generator.generate_resources(
+            self.cred_provider, admin=True)
+        resource_types = [k for k, _ in resources]
+        # all options on, expect six credentials
+        self.assertEqual(6, len(resources))
+        # Ensure create_user was invoked 6 times (6 distinct users)
+        self.assertEqual(6, self.user_create_fixture.mock.call_count)
+        self.assertIn('primary', resource_types)
+        self.assertIn('alt', resource_types)
+        self.assertIn('admin', resource_types)
+        self.assertIn(['fake_operator'], resource_types)
+        self.assertIn(['fake_reseller'], resource_types)
+        self.assertIn(['fake_owner'], resource_types)
+        for resource in resources:
+            self.assertIsNotNone(resource[1].network)
+            self.assertIsNotNone(resource[1].router)
+            self.assertIsNotNone(resource[1].subnet)
+
+
+class TestGenerateResourcesV3(TestGenerateResourcesV2):
+
+    identity_version = 3
+    identity_response = fake_identity._fake_v3_response
+    cred_client = 'tempest.common.cred_client.V3CredsClient'
+
+    def setUp(self):
+        self.mock_domains()
+        super(TestGenerateResourcesV3, self).setUp()
+
+
+class TestDumpAccountsV2(base.TestCase, MockHelpersMixin):
+
+    identity_version = 2
+    identity_response = fake_identity._fake_v2_response
+    cred_client = 'tempest.common.cred_client.V2CredsClient'
+    dynamic_creds = 'tempest.common.dynamic_creds.DynamicCredentialProvider'
+    domain_is_in = False
+
+    def setUp(self):
+        super(TestDumpAccountsV2, self).setUp()
+        self.mock_config_and_opts(self.identity_version)
+        self.useFixture(fixtures.MockPatch(
+            'tempest.lib.auth.AuthProvider.set_auth',
+            return_value=self.identity_response))
+        self.cred_provider = account_generator.get_credential_provider(
+            self.opts)
+        self.mock_resource_creation()
+        cfg.CONF.set_default('swift', True, group='service_available')
+        cfg.CONF.set_default('heat', True, group='service_available')
+        self.resources = account_generator.generate_resources(
+            self.cred_provider, admin=True)
+
+    def test_dump_accounts(self):
+        self.useFixture(fixtures.MockPatch('os.path.exists',
+                                           return_value=False))
+        mocked_open = mock.mock_open()
+        with mock.patch('{}.open'.format(account_generator.__name__),
+                        mocked_open, create=True):
+            with mock.patch('yaml.safe_dump') as yaml_dump_mock:
+                account_generator.setup_logging()
+                account_generator.dump_accounts(self.resources,
+                                                self.opts.identity_version,
+                                                self.opts.accounts)
+        mocked_open.assert_called_once_with(self.opts.accounts, 'w')
+        handle = mocked_open()
+        # Ordered args in [0], keyword args in [1]
+        accounts, f = yaml_dump_mock.call_args[0]
+        self.assertEqual(handle, f)
+        self.assertEqual(6, len(accounts))
+        if self.domain_is_in:
+            self.assertIn('domain_name', accounts[0].keys())
+        else:
+            self.assertNotIn('domain_name', accounts[0].keys())
+        self.assertEqual(1, len([x for x in accounts if
+                                 x.get('types') == ['admin']]))
+        self.assertEqual(3, len([x for x in accounts if 'roles' in x]))
+        for account in accounts:
+            self.assertIn('resources', account)
+            self.assertIn('network', account.get('resources'))
+
+    def test_dump_accounts_existing_file(self):
+        self.useFixture(fixtures.MockPatch('os.path.exists',
+                                           return_value=True))
+        rename_mock = self.useFixture(fixtures.MockPatch('os.rename')).mock
+        backup_file = '.'.join((self.opts.accounts, 'bak'))
+        mocked_open = mock.mock_open()
+        with mock.patch('{}.open'.format(account_generator.__name__),
+                        mocked_open, create=True):
+            with mock.patch('yaml.safe_dump') as yaml_dump_mock:
+                account_generator.setup_logging()
+                account_generator.dump_accounts(self.resources,
+                                                self.opts.identity_version,
+                                                self.opts.accounts)
+        rename_mock.assert_called_once_with(self.opts.accounts, backup_file)
+        mocked_open.assert_called_once_with(self.opts.accounts, 'w')
+        handle = mocked_open()
+        # Ordered args in [0], keyword args in [1]
+        accounts, f = yaml_dump_mock.call_args[0]
+        self.assertEqual(handle, f)
+        self.assertEqual(6, len(accounts))
+        if self.domain_is_in:
+            self.assertIn('domain_name', accounts[0].keys())
+        else:
+            self.assertNotIn('domain_name', accounts[0].keys())
+        self.assertEqual(1, len([x for x in accounts if
+                                 x.get('types') == ['admin']]))
+        self.assertEqual(3, len([x for x in accounts if 'roles' in x]))
+        for account in accounts:
+            self.assertIn('resources', account)
+            self.assertIn('network', account.get('resources'))
+
+
+class TestDumpAccountsV3(TestDumpAccountsV2):
+
+    identity_version = 3
+    identity_response = fake_identity._fake_v3_response
+    cred_client = 'tempest.common.cred_client.V3CredsClient'
+    domain_is_in = True
+
+    def setUp(self):
+        self.mock_domains()
+        super(TestDumpAccountsV3, self).setUp()
diff --git a/tempest/tests/test_base_test.py b/tempest/tests/test_base_test.py
index dc355b4..01b8a72 100644
--- a/tempest/tests/test_base_test.py
+++ b/tempest/tests/test_base_test.py
@@ -66,7 +66,7 @@
         test.BaseTestCase.get_tenant_network()
 
         mock_man.assert_called_once_with(
-            mock_prov.get_admin_creds.return_value)
+            mock_prov.get_admin_creds.return_value.credentials)
         mock_iaa.assert_called_once_with(
             identity_version=mock_giv.return_value)
         mock_gcp.assert_called_once_with()