Merge "Migrate to override_role for volume module (part 2)"
diff --git a/patrole_tempest_plugin/rbac_utils.py b/patrole_tempest_plugin/rbac_utils.py
index 51d1d25..49cb5e1 100644
--- a/patrole_tempest_plugin/rbac_utils.py
+++ b/patrole_tempest_plugin/rbac_utils.py
@@ -116,7 +116,7 @@
         * admin if `toggle_rbac_role` is False
         * `CONF.patrole.rbac_test_role` if `toggle_rbac_role` is True
 
-        :param test_obj: test object of type tempest.lib.base.BaseTestCase
+        :param test_obj: instance of :py:class:`tempest.test.BaseTestCase`
         :param toggle_rbac_role: role to switch `os_primary` Tempest creds to
         """
         self._override_role(test_obj, toggle_rbac_role)
@@ -124,7 +124,7 @@
     def _override_role(self, test_obj, toggle_rbac_role=False):
         """Private helper for overriding ``os_primary`` Tempest credentials.
 
-        :param test_obj: test object of type tempest.lib.base.BaseTestCase
+        :param test_obj: instance of :py:class:`tempest.test.BaseTestCase`
         :param toggle_rbac_role: Boolean value that controls the role that
             overrides default role of ``os_primary`` credentials.
             * If True: role is set to ``[patrole] rbac_test_role``
@@ -205,6 +205,39 @@
         return False
 
 
