Merge "[V3] Separating groups_client from identity_client"
diff --git a/tempest/api/identity/admin/v3/test_groups.py b/tempest/api/identity/admin/v3/test_groups.py
index 5ce6354..d5af4b4 100644
--- a/tempest/api/identity/admin/v3/test_groups.py
+++ b/tempest/api/identity/admin/v3/test_groups.py
@@ -24,21 +24,20 @@
     def test_group_create_update_get(self):
         name = data_utils.rand_name('Group')
         description = data_utils.rand_name('Description')
-        group = self.client.create_group(name,
-                                         description=description)['group']
-        self.addCleanup(self.client.delete_group, group['id'])
+        group = self.groups_client.create_group(
+            name, description=description)['group']
+        self.addCleanup(self.groups_client.delete_group, group['id'])
         self.assertEqual(group['name'], name)
         self.assertEqual(group['description'], description)
 
         new_name = data_utils.rand_name('UpdateGroup')
         new_desc = data_utils.rand_name('UpdateDescription')
-        updated_group = self.client.update_group(group['id'],
-                                                 name=new_name,
-                                                 description=new_desc)['group']
+        updated_group = self.groups_client.update_group(
+            group['id'], name=new_name, description=new_desc)['group']
         self.assertEqual(updated_group['name'], new_name)
         self.assertEqual(updated_group['description'], new_desc)
 
-        new_group = self.client.get_group(group['id'])['group']
+        new_group = self.groups_client.get_group(group['id'])['group']
         self.assertEqual(group['id'], new_group['id'])
         self.assertEqual(new_name, new_group['name'])
         self.assertEqual(new_desc, new_group['description'])
@@ -47,8 +46,8 @@
     @test.idempotent_id('1598521a-2f36-4606-8df9-30772bd51339')
     def test_group_users_add_list_delete(self):
         name = data_utils.rand_name('Group')
-        group = self.client.create_group(name)['group']
-        self.addCleanup(self.client.delete_group, group['id'])
+        group = self.groups_client.create_group(name)['group']
+        self.addCleanup(self.groups_client.delete_group, group['id'])
         # add user into group
         users = []
         for i in range(3):
@@ -56,16 +55,15 @@
             user = self.client.create_user(name)['user']
             users.append(user)
             self.addCleanup(self.client.delete_user, user['id'])
-            self.client.add_group_user(group['id'], user['id'])
+            self.groups_client.add_group_user(group['id'], user['id'])
 
         # list users in group
-        group_users = self.client.list_group_users(group['id'])['users']
+        group_users = self.groups_client.list_group_users(group['id'])['users']
         self.assertEqual(sorted(users), sorted(group_users))
         # delete user in group
         for user in users:
-            self.client.delete_group_user(group['id'],
-                                          user['id'])
-        group_users = self.client.list_group_users(group['id'])['users']
+            self.groups_client.delete_group_user(group['id'], user['id'])
+        group_users = self.groups_client.list_group_users(group['id'])['users']
         self.assertEqual(len(group_users), 0)
 
     @test.idempotent_id('64573281-d26a-4a52-b899-503cb0f4e4ec')
@@ -79,10 +77,10 @@
         groups = []
         for i in range(2):
             name = data_utils.rand_name('Group')
-            group = self.client.create_group(name)['group']
+            group = self.groups_client.create_group(name)['group']
             groups.append(group)
-            self.addCleanup(self.client.delete_group, group['id'])
-            self.client.add_group_user(group['id'], user['id'])
+            self.addCleanup(self.groups_client.delete_group, group['id'])
+            self.groups_client.add_group_user(group['id'], user['id'])
         # list groups which user belongs to
         user_groups = self.client.list_user_groups(user['id'])['groups']
         self.assertEqual(sorted(groups), sorted(user_groups))
@@ -96,12 +94,12 @@
         for _ in range(3):
             name = data_utils.rand_name('Group')
             description = data_utils.rand_name('Description')
-            group = self.client.create_group(name,
-                                             description=description)['group']
-            self.addCleanup(self.client.delete_group, group['id'])
+            group = self.groups_client.create_group(
+                name, description=description)['group']
+            self.addCleanup(self.groups_client.delete_group, group['id'])
             group_ids.append(group['id'])
         # List and Verify Groups
