Separate projects client from identity V3 client

Partially implements blueprint consistent-service-method-names

Change-Id: I236b2468de81539ae06937c2644c9840bea68b96
diff --git a/tempest/api/identity/admin/v3/test_credentials.py b/tempest/api/identity/admin/v3/test_credentials.py
index b81bff7..0e76d37 100644
--- a/tempest/api/identity/admin/v3/test_credentials.py
+++ b/tempest/api/identity/admin/v3/test_credentials.py
@@ -31,7 +31,7 @@
         u_email = '%s@testmail.tm' % u_name
         u_password = data_utils.rand_password()
         for i in range(2):
-            cls.project = cls.client.create_project(
+            cls.project = cls.projects_client.create_project(
                 data_utils.rand_name('project'),
                 description=data_utils.rand_name('project-desc'))['project']
             cls.projects.append(cls.project['id'])
@@ -44,7 +44,7 @@
     def resource_cleanup(cls):
         cls.client.delete_user(cls.user_body['id'])
         for p in cls.projects:
-            cls.client.delete_project(p)
+            cls.projects_client.delete_project(p)
         super(CredentialsTestJSON, cls).resource_cleanup()
 
     def _delete_credential(self, cred_id):
diff --git a/tempest/api/identity/admin/v3/test_default_project_id.py b/tempest/api/identity/admin/v3/test_default_project_id.py
index 53861ca..661adb8 100644
--- a/tempest/api/identity/admin/v3/test_default_project_id.py
+++ b/tempest/api/identity/admin/v3/test_default_project_id.py
@@ -45,10 +45,10 @@
 
         # create a project in the domain
         proj_name = data_utils.rand_name('proj')
-        proj_body = self.client.create_project(proj_name,
-                                               domain_id=dom_id)['project']
+        proj_body = self.projects_client.create_project(
+            proj_name, domain_id=dom_id)['project']
         proj_id = proj_body['id']
-        self.addCleanup(self.client.delete_project, proj_id)
+        self.addCleanup(self.projects_client.delete_project, proj_id)
         self.assertEqual(proj_body['domain_id'], dom_id,
                          "project " + proj_name +
                          "doesn't have domain id " + dom_id)
diff --git a/tempest/api/identity/admin/v3/test_list_projects.py b/tempest/api/identity/admin/v3/test_list_projects.py
index aaed467..928437c 100644
--- a/tempest/api/identity/admin/v3/test_list_projects.py
+++ b/tempest/api/identity/admin/v3/test_list_projects.py
@@ -27,24 +27,24 @@
         cls.data.setup_test_domain()
         # Create project with domain
         cls.p1_name = data_utils.rand_name('project')
-        cls.p1 = cls.client.create_project(
+        cls.p1 = cls.projects_client.create_project(
             cls.p1_name, enabled=False,
             domain_id=cls.data.domain['id'])['project']
         cls.data.projects.append(cls.p1)
         cls.project_ids.append(cls.p1['id'])
         # Create default project
         p2_name = data_utils.rand_name('project')
-        cls.p2 = cls.client.create_project(p2_name)['project']
+        cls.p2 = cls.projects_client.create_project(p2_name)['project']
         cls.data.projects.append(cls.p2)
         cls.project_ids.append(cls.p2['id'])
 
     @test.idempotent_id('1d830662-22ad-427c-8c3e-4ec854b0af44')
     def test_projects_list(self):
         # List projects
-        list_projects = self.client.list_projects()['projects']
+        list_projects = self.projects_client.list_projects()['projects']
 
         for p in self.project_ids:
-            show_project = self.client.show_project(p)['project']
+            show_project = self.projects_client.show_project(p)['project']
             self.assertIn(show_project, list_projects)
 
     @test.idempotent_id('fab13f3c-f6a6-4b9f-829b-d32fd44fdf10')
@@ -64,6 +64,6 @@
         self._list_projects_with_params({'name': self.p1_name}, 'name')
 
     def _list_projects_with_params(self, params, key):
-        body = self.client.list_projects(params)['projects']
+        body = self.projects_client.list_projects(params)['projects']
         self.assertIn(self.p1[key], map(lambda x: x[key], body))
         self.assertNotIn(self.p2[key], map(lambda x: x[key], body))
diff --git a/tempest/api/identity/admin/v3/test_projects.py b/tempest/api/identity/admin/v3/test_projects.py
index 2f4cc51..92f5a40 100644
--- a/tempest/api/identity/admin/v3/test_projects.py
+++ b/tempest/api/identity/admin/v3/test_projects.py
@@ -25,14 +25,14 @@
         # Create project with a description
         project_name = data_utils.rand_name('project')
         project_desc = data_utils.rand_name('desc')
-        project = self.client.create_project(
+        project = self.projects_client.create_project(
             project_name, description=project_desc)['project']
         self.data.projects.append(project)
         project_id = project['id']
         desc1 = project['description']
         self.assertEqual(desc1, project_desc, 'Description should have '
                          'been sent in response for create')
-        body = self.client.show_project(project_id)['project']
+        body = self.projects_client.show_project(project_id)['project']
         desc2 = body['description']
         self.assertEqual(desc2, project_desc, 'Description does not appear'
                          'to be set')
@@ -42,13 +42,13 @@
         # Create project with a domain
         self.data.setup_test_domain()
         project_name = data_utils.rand_name('project')
-        project = self.client.create_project(
+        project = self.projects_client.create_project(
             project_name, domain_id=self.data.domain['id'])['project']
         self.data.projects.append(project)
         project_id = project['id']
         self.assertEqual(project_name, project['name'])
         self.assertEqual(self.data.domain['id'], project['domain_id'])
-        body = self.client.show_project(project_id)['project']
+        body = self.projects_client.show_project(project_id)['project']
         self.assertEqual(project_name, body['name'])
         self.assertEqual(self.data.domain['id'], body['domain_id'])
 
@@ -56,13 +56,13 @@
     def test_project_create_enabled(self):
         # Create a project that is enabled
         project_name = data_utils.rand_name('project')
-        project = self.client.create_project(
+        project = self.projects_client.create_project(
             project_name, enabled=True)['project']
         self.data.projects.append(project)
         project_id = project['id']
         en1 = project['enabled']
         self.assertTrue(en1, 'Enable should be True in response')
-        body = self.client.show_project(project_id)['project']
+        body = self.projects_client.show_project(project_id)['project']
         en2 = body['enabled']
         self.assertTrue(en2, 'Enable should be True in lookup')
 
@@ -70,13 +70,13 @@
     def test_project_create_not_enabled(self):
         # Create a project that is not enabled
         project_name = data_utils.rand_name('project')
-        project = self.client.create_project(
+        project = self.projects_client.create_project(
             project_name, enabled=False)['project']
         self.data.projects.append(project)
         en1 = project['enabled']
         self.assertEqual('false', str(en1).lower(),
                          'Enable should be False in response')
-        body = self.client.show_project(project['id'])['project']
+        body = self.projects_client.show_project(project['id'])['project']
         en2 = body['enabled']
         self.assertEqual('false', str(en2).lower(),
                          'Enable should be False in lookup')
@@ -85,18 +85,18 @@
     def test_project_update_name(self):
         # Update name attribute of a project
         p_name1 = data_utils.rand_name('project')
-        project = self.client.create_project(p_name1)['project']
+        project = self.projects_client.create_project(p_name1)['project']
         self.data.projects.append(project)
 
         resp1_name = project['name']
 
         p_name2 = data_utils.rand_name('project2')
-        body = self.client.update_project(project['id'],
-                                          name=p_name2)['project']
+        body = self.projects_client.update_project(project['id'],
+                                                   name=p_name2)['project']
         resp2_name = body['name']
         self.assertNotEqual(resp1_name, resp2_name)
 
-        body = self.client.show_project(project['id'])['project']
+        body = self.projects_client.show_project(project['id'])['project']
         resp3_name = body['name']
 
         self.assertNotEqual(resp1_name, resp3_name)
@@ -108,18 +108,18 @@
         # Update description attribute of a project
         p_name = data_utils.rand_name('project')
         p_desc = data_utils.rand_name('desc')
-        project = self.client.create_project(
+        project = self.projects_client.create_project(
             p_name, description=p_desc)['project']
         self.data.projects.append(project)
         resp1_desc = project['description']
 
         p_desc2 = data_utils.rand_name('desc2')
-        body = self.client.update_project(
+        body = self.projects_client.update_project(
             project['id'], description=p_desc2)['project']
         resp2_desc = body['description']
         self.assertNotEqual(resp1_desc, resp2_desc)
 
-        body = self.client.show_project(project['id'])['project']
+        body = self.projects_client.show_project(project['id'])['project']
         resp3_desc = body['description']
 
         self.assertNotEqual(resp1_desc, resp3_desc)
@@ -131,18 +131,19 @@
         # Update the enabled attribute of a project
         p_name = data_utils.rand_name('project')
         p_en = False
-        project = self.client.create_project(p_name, enabled=p_en)['project']
+        project = self.projects_client.create_project(p_name,
+                                                      enabled=p_en)['project']
         self.data.projects.append(project)
 
         resp1_en = project['enabled']
 
         p_en2 = True
-        body = self.client.update_project(
-            project['id'], enabled=p_en2)['project']
+        body = self.projects_client.update_project(project['id'],
+                                                   enabled=p_en2)['project']
         resp2_en = body['enabled']
         self.assertNotEqual(resp1_en, resp2_en)
 
-        body = self.client.show_project(project['id'])['project']
+        body = self.projects_client.show_project(project['id'])['project']
         resp3_en = body['enabled']
 
         self.assertNotEqual(resp1_en, resp3_en)
@@ -154,7 +155,7 @@
         # Associate a user to a project
         # Create a Project
         p_name = data_utils.rand_name('project')
-        project = self.client.create_project(p_name)['project']
+        project = self.projects_client.create_project(p_name)['project']
         self.data.projects.append(project)
 
         # Create a User
diff --git a/tempest/api/identity/admin/v3/test_projects_negative.py b/tempest/api/identity/admin/v3/test_projects_negative.py
index 9b60d54..79cfc91 100644
--- a/tempest/api/identity/admin/v3/test_projects_negative.py
+++ b/tempest/api/identity/admin/v3/test_projects_negative.py
@@ -27,18 +27,18 @@
     def test_list_projects_by_unauthorized_user(self):
         # Non-admin user should not be able to list projects
         self.assertRaises(lib_exc.Forbidden,
-                          self.non_admin_client.list_projects)
+                          self.non_admin_projects_client.list_projects)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('874c3e84-d174-4348-a16b-8c01f599561b')
     def test_project_create_duplicate(self):
         # Project names should be unique
         project_name = data_utils.rand_name('project-dup')
-        project = self.client.create_project(project_name)['project']
+        project = self.projects_client.create_project(project_name)['project']
         self.data.projects.append(project)
 
-        self.assertRaises(
-            lib_exc.Conflict, self.client.create_project, project_name)
+        self.assertRaises(lib_exc.Conflict,
+                          self.projects_client.create_project, project_name)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('8fba9de2-3e1f-4e77-812a-60cb68f8df13')
@@ -46,38 +46,39 @@
         # Non-admin user should not be authorized to create a project
         project_name = data_utils.rand_name('project')
         self.assertRaises(
-            lib_exc.Forbidden, self.non_admin_client.create_project,
+            lib_exc.Forbidden, self.non_admin_projects_client.create_project,
             project_name)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('7828db17-95e5-475b-9432-9a51b4aa79a9')
     def test_create_project_with_empty_name(self):
         # Project name should not be empty
-        self.assertRaises(lib_exc.BadRequest, self.client.create_project,
-                          name='')
+        self.assertRaises(lib_exc.BadRequest,
+                          self.projects_client.create_project, name='')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('502b6ceb-b0c8-4422-bf53-f08fdb21e2f0')
     def test_create_projects_name_length_over_64(self):
         # Project name length should not be greater than 64 characters
         project_name = 'a' * 65
-        self.assertRaises(lib_exc.BadRequest, self.client.create_project,
-                          project_name)
+        self.assertRaises(lib_exc.BadRequest,
+                          self.projects_client.create_project, project_name)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('8d68c012-89e0-4394-8d6b-ccd7196def97')
     def test_project_delete_by_unauthorized_user(self):
         # Non-admin user should not be able to delete a project
         project_name = data_utils.rand_name('project')
-        project = self.client.create_project(project_name)['project']
+        project = self.projects_client.create_project(project_name)['project']
         self.data.projects.append(project)
         self.assertRaises(
-            lib_exc.Forbidden, self.non_admin_client.delete_project,
+            lib_exc.Forbidden, self.non_admin_projects_client.delete_project,
             project['id'])
 
     @test.attr(type=['negative'])
     @test.idempotent_id('7965b581-60c1-43b7-8169-95d4ab7fc6fb')
     def test_delete_non_existent_project(self):
         # Attempt to delete a non existent project should fail
-        self.assertRaises(lib_exc.NotFound, self.client.delete_project,
+        self.assertRaises(lib_exc.NotFound,
+                          self.projects_client.delete_project,
                           data_utils.rand_uuid_hex())
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index f1f06ee..f3cdd96 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -35,7 +35,7 @@
         cls.domain = cls.client.create_domain(
             data_utils.rand_name('domain'),
             description=data_utils.rand_name('domain-desc'))['domain']
-        cls.project = cls.client.create_project(
+        cls.project = cls.projects_client.create_project(
             data_utils.rand_name('project'),
             description=data_utils.rand_name('project-desc'),
             domain_id=cls.domain['id'])['project']
@@ -54,7 +54,7 @@
         cls.client.delete_role(cls.role['id'])
         cls.groups_client.delete_group(cls.group_body['id'])
         cls.client.delete_user(cls.user_body['id'])
-        cls.client.delete_project(cls.project['id'])
+        cls.projects_client.delete_project(cls.project['id'])
         # NOTE(harika-vakadi): It is necessary to disable the domain
         # before deleting,or else it would result in unauthorized error
         cls.client.update_domain(cls.domain['id'], enabled=False)
diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py
index b1446cf..3452d95 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -67,12 +67,14 @@
 
         # Create a couple projects
         project1_name = data_utils.rand_name(name='project')
-        project1 = self.client.create_project(project1_name)['project']
-        self.addCleanup(self.client.delete_project, project1['id'])
+        project1 = self.projects_client.create_project(
+            project1_name)['project']
+        self.addCleanup(self.projects_client.delete_project, project1['id'])
 
         project2_name = data_utils.rand_name(name='project')
-        project2 = self.client.create_project(project2_name)['project']
-        self.addCleanup(self.client.delete_project, project2['id'])
+        project2 = self.projects_client.create_project(
+            project2_name)['project']
+        self.addCleanup(self.projects_client.delete_project, project2['id'])
 
         # Create a role
         role_name = data_utils.rand_name(name='role')
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 2ffc596..5f44b8e 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -46,8 +46,8 @@
     def create_trustor_and_roles(self):
         # create a project that trusts will be granted on
         self.trustor_project_name = data_utils.rand_name(name='project')
-        project = self.client.create_project(self.trustor_project_name,
-                                             domain_id='default')['project']
+        project = self.projects_client.create_project(
+            self.trustor_project_name, domain_id='default')['project']
         self.trustor_project_id = project['id']
         self.assertIsNotNone(self.trustor_project_id)
 
@@ -103,7 +103,7 @@
         if self.trustor_user_id:
             self.client.delete_user(self.trustor_user_id)
         if self.trustor_project_id:
-            self.client.delete_project(self.trustor_project_id)
+            self.projects_client.delete_project(self.trustor_project_id)
         if self.delegated_role_id:
             self.client.delete_role(self.delegated_role_id)
         if self.not_delegated_role_id:
diff --git a/tempest/api/identity/admin/v3/test_users.py b/tempest/api/identity/admin/v3/test_users.py
index 7c0c223..75c8ca5 100644
--- a/tempest/api/identity/admin/v3/test_users.py
+++ b/tempest/api/identity/admin/v3/test_users.py
@@ -36,11 +36,11 @@
         # Delete the User at the end of this method
         self.addCleanup(self.client.delete_user, user['id'])
         # Creating second project for updation
-        project = self.client.create_project(
+        project = self.projects_client.create_project(
             data_utils.rand_name('project'),
             description=data_utils.rand_name('project-desc'))['project']
         # Delete the Project at the end of this method
-        self.addCleanup(self.client.delete_project, project['id'])
+        self.addCleanup(self.projects_client.delete_project, project['id'])
         # Updating user details with new values
         u_name2 = data_utils.rand_name('user2')
         u_email2 = u_name2 + '@testmail.tm'
@@ -100,11 +100,11 @@
         # List the projects that a user has access upon
         assigned_project_ids = list()
         fetched_project_ids = list()
-        u_project = self.client.create_project(
+        u_project = self.projects_client.create_project(
             data_utils.rand_name('project'),
             description=data_utils.rand_name('project-desc'))['project']
         # Delete the Project at the end of this method
-        self.addCleanup(self.client.delete_project, u_project['id'])
+        self.addCleanup(self.projects_client.delete_project, u_project['id'])
         # Create a user.
         u_name = data_utils.rand_name('user')
         u_desc = u_name + 'description'
@@ -125,12 +125,14 @@
         role = self.client.show_role(role_body['id'])['role']
         for i in range(2):
             # Creating project so as to assign role
-            project_body = self.client.create_project(
+            project_body = self.projects_client.create_project(
                 data_utils.rand_name('project'),
                 description=data_utils.rand_name('project-desc'))['project']
-            project = self.client.show_project(project_body['id'])['project']
+            project = self.projects_client.show_project(
+                project_body['id'])['project']
             # Delete the Project at the end of this method
-            self.addCleanup(self.client.delete_project, project_body['id'])
+            self.addCleanup(
+                self.projects_client.delete_project, project_body['id'])
             # Assigning roles to user on project
             self.client.assign_user_role(project['id'],
                                          user['id'],
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index d31569b..455a2c6 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -48,7 +48,7 @@
         try:
             tenants = cls.tenants_client.list_tenants()['tenants']
         except AttributeError:
-            tenants = cls.client.list_projects()['projects']
+            tenants = cls.projects_client.list_projects()['projects']
         tenant = [t for t in tenants if t['name'] == name]
         if len(tenant) > 0:
             return tenant[0]
@@ -130,6 +130,7 @@
         super(BaseIdentityV3Test, cls).setup_clients()
         cls.non_admin_client = cls.os.identity_v3_client
         cls.non_admin_token = cls.os.token_v3_client
+        cls.non_admin_projects_client = cls.os.projects_client
 
     @classmethod
     def resource_cleanup(cls):
@@ -151,11 +152,12 @@
         cls.policies_client = cls.os_adm.policies_client
         cls.creds_client = cls.os_adm.credentials_client
         cls.groups_client = cls.os_adm.groups_client
+        cls.projects_client = cls.os_adm.projects_client
 
     @classmethod
     def resource_setup(cls):
         super(BaseIdentityV3AdminTest, cls).resource_setup()
-        cls.data = DataGeneratorV3(cls.client)
+        cls.data = DataGeneratorV3(cls.client, cls.projects_client)
 
     @classmethod
     def resource_cleanup(cls):
@@ -170,13 +172,6 @@
             return user[0]
 
     @classmethod
-    def get_tenant_by_name(cls, name):
-        tenants = cls.client.list_projects()['projects']
-        tenant = [t for t in tenants if t['name'] == name]
-        if len(tenant) > 0:
-            return tenant[0]
-
-    @classmethod
     def get_role_by_name(cls, name):
         roles = cls.client.list_roles()['roles']
         role = [r for r in roles if r['name'] == name]
@@ -197,10 +192,10 @@
 
 class BaseDataGenerator(object):
 
-    def __init__(self, client, projects_client=None,
+    def __init__(self, client, projects_client,
                  users_client=None, roles_client=None):
         self.client = client
-        self.projects_client = projects_client or client
+        self.projects_client = projects_client
         self.users_client = users_client or client
         self.roles_client = roles_client or client
 
diff --git a/tempest/clients.py b/tempest/clients.py
index d617b0f..f53c2c1 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -119,6 +119,7 @@
 from tempest.services.identity.v3.json.identity_client import IdentityV3Client
 from tempest.services.identity.v3.json.policies_client import \
     PoliciesClient as PoliciesV3Client
+from tempest.services.identity.v3.json.projects_client import ProjectsClient
 from tempest.services.identity.v3.json.regions_client import \
     RegionsClient as RegionsV3Client
 from tempest.services.identity.v3.json.services_client import \
@@ -540,6 +541,7 @@
             self.auth_provider, **params_v3)
         self.policies_client = PoliciesV3Client(self.auth_provider,
                                                 **params_v3)
+        self.projects_client = ProjectsClient(self.auth_provider, **params_v3)
         self.regions_client = RegionsV3Client(self.auth_provider, **params_v3)
         self.credentials_client = CredentialsV3Client(self.auth_provider,
                                                       **params_v3)
diff --git a/tempest/common/cred_client.py b/tempest/common/cred_client.py
index 6df7eb2..e2309bf 100644
--- a/tempest/common/cred_client.py
+++ b/tempest/common/cred_client.py
@@ -31,13 +31,13 @@
      admin credentials used for generating credentials.
     """
 
-    def __init__(self, identity_client, projects_client=None,
+    def __init__(self, identity_client, projects_client,
                  roles_client=None, users_client=None):
         # The client implies version and credentials
         self.identity_client = identity_client
-        # this is temporary until the v3 project client is
-        # separated, then projects_client will become mandatory
-        self.projects_client = projects_client or identity_client
+        self.projects_client = projects_client
+        # this is temporary until v3 roles client and v3 users client are
+        # separated, then these clients will become mandatory
         self.roles_client = roles_client or identity_client
         self.users_client = users_client or identity_client
 
@@ -48,6 +48,9 @@
             user = user['user']
         return user
 
+    def delete_user(self, user_id):
+        self.users_client.delete_user(user_id)
+
     @abc.abstractmethod
     def create_project(self, name, description):
         pass
@@ -87,9 +90,6 @@
         """
         pass
 
-    def delete_user(self, user_id):
-        self.users_client.delete_user(user_id)
-
     def _list_roles(self):
         roles = self.roles_client.list_roles()['roles']
         return roles
@@ -109,6 +109,9 @@
             name=name, description=description)['tenant']
         return tenant
 
+    def delete_project(self, project_id):
+        self.projects_client.delete_tenant(project_id)
+
     def get_credentials(self, user, project, password):
         # User and project already include both ID and name here,
         # so there's no need to use the fill_in mode
@@ -120,14 +123,11 @@
             tenant_name=project['name'], tenant_id=project['id'],
             password=password)
 
-    def delete_project(self, project_id):
-        self.projects_client.delete_tenant(project_id)
-
 
 class V3CredsClient(CredsClient):
 
-    def __init__(self, identity_client, domain_name):
-        super(V3CredsClient, self).__init__(identity_client)
+    def __init__(self, identity_client, projects_client, domain_name):
+        super(V3CredsClient, self).__init__(identity_client, projects_client)
         try:
             # Domain names must be unique, in any case a list is returned,
             # selecting the first (and only) element
@@ -139,11 +139,14 @@
             raise lib_exc.InvalidCredentials(msg)
 
     def create_project(self, name, description):
-        project = self.identity_client.create_project(
+        project = self.projects_client.create_project(
             name=name, description=description,
             domain_id=self.creds_domain['id'])['project']
         return project
 
+    def delete_project(self, project_id):
+        self.projects_client.delete_project(project_id)
+
     def get_credentials(self, user, project, password):
         # User, project and domain already include both ID and name here,
         # so there's no need to use the fill_in mode.
@@ -157,16 +160,13 @@
             project_domain_id=self.creds_domain['id'],
             project_domain_name=self.creds_domain['name'])
 
-    def delete_project(self, project_id):
-        self.identity_client.delete_project(project_id)
-
     def _list_roles(self):
         roles = self.identity_client.list_roles()['roles']
         return roles
 
 
 def get_creds_client(identity_client,
-                     projects_client=None,
+                     projects_client,
                      roles_client=None,
                      users_client=None,
                      project_domain_name=None):
@@ -174,4 +174,5 @@
         return V2CredsClient(identity_client, projects_client, roles_client,
                              users_client)
     else:
-        return V3CredsClient(identity_client, project_domain_name)
+        return V3CredsClient(identity_client,
+                             projects_client, project_domain_name)
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index 8d3a24d..9e5e0dd 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -57,7 +57,8 @@
         self._creds = {}
         self.ports = []
         self.default_admin_creds = admin_creds
-        (self.identity_admin_client, self.tenants_admin_client,
+        (self.identity_admin_client,
+         self.tenants_admin_client,
          self.roles_admin_client,
          self.users_admin_client,
          self.network_admin_client,
@@ -93,9 +94,9 @@
                     os.subnets_client, os.ports_client,
                     os.security_groups_client)
         else:
-            return (os.identity_v3_client, None, None, None, os.network_client,
-                    os.networks_client, os.subnets_client, os.ports_client,
-                    os.security_groups_client)
+            return (os.identity_v3_client, os.projects_client, None, None,
+                    os.network_client, os.networks_client, os.subnets_client,
+                    os.ports_client, os.security_groups_client)
 
     def _create_creds(self, suffix="", admin=False, roles=None):
         """Create random credentials under the following schema.
diff --git a/tempest/services/identity/v3/json/identity_client.py b/tempest/services/identity/v3/json/identity_client.py
index 15f0577..252fad7 100644
--- a/tempest/services/identity/v3/json/identity_client.py
+++ b/tempest/services/identity/v3/json/identity_client.py
@@ -122,64 +122,6 @@
         self.expected_success(204, resp.status)
         return service_client.ResponseBody(resp, body)
 
-    def create_project(self, name, **kwargs):
-        """Creates a project."""
-        description = kwargs.get('description', None)
-        en = kwargs.get('enabled', True)
-        domain_id = kwargs.get('domain_id', 'default')
-        post_body = {
-            'description': description,
-            'domain_id': domain_id,
-            'enabled': en,
-            'name': name
-        }
-        post_body = json.dumps({'project': post_body})
-        resp, body = self.post('projects', post_body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def list_projects(self, params=None):
-        url = "projects"
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def update_project(self, project_id, **kwargs):
-        body = self.show_project(project_id)['project']
-        name = kwargs.get('name', body['name'])
-        desc = kwargs.get('description', body['description'])
-        en = kwargs.get('enabled', body['enabled'])
-        domain_id = kwargs.get('domain_id', body['domain_id'])
-        post_body = {
-            'id': project_id,
-            'name': name,
-            'description': desc,
-            'enabled': en,
-            'domain_id': domain_id,
-        }
-        post_body = json.dumps({'project': post_body})
-        resp, body = self.patch('projects/%s' % project_id, post_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def show_project(self, project_id):
-        """GET a Project."""
-        resp, body = self.get("projects/%s" % project_id)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def delete_project(self, project_id):
-        """Delete a project."""
-        resp, body = self.delete('projects/%s' % str(project_id))
-        self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
-
     def create_role(self, **kwargs):
         """Create a Role.
 
diff --git a/tempest/services/identity/v3/json/projects_client.py b/tempest/services/identity/v3/json/projects_client.py
new file mode 100644
index 0000000..2fa822f
--- /dev/null
+++ b/tempest/services/identity/v3/json/projects_client.py
@@ -0,0 +1,81 @@
+# Copyright 2013 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.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.common import service_client
+
+
+class ProjectsClient(service_client.ServiceClient):
+    api_version = "v3"
+
+    def create_project(self, name, **kwargs):
+        """Creates a project."""
+        description = kwargs.get('description', None)
+        en = kwargs.get('enabled', True)
+        domain_id = kwargs.get('domain_id', 'default')
+        post_body = {
+            'description': description,
+            'domain_id': domain_id,
+            'enabled': en,
+            'name': name
+        }
+        post_body = json.dumps({'project': post_body})
+        resp, body = self.post('projects', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def list_projects(self, params=None):
+        url = "projects"
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def update_project(self, project_id, **kwargs):
+        body = self.show_project(project_id)['project']
+        name = kwargs.get('name', body['name'])
+        desc = kwargs.get('description', body['description'])
+        en = kwargs.get('enabled', body['enabled'])
+        domain_id = kwargs.get('domain_id', body['domain_id'])
+        post_body = {
+            'id': project_id,
+            'name': name,
+            'description': desc,
+            'enabled': en,
+            'domain_id': domain_id,
+        }
+        post_body = json.dumps({'project': post_body})
+        resp, body = self.patch('projects/%s' % project_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def show_project(self, project_id):
+        """GET a Project."""
+        resp, body = self.get("projects/%s" % project_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def delete_project(self, project_id):
+        """Delete a project."""
+        resp, body = self.delete('projects/%s' % str(project_id))
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
diff --git a/tempest/stress/driver.py b/tempest/stress/driver.py
index 02cb901..1f9df5e 100644
--- a/tempest/stress/driver.py
+++ b/tempest/stress/driver.py
@@ -151,7 +151,7 @@
                     users_client = admin_manager.users_client
                 else:
                     identity_client = admin_manager.identity_v3_client
-                    projects_client = None
+                    projects_client = admin_manager.projects_client
                     roles_client = None
                     users_client = None
                 domain = (identity_client.auth_provider.credentials.
diff --git a/tempest/test.py b/tempest/test.py
index 9c04ea1..6a0095f 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -442,7 +442,7 @@
             users_client = self.os_admin.users_client
         else:
             client = self.os_admin.identity_v3_client
-            project_client = None
+            project_client = self.os_adm.projects_client
             roles_client = None
             users_client = None