+class RbacUtilsMixin(object):
+    """Mixin class to be used alongside an instance of
+    :py:class:`tempest.test.BaseTestCase`.
+
+    Should be used to perform Patrole class setup for a base RBAC class. Child
+    classes should not use this mixin.
+
+    Example::
+
+        class BaseRbacTest(rbac_utils.RbacUtilsMixin, base.BaseV2ComputeTest):
+
+            @classmethod
+            def skip_checks(cls):
+                super(BaseRbacTest, cls).skip_checks()
+                cls.skip_rbac_checks()
+
+            @classmethod
+            def setup_clients(cls):
+                super(BaseRbacTest, cls).setup_clients()
+                cls.setup_rbac_utils()
+    """
+
+    @classmethod
+    def skip_rbac_checks(cls):
+        if not CONF.patrole.enable_rbac:
+            raise cls.skipException(
+                '%s skipped as Patrole testing not enabled.' % cls.__name__)
+
+    @classmethod
+    def setup_rbac_utils(cls):
+        cls.rbac_utils = RbacUtils(cls)
+
+
 def is_admin():
     """Verifies whether the current test role equals the admin role.
 
diff --git a/patrole_tempest_plugin/tests/api/compute/rbac_base.py b/patrole_tempest_plugin/tests/api/compute/rbac_base.py
index 6246446..1bd1cc7 100644
--- a/patrole_tempest_plugin/tests/api/compute/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/compute/rbac_base.py
@@ -21,20 +21,18 @@
 CONF = config.CONF
 
 
-class BaseV2ComputeRbacTest(compute_base.BaseV2ComputeTest):
+class BaseV2ComputeRbacTest(rbac_utils.RbacUtilsMixin,
+                            compute_base.BaseV2ComputeTest):
 
     @classmethod
     def skip_checks(cls):
         super(BaseV2ComputeRbacTest, cls).skip_checks()
-        if not CONF.patrole.enable_rbac:
-            raise cls.skipException(
-                '%s skipped as RBAC testing not enabled' % cls.__name__)
+        cls.skip_rbac_checks()
 
     @classmethod
     def setup_clients(cls):
         super(BaseV2ComputeRbacTest, cls).setup_clients()
-        cls.rbac_utils = rbac_utils.RbacUtils(cls)
-
+        cls.setup_rbac_utils()
         cls.hosts_client = cls.os_primary.hosts_client
         cls.tenant_usages_client = cls.os_primary.tenant_usages_client
 
diff --git a/patrole_tempest_plugin/tests/api/identity/rbac_base.py b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
index a99365d..63f6ff8 100644
--- a/patrole_tempest_plugin/tests/api/identity/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
@@ -26,19 +26,18 @@
 LOG = logging.getLogger(__name__)
 
 
-class BaseIdentityRbacTest(base.BaseIdentityTest):
+class BaseIdentityRbacTest(rbac_utils.RbacUtilsMixin,
+                           base.BaseIdentityTest):
 
     @classmethod
     def skip_checks(cls):
         super(BaseIdentityRbacTest, cls).skip_checks()
-        if not CONF.patrole.enable_rbac:
-            raise cls.skipException(
-                "%s skipped as RBAC testing not enabled" % cls.__name__)
+        cls.skip_rbac_checks()
 
     @classmethod
     def setup_clients(cls):
         super(BaseIdentityRbacTest, cls).setup_clients()
-        cls.rbac_utils = rbac_utils.RbacUtils(cls)
+        cls.setup_rbac_utils()
 
     @classmethod
     def setup_test_endpoint(cls, service=None):
diff --git a/patrole_tempest_plugin/tests/api/image/rbac_base.py b/patrole_tempest_plugin/tests/api/image/rbac_base.py
index ed69c3d..954790d 100644
--- a/patrole_tempest_plugin/tests/api/image/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/image/rbac_base.py
@@ -19,16 +19,15 @@
 CONF = config.CONF
 
 
-class BaseV2ImageRbacTest(image_base.BaseV2ImageTest):
+class BaseV2ImageRbacTest(rbac_utils.RbacUtilsMixin,
+                          image_base.BaseV2ImageTest):
 
     @classmethod
     def skip_checks(cls):
         super(BaseV2ImageRbacTest, cls).skip_checks()
-        if not CONF.patrole.enable_rbac:
-            raise cls.skipException(
-                "%s skipped as RBAC testing not enabled" % cls.__name__)
+        cls.skip_rbac_checks()
 
     @classmethod
     def setup_clients(cls):
         super(BaseV2ImageRbacTest, cls).setup_clients()
-        cls.rbac_utils = rbac_utils.RbacUtils(cls)
+        cls.setup_rbac_utils()
diff --git a/patrole_tempest_plugin/tests/api/network/rbac_base.py b/patrole_tempest_plugin/tests/api/network/rbac_base.py
index b495098..3065c13 100644
--- a/patrole_tempest_plugin/tests/api/network/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/network/rbac_base.py
@@ -21,16 +21,15 @@
 CONF = config.CONF
 
 
-class BaseNetworkRbacTest(network_base.BaseNetworkTest):
+class BaseNetworkRbacTest(rbac_utils.RbacUtilsMixin,
+                          network_base.BaseNetworkTest):
 
     @classmethod
     def skip_checks(cls):
         super(BaseNetworkRbacTest, cls).skip_checks()
-        if not CONF.patrole.enable_rbac:
-            raise cls.skipException(
-                "%s skipped as RBAC testing not enabled" % cls.__name__)
+        cls.skip_rbac_checks()
 
     @classmethod
     def setup_clients(cls):
         super(BaseNetworkRbacTest, cls).setup_clients()
-        cls.rbac_utils = rbac_utils.RbacUtils(cls)
+        cls.setup_rbac_utils()
diff --git a/patrole_tempest_plugin/tests/api/network/test_metering_label_rules_rbac.py b/patrole_tempest_plugin/tests/api/network/test_metering_label_rules_rbac.py
index 5ffc966..7a9d814 100644
--- a/patrole_tempest_plugin/tests/api/network/test_metering_label_rules_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_metering_label_rules_rbac.py
@@ -70,8 +70,8 @@
 
         RBAC test for the neutron create_metering_label_rule policy
         """
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._create_metering_label_rule(self.label)
+        with self.rbac_utils.override_role(self):
+            self._create_metering_label_rule(self.label)
 
     @rbac_rule_validation.action(service="neutron",
                                  rule="get_metering_label_rule",
@@ -83,9 +83,9 @@
         RBAC test for the neutron get_metering_label_rule policy
         """
         label_rule = self._create_metering_label_rule(self.label)
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.metering_label_rules_client.show_metering_label_rule(
-            label_rule['id'])
+        with self.rbac_utils.override_role(self):
+            self.metering_label_rules_client.show_metering_label_rule(
+                label_rule['id'])
 
     @rbac_rule_validation.action(service="neutron",
                                  rule="delete_metering_label_rule",
@@ -97,6 +97,6 @@
         RBAC test for the neutron delete_metering_label_rule policy
         """
         label_rule = self._create_metering_label_rule(self.label)
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.metering_label_rules_client.delete_metering_label_rule(
-            label_rule['id'])
+        with self.rbac_utils.override_role(self):
+            self.metering_label_rules_client.delete_metering_label_rule(
+                label_rule['id'])
diff --git a/patrole_tempest_plugin/tests/api/network/test_metering_labels_rbac.py b/patrole_tempest_plugin/tests/api/network/test_metering_labels_rbac.py
index 64df7c5..abd7326 100644
--- a/patrole_tempest_plugin/tests/api/network/test_metering_labels_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_metering_labels_rbac.py
@@ -54,8 +54,8 @@
 
         RBAC test for the neutron "create_metering_label" policy
         """
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._create_metering_label()
+        with self.rbac_utils.override_role(self):
+            self._create_metering_label()
 
     @rbac_rule_validation.action(service="neutron",
                                  rule="get_metering_label",
@@ -67,8 +67,8 @@
         RBAC test for the neutron "get_metering_label" policy
         """
         label = self._create_metering_label()
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.metering_labels_client.show_metering_label(label['id'])
+        with self.rbac_utils.override_role(self):
+            self.metering_labels_client.show_metering_label(label['id'])
 
     @rbac_rule_validation.action(service="neutron",
                                  rule="delete_metering_label",
@@ -80,5 +80,5 @@
         RBAC test for the neutron "delete_metering_label" policy
         """
         label = self._create_metering_label()
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.metering_labels_client.delete_metering_label(label['id'])
+        with self.rbac_utils.override_role(self):
+            self.metering_labels_client.delete_metering_label(label['id'])
diff --git a/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py b/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py
index e0cf098..84ce2c7 100644
--- a/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py
@@ -95,8 +95,8 @@
 
         RBAC test for the neutron create_network policy
         """
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._create_network()
+        with self.rbac_utils.override_role(self):
+            self._create_network()
 
     @rbac_rule_validation.action(service="neutron",
                                  rule="create_network:shared")
@@ -107,8 +107,8 @@
 
         RBAC test for the neutron create_network:shared policy
         """
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._create_network(shared=True)
+        with self.rbac_utils.override_role(self):
+            self._create_network(shared=True)
 
     @utils.requires_ext(extension='external-net', service='network')
     @rbac_rule_validation.action(service="neutron",
@@ -120,8 +120,8 @@
 
         RBAC test for the neutron create_network:router:external policy
         """
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._create_network(router_external=True)
+        with self.rbac_utils.override_role(self):
+            self._create_network(router_external=True)
 
     @utils.requires_ext(extension='provider', service='network')
     @rbac_rule_validation.action(service="neutron",
@@ -133,8 +133,8 @@
 
         RBAC test for the neutron create_network:provider:network_type policy
         """
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._create_network(provider_network_type='vxlan')
+        with self.rbac_utils.override_role(self):
+            self._create_network(provider_network_type='vxlan')
 
     @utils.requires_ext(extension='provider', service='network')
     @rbac_rule_validation.action(
@@ -147,9 +147,9 @@
 
         RBAC test for the neutron create_network:provider:segmentation_id
         """
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._create_network(provider_network_type='vxlan',
-                             provider_segmentation_id=200)
+        with self.rbac_utils.override_role(self):
+            self._create_network(provider_network_type='vxlan',
+                                 provider_segmentation_id=200)
 
     @rbac_rule_validation.action(service="neutron",
                                  rule="update_network")
@@ -163,8 +163,8 @@
         updated_name = data_utils.rand_name(
             self.__class__.__name__ + '-Network')
 
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._update_network(name=updated_name)
+        with self.rbac_utils.override_role(self):
+            self._update_network(name=updated_name)
 
     @rbac_rule_validation.action(service="neutron",
                                  rule="update_network:shared")
@@ -175,8 +175,8 @@
 
         RBAC test for the neutron update_network:shared policy
         """
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._update_network(shared_network=True)
+        with self.rbac_utils.override_role(self):
+            self._update_network(shared_network=True)
         self.addCleanup(self._update_network, shared_network=False)
 
     @utils.requires_ext(extension='external-net', service='network')
@@ -190,8 +190,8 @@
         RBAC test for the neutron update_network:router:external policy
         """
         network = self._create_network()
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._update_network(net_id=network['id'], router_external=True)
+        with self.rbac_utils.override_role(self):
+            self._update_network(net_id=network['id'], router_external=True)
 
     @rbac_rule_validation.action(service="neutron",
                                  rule="get_network")
@@ -202,8 +202,8 @@
 
         RBAC test for the neutron get_network policy
         """
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.networks_client.show_network(self.network['id'])
+        with self.rbac_utils.override_role(self):
+            self.networks_client.show_network(self.network['id'])
 
     @utils.requires_ext(extension='external-net', service='network')
     @rbac_rule_validation.action(service="neutron",
@@ -217,9 +217,9 @@
         """
         kwargs = {'fields': 'router:external'}
 
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.networks_client.show_network(self.network['id'],
-                                          **kwargs)
+        with self.rbac_utils.override_role(self):
+            self.networks_client.show_network(self.network['id'],
+                                              **kwargs)
 
     @utils.requires_ext(extension='provider', service='network')
     @rbac_rule_validation.action(service="neutron",
@@ -233,9 +233,9 @@
         """
         kwargs = {'fields': 'provider:network_type'}
 
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        retrieved_network = self.networks_client.show_network(
-            self.network['id'], **kwargs)['network']
+        with self.rbac_utils.override_role(self):
+            retrieved_network = self.networks_client.show_network(
+                self.network['id'], **kwargs)['network']
 
         if len(retrieved_network) == 0:
             raise rbac_exceptions.RbacMalformedResponse(True)
@@ -252,9 +252,9 @@
         """
         kwargs = {'fields': 'provider:physical_network'}
 
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        retrieved_network = self.networks_client.show_network(
-            self.network['id'], **kwargs)['network']
+        with self.rbac_utils.override_role(self):
+            retrieved_network = self.networks_client.show_network(
+                self.network['id'], **kwargs)['network']
 
         if len(retrieved_network) == 0:
             raise rbac_exceptions.RbacMalformedResponse(empty=True)
@@ -271,9 +271,9 @@
         """
         kwargs = {'fields': 'provider:segmentation_id'}
 
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        retrieved_network = self.networks_client.show_network(
-            self.network['id'], **kwargs)['network']
+        with self.rbac_utils.override_role(self):
+            retrieved_network = self.networks_client.show_network(
+                self.network['id'], **kwargs)['network']
 
         if len(retrieved_network) == 0:
             raise rbac_exceptions.RbacMalformedResponse(empty=True)
@@ -291,8 +291,8 @@
         RBAC test for the neutron delete_network policy
         """
         network = self._create_network()
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.networks_client.delete_network(network['id'])
+        with self.rbac_utils.override_role(self):
+            self.networks_client.delete_network(network['id'])
 
     @rbac_rule_validation.action(service="neutron",
                                  rule="create_subnet")
@@ -305,8 +305,8 @@
         """
         network = self._create_network()
 
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.create_subnet(network, enable_dhcp=False)
+        with self.rbac_utils.override_role(self):
+            self.create_subnet(network, enable_dhcp=False)
 
     @rbac_rule_validation.action(service="neutron",
                                  rule="get_subnet")
@@ -317,8 +317,8 @@
 
         RBAC test for the neutron get_subnet policy
         """
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.subnets_client.show_subnet(self.subnet['id'])
+        with self.rbac_utils.override_role(self):
+            self.subnets_client.show_subnet(self.subnet['id'])
 
     @rbac_rule_validation.action(service="neutron",
                                  rule="update_subnet")
@@ -332,9 +332,9 @@
         updated_name = data_utils.rand_name(
             self.__class__.__name__ + '-Network')
 
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.subnets_client.update_subnet(self.subnet['id'],
-                                          name=updated_name)
+        with self.rbac_utils.override_role(self):
+            self.subnets_client.update_subnet(self.subnet['id'],
+                                              name=updated_name)
 
     @rbac_rule_validation.action(service="neutron",
                                  rule="delete_subnet")
@@ -348,8 +348,8 @@
         network = self._create_network()
         subnet = self.create_subnet(network, enable_dhcp=False)
 
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.subnets_client.delete_subnet(subnet['id'])
+        with self.rbac_utils.override_role(self):
+            self.subnets_client.delete_subnet(subnet['id'])
 
     @utils.requires_ext(extension='dhcp_agent_scheduler', service='network')
     @decorators.idempotent_id('b524f19f-fbb4-4d11-a85d-03bfae17bf0e')
@@ -361,6 +361,6 @@
 
         RBAC test for the neutron "get_dhcp-agents" policy
         """
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.networks_client.list_dhcp_agents_on_hosting_network(
-            self.network['id'])
+        with self.rbac_utils.override_role(self):
+            self.networks_client.list_dhcp_agents_on_hosting_network(
+                self.network['id'])
diff --git a/patrole_tempest_plugin/tests/api/volume/rbac_base.py b/patrole_tempest_plugin/tests/api/volume/rbac_base.py
index 7e2ebad..798f311 100644
--- a/patrole_tempest_plugin/tests/api/volume/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/volume/rbac_base.py
@@ -21,7 +21,8 @@
 CONF = config.CONF
 
 
-class BaseVolumeRbacTest(vol_base.BaseVolumeTest):
+class BaseVolumeRbacTest(rbac_utils.RbacUtilsMixin,
+                         vol_base.BaseVolumeTest):
     # NOTE(felipemonteiro): Patrole currently only tests the v3 Cinder API
     # because it is the current API and because policy enforcement does not
     # change between API major versions. So, it is not necessary to specify
@@ -32,15 +33,12 @@
     @classmethod
     def skip_checks(cls):
         super(BaseVolumeRbacTest, cls).skip_checks()
-        if not CONF.patrole.enable_rbac:
-            raise cls.skipException(
-                "%s skipped as RBAC testing not enabled" % cls.__name__)
+        cls.skip_rbac_checks()
 
     @classmethod
     def setup_clients(cls):
         super(BaseVolumeRbacTest, cls).setup_clients()
-        cls.rbac_utils = rbac_utils.RbacUtils(cls)
-
+        cls.setup_rbac_utils()
         cls.volume_hosts_client = cls.os_primary.volume_hosts_v2_client
         cls.volume_types_client = cls.os_primary.volume_types_v2_client
         cls.groups_client = cls.os_primary.groups_v3_client
diff --git a/patrole_tempest_plugin/tests/unit/test_rbac_utils.py b/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
index 0d75c3e..55db501 100644
--- a/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
+++ b/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
@@ -17,6 +17,7 @@
 import testtools
 
 from tempest.lib import exceptions as lib_exc
+from tempest import test
 from tempest.tests import base
 
 from patrole_tempest_plugin import rbac_exceptions
@@ -199,3 +200,60 @@
             _do_test()
         mock_override_role.assert_called_once_with(_rbac_utils, test_obj,
                                                    False)
+
+
+class RBACUtilsMixinTest(base.TestCase):
+
+    def setUp(self):
+        super(RBACUtilsMixinTest, self).setUp()
+
+        class FakeRbacTest(rbac_utils.RbacUtilsMixin, test.BaseTestCase):
+
+            @classmethod
+            def skip_checks(cls):
+                super(FakeRbacTest, cls).skip_checks()
+                cls.skip_rbac_checks()
+
+            @classmethod
+            def setup_clients(cls):
+                super(FakeRbacTest, cls).setup_clients()
+                cls.setup_rbac_utils()
+
+            def runTest(self):
+                pass
+
+        self.parent_class = FakeRbacTest
+
+    def test_setup_rbac_utils(self):
+        """Validate that the child class has the `rbac_utils` attribute after
+        running parent class's `cls.setup_rbac_utils`.
+        """
+        class ChildRbacTest(self.parent_class):
+            pass
+
+        child_test = ChildRbacTest()
+
+        with mock.patch.object(rbac_utils.RbacUtils, '__init__',
+                               lambda *args: None):
+            child_test.setUpClass()
+
+        self.assertTrue(hasattr(child_test, 'rbac_utils'))
+        self.assertIsInstance(child_test.rbac_utils, rbac_utils.RbacUtils)
+
+    def test_skip_rbac_checks(self):
+        """Validate that the child class is skipped if `[patrole] enable_rbac`
+        is False and that the child class's name is in the skip message.
+        """
+        self.useFixture(patrole_fixtures.ConfPatcher(enable_rbac=False,
+                                                     group='patrole'))
+
+        class ChildRbacTest(self.parent_class):
+            pass
+
+        child_test = ChildRbacTest()
+
+        with testtools.ExpectedException(
+                testtools.TestCase.skipException,
+                value_re=('%s skipped as Patrole testing not enabled.'
+                          % ChildRbacTest.__name__)):
+            child_test.setUpClass()