-        body = self.client.list_groups()['groups']
+        body = self.groups_client.list_groups()['groups']
         for g in body:
             fetched_ids.append(g['id'])
         missing_groups = [g for g in group_ids if g not in fetched_ids]
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index 8595dde..524340c 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -39,7 +39,7 @@
             data_utils.rand_name('project'),
             description=data_utils.rand_name('project-desc'),
             domain_id=cls.domain['id'])['project']
-        cls.group_body = cls.client.create_group(
+        cls.group_body = cls.groups_client.create_group(
             data_utils.rand_name('Group'), project_id=cls.project['id'],
             domain_id=cls.domain['id'])['group']
         cls.user_body = cls.client.create_user(
@@ -52,7 +52,7 @@
     @classmethod
     def resource_cleanup(cls):
         cls.client.delete_role(cls.role['id'])
-        cls.client.delete_group(cls.group_body['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'])
         # NOTE(harika-vakadi): It is necessary to disable the domain
@@ -137,8 +137,9 @@
         self._list_assertions(roles, self.fetched_role_ids,
                               self.role['id'])
         # Add user to group, and insure user has role on project
-        self.client.add_group_user(self.group_body['id'], self.user_body['id'])
-        self.addCleanup(self.client.delete_group_user,
+        self.groups_client.add_group_user(self.group_body['id'],
+                                          self.user_body['id'])
+        self.addCleanup(self.groups_client.delete_group_user,
                         self.group_body['id'], self.user_body['id'])
         body = self.token.auth(user_id=self.user_body['id'],
                                password=self.u_password,
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 6334531..c56f4fb 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -137,10 +137,12 @@
         cls.token = cls.os_adm.token_v3_client
         cls.endpoints_client = cls.os_adm.endpoints_client
         cls.region_client = cls.os_adm.region_client
-        cls.data = DataGenerator(cls.client)
         cls.service_client = cls.os_adm.service_client
         cls.policy_client = cls.os_adm.policy_client
         cls.creds_client = cls.os_adm.credentials_client
+        cls.groups_client = cls.os_adm.groups_client
+
+        cls.data = DataGenerator(cls.client)
 
     @classmethod
     def resource_cleanup(cls):
diff --git a/tempest/clients.py b/tempest/clients.py
index d9e624a..e82a159 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -94,6 +94,7 @@
     CredentialsClient
 from tempest.services.identity.v3.json.endpoints_client import \
     EndPointClient
+from tempest.services.identity.v3.json.groups_client import GroupsClient
 from tempest.services.identity.v3.json.identity_client import \
     IdentityV3Client
 from tempest.services.identity.v3.json.policy_client import PolicyClient
@@ -408,6 +409,7 @@
         self.region_client = RegionClient(self.auth_provider, **params)
         self.credentials_client = CredentialsClient(self.auth_provider,
                                                     **params)
+        self.groups_client = GroupsClient(self.auth_provider, **params_v3)
         # Token clients do not use the catalog. They only need default_params.
         # They read auth_url, so they should only be set if the corresponding
         # API version is marked as enabled
diff --git a/tempest/hacking/ignored_list_T110.txt b/tempest/hacking/ignored_list_T110.txt
index 7c3e830..d671616 100644
--- a/tempest/hacking/ignored_list_T110.txt
+++ b/tempest/hacking/ignored_list_T110.txt
@@ -1,6 +1,7 @@
 ./tempest/services/compute/json/servers_client.py
 ./tempest/services/database/json/flavors_client.py
 ./tempest/services/identity/v3/json/credentials_client.py
+./tempest/services/identity/v3/json/groups_client.py
 ./tempest/services/identity/v3/json/identity_client.py
 ./tempest/services/identity/v3/json/policy_client.py
 ./tempest/services/identity/v3/json/region_client.py
diff --git a/tempest/services/identity/v3/json/groups_client.py b/tempest/services/identity/v3/json/groups_client.py
new file mode 100644
index 0000000..5bd610d
--- /dev/null
+++ b/tempest/services/identity/v3/json/groups_client.py
@@ -0,0 +1,94 @@
+# 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 tempest.common import service_client
+
+
+class GroupsClient(service_client.ServiceClient):
+    api_version = "v3"
+
+    def create_group(self, name, **kwargs):
+        """Creates a group."""
+        description = kwargs.get('description', None)
+        domain_id = kwargs.get('domain_id', 'default')
+        project_id = kwargs.get('project_id', None)
+        post_body = {
+            'description': description,
+            'domain_id': domain_id,
+            'project_id': project_id,
+            'name': name
+        }
+        post_body = json.dumps({'group': post_body})
+        resp, body = self.post('groups', post_body)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def get_group(self, group_id):
+        """Get group details."""
+        resp, body = self.get('groups/%s' % group_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def list_groups(self):
+        """Lists the groups."""
+        resp, body = self.get('groups')
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def update_group(self, group_id, **kwargs):
+        """Updates a group."""
+        body = self.get_group(group_id)['group']
+        name = kwargs.get('name', body['name'])
+        description = kwargs.get('description', body['description'])
+        post_body = {
+            'name': name,
+            'description': description
+        }
+        post_body = json.dumps({'group': post_body})
+        resp, body = self.patch('groups/%s' % group_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def delete_group(self, group_id):
+        """Delete a group."""
+        resp, body = self.delete('groups/%s' % str(group_id))
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def add_group_user(self, group_id, user_id):
+        """Add user into group."""
+        resp, body = self.put('groups/%s/users/%s' % (group_id, user_id),
+                              None)
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
+
+    def list_group_users(self, group_id):
+        """List users in group."""
+        resp, body = self.get('groups/%s/users' % group_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return service_client.ResponseBody(resp, body)
+
+    def delete_group_user(self, group_id, user_id):
+        """Delete user in group."""
+        resp, body = self.delete('groups/%s/users/%s' % (group_id, user_id))
+        self.expected_success(204, resp.status)
+        return service_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v3/json/identity_client.py b/tempest/services/identity/v3/json/identity_client.py
index f7913d0..a26544e 100644
--- a/tempest/services/identity/v3/json/identity_client.py
+++ b/tempest/services/identity/v3/json/identity_client.py
@@ -299,72 +299,6 @@
         self.expected_success(204, resp.status)
         return service_client.ResponseBody(resp, body)
 
-    def create_group(self, name, **kwargs):
-        """Creates a group."""
-        description = kwargs.get('description', None)
-        domain_id = kwargs.get('domain_id', 'default')
-        project_id = kwargs.get('project_id', None)
-        post_body = {
-            'description': description,
-            'domain_id': domain_id,
-            'project_id': project_id,
-            'name': name
-        }
-        post_body = json.dumps({'group': post_body})
-        resp, body = self.post('groups', post_body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def get_group(self, group_id):
-        """Get group details."""
-        resp, body = self.get('groups/%s' % group_id)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def list_groups(self):
-        """Lists the groups."""
-        resp, body = self.get('groups')
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def update_group(self, group_id, **kwargs):
-        """Updates a group."""
-        body = self.get_group(group_id)['group']
-        name = kwargs.get('name', body['name'])
-        description = kwargs.get('description', body['description'])
-        post_body = {
-            'name': name,
-            'description': description
-        }
-        post_body = json.dumps({'group': post_body})
-        resp, body = self.patch('groups/%s' % group_id, post_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
-    def delete_group(self, group_id):
-        """Delete a group."""
-        resp, body = self.delete('groups/%s' % str(group_id))
-        self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def add_group_user(self, group_id, user_id):
-        """Add user into group."""
-        resp, body = self.put('groups/%s/users/%s' % (group_id, user_id),
-                              None)
-        self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
-
-    def list_group_users(self, group_id):
-        """List users in group."""
-        resp, body = self.get('groups/%s/users' % group_id)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
-
     def list_user_groups(self, user_id):
         """Lists groups which a user belongs to."""
         resp, body = self.get('users/%s/groups' % user_id)
@@ -372,12 +306,6 @@
         body = json.loads(body)
         return service_client.ResponseBody(resp, body)
 
-    def delete_group_user(self, group_id, user_id):
-        """Delete user in group."""
-        resp, body = self.delete('groups/%s/users/%s' % (group_id, user_id))
-        self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
-
     def assign_user_role_on_project(self, project_id, user_id, role_id):
         """Add roles to a user on a project."""
         resp, body = self.put('projects/%s/users/%s/roles/%s' %