Merge "Add a job for S3 backup driver"
diff --git a/.zuul.yaml b/.zuul.yaml
index 68d0178..9c6c9a6 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -16,6 +16,9 @@
         - cinder-tempest-plugin-basic-victoria
         - cinder-tempest-plugin-basic-ussuri
         - cinder-tempest-plugin-basic-train
+        # Set this job to voting once we have some actual tests to run
+        - cinder-tempest-plugin-protection-functional:
+            voting: false
     gate:
       jobs:
         - cinder-tempest-plugin-lvm-lio-barbican
@@ -29,6 +32,26 @@
         - cinder-tempest-plugin-cbak-ceph-train
 
 - job:
+    name: cinder-tempest-plugin-protection-functional
+    parent: devstack-tempest
+    required-projects:
+      - opendev.org/openstack/cinder-tempest-plugin
+      - opendev.org/openstack/cinder
+    vars:
+      tox_envlist: all
+      tempest_test_regex: 'cinder_tempest_plugin.rbac'
+      devstack_local_conf:
+        test-config:
+          $CINDER_CONF:
+            oslo_policy:
+              enforce_new_defaults: True
+          $TEMPEST_CONFIG:
+            enforce_scope:
+              cinder: True
+      tempest_plugins:
+        - cinder-tempest-plugin
+
+- job:
     name: cinder-tempest-plugin-lvm-barbican-base-abstract
     description: |
       This is a base job for lvm with lio & tgt targets
diff --git a/cinder_tempest_plugin/rbac/__init__.py b/cinder_tempest_plugin/rbac/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cinder_tempest_plugin/rbac/__init__.py
diff --git a/cinder_tempest_plugin/rbac/v3/__init__.py b/cinder_tempest_plugin/rbac/v3/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cinder_tempest_plugin/rbac/v3/__init__.py
diff --git a/cinder_tempest_plugin/rbac/v3/base.py b/cinder_tempest_plugin/rbac/v3/base.py
new file mode 100644
index 0000000..d1a11e5
--- /dev/null
+++ b/cinder_tempest_plugin/rbac/v3/base.py
@@ -0,0 +1,42 @@
+#    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 import config
+
+CONF = config.CONF
+
+
+class VolumeV3RbacBaseTests(object):
+
+    identity_version = 'v3'
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumeV3RbacBaseTests, cls).skip_checks()
+        if not CONF.enforce_scope.cinder:
+            raise cls.skipException(
+                "Tempest is not configured to enforce_scope for cinder, "
+                "skipping RBAC tests. To enable these tests set "
+                "`tempest.conf [enforce_scope] cinder=True`."
+            )
+
+    def do_request(self, method, expected_status=200, client=None, **payload):
+        if not client:
+            client = self.client
+        if isinstance(expected_status, type(Exception)):
+            self.assertRaises(expected_status,
+                              getattr(client, method),
+                              **payload)
+        else:
+            response = getattr(client, method)(**payload)
+            self.assertEqual(response.response.status, expected_status)
+            return response
diff --git a/cinder_tempest_plugin/rbac/v3/test_capabilities.py b/cinder_tempest_plugin/rbac/v3/test_capabilities.py
new file mode 100644
index 0000000..1fa542d
--- /dev/null
+++ b/cinder_tempest_plugin/rbac/v3/test_capabilities.py
@@ -0,0 +1,80 @@
+#    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 abc
+
+from tempest.lib import exceptions
+
+from cinder_tempest_plugin.api.volume import base
+from cinder_tempest_plugin.rbac.v3 import base as rbac_base
+
+
+class VolumeV3RbacCapabilityTests(rbac_base.VolumeV3RbacBaseTests,
+                                  metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super().setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.volume_capabilities_client_latest
+        # NOTE(lbragstad): This admin_client will be more useful later when
+        # cinder supports system-scope and we need it for administrative
+        # operations. For now, keep os_project_admin as the admin client until
+        # we have system-scope.
+        admin_client = cls.os_project_admin
+        cls.admin_capabilities_client = (
+            admin_client.volume_capabilities_client_latest)
+        cls.admin_stats_client = (
+            admin_client.volume_scheduler_stats_client_latest)
+
+    @classmethod
+    def setup_credentials(cls):
+        super().setup_credentials()
+        cls.os_primary = getattr(cls, 'os_%s' % cls.credentials[0])
+
+    @abc.abstractmethod
+    def test_get_capabilities(self):
+        """Test volume_extension:capabilities policy.
+
+        This test must check:
+          * whether the persona can fetch capabilities for a host.
+
+        """
+        pass
+
+
+class ProjectAdminTests(VolumeV3RbacCapabilityTests, base.BaseVolumeTest):
+
+    credentials = ['project_admin', 'system_admin']
+
+    def test_get_capabilities(self):
+        pools = self.admin_stats_client.list_pools()['pools']
+        host_name = pools[0]['name']
+        self.do_request('show_backend_capabilities', expected_status=200,
+                        host=host_name)
+
+
+class ProjectMemberTests(ProjectAdminTests, base.BaseVolumeTest):
+
+    credentials = ['project_member', 'project_admin', 'system_admin']
+
+    def test_get_capabilities(self):
+        pools = self.admin_stats_client.list_pools()['pools']
+        host_name = pools[0]['name']
+        self.do_request('show_backend_capabilities',
+                        expected_status=exceptions.Forbidden,
+                        host=host_name)
+
+
+class ProjectReaderTests(ProjectMemberTests, base.BaseVolumeTest):
+
+    credentials = ['project_reader', 'project_admin', 'system_admin']