Merge "RBAC tests for volume v3 groups and group types"
diff --git a/patrole_tempest_plugin/tests/api/volume/rbac_base.py b/patrole_tempest_plugin/tests/api/volume/rbac_base.py
index 1d390b7..953a834 100644
--- a/patrole_tempest_plugin/tests/api/volume/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/volume/rbac_base.py
@@ -47,6 +47,8 @@
         }
         cls.volume_hosts_client, cls.volume_types_client = \
             version_checker[cls._api_version]
+        cls.groups_client = cls.os_primary.groups_v3_client
+        cls.group_types_client = cls.os_primary.group_types_v3_client
 
     @classmethod
     def resource_setup(cls):
@@ -56,6 +58,8 @@
     @classmethod
     def resource_cleanup(cls):
         super(BaseVolumeRbacTest, cls).resource_cleanup()
+        # Allow volumes to be cleared first, so only clear volume types
+        # after super's resource_cleanup.
         cls.clear_volume_types()
 
     @classmethod
@@ -64,15 +68,33 @@
         name = name or data_utils.rand_name(cls.__name__ + '-volume-type')
         volume_type = cls.volume_types_client.create_volume_type(
             name=name, **kwargs)['volume_type']
-        cls.volume_types.append(volume_type['id'])
+        cls.volume_types.append(volume_type)
         return volume_type
 
+    def create_group_type(self, name=None, ignore_notfound=False, **kwargs):
+        """Create a test group-type"""
+        name = name or data_utils.rand_name(
+            self.__class__.__name__ + '-group-type')
+        group_type = self.group_types_client.create_group_type(
+            name=name, **kwargs)['group_type']
+
+        if ignore_notfound:
+            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                            self.group_types_client.delete_group_type,
+                            group_type['id'])
+        else:
+            self.addCleanup(self.group_types_client.delete_group_type,
+                            group_type['id'])
+
+        return group_type
+
     @classmethod
     def clear_volume_types(cls):
         for vol_type in cls.volume_types:
             test_utils.call_and_ignore_notfound_exc(
-                cls.volume_types_client.delete_volume_type, vol_type)
+                cls.volume_types_client.delete_volume_type, vol_type['id'])
 
         for vol_type in cls.volume_types:
             test_utils.call_and_ignore_notfound_exc(
-                cls.volume_types_client.wait_for_resource_deletion, vol_type)
+                cls.volume_types_client.wait_for_resource_deletion,
+                vol_type['id'])
diff --git a/patrole_tempest_plugin/tests/api/volume/test_groups_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_groups_rbac.py
new file mode 100644
index 0000000..6b07aaa
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/volume/test_groups_rbac.py
@@ -0,0 +1,143 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.common import waiters
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+
+from patrole_tempest_plugin import rbac_exceptions
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.volume import rbac_base
+
+
+class GroupsV3RbacTest(rbac_base.BaseVolumeRbacTest):
+    _api_version = 3
+    min_microversion = '3.14'
+    max_microversion = 'latest'
+
+    def setUp(self):
+        super(GroupsV3RbacTest, self).setUp()
+        self.volume_type_id = self.create_volume_type()['id']
+        self.group_type_id = self.create_group_type()['id']
+
+    def _create_group(self, name=None, ignore_notfound=False, **kwargs):
+        group_name = name or data_utils.rand_name(
+            self.__class__.__name__ + '-Group')
+        group = self.groups_client.create_group(name=group_name, **kwargs)[
+            'group']
+        waiters.wait_for_volume_resource_status(
+            self.groups_client, group['id'], 'available')
+
+        if ignore_notfound:
+            self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                            self._delete_group, group['id'])
+        else:
+            self.addCleanup(self._delete_group, group['id'])
+
+        return group
+
+    def _delete_group(self, group_id, delete_volumes=True):
+        self.groups_client.delete_group(group_id, delete_volumes)
+        self.groups_client.wait_for_resource_deletion(group_id)
+
+    @decorators.idempotent_id('43235328-66ae-424f-bc7f-f709c0ca268c')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="group:create")
+    def test_create_group(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self._create_group(ignore_notfound=True,
+                           group_type=self.group_type_id,
+                           volume_types=[self.volume_type_id])
+
+    @decorators.idempotent_id('9dc34a62-ae3e-439e-92b6-9389ea4c2863')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="group:get")
+    def test_show_group(self):
+        group = self._create_group(group_type=self.group_type_id,
+                                   volume_types=[self.volume_type_id])
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.groups_client.show_group(group['id'])
+
+    @decorators.idempotent_id('db43841b-a173-4317-acfc-f83e4e48e4ee')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="group:get_all")
+    def test_list_groups(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.groups_client.list_groups()['groups']
+
+    @decorators.idempotent_id('5378da93-9c26-4ad4-b039-0555e2b8f668')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="group:get_all")
+    def test_list_groups_with_details(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.groups_client.list_groups(detail=True)['groups']
+
+    @decorators.idempotent_id('66fda391-5774-42a9-a018-80b34e57ab76')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="group:delete")
+    def test_delete_group(self):
+        group = self._create_group(ignore_notfound=True,
+                                   group_type=self.group_type_id,
+                                   volume_types=[self.volume_type_id])
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.groups_client.delete_group(group['id'])
+
+
+class GroupTypesV3RbacTest(rbac_base.BaseVolumeRbacTest):
+    _api_version = 3
+    min_microversion = '3.11'
+    max_microversion = 'latest'
+
+    @decorators.idempotent_id('2820f12c-4681-4c7f-b28d-e6925637dff6')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="group:group_types_manage")
+    def test_create_group_type(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.create_group_type(ignore_notfound=True)
+
+    @decorators.idempotent_id('a5f88c26-df7c-4f21-a3ae-7a4c2d6212b4')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="group:access_group_types_specs")
+    def test_create_group_type_group_specs(self):
+        # TODO(felipemonteiro): Combine with ``test_create_group_type``
+        # once multiple policy testing is supported. This policy is
+        # only enforced after "group:group_types_manage".
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        group_type = self.create_group_type(ignore_notfound=True)
+
+        if 'group_specs' not in group_type:
+            raise rbac_exceptions.RbacActionFailed(
+                'Policy %s does not return %s in response body.' %
+                ('group:access_group_types_specs', 'group_specs'))
+
+    @decorators.idempotent_id('f77f8156-4fc9-4f02-be15-8930f748e10c')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="group:group_types_manage")
+    def test_delete_group_type(self):
+        goup_type = self.create_group_type(ignore_notfound=True)
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.group_types_client.delete_group_type(goup_type['id'])
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_actions_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_actions_rbac.py
index 8bb92f4..b666a2d 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_actions_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_actions_rbac.py
@@ -155,10 +155,13 @@
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume:retype")
     def test_volume_retype(self):
-        volume = self.create_volume()
         vol_type = self.create_volume_type()['name']
+        volume = self.create_volume()
+
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.volumes_client.retype_volume(volume['id'], new_type=vol_type)
+        waiters.wait_for_volume_retype(
+            self.os_admin.volumes_client, volume['id'], vol_type)
 
     @rbac_rule_validation.action(
         service="cinder",
diff --git a/releasenotes/notes/volume-v3-groups-rbac-tests-60bddf6fa509545d.yaml b/releasenotes/notes/volume-v3-groups-rbac-tests-60bddf6fa509545d.yaml
new file mode 100644
index 0000000..92b1123
--- /dev/null
+++ b/releasenotes/notes/volume-v3-groups-rbac-tests-60bddf6fa509545d.yaml
@@ -0,0 +1,12 @@
+---
+features:
+  - |
+    Add RBAC tests for the volume v3 groups and group types APIs, providing
+    coverage for the following policy actions:
+
+    * group:create
+    * group:get
+    * group:get_all
+    * group:delete
+    * group:group_types_manage
+    * group:access_group_types_specs