Merge "Volume test for backup:backup_project_attribute"
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
deleted file mode 100644
index 243a9c0..0000000
--- a/CONTRIBUTING.rst
+++ /dev/null
@@ -1,17 +0,0 @@
-If you would like to contribute to the development of OpenStack, you must
-follow the steps in this page:
-
-   https://docs.openstack.org/infra/manual/developers.html
-
-If you already have a good understanding of how the system works and your
-OpenStack accounts are set up, you can skip to the development workflow
-section of this documentation to learn how changes to OpenStack should be
-submitted for review via the Gerrit tool:
-
-   https://docs.openstack.org/infra/manual/developers.html#development-workflow
-
-Pull requests submitted through GitHub will be ignored.
-
-Bugs should be filed on Launchpad, not GitHub:
-
-   https://bugs.launchpad.net/patrole
diff --git a/HACKING.rst b/HACKING.rst
index f89910b..a94b47c 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -32,8 +32,7 @@
 The following are Patrole's specific Commandments:
 
 - [P100] The ``rbac_rule_validation.action`` decorator must be applied to
-         an RBAC test (the check fails if the decorator is not one of the
-         two decorators directly above the function declaration)
+         an RBAC test
 - [P101] RBAC test filenames must end with "_rbac.py"; for example,
          test_servers_rbac.py, not test_servers.py
 - [P102] RBAC test class names must end in 'RbacTest'
@@ -47,7 +46,7 @@
 test does not call ``rbac_utils.switch_role`` with ``toggle_rbac_role=True``
 within the RBAC test, then the test is *not* a valid RBAC test: The API
 endpoint under test will be performed with admin credentials, which is always
-wrong unless ``CONF.rbac_test_role`` is admin.
+wrong unless ``CONF.patrole.rbac_test_role`` is admin.
 
 .. note::
 
diff --git a/doc/source/HACKING.rst b/doc/source/HACKING.rst
new file mode 100644
index 0000000..1847447
--- /dev/null
+++ b/doc/source/HACKING.rst
@@ -0,0 +1,4 @@
+=======
+Hacking
+=======
+.. include:: ../../HACKING.rst
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 550d2ce..8ec0013 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -5,7 +5,7 @@
 
 Patrole can be customized by updating Tempest's ``tempest.conf`` configuration
 file. All Patrole-specific configuration options should be included under
-the ``rbac`` group.
+the ``patrole`` group.
 
 RBAC Test Role
 --------------
diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst
deleted file mode 100644
index 1728a61..0000000
--- a/doc/source/contributing.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-============
-Contributing
-============
-.. include:: ../../CONTRIBUTING.rst
diff --git a/doc/source/index.rst b/doc/source/index.rst
index f58ee7f..e2cc0bd 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -20,9 +20,17 @@
 =================
 
 .. toctree::
+   :maxdepth: 1
+
+   HACKING
+
+Framework
+---------
+
+.. toctree::
    :maxdepth: 2
 
-   contributing
+   rbac_validation
 
 Indices and tables
 ==================
diff --git a/doc/source/rbac_validation.rst b/doc/source/rbac_validation.rst
new file mode 100644
index 0000000..ccaf3c8
--- /dev/null
+++ b/doc/source/rbac_validation.rst
@@ -0,0 +1,64 @@
+.. _rbac-validation:
+
+RBAC Testing Validation
+=======================
+
+--------
+Overview
+--------
+
+RBAC Testing Validation is broken up into 3 stages:
+
+  1. "Expected" stage. Determine whether the test should be able to succeed
+     or fail based on the test role defined by ``[patrole] rbac_test_role``)
+     and the policy action that the test enforces.
+  2. "Actual" stage. Run the test by calling the API endpoint that enforces
+     the expected policy action using the test role.
+  3. Comparing the outputs from both stages for consistency. A "consistent"
+     result is treated as a pass and an "inconsistent" result is treated
+     as a failure. "Consistent" (or successful) cases include:
+
+      * Expected result is ``True`` and the test passes.
+      * Expected result is ``False`` and the test fails.
+
+     "Inconsistent" (or failing) cases include:
+
+      * Expected result is ``False`` and the test passes. This results in an
+        ``RbacOverPermission`` exception getting thrown.
+      * Expected result is ``True`` and the test fails. This results in a
+        ``Forbidden`` exception getting thrown.
+
+     For example, a 200 from the API call and a ``True`` result from
+     ``oslo.policy`` or a 403 from the API call and a ``False`` result from
+     ``oslo.policy`` are successful results.
+
+-------------------------------
+The RBAC Rule Validation Module
+-------------------------------
+
+High-level module that implements decorator inside which the "Expected" stage
+is initiated.
+
+.. automodule:: patrole_tempest_plugin.rbac_rule_validation
+   :members:
+
+---------------------------
+The Policy Authority Module
+---------------------------
+
+Using the Policy Authority Module, policy verification is performed by:
+
+1. Pooling together the default `in-code` policy rules.
+2. Overriding the defaults with custom policy rules located in a policy.json,
+   if the policy file exists and the custom policy definition is explicitly
+   defined therein.
+3. Confirming that the policy action -- for example, "list_users" -- exists.
+   (``oslo.policy`` otherwise claims that role "foo" is allowed to
+   perform policy action "bar", for example, because it defers to the
+   "default" policy rule and oftentimes the default can be "anyone allowed").
+4. Performing a call with all necessary data to ``oslo.policy`` and returning
+   the expected result back to ``rbac_rule_validation`` decorator.
+
+.. automodule:: patrole_tempest_plugin.policy_authority
+   :members:
+   :special-members:
diff --git a/doc/source/sampleconf.rst b/doc/source/sampleconf.rst
index 43933db..94ebc4d 100644
--- a/doc/source/sampleconf.rst
+++ b/doc/source/sampleconf.rst
@@ -7,7 +7,7 @@
 
 .. code-block:: ini
 
-    [rbac]
+    [patrole]
 
     # The role that you want the RBAC tests to use for RBAC testing
     # This needs to be edited to run the test as a different role.
diff --git a/doc/source/usage.rst b/doc/source/usage.rst
index 7470e9e..dff43f2 100644
--- a/doc/source/usage.rst
+++ b/doc/source/usage.rst
@@ -30,7 +30,7 @@
 To change the role that the patrole tests are being run as, edit
 ``rbac_test_role`` in the ``rbac`` section of tempest.conf: ::
 
-    [rbac]
+    [patrole]
     rbac_test_role = Member
     ...
 
diff --git a/patrole_tempest_plugin/config.py b/patrole_tempest_plugin/config.py
index a6f30e7..fcf29af 100644
--- a/patrole_tempest_plugin/config.py
+++ b/patrole_tempest_plugin/config.py
@@ -15,19 +15,23 @@
 
 from oslo_config import cfg
 
-rbac_group = cfg.OptGroup(name='rbac',
-                          title='RBAC testing options')
 
-RbacGroup = [
+patrole_group = cfg.OptGroup(name='patrole', title='Patrole Testing Options')
+
+
+PatroleGroup = [
     cfg.StrOpt('rbac_test_role',
                default='admin',
+               deprecated_group='rbac',
                help="""The current RBAC role against which to run Patrole
 tests."""),
     cfg.BoolOpt('enable_rbac',
                 default=True,
+                deprecated_group='rbac',
                 help="Enables RBAC tests."),
     cfg.BoolOpt('strict_policy_check',
                 default=False,
+                deprecated_group='rbac',
                 help="""If true, throws RbacParsingException for policies which
 don't exist or are not included in the service's policy file. If false, throws
 skipException."""),
@@ -35,6 +39,7 @@
     # other hosts. It may be possible to leverage the v3 identity policy API.
     cfg.ListOpt('custom_policy_files',
                 default=['/etc/%s/policy.json'],
+                deprecated_group='rbac',
                 help="""List of the paths to search for policy files. Each
 policy path assumes that the service name is included in the path once. Also
 assumes Patrole is on the same host as the policy files. The paths should be
@@ -45,6 +50,7 @@
                default='/etc/cinder/policy.json',
                help="""Location of the Cinder policy file. Assumed to be on
 the same host as Patrole.""",
+               deprecated_group='rbac',
                deprecated_for_removal=True,
                deprecated_reason="It is better to use `custom_policy_files` "
                                  "which supports any OpenStack service."),
@@ -52,6 +58,7 @@
                default='/etc/glance/policy.json',
                help="""Location of the Glance policy file. Assumed to be on
 the same host as Patrole.""",
+               deprecated_group='rbac',
                deprecated_for_removal=True,
                deprecated_reason="It is better to use `custom_policy_files` "
                                  "which supports any OpenStack service."),
@@ -59,6 +66,7 @@
                default='/etc/keystone/policy.json',
                help="""Location of the custom Keystone policy file. Assumed to
 be on the same host as Patrole.""",
+               deprecated_group='rbac',
                deprecated_for_removal=True,
                deprecated_reason="It is better to use `custom_policy_files` "
                                  "which supports any OpenStack service."),
@@ -66,6 +74,7 @@
                default='/etc/neutron/policy.json',
                help="""Location of the Neutron policy file. Assumed to be on
 the same host as Patrole.""",
+               deprecated_group='rbac',
                deprecated_for_removal=True,
                deprecated_reason="It is better to use `custom_policy_files` "
                                  "which supports any OpenStack service."),
@@ -73,11 +82,13 @@
                default='/etc/nova/policy.json',
                help="""Location of the custom Nova policy file. Assumed to be
 on the same host as Patrole.""",
+               deprecated_group='rbac',
                deprecated_for_removal=True,
                deprecated_reason="It is better to use `custom_policy_files` "
                                  "which supports any OpenStack service."),
     cfg.BoolOpt('test_custom_requirements',
                 default=False,
+                deprecated_group='rbac',
                 help="""
 This option determines whether Patrole should run against a
 `custom_requirements_file` which defines RBAC requirements. The
@@ -101,6 +112,7 @@
 test result: fail (over-permission)
 """),
     cfg.StrOpt('custom_requirements_file',
+               deprecated_group='rbac',
                help="""
 File path of the yaml file that defines your RBAC requirements. This
 file must be located on the same host that Patrole runs on. The yaml
@@ -128,3 +140,30 @@
 allowed_role = the Keystone role that is allowed to perform the API
 """)
 ]
+
+
+rbac_group = cfg.OptGroup(name='rbac',
+                          title='RBAC testing options',
+                          help="This group is deprecated and will be removed "
+                               "in the next release. Use the [patrole] group "
+                               "instead.")
+
+patrole_log_group = cfg.OptGroup(
+    name='patrole_log', title='Patrole Logging Options')
+
+PatroleLogGroup = [
+    cfg.BoolOpt('enable_reporting',
+                default=False,
+                help="Enables reporting on RBAC expected and actual test "
+                     "results for each Patrole test"),
+    cfg.StrOpt('report_log_name',
+               default='patrole.log',
+               help="Name of file where output from 'enable_reporting' is "
+                    "logged. Note that this file is recreated on each "
+                    "invocation of patrole"),
+    cfg.StrOpt('report_log_path',
+               default='.',
+               help="Path (relative or absolute) where the output from "
+                    "'enable_reporting' is logged. This is combined with"
+                    "report_log_name to generate the full path."),
+]
diff --git a/patrole_tempest_plugin/hacking/checks.py b/patrole_tempest_plugin/hacking/checks.py
index a3ef01f..eb73ef1 100644
--- a/patrole_tempest_plugin/hacking/checks.py
+++ b/patrole_tempest_plugin/hacking/checks.py
@@ -31,14 +31,13 @@
 RAND_NAME_HYPHEN_RE = re.compile(r".*rand_name\(.+[\-\_][\"\']\)")
 MUTABLE_DEFAULT_ARGS = re.compile(r"^\s*def .+\((.+=\{\}|.+=\[\])")
 TESTTOOLS_SKIP_DECORATOR = re.compile(r'\s*@testtools\.skip\((.*)\)')
-TEST_METHOD = re.compile(r"^    def test_.+")
 CLASS = re.compile(r"^class .+")
 RBAC_CLASS_NAME_RE = re.compile(r'class .+RbacTest')
 RULE_VALIDATION_DECORATOR = re.compile(
-    r'\s*@.*rbac_rule_validation.action\((.*)\)')
+    r'\s*@rbac_rule_validation.action\(.*')
 IDEMPOTENT_ID_DECORATOR = re.compile(r'\s*@decorators\.idempotent_id\((.*)\)')
 
-previous_decorator = None
+have_rbac_decorator = False
 
 
 def import_no_clients_in_api_tests(physical_line, filename):
@@ -144,8 +143,7 @@
     yield (0, msg)
 
 
-def no_rbac_rule_validation_decorator(physical_line, filename,
-                                      previous_logical):
+def no_rbac_rule_validation_decorator(physical_line, filename):
     """Check that each test has the ``rbac_rule_validation.action`` decorator.
 
     Checks whether the test function has "@rbac_rule_validation.action"
@@ -157,22 +155,24 @@
 
     P100
     """
-    global previous_decorator
+    global have_rbac_decorator
 
-    if "patrole_tempest_plugin/tests/api" in filename:
+    if ("patrole_tempest_plugin/tests/api" in filename or
+            "patrole_tempest_plugin/tests/scenario" in filename):
 
-        if IDEMPOTENT_ID_DECORATOR.match(physical_line):
-            previous_decorator = previous_logical
+        if RULE_VALIDATION_DECORATOR.match(physical_line):
+            have_rbac_decorator = True
             return
 
-        if TEST_METHOD.match(physical_line):
-            if not RULE_VALIDATION_DECORATOR.match(previous_logical) and \
-                not RULE_VALIDATION_DECORATOR.match(previous_decorator):
-                    return (0, "Must use rbac_rule_validation.action "
-                               "decorator for API and scenario tests")
+        if TEST_DEFINITION.match(physical_line):
+            if not have_rbac_decorator:
+                return (0, "Must use rbac_rule_validation.action "
+                           "decorator for API and scenario tests")
+
+            have_rbac_decorator = False
 
 
-def no_rbac_suffix_in_test_filename(physical_line, filename, previous_logical):
+def no_rbac_suffix_in_test_filename(filename):
     """Check that RBAC filenames end with "_rbac" suffix.
 
     P101
@@ -186,8 +186,7 @@
             return 0, "RBAC test filenames must end in _rbac suffix"
 
 
-def no_rbac_test_suffix_in_test_class_name(physical_line, filename,
-                                           previous_logical):
+def no_rbac_test_suffix_in_test_class_name(physical_line, filename):
     """Check that RBAC class names end with "RbacTest"
 
     P102
@@ -202,7 +201,7 @@
                 return 0, "RBAC test class names must end in 'RbacTest'"
 
 
-def no_client_alias_in_test_cases(filename, logical_line):
+def no_client_alias_in_test_cases(logical_line, filename):
     """Check that test cases don't use "self.client" to define a client.
 
     P103
diff --git a/patrole_tempest_plugin/plugin.py b/patrole_tempest_plugin/plugin.py
index 28ce12c..b7717ea 100644
--- a/patrole_tempest_plugin/plugin.py
+++ b/patrole_tempest_plugin/plugin.py
@@ -13,15 +13,21 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import logging
 import os
 
+from oslo_concurrency import lockutils
+
 from tempest import config
 from tempest.test_discover import plugins
 
 from patrole_tempest_plugin import config as project_config
 
+RBACLOG = logging.getLogger('rbac_reporting')
+
 
 class PatroleTempestPlugin(plugins.TempestPlugin):
+
     def load_tests(self):
         base_path = os.path.split(os.path.dirname(
             os.path.abspath(__file__)))[0]
@@ -29,11 +35,51 @@
         full_test_dir = os.path.join(base_path, test_dir)
         return full_test_dir, base_path
 
+    @lockutils.synchronized('_reset_log_file')
+    def _reset_log_file(self, logfile):
+        try:
+            os.remove(logfile)
+        except OSError:
+            pass
+
+    def _configure_per_test_logging(self, conf):
+        # Separate log handler for rbac reporting
+        RBACLOG.setLevel(level=logging.INFO)
+        # Set up proper directory handling
+        report_abs_path = os.path.abspath(conf.patrole_log.report_log_path)
+        report_path = os.path.join(
+            report_abs_path, conf.patrole_log.report_log_name)
+
+        # Remove the log file if it exists
+        self._reset_log_file(report_path)
+
+        # Delay=True so that we don't end up creating an empty file if we
+        # never log to it.
+        rbac_report_handler = logging.FileHandler(
+            filename=report_path, delay=True, mode='a')
+        rbac_report_handler.setFormatter(
+            fmt=logging.Formatter(fmt='%(message)s'))
+        RBACLOG.addHandler(rbac_report_handler)
+
     def register_opts(self, conf):
+        # TODO(fmontei): Remove ``rbac_group`` in a future release as it is
+        # currently deprecated.
         config.register_opt_group(
             conf,
             project_config.rbac_group,
-            project_config.RbacGroup)
+            project_config.PatroleGroup)
+        config.register_opt_group(
+            conf,
+            project_config.patrole_group,
+            project_config.PatroleGroup)
+        config.register_opt_group(
+            conf,
+            project_config.patrole_log_group,
+            project_config.PatroleLogGroup)
+
+        if conf.patrole_log.enable_reporting:
+            self._configure_per_test_logging(conf)
 
     def get_opt_lists(self):
-        return [(project_config.rbac_group.name, project_config.RbacGroup)]
+        return [(project_config.patrole_group.name,
+                 project_config.PatroleGroup)]
diff --git a/patrole_tempest_plugin/rbac_policy_parser.py b/patrole_tempest_plugin/policy_authority.py
similarity index 96%
rename from patrole_tempest_plugin/rbac_policy_parser.py
rename to patrole_tempest_plugin/policy_authority.py
index 88e9faa..af227c4 100644
--- a/patrole_tempest_plugin/rbac_policy_parser.py
+++ b/patrole_tempest_plugin/policy_authority.py
@@ -31,7 +31,7 @@
 LOG = logging.getLogger(__name__)
 
 
-class RbacPolicyParser(RbacAuthority):
+class PolicyAuthority(RbacAuthority):
     """A class for parsing policy rules into lists of allowed roles.
 
     RBAC testing requires that each rule in a policy file be broken up into
@@ -72,11 +72,11 @@
 
         # Prioritize dynamically searching for policy files over relying on
         # deprecated service-specific policy file locations.
-        if CONF.rbac.custom_policy_files:
+        if CONF.patrole.custom_policy_files:
             self.discover_policy_files()
             self.path = self.policy_files.get(service)
         else:
-            self.path = getattr(CONF.rbac, '%s_policy_file' % str(service),
+            self.path = getattr(CONF.patrole, '%s_policy_file' % str(service),
                                 None)
 
         self.rules = policy.Rules.load(self._get_policy_data(service),
@@ -110,11 +110,11 @@
     def discover_policy_files(cls):
         # Dynamically discover the policy file for each service in
         # ``cls.available_services``. Pick the first ``candidate_path`` found
-        # out of the potential paths in ``CONF.rbac.custom_policy_files``.
+        # out of the potential paths in ``CONF.patrole.custom_policy_files``.
         if not hasattr(cls, 'policy_files'):
             cls.policy_files = {}
             for service in cls.available_services:
-                for candidate_path in CONF.rbac.custom_policy_files:
+                for candidate_path in CONF.patrole.custom_policy_files:
                     if os.path.isfile(candidate_path % service):
                         cls.policy_files.setdefault(service,
                                                     candidate_path % service)
@@ -178,7 +178,7 @@
             error_message = (
                 'Policy file for {0} service neither found in code nor at {1}.'
                 .format(service, [loc % service for loc in
-                                  CONF.rbac.custom_policy_files])
+                                  CONF.patrole.custom_policy_files])
             )
             raise rbac_exceptions.RbacParsingException(error_message)
 
diff --git a/patrole_tempest_plugin/rbac_rule_validation.py b/patrole_tempest_plugin/rbac_rule_validation.py
index c7bd38b..69274b3 100644
--- a/patrole_tempest_plugin/rbac_rule_validation.py
+++ b/patrole_tempest_plugin/rbac_rule_validation.py
@@ -23,8 +23,8 @@
 from tempest.lib import exceptions
 from tempest import test
 
+from patrole_tempest_plugin import policy_authority
 from patrole_tempest_plugin import rbac_exceptions
-from patrole_tempest_plugin import rbac_policy_parser
 from patrole_tempest_plugin import rbac_utils
 from patrole_tempest_plugin import requirements_authority
 
@@ -33,46 +33,95 @@
 
 _SUPPORTED_ERROR_CODES = [403, 404]
 
+RBACLOG = logging.getLogger('rbac_reporting')
+
 
 def action(service, rule='', admin_only=False, expected_error_code=403,
            extra_target_data=None):
-    """A decorator which does a policy check and matches it against test run.
+    """A decorator for verifying policy enforcement.
 
-    A decorator which allows for positive and negative RBAC testing. Given
-    an OpenStack service and a policy action enforced by that service, an
-    oslo.policy lookup is performed by calling `authority.get_permission`.
-    Alternatively, the RBAC tests can run against a YAML file that defines
-    policy requirements.
+    A decorator which allows for positive and negative RBAC testing. Given:
 
-    The following cases are possible:
+        * an OpenStack service,
+        * a policy action (``rule``) enforced by that service, and
+        * the test role defined by ``[patrole] rbac_test_role``
 
-    * If `allowed` is True and the test passes, this is a success.
-    * If `allowed` is True and the test fails, this is a failure.
-    * If `allowed` is False and the test passes, this is a failure.
-    * If `allowed` is False and the test fails, this is a success.
+    determines whether the test role has sufficient permissions to perform an
+    API call that enforces the ``rule``.
 
-    :param service: A OpenStack service: for example, "nova" or "neutron".
-    :param rule: A policy action defined in a policy.json file (or in code).
-    :param admin_only: Skips over oslo.policy check because the policy action
-        defined by `rule` is not enforced by the service's  policy enforcement
-        logic. For example, Keystone v2 performs an admin check for most of its
-        endpoints. If True, `rule` is effectively ignored.
+    This decorator should only be applied to an instance or subclass of
+        `tempest.base.BaseTestCase`.
+
+    The result from ``_is_authorized`` is used to determine the *expected*
+    test result. The *actual* test result is determined by running the
+    Tempest test this decorator applies to.
+
+    Below are the following possibilities from comparing the *expected* and
+    *actual* results:
+
+    1) If *expected* is True and the test passes (*actual*), this is a success.
+    2) If *expected* is True and the test fails (*actual*), this results in a
+       `Forbidden` exception failure.
+    3) If *expected* is False and the test passes (*actual*), this results in
+       an `OverPermission` exception failure.
+    4) If *expected* is False and the test fails (*actual*), this is a success.
+
+    As such, negative and positive testing can be applied using this decorator.
+
+    :param service: A OpenStack service. Examples: "nova" or "neutron".
+    :param rule: A policy action defined in a policy.json file (or in
+        code).
+
+        .. note::
+
+            Patrole currently only supports custom JSON policy files.
+
+    :param admin_only: Skips over `oslo.policy` check because the policy action
+        defined by `rule` is not enforced by the service's policy
+        enforcement engine. For example, Keystone v2 performs an admin check
+        for most of its endpoints. If True, `rule` is effectively
+        ignored.
     :param expected_error_code: Overrides default value of 403 (Forbidden)
         with endpoint-specific error code. Currently only supports 403 and 404.
         Support for 404 is needed because some services, like Neutron,
         intentionally throw a 404 for security reasons.
 
-    :raises NotFound: if `service` is invalid or
-                      if Tempest credentials cannot be found.
-    :raises Forbidden: for bullet (2) above.
-    :raises RbacOverPermission: for bullet (3) above.
+        .. warning::
+
+            A 404 should not be provided *unless* the endpoint masks a
+            `Forbidden` exception as a `Not Found` exception.
+
+    :param extra_target_data: Dictionary, keyed with `oslo.policy` generic
+        check names, whose values are string literals that reference nested
+        `tempest.base.BaseTestCase` attributes. Used by `oslo.policy` for
+        performing matching against attributes that are sent along with the API
+        calls. Example::
+
+            extra_target_data={
+                "target.token.user_id":
+                "os_alt.auth_provider.credentials.user_id"
+            })
+
+    :raises NotFound: If `service` is invalid or if Tempest credentials cannot
+        be found.
+    :raises Forbidden: For item (2) above.
+    :raises RbacOverPermission: For item (3) above.
+
+    Examples::
+
+        @rbac_rule_validation.action(
+            service="nova", rule="os_compute_api:os-agents")
+        def test_list_agents_rbac(self):
+            # The call to ``switch_role`` is mandatory.
+            self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+            self.agents_client.list_agents()
     """
 
     if extra_target_data is None:
         extra_target_data = {}
 
-    def decorator(func):
-        role = CONF.rbac.rbac_test_role
+    def decorator(test_func):
+        role = CONF.patrole.rbac_test_role
 
         def wrapper(*args, **kwargs):
             if args and isinstance(args[0], test.BaseTestCase):
@@ -82,28 +131,26 @@
                     '`rbac_rule_validation` decorator can only be applied to '
                     'an instance of `tempest.test.BaseTestCase`.')
 
-            if admin_only:
-                LOG.info("As admin_only is True, only admin role should be "
-                         "allowed to perform the API. Skipping oslo.policy "
-                         "check for policy action {0}.".format(rule))
-                allowed = rbac_utils.is_admin()
-            else:
-                allowed = _is_authorized(test_obj, service, rule,
-                                         extra_target_data)
+            allowed = _is_authorized(test_obj, service, rule,
+                                     extra_target_data, admin_only)
 
             expected_exception, irregular_msg = _get_exception_type(
                 expected_error_code)
 
+            test_status = 'Allowed'
+
             try:
-                func(*args, **kwargs)
+                test_func(*args, **kwargs)
             except rbac_exceptions.RbacInvalidService as e:
                 msg = ("%s is not a valid service." % service)
+                test_status = ('Error, %s' % (msg))
                 LOG.error(msg)
                 raise exceptions.NotFound(
                     "%s RbacInvalidService was: %s" % (msg, e))
             except (expected_exception,
                     rbac_exceptions.RbacConflictingPolicies,
                     rbac_exceptions.RbacMalformedResponse) as e:
+                test_status = 'Denied'
                 if irregular_msg:
                     LOG.warning(irregular_msg.format(rule, service))
                 if allowed:
@@ -115,9 +162,10 @@
             except Exception as e:
                 exc_info = sys.exc_info()
                 error_details = exc_info[1].__str__()
-                msg = ("%s An unexpected exception has occurred: Expected "
-                       "exception was %s, which was not thrown."
-                       % (error_details, expected_exception.__name__))
+                msg = ("An unexpected exception has occurred during test: %s, "
+                       "Exception was: %s"
+                       % (test_func.__name__, error_details))
+                test_status = ('Error, %s' % (error_details))
                 LOG.error(msg)
                 six.reraise(exc_info[0], exc_info[0](msg), exc_info[2])
             else:
@@ -130,30 +178,54 @@
             finally:
                 test_obj.rbac_utils.switch_role(test_obj,
                                                 toggle_rbac_role=False)
+                if CONF.patrole_log.enable_reporting:
+                    RBACLOG.info(
+                        "[Service]: %s, [Test]: %s, [Rule]: %s, "
+                        "[Expected]: %s, [Actual]: %s",
+                        service, test_func.__name__, rule,
+                        "Allowed" if allowed else "Denied",
+                        test_status)
 
         _wrapper = testtools.testcase.attr(role)(wrapper)
         return _wrapper
     return decorator
 
 
-def _is_authorized(test_obj, service, rule_name, extra_target_data):
+def _is_authorized(test_obj, service, rule, extra_target_data, admin_only):
     """Validates whether current RBAC role has permission to do policy action.
 
-    :param test_obj: type BaseTestCase (tempest base test class)
-    :param service: the OpenStack service that enforces ``rule_name``
-    :param rule_name: the name of the policy action
-    :param extra_target_data: dictionary with unresolved string literals that
-        reference nested BaseTestCase attributes
-    :returns: True if the current RBAC role can perform the policy action else
-        False
+    :param test_obj: An instance or subclass of `tempest.base.BaseTestCase`.
+    :param service: The OpenStack service that enforces ``rule``.
+    :param rule: The name of the policy action. Examples include
+        "identity:create_user" or "os_compute_api:os-agents".
+    :param extra_target_data: Dictionary, keyed with `oslo.policy` generic
+        check names, whose values are string literals that reference nested
+        `tempest.base.BaseTestCase` attributes. Used by `oslo.policy` for
+        performing matching against attributes that are sent along with the API
+        calls.
+    :param admin_only: Skips over `oslo.policy` check because the policy action
+        defined by `rule` is not enforced by the service's policy
+        enforcement engine. For example, Keystone v2 performs an admin check
+        for most of its endpoints. If True, `rule` is effectively
+        ignored.
 
-    :raises RbacResourceSetupFailed: if project_id or user_id are missing from
-        the Tempest test object's `auth_provider`
-    :raises RbacParsingException: if ``CONF.rbac.strict_policy_check`` is
-        enabled and the ``rule_name`` does not exist in the system
-    :raises skipException: if ``CONF.rbac.strict_policy_check`` is
-        disabled and the ``rule_name`` does not exist in the system
+    :returns: True if the current RBAC role can perform the policy action,
+        else False.
+
+    :raises RbacResourceSetupFailed: If `project_id` or `user_id` are missing
+        from the `auth_provider` attribute in `test_obj`.
+    :raises RbacParsingException: if ``[patrole] strict_policy_check`` is True
+        and the ``rule`` does not exist in the system.
+    :raises skipException: If ``[patrole] strict_policy_check`` is False and
+        the ``rule`` does not exist in the system.
     """
+
+    if admin_only:
+        LOG.info("As admin_only is True, only admin role should be "
+                 "allowed to perform the API. Skipping oslo.policy "
+                 "check for policy action {0}.".format(rule))
+        return rbac_utils.is_admin()
+
     try:
         project_id = test_obj.os_primary.credentials.project_id
         user_id = test_obj.os_primary.credentials.user_id
@@ -164,28 +236,28 @@
         raise rbac_exceptions.RbacResourceSetupFailed(msg)
 
     try:
-        role = CONF.rbac.rbac_test_role
-        # Test RBAC against custom requirements. Otherwise use oslo.policy
-        if CONF.rbac.test_custom_requirements:
+        role = CONF.patrole.rbac_test_role
+        # Test RBAC against custom requirements. Otherwise use oslo.policy.
+        if CONF.patrole.test_custom_requirements:
             authority = requirements_authority.RequirementsAuthority(
-                CONF.rbac.custom_requirements_file, service)
+                CONF.patrole.custom_requirements_file, service)
         else:
             formatted_target_data = _format_extra_target_data(
                 test_obj, extra_target_data)
-            authority = rbac_policy_parser.RbacPolicyParser(
+            authority = policy_authority.PolicyAuthority(
                 project_id, user_id, service,
                 extra_target_data=formatted_target_data)
-        is_allowed = authority.allowed(rule_name, role)
+        is_allowed = authority.allowed(rule, role)
 
         if is_allowed:
-            LOG.debug("[Action]: %s, [Role]: %s is allowed!", rule_name,
+            LOG.debug("[Action]: %s, [Role]: %s is allowed!", rule,
                       role)
         else:
             LOG.debug("[Action]: %s, [Role]: %s is NOT allowed!",
-                      rule_name, role)
+                      rule, role)
         return is_allowed
     except rbac_exceptions.RbacParsingException as e:
-        if CONF.rbac.strict_policy_check:
+        if CONF.patrole.strict_policy_check:
             raise e
         else:
             raise testtools.TestCase.skipException(str(e))
@@ -210,7 +282,7 @@
     irregular_msg = None
 
     if not isinstance(expected_error_code, six.integer_types) \
-        or expected_error_code not in _SUPPORTED_ERROR_CODES:
+            or expected_error_code not in _SUPPORTED_ERROR_CODES:
         msg = ("Please pass an expected error code. Currently "
                "supported codes: {0}".format(_SUPPORTED_ERROR_CODES))
         LOG.error(msg)
@@ -231,21 +303,24 @@
     """Formats the "extra_target_data" dictionary with correct test data.
 
     Before being formatted, "extra_target_data" is a dictionary that maps a
-    policy string like "trust.trustor_user_id" to a nested list of BaseTestCase
-    attributes. For example, the attribute list in:
+    policy string like "trust.trustor_user_id" to a nested list of
+    `tempest.base.BaseTestCase` attributes. For example, the attribute list in:
 
         "trust.trustor_user_id": "os.auth_provider.credentials.user_id"
 
-    is parsed by iteratively calling `getattr` until the value of "user_id"
+    is parsed by iteratively calling ``getattr`` until the value of "user_id"
     is resolved. The resulting dictionary returns:
 
-        "trust.trustor_user_id": "the user_id of the `primary` credential"
+        "trust.trustor_user_id": "the user_id of the `os_primary` credential"
 
-    :param test_obj: type BaseTestCase (tempest base test class)
-    :param extra_target_data: dictionary with unresolved string literals that
-        reference nested BaseTestCase attributes
-    :returns: dictionary containing additional object data needed by
-        oslo.policy to validate generic checks
+    :param test_obj: An instance or subclass of `tempest.base.BaseTestCase`.
+    :param extra_target_data: Dictionary, keyed with `oslo.policy` generic
+        check names, whose values are string literals that reference nested
+        `tempest.base.BaseTestCase` attributes. Used by `oslo.policy` for
+        performing matching against attributes that are sent along with the API
+        calls.
+    :returns: Dictionary containing additional object data needed by
+        `oslo.policy` to validate generic checks.
     """
     attr_value = test_obj
     formatted_target_data = {}
diff --git a/patrole_tempest_plugin/rbac_utils.py b/patrole_tempest_plugin/rbac_utils.py
index 5543cbb..9fa3740 100644
--- a/patrole_tempest_plugin/rbac_utils.py
+++ b/patrole_tempest_plugin/rbac_utils.py
@@ -20,8 +20,8 @@
 
 from oslo_log import log as logging
 from oslo_utils import excutils
-import testtools
 
+from tempest import clients
 from tempest.common import credentials_factory as credentials
 from tempest import config
 
@@ -39,7 +39,7 @@
     seamlessly swap between admin credentials, needed for setup and clean up,
     and primary credentials, needed to perform the API call which does
     policy enforcement. The primary credentials always cycle between roles
-    defined by ``[identity] admin_role`` and ``[rbac] rbac_test_role``.
+    defined by ``CONF.identity.admin_role`` and `CONF.patrole.rbac_test_role``.
     """
 
     def __init__(self, test_obj):
@@ -47,19 +47,13 @@
 
         :param test_obj: An instance of `tempest.test.BaseTestCase`.
         """
-        # Since we are going to instantiate a client manager with
-        # admin credentials, first check if admin is available.
-        if not credentials.is_admin_available(
-                identity_version=test_obj.get_identity_version()):
-            msg = "Missing Identity Admin API credentials in configuration."
-            raise testtools.TestCase.skipException(msg)
-
         # Intialize the admin roles_client to perform role switching.
-        admin_creds = test_obj.get_client_manager(credential_type='admin')
+        admin_mgr = clients.Manager(
+            credentials.get_configured_admin_credentials())
         if test_obj.get_identity_version() == 'v3':
-            admin_roles_client = admin_creds.roles_v3_client
+            admin_roles_client = admin_mgr.roles_v3_client
         else:
-            admin_roles_client = admin_creds.roles_client
+            admin_roles_client = admin_mgr.roles_client
 
         self.admin_roles_client = admin_roles_client
         self.switch_role(test_obj, toggle_rbac_role=False)
@@ -78,14 +72,10 @@
 
         Switch the role used by `os_primary` credentials to:
           * admin if `toggle_rbac_role` is False
-          * `[rbac] rbac_test_role` if `toggle_rbac_role` is True
+          * `CONF.patrole.rbac_test_role` if `toggle_rbac_role` is True
 
-        :param test_obj: An instance of `tempest.test.BaseTestCase`.
-        :param toggle_rbac_role: Role to switch `os_primary` Tempest creds to.
-        :returns: None.
-        :raises RbacResourceSetupFailed: If admin or test roles are missing. Or
-            if `toggle_rbac_role` is not a boolean value or role validation
-            fails.
+        :param test_obj: test object of type tempest.lib.base.BaseTestCase
+        :param toggle_rbac_role: role to switch `os_primary` Tempest creds to
         """
         self.user_id = test_obj.os_primary.credentials.user_id
         self.project_id = test_obj.os_primary.credentials.tenant_id
@@ -127,14 +117,14 @@
         admin_role_id = rbac_role_id = None
 
         for role in available_roles['roles']:
-            if role['name'] == CONF.rbac.rbac_test_role:
+            if role['name'] == CONF.patrole.rbac_test_role:
                 rbac_role_id = role['id']
             if role['name'] == CONF.identity.admin_role:
                 admin_role_id = role['id']
 
         if not all([admin_role_id, rbac_role_id]):
-            msg = ("Roles defined by `[rbac] rbac_test_role` and `[identity] "
-                   "admin_role` must be defined in the system.")
+            msg = ("Roles defined by `[patrole] rbac_test_role` and "
+                   "`[identity] admin_role` must be defined in the system.")
             raise rbac_exceptions.RbacResourceSetupFailed(msg)
 
         self.admin_role_id = admin_role_id
@@ -207,14 +197,24 @@
 
     :returns: True if ``rbac_test_role`` is the admin role.
     """
-    return CONF.rbac.rbac_test_role == CONF.identity.admin_role
+    return CONF.patrole.rbac_test_role == CONF.identity.admin_role
 
 
 @six.add_metaclass(abc.ABCMeta)
 class RbacAuthority(object):
-    # TODO(rb560u): Add documentation explaining what this class is for
+    """Class for validating whether a given role can perform a policy action.
+
+    Any class that extends ``RbacAuthority`` provides the logic for determining
+    whether a role has permissions to execute a policy action.
+    """
 
     @abc.abstractmethod
-    def allowed(self, rule_name, role):
-        """Determine whether the role should be able to perform the API"""
-        return
+    def allowed(self, rule, role):
+        """Determine whether the role should be able to perform the API.
+
+        :param rule: The name of the policy enforced by the API.
+        :param role: The role used to determine whether ``rule`` can be
+            executed.
+        :returns: True if the ``role`` has permissions to execute
+            ``rule``, else False.
+        """
diff --git a/patrole_tempest_plugin/tests/api/compute/rbac_base.py b/patrole_tempest_plugin/tests/api/compute/rbac_base.py
index 7e9a402..bab193e 100644
--- a/patrole_tempest_plugin/tests/api/compute/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/compute/rbac_base.py
@@ -26,7 +26,7 @@
     @classmethod
     def skip_checks(cls):
         super(BaseV2ComputeRbacTest, cls).skip_checks()
-        if not CONF.rbac.enable_rbac:
+        if not CONF.patrole.enable_rbac:
             raise cls.skipException(
                 '%s skipped as RBAC testing not enabled' % cls.__name__)
 
@@ -53,9 +53,12 @@
         for flavor in cls.flavors:
             test_utils.call_and_ignore_notfound_exc(
                 cls.flavors_client.delete_flavor, flavor['id'])
+        for flavor in cls.flavors:
+            test_utils.call_and_ignore_notfound_exc(
+                cls.flavors_client.wait_for_resource_deletion, flavor['id'])
 
     @classmethod
-    def _create_flavor(cls, **kwargs):
+    def create_flavor(cls, **kwargs):
         flavor_kwargs = {
             "name": data_utils.rand_name(cls.__name__ + '-flavor'),
             "ram": data_utils.rand_int_id(1, 10),
diff --git a/patrole_tempest_plugin/tests/api/compute/test_flavor_access_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_flavor_access_rbac.py
index b196d93..26c9957 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_flavor_access_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_flavor_access_rbac.py
@@ -38,7 +38,7 @@
     @classmethod
     def resource_setup(cls):
         super(FlavorAccessRbacTest, cls).resource_setup()
-        cls.flavor_id = cls._create_flavor(is_public=False)['id']
+        cls.flavor_id = cls.create_flavor(is_public=False)['id']
         cls.public_flavor_id = CONF.compute.flavor_ref
         cls.tenant_id = cls.os_primary.credentials.tenant_id
 
diff --git a/patrole_tempest_plugin/tests/api/compute/test_flavor_extra_specs_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_flavor_extra_specs_rbac.py
index e59fd78..031d8ad 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_flavor_extra_specs_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_flavor_extra_specs_rbac.py
@@ -34,7 +34,7 @@
     @classmethod
     def resource_setup(cls):
         super(FlavorExtraSpecsRbacTest, cls).resource_setup()
-        cls.flavor = cls._create_flavor()
+        cls.flavor = cls.create_flavor()
 
     @classmethod
     def resource_cleanup(cls):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_flavor_manage_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_flavor_manage_rbac.py
new file mode 100644
index 0000000..519a55a
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/compute/test_flavor_manage_rbac.py
@@ -0,0 +1,53 @@
+#    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.lib import decorators
+from tempest import test
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.compute import rbac_base
+
+
+class FlavorManageRbacTest(rbac_base.BaseV2ComputeRbacTest):
+
+    # Need admin to wait for resource deletion below to avoid test role
+    # having to pass extra policies.
+    credentials = ['primary', 'admin']
+
+    @classmethod
+    def skip_checks(cls):
+        super(FlavorManageRbacTest, cls).skip_checks()
+        if not test.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
+            msg = "OS-FLV-EXT-DATA extension not enabled."
+            raise cls.skipException(msg)
+
+    @decorators.idempotent_id('a4e7faec-7a4b-4809-9856-90d5b747ca35')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-flavor-manage:create")
+    def test_create_flavor_manage(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.create_flavor()
+
+    @decorators.idempotent_id('782e988e-061b-4c40-896f-a77c70c2b057')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-flavor-manage:delete")
+    def test_delete_flavor_manage(self):
+        flavor_id = self.create_flavor()['id']
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.flavors_client.delete_flavor(flavor_id)
+        self.os_admin.flavors_client.wait_for_resource_deletion(flavor_id)
diff --git a/patrole_tempest_plugin/tests/api/compute/test_flavor_rxtx_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_flavor_rxtx_rbac.py
index 33b1564..7340689 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_flavor_rxtx_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_flavor_rxtx_rbac.py
@@ -13,12 +13,17 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_config import cfg
+
 from tempest.lib import decorators
 from tempest import test
 
+from patrole_tempest_plugin import rbac_exceptions
 from patrole_tempest_plugin import rbac_rule_validation
 from patrole_tempest_plugin.tests.api.compute import rbac_base
 
+CONF = cfg.CONF
+
 
 class FlavorRxtxRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
@@ -29,11 +34,27 @@
             msg = "os-flavor-rxtx extension not enabled."
             raise cls.skipException(msg)
 
-    @decorators.idempotent_id('0278677c-6e69-4293-a387-b485781e61a1')
+    @decorators.idempotent_id('5e1fd9f0-9a08-485a-ad9c-0fc66e4d64b7')
     @rbac_rule_validation.action(
         service="nova",
         rule="os_compute_api:os-flavor-rxtx")
-    def test_create_flavor_rxtx(self):
+    def test_list_flavors_details_rxtx(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        # Enforces os_compute_api:os-flavor-rxtx.
-        self.flavors_client.list_flavors(detail=True)['flavors']
+        # Enforces os_compute_api:os-flavor-rxtx
+        result = self.flavors_client.list_flavors(detail=True)['flavors']
+        if 'rxtx_factor' not in result[0]:
+            raise rbac_exceptions.RbacMalformedResponse(
+                attribute='rxtx_factor')
+
+    @decorators.idempotent_id('70c55a07-c843-4627-a29d-ba78673c1e63')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-flavor-rxtx")
+    def test_get_flavor_rxtx(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        # Enforces os_compute_api:os-flavor-rxtx
+        result =\
+            self.flavors_client.show_flavor(CONF.compute.flavor_ref)['flavor']
+        if 'rxtx_factor' not in result:
+            raise rbac_exceptions.RbacMalformedResponse(
+                attribute='rxtx_factor')
diff --git a/patrole_tempest_plugin/tests/api/compute/test_security_groups_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_security_groups_rbac.py
index 17a6c74..36d8c2c 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_security_groups_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_security_groups_rbac.py
@@ -14,19 +14,104 @@
 #    under the License.
 
 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_rule_validation
 from patrole_tempest_plugin.tests.api.compute import rbac_base
 
 
-class SecurityGroupsRbacTest(rbac_base.BaseV2ComputeRbacTest):
+class SecurtiyGroupsRbacTest(rbac_base.BaseV2ComputeRbacTest):
+    """Tests non-deprecated security group policies. Requires network service.
+
+    This class tests non-deprecated policies for adding and removing a security
+    group to and from a server.
+    """
+
+    @classmethod
+    def skip_checks(cls):
+        super(SecurtiyGroupsRbacTest, cls).skip_checks()
+        # All the tests below require the network service.
+        # NOTE(gmann) Currently 'network' service is always True in
+        # test.get_service_list() So below check is not much of use.
+        # Commenting the below check as Tempest is moving the get_service_list
+        # from test.py to utils.
+        # If we want to check 'network' service availability, then
+        # get_service_list can be used from new location.
+        # if not test.get_service_list()['network']:
+        #    raise cls.skipException(
+        #        'Skipped because the network service is not available')
+
+    @classmethod
+    def setup_credentials(cls):
+        # A network and a subnet will be created for these tests.
+        cls.set_network_resources(network=True, subnet=True)
+        super(SecurtiyGroupsRbacTest, cls).setup_credentials()
+
+    @classmethod
+    def resource_setup(cls):
+        super(SecurtiyGroupsRbacTest, cls).resource_setup()
+        cls.server = cls.create_test_server(wait_until='ACTIVE')
+
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-security-groups")
+    @decorators.idempotent_id('3db159c6-a467-469f-9a25-574197885520')
+    def test_list_security_groups_by_server(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.servers_client.list_security_groups_by_server(self.server['id'])
+
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-security-groups")
+    @decorators.idempotent_id('ea1ca73f-2d1d-43cb-9a46-900d7927b357')
+    def test_create_security_group_for_server(self):
+        sg_name = self.create_security_group()['name']
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.servers_client.add_security_group(self.server['id'], name=sg_name)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.servers_client.remove_security_group,
+                        self.server['id'], name=sg_name)
+
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-security-groups")
+    @decorators.idempotent_id('0ad2e856-e2d3-4ac5-a620-f93d0d3d2626')
+    def test_remove_security_group_from_server(self):
+        sg_name = self.create_security_group()['name']
+
+        self.servers_client.add_security_group(self.server['id'], name=sg_name)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.servers_client.remove_security_group,
+                        self.server['id'], name=sg_name)
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.servers_client.remove_security_group(
+            self.server['id'], name=sg_name)
+
+
+class SecurityGroupsRbacMaxV235Test(rbac_base.BaseV2ComputeRbacTest):
 
     # Tests in this class will fail with a 404 from microversion 2.36,
     # according to:
     # https://developer.openstack.org/api-ref/compute/#security-groups-os-security-groups-deprecated
     max_microversion = '2.35'
 
+    @classmethod
+    def skip_checks(cls):
+        super(SecurityGroupsRbacMaxV235Test, cls).skip_checks()
+        # All the tests below require the network service.
+        # NOTE(gmann) Currently 'network' service is always True in
+        # test.get_service_list() So below check is not much of use.
+        # Commenting the below check as Tempest is moving the get_service_list
+        # from test.py to utils.
+        # If we want to check 'network' service availability, then
+        # get_service_list can be used from new location.
+        # if not test.get_service_list()['network']:
+        #    raise cls.skipException(
+        #        'Skipped because the network service is not available')
+
     @rbac_rule_validation.action(
         service="nova",
         rule="os_compute_api:os-security-groups")
@@ -58,9 +143,10 @@
     @decorators.idempotent_id('3de5c6bc-b822-469e-a627-82427d38b067')
     def test_update_security_groups(self):
         sec_group_id = self.create_security_group()['id']
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         new_name = data_utils.rand_name()
         new_desc = data_utils.rand_name()
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.security_groups_client.update_security_group(sec_group_id,
                                                           name=new_name,
                                                           description=new_desc)
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py
index b1956c2..654d3f1 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py
@@ -42,6 +42,8 @@
     Only applies to:
       * policy "families" that require server creation
       * small policy "families" -- i.e. containing one to three policies
+
+    Tests are ordered by policy name.
     """
 
     credentials = ['primary', 'admin']
@@ -96,7 +98,6 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.servers_client.inject_network_info(self.server['id'])
 
-    @decorators.attr(type=['slow'])
     @test.requires_ext(extension='os-admin-actions', service='compute')
     @rbac_rule_validation.action(
         service="nova",
@@ -135,10 +136,11 @@
         """Test list servers with config_drive property in response body."""
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         body = self.servers_client.list_servers(detail=True)['servers']
+        expected_attr = 'config_drive'
         # If the first server contains "config_drive", then all the others do.
-        if 'config_drive' not in body[0]:
+        if expected_attr not in body[0]:
             raise rbac_exceptions.RbacMalformedResponse(
-                attribute='config_drive')
+                attribute=expected_attr)
 
     @test.requires_ext(extension='os-config-drive', service='compute')
     @decorators.idempotent_id('55c62ef7-b72b-4970-acc6-05b0a4316e5d')
@@ -148,10 +150,12 @@
     def test_show_server_config_drive(self):
         """Test show server with config_drive property in response body."""
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+
         body = self.servers_client.show_server(self.server['id'])['server']
-        if 'config_drive' not in body:
+        expected_attr = 'config_drive'
+        if expected_attr not in body:
             raise rbac_exceptions.RbacMalformedResponse(
-                attribute="config_drive")
+                attribute=expected_attr)
 
     @test.requires_ext(extension='os-deferred-delete', service='compute')
     @decorators.idempotent_id('189bfed4-1e6d-475c-bb8c-d57e60895391')
@@ -164,6 +168,105 @@
         # Force-deleting a server enforces os-deferred-delete.
         self.servers_client.force_delete_server(self.server['id'])
 
+    @decorators.idempotent_id('d873740a-7b10-40a9-943d-7cc18115370e')
+    @test.requires_ext(extension='OS-EXT-AZ', service='compute')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-extended-availability-zone")
+    def test_list_servers_with_details_extended_availability_zone(self):
+        """Test list servers OS-EXT-AZ:availability_zone attr in resp body."""
+        expected_attr = 'OS-EXT-AZ:availability_zone'
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        body = self.servers_client.list_servers(detail=True)['servers']
+        # If the first server contains `expected_attr`, then all the others do.
+        if expected_attr not in body[0]:
+            raise rbac_exceptions.RbacMalformedResponse(
+                attribute=expected_attr)
+
+    @decorators.idempotent_id('727e5360-770a-4b9c-8015-513a40216635')
+    @test.requires_ext(extension='OS-EXT-AZ', service='compute')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-extended-availability-zone")
+    def test_show_server_extended_availability_zone(self):
+        """Test show server OS-EXT-AZ:availability_zone attr in resp body."""
+        expected_attr = 'OS-EXT-AZ:availability_zone'
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        body = self.servers_client.show_server(self.server['id'])['server']
+        if expected_attr not in body:
+            raise rbac_exceptions.RbacMalformedResponse(
+                attribute=expected_attr)
+
+    @decorators.idempotent_id('82053c27-3134-4003-9b55-bc9fafdb0e3b')
+    @test.requires_ext(extension='OS-EXT-STS', service='compute')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-extended-status")
+    def test_list_servers_extended_status(self):
+        """Test list servers with extended properties in response body."""
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        body = self.servers_client.list_servers(detail=True)['servers']
+
+        expected_attrs = ('OS-EXT-STS:task_state', 'OS-EXT-STS:vm_state',
+                          'OS-EXT-STS:power_state')
+        for attr in expected_attrs:
+            if attr not in body[0]:
+                raise rbac_exceptions.RbacMalformedResponse(
+                    attribute=attr)
+
+    @decorators.idempotent_id('7d2620a5-eea1-4a8b-96ea-86ad77a73fc8')
+    @test.requires_ext(extension='OS-EXT-STS', service='compute')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-extended-status")
+    def test_show_server_extended_status(self):
+        """Test show server with extended properties in response body."""
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        body = self.servers_client.show_server(self.server['id'])['server']
+
+        expected_attrs = ('OS-EXT-STS:task_state', 'OS-EXT-STS:vm_state',
+                          'OS-EXT-STS:power_state')
+        for attr in expected_attrs:
+            if attr not in body:
+                raise rbac_exceptions.RbacMalformedResponse(
+                    attribute=attr)
+
+    @decorators.idempotent_id('21e39cbe-6c32-48fc-80dd-3e1fece6053f')
+    @test.requires_ext(extension='os-extended-volumes', service='compute')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-extended-volumes")
+    def test_list_servers_with_details_extended_volumes(self):
+        """Test list servers os-extended-volumes:volumes_attached attr in resp
+        body.
+        """
+        expected_attr = 'os-extended-volumes:volumes_attached'
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        body = self.servers_client.list_servers(detail=True)['servers']
+        if expected_attr not in body[0]:
+            raise rbac_exceptions.RbacMalformedResponse(
+                attribute=expected_attr)
+
+    @decorators.idempotent_id('7f163708-0d25-4138-8512-dfdd72a92989')
+    @test.requires_ext(extension='os-extended-volumes', service='compute')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-extended-volumes")
+    def test_show_server_extended_volumes(self):
+        """Test show server os-extended-volumes:volumes_attached attr in resp
+        body.
+        """
+        expected_attr = 'os-extended-volumes:volumes_attached'
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        body = self.servers_client.show_server(self.server['id'])['server']
+        if expected_attr not in body:
+            raise rbac_exceptions.RbacMalformedResponse(
+                attribute=expected_attr)
+
     @test.requires_ext(extension='os-instance-actions', service='compute')
     @decorators.idempotent_id('9d1b131d-407e-4fa3-8eef-eb2c4526f1da')
     @rbac_rule_validation.action(
@@ -200,39 +303,28 @@
             raise rbac_exceptions.RbacMalformedResponse(
                 attribute='events.traceback')
 
-    @decorators.idempotent_id('82053c27-3134-4003-9b55-bc9fafdb0e3b')
-    @test.requires_ext(extension='OS-EXT-STS', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-extended-status")
-    def test_list_servers_extended_status(self):
-        """Test list servers with extended properties in response body."""
+        rule="os_compute_api:os-keypairs")
+    @decorators.idempotent_id('81e6fa34-c06b-42ca-b195-82bf8699b940')
+    def test_show_server_keypair(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        body = self.servers_client.list_servers(detail=True)['servers']
+        result =\
+            self.servers_client.show_server(self.server['id'])['server']
+        if 'key_name' not in result:
+            raise rbac_exceptions.RbacMalformedResponse(
+                attribute='key_name')
 
-        expected_attrs = ('OS-EXT-STS:task_state', 'OS-EXT-STS:vm_state',
-                          'OS-EXT-STS:power_state')
-        for attr in expected_attrs:
-            if attr not in body[0]:
-                raise rbac_exceptions.RbacMalformedResponse(
-                    attribute=attr)
-
-    @decorators.idempotent_id('7d2620a5-eea1-4a8b-96ea-86ad77a73fc8')
-    @test.requires_ext(extension='OS-EXT-STS', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-extended-status")
-    def test_show_server_extended_status(self):
-        """Test show server with extended properties in response body."""
+        rule="os_compute_api:os-keypairs")
+    @decorators.idempotent_id('41ca4280-ec59-4b80-a9b1-6bc6366faf39')
+    def test_list_servers_keypairs(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        body = self.servers_client.show_server(self.server['id'])['server']
-
-        expected_attrs = ('OS-EXT-STS:task_state', 'OS-EXT-STS:vm_state',
-                          'OS-EXT-STS:power_state')
-        for attr in expected_attrs:
-            if attr not in body:
-                raise rbac_exceptions.RbacMalformedResponse(
-                    attribute=attr)
+        result = self.servers_client.list_servers(detail=True)['servers']
+        if 'key_name' not in result[0]:
+            raise rbac_exceptions.RbacMalformedResponse(
+                attribute='key_name')
 
     @rbac_rule_validation.action(
         service="nova",
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
index 10ea801..35ca437 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
@@ -18,7 +18,6 @@
 from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions
 from tempest import test
@@ -41,69 +40,12 @@
         cls.networks_client = cls.os_primary.networks_client
         cls.ports_client = cls.os_primary.ports_client
         cls.subnets_client = cls.os_primary.subnets_client
+        cls.admin_servers_client = cls.os_admin.servers_client
 
     @classmethod
     def resource_setup(cls):
         super(ComputeServersRbacTest, cls).resource_setup()
         cls.server = cls.create_test_server(wait_until='ACTIVE')
-        # Create a volume
-        volume_name = data_utils.rand_name(cls.__name__ + '-volume')
-        name_field = 'name'
-        if not CONF.volume_feature_enabled.api_v2:
-            name_field = 'display_name'
-
-        params = {name_field: volume_name,
-                  'imageRef': CONF.compute.image_ref,
-                  'size': CONF.volume.volume_size}
-
-        volume = cls.volumes_client.create_volume(**params)['volume']
-        waiters.wait_for_volume_resource_status(cls.volumes_client,
-                                                volume['id'], 'available')
-        cls.volumes.append(volume)
-        cls.volume_id = volume['id']
-
-    def _create_network_resources(self):
-        # Create network
-        network_name = data_utils.rand_name(
-            self.__class__.__name__ + '-network')
-
-        network = self.networks_client.create_network(
-            name=network_name, port_security_enabled=True)['network']
-        self.addCleanup(self.networks_client.delete_network, network['id'])
-
-        # Create subnet for the network
-        subnet_name = data_utils.rand_name(self.__class__.__name__ + '-subnet')
-        subnet = self.subnets_client.create_subnet(
-            name=subnet_name,
-            network_id=network['id'],
-            cidr=CONF.network.project_network_cidr,
-            ip_version=4)['subnet']
-        self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
-
-        return network
-
-    def _create_test_server_with_volume(self, volume_id):
-        # Create a server with the volume created earlier
-        server_name = data_utils.rand_name(self.__class__.__name__ + "-server")
-        bd_map_v2 = [{'uuid': volume_id,
-                      'source_type': 'volume',
-                      'destination_type': 'volume',
-                      'boot_index': 0,
-                      'delete_on_termination': True}]
-        device_mapping = {'block_device_mapping_v2': bd_map_v2}
-
-        # Since the server is booted from volume, the imageRef does not need
-        # to be specified.
-        server = self.servers_client.create_server(
-            name=server_name, imageRef='',
-            flavorRef=CONF.compute.flavor_ref,
-            **device_mapping)['server']
-
-        waiters.wait_for_server_status(
-            self.os_admin.servers_client, server['id'], 'ACTIVE')
-
-        self.servers.append(server)
-        return server
 
     @rbac_rule_validation.action(
         service="nova",
@@ -139,8 +81,25 @@
         rule="os_compute_api:servers:create:attach_volume")
     @decorators.idempotent_id('eeddac5e-15aa-454f-838d-db608aae4dd8')
     def test_create_server_attach_volume(self):
+        # To create a bootable volume, the UUID of the image from which
+        # to create the volume must be included as the imageRef attribute in
+        # the request body.
+        volume_id = self.create_volume(
+            imageRef=CONF.compute.image_ref,
+            size=CONF.volume.volume_size)['id']
+
+        server_name = data_utils.rand_name(self.__class__.__name__ + "-server")
+        bd_map_v2 = [{'uuid': volume_id,
+                      'source_type': 'volume',
+                      'destination_type': 'volume',
+                      'boot_index': 0,
+                      'delete_on_termination': True}]
+        device_mapping = {'block_device_mapping_v2': bd_map_v2}
+
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._create_test_server_with_volume(self.volume_id)
+        # Use image_id='' to avoid using the default image in tempest.conf.
+        self.create_test_server(name=server_name, image_id='',
+                                **device_mapping)
 
     @test.services('network')
     @rbac_rule_validation.action(
@@ -148,12 +107,33 @@
         rule="os_compute_api:servers:create:attach_network")
     @decorators.idempotent_id('b44cd4ff-50a4-42ce-ada3-724e213cd540')
     def test_create_server_attach_network(self):
-        network = self._create_network_resources()
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        def _create_network_resources():
+            # Create network
+            network_name = data_utils.rand_name(
+                self.__class__.__name__ + '-network')
+
+            network = self.networks_client.create_network(
+                name=network_name, port_security_enabled=True)['network']
+            self.addCleanup(self.networks_client.delete_network, network['id'])
+
+            # Create subnet for the network
+            subnet_name = data_utils.rand_name(
+                self.__class__.__name__ + '-subnet')
+            subnet = self.subnets_client.create_subnet(
+                name=subnet_name,
+                network_id=network['id'],
+                cidr=CONF.network.project_network_cidr,
+                ip_version=4)['subnet']
+            self.addCleanup(self.subnets_client.delete_subnet, subnet['id'])
+
+            return network
+
+        network = _create_network_resources()
         network_id = {'uuid': network['id']}
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         server = self.create_test_server(wait_until='ACTIVE',
                                          networks=[network_id])
-
         self.addCleanup(waiters.wait_for_server_termination,
                         self.servers_client, server['id'])
         self.addCleanup(self.servers_client.delete_server, server['id'])
@@ -164,10 +144,11 @@
     @decorators.idempotent_id('062e3440-e873-4b41-9317-bf6d8be50c12')
     def test_delete_server(self):
         server = self.create_test_server(wait_until='ACTIVE')
+
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.servers_client.delete_server(server['id'])
         waiters.wait_for_server_termination(
-            self.os_admin.servers_client, server['id'])
+            self.admin_servers_client, server['id'])
 
     @rbac_rule_validation.action(
         service="nova",
@@ -178,76 +159,10 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         try:
             self.servers_client.update_server(self.server['id'], name=new_name)
-            waiters.wait_for_server_status(self.os_admin.servers_client,
+            waiters.wait_for_server_status(self.admin_servers_client,
                                            self.server['id'], 'ACTIVE')
         except exceptions.ServerFault as e:
             # Some other policy may have blocked it.
             LOG.info("ServerFault exception caught. Some other policy "
                      "blocked updating of server")
             raise rbac_exceptions.RbacConflictingPolicies(e)
-
-
-class SecurtiyGroupsRbacTest(base.BaseV2ComputeRbacTest):
-    """Tests non-deprecated security group policies. Requires network service.
-
-    This class tests non-deprecated policies for adding and removing a security
-    group to and from a server.
-    """
-
-    @classmethod
-    def setup_credentials(cls):
-        # A network and a subnet will be created for these tests.
-        cls.set_network_resources(network=True, subnet=True)
-        super(SecurtiyGroupsRbacTest, cls).setup_credentials()
-
-    @classmethod
-    def skip_checks(cls):
-        super(SecurtiyGroupsRbacTest, cls).skip_checks()
-        # All the tests below require the network service.
-        if not test.get_service_list()['network']:
-            raise cls.skipException(
-                'Skipped because the network service is not available')
-
-    @classmethod
-    def resource_setup(cls):
-        super(SecurtiyGroupsRbacTest, cls).resource_setup()
-        cls.server = cls.create_test_server(wait_until='ACTIVE')
-
-    @rbac_rule_validation.action(
-        service="nova",
-        rule="os_compute_api:os-security-groups")
-    @decorators.idempotent_id('3db159c6-a467-469f-9a25-574197885520')
-    def test_list_security_groups_by_server(self):
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.servers_client.list_security_groups_by_server(self.server['id'])
-
-    @decorators.attr(type=["slow"])
-    @rbac_rule_validation.action(
-        service="nova",
-        rule="os_compute_api:os-security-groups")
-    @decorators.idempotent_id('ea1ca73f-2d1d-43cb-9a46-900d7927b357')
-    def test_create_security_group_for_server(self):
-        sg_name = self.create_security_group()['name']
-
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.servers_client.add_security_group(self.server['id'], name=sg_name)
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.servers_client.remove_security_group,
-                        self.server['id'], name=sg_name)
-
-    @decorators.attr(type=["slow"])
-    @rbac_rule_validation.action(
-        service="nova",
-        rule="os_compute_api:os-security-groups")
-    @decorators.idempotent_id('0ad2e856-e2d3-4ac5-a620-f93d0d3d2626')
-    def test_remove_security_group_from_server(self):
-        sg_name = self.create_security_group()['name']
-
-        self.servers_client.add_security_group(self.server['id'], name=sg_name)
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.servers_client.remove_security_group,
-                        self.server['id'], name=sg_name)
-
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.servers_client.remove_security_group(
-            self.server['id'], name=sg_name)
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_volume_attachments_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_volume_attachments_rbac.py
index 943cb69..65d9edb 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_volume_attachments_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_volume_attachments_rbac.py
@@ -97,6 +97,7 @@
                         self.servers_client.detach_volume,
                         self.server['id'], alt_volume['id'])
 
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="nova",
         rule="os_compute_api:os-volumes-attachments:delete")
diff --git a/patrole_tempest_plugin/tests/api/identity/rbac_base.py b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
index 8ee80c6..51daf96 100644
--- a/patrole_tempest_plugin/tests/api/identity/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
@@ -31,7 +31,7 @@
     @classmethod
     def skip_checks(cls):
         super(BaseIdentityV2RbacTest, cls).skip_checks()
-        if not CONF.rbac.enable_rbac:
+        if not CONF.patrole.enable_rbac:
             raise cls.skipException(
                 "%s skipped as RBAC testing not enabled" % cls.__name__)
 
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_users_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_users_rbac.py
index 0c85240..5812f9e 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_users_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_users_rbac.py
@@ -20,11 +20,11 @@
 from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
-class IdentityUserV3AdminRbacTest(rbac_base.BaseIdentityV3RbacTest):
+class IdentityUserV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
 
     @classmethod
     def resource_setup(cls):
-        super(IdentityUserV3AdminRbacTest, cls).resource_setup()
+        super(IdentityUserV3RbacTest, cls).resource_setup()
         cls.default_user_id = cls.os_primary.credentials.user_id
 
     @rbac_rule_validation.action(service="keystone",
@@ -71,19 +71,6 @@
         self.users_client.show_user(self.default_user_id)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:change_password")
-    @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d90a')
-    def test_change_password(self):
-        original_password = data_utils.rand_password()
-        user = self.setup_test_user(password=original_password)
-
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.users_client.update_user_password(
-            user['id'],
-            original_password=original_password,
-            password=data_utils.rand_password())
-
-    @rbac_rule_validation.action(service="keystone",
                                  rule="identity:list_groups_for_user")
     @decorators.idempotent_id('bd5946d4-46d2-423d-a800-a3e7aabc18b3')
     def test_list_own_user_group(self):
diff --git a/patrole_tempest_plugin/tests/api/image/rbac_base.py b/patrole_tempest_plugin/tests/api/image/rbac_base.py
index e49e2f6..dd4e5ed 100644
--- a/patrole_tempest_plugin/tests/api/image/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/image/rbac_base.py
@@ -24,7 +24,7 @@
     @classmethod
     def skip_checks(cls):
         super(BaseV1ImageRbacTest, cls).skip_checks()
-        if not CONF.rbac.enable_rbac:
+        if not CONF.patrole.enable_rbac:
             raise cls.skipException(
                 "%s skipped as RBAC testing not enabled" % cls.__name__)
 
@@ -39,7 +39,7 @@
     @classmethod
     def skip_checks(cls):
         super(BaseV2ImageRbacTest, cls).skip_checks()
-        if not CONF.rbac.enable_rbac:
+        if not CONF.patrole.enable_rbac:
             raise cls.skipException(
                 "%s skipped as RBAC testing not enabled" % cls.__name__)
 
diff --git a/patrole_tempest_plugin/tests/api/image/test_image_resource_types_rbac.py b/patrole_tempest_plugin/tests/api/image/test_image_resource_types_rbac.py
index 6727cc8..456e10b 100644
--- a/patrole_tempest_plugin/tests/api/image/test_image_resource_types_rbac.py
+++ b/patrole_tempest_plugin/tests/api/image/test_image_resource_types_rbac.py
@@ -37,7 +37,7 @@
         test_utils.call_and_ignore_notfound_exc(
             cls.namespaces_client.delete_namespace,
             cls.namespace_name)
-        super(ImageResourceTypesRbacTest, cls).resource_setup()
+        super(ImageResourceTypesRbacTest, cls).resource_cleanup()
 
     @rbac_rule_validation.action(service="glance",
                                  rule="list_metadef_resource_types")
diff --git a/patrole_tempest_plugin/tests/api/network/rbac_base.py b/patrole_tempest_plugin/tests/api/network/rbac_base.py
index 6cf0138..b495098 100644
--- a/patrole_tempest_plugin/tests/api/network/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/network/rbac_base.py
@@ -26,7 +26,7 @@
     @classmethod
     def skip_checks(cls):
         super(BaseNetworkRbacTest, cls).skip_checks()
-        if not CONF.rbac.enable_rbac:
+        if not CONF.patrole.enable_rbac:
             raise cls.skipException(
                 "%s skipped as RBAC testing not enabled" % cls.__name__)
 
diff --git a/patrole_tempest_plugin/tests/api/network/test_routers_rbac.py b/patrole_tempest_plugin/tests/api/network/test_routers_rbac.py
index 9ed9eb6..45a5cda 100644
--- a/patrole_tempest_plugin/tests/api/network/test_routers_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_routers_rbac.py
@@ -296,8 +296,7 @@
                         distributed=False)
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="delete_router",
-                                 expected_error_code=404)
+                                 rule="delete_router")
     @decorators.idempotent_id('c0634dd5-0467-48f7-a4ae-1014d8edb2a7')
     def test_delete_router(self):
         """Delete Router
@@ -309,10 +308,9 @@
         self.routers_client.delete_router(router['id'])
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="add_router_interface",
-                                 expected_error_code=404)
+                                 rule="add_router_interface")
     @decorators.idempotent_id('a0627778-d68d-4913-881b-e345360cca19')
-    def test_add_router_interfaces(self):
+    def test_add_router_interface(self):
         """Add Router Interface
 
         RBAC test for the neutron add_router_interface policy
@@ -331,10 +329,9 @@
             subnet_id=subnet['id'])
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="remove_router_interface",
-                                 expected_error_code=404)
+                                 rule="remove_router_interface")
     @decorators.idempotent_id('ff2593a4-2bff-4c27-97d3-dd3702b27dfb')
-    def test_remove_router_interfaces(self):
+    def test_remove_router_interface(self):
         """Remove Router Interface
 
         RBAC test for the neutron remove_router_interface policy
diff --git a/patrole_tempest_plugin/tests/api/network/test_subnetpools_rbac.py b/patrole_tempest_plugin/tests/api/network/test_subnetpools_rbac.py
index 8c799b6..44f6be4 100644
--- a/patrole_tempest_plugin/tests/api/network/test_subnetpools_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_subnetpools_rbac.py
@@ -102,8 +102,7 @@
 
     @decorators.idempotent_id('a16f4e5c-0675-415f-b636-00af00638693')
     @rbac_rule_validation.action(service="neutron",
-                                 rule="update_subnetpool:is_default",
-                                 expected_error_code=404)
+                                 rule="update_subnetpool:is_default")
     def test_update_subnetpool_is_default(self):
         """Update default subnetpool.
 
@@ -123,8 +122,7 @@
             default_pool['id'], description=original_desc, is_default=True)
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="delete_subnetpool",
-                                 expected_error_code=404)
+                                 rule="delete_subnetpool")
     @decorators.idempotent_id('50f5944e-43e5-457b-ab50-fb48a73f0d3e')
     def test_delete_subnetpool(self):
         """Delete subnetpool.
diff --git a/patrole_tempest_plugin/tests/api/volume/rbac_base.py b/patrole_tempest_plugin/tests/api/volume/rbac_base.py
index 4d3b612..6db364e 100644
--- a/patrole_tempest_plugin/tests/api/volume/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/volume/rbac_base.py
@@ -26,7 +26,7 @@
     @classmethod
     def skip_checks(cls):
         super(BaseVolumeRbacTest, cls).skip_checks()
-        if not CONF.rbac.enable_rbac:
+        if not CONF.patrole.enable_rbac:
             raise cls.skipException(
                 "%s skipped as RBAC testing not enabled" % cls.__name__)
 
@@ -35,14 +35,8 @@
         super(BaseVolumeRbacTest, cls).setup_clients()
         cls.rbac_utils = rbac_utils.RbacUtils(cls)
 
-        version_checker = {
-            2: [cls.os_primary.volume_hosts_v2_client,
-                cls.os_primary.volume_types_v2_client],
-            3: [cls.os_primary.volume_hosts_v2_client,
-                cls.os_primary.volume_types_v2_client]
-        }
-        cls.volume_hosts_client, cls.volume_types_client = \
-            version_checker[cls._api_version]
+        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
         cls.group_types_client = cls.os_primary.group_types_v3_client
 
diff --git a/patrole_tempest_plugin/tests/api/volume/test_qos_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_qos_rbac.py
index 7d9ec0f..2327de8 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_qos_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_qos_rbac.py
@@ -146,3 +146,7 @@
         self.qos_client.disassociate_all_qos(qos['id'])
         waiters.wait_for_qos_operations(self.admin_qos_client, qos['id'],
                                         'disassociate-all')
+
+
+class VolumeQOSV3RbacTest(VolumeQOSRbacTest):
+    _api_version = 3
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_basic_crud_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_basic_crud_rbac.py
index d780de7..3f5227e 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_basic_crud_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_basic_crud_rbac.py
@@ -20,11 +20,11 @@
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
 
-class VolumesV2BasicCrudRbacTest(rbac_base.BaseVolumeRbacTest):
+class VolumesBasicCrudRbacTest(rbac_base.BaseVolumeRbacTest):
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesV2BasicCrudRbacTest, cls).resource_setup()
+        super(VolumesBasicCrudRbacTest, cls).resource_setup()
         cls.volume = cls.create_volume()
 
     @rbac_rule_validation.action(service="cinder",
@@ -72,5 +72,5 @@
         self.volumes_client.list_volumes(detail=True)
 
 
-class VolumesV3BasicCrudRbacTest(VolumesV2BasicCrudRbacTest):
+class VolumesBasicCrudV3RbacTest(VolumesBasicCrudRbacTest):
     _api_version = 3
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_hosts_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_hosts_rbac.py
index 18a2768..ee0a0be 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_hosts_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_hosts_rbac.py
@@ -27,3 +27,19 @@
     def test_list_hosts(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.volume_hosts_client.list_hosts()
+
+    @decorators.idempotent_id('9ddf321e-788f-4787-b8cc-dfa59e264143')
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="volume_extension:hosts")
+    def test_show_host(self):
+        hosts = self.volume_hosts_client.list_hosts()['hosts']
+        host_names = [host['host_name'] for host in hosts]
+        self.assertNotEmpty(host_names, "No available volume host was found, "
+                                        "all hosts found were: %s" % hosts)
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.volume_hosts_client.show_host(host_names[0])
+
+
+class VolumeHostsV3RbacTest(VolumeHostsRbacTest):
+    _api_version = 3
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_metadata_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_metadata_rbac.py
index 8c4185b..f9114a8 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_metadata_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_metadata_rbac.py
@@ -103,3 +103,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.volumes_client.update_volume_image_metadata(
             self.volume['id'], image_id=self.image_id)
+
+
+class VolumeMetadataV3RbacTest(VolumeMetadataRbacTest):
+    _api_version = 3
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volumes_backup_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volumes_backup_rbac.py
index 884d7ad..7ca3d9f 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volumes_backup_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volumes_backup_rbac.py
@@ -93,6 +93,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.backups_client.list_backups(detail=True)
 
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('50f43bde-205e-438e-9a05-5eac07fc3d63')
     @rbac_rule_validation.action(
         service="cinder",
diff --git a/patrole_tempest_plugin/tests/unit/fixtures.py b/patrole_tempest_plugin/tests/unit/fixtures.py
index ed50e15..ce13029 100644
--- a/patrole_tempest_plugin/tests/unit/fixtures.py
+++ b/patrole_tempest_plugin/tests/unit/fixtures.py
@@ -19,6 +19,8 @@
 import fixtures
 import mock
 
+from tempest import clients
+from tempest.common import credentials_factory as credentials
 from tempest import config
 
 from patrole_tempest_plugin import rbac_utils
@@ -61,7 +63,7 @@
     def setUp(self):
         super(RbacUtilsFixture, self).setUp()
 
-        self.useFixture(ConfPatcher(rbac_test_role='member', group='rbac'))
+        self.useFixture(ConfPatcher(rbac_test_role='member', group='patrole'))
         self.useFixture(ConfPatcher(
             admin_role='admin', auth_version='v3', group='identity'))
 
@@ -71,11 +73,14 @@
             'os_primary.credentials.project_id': self.PROJECT_ID,
             'get_identity_version.return_value': 'v3'
         }
-        self.mock_test_obj = mock.Mock(**test_obj_kwargs)
-        self.mock_time = mock.patch.object(rbac_utils, 'time').start()
+        self.mock_test_obj = mock.Mock(__name__='foo', **test_obj_kwargs)
 
-        self.roles_v3_client = (
-            self.mock_test_obj.get_client_manager.return_value.roles_v3_client)
+        # Mock out functionality that can't be used by unit tests.
+        self.mock_time = mock.patch.object(rbac_utils, 'time').start()
+        mock.patch.object(
+            credentials, 'get_configured_admin_credentials').start()
+        mock_admin_mgr = mock.patch.object(clients, 'Manager').start()
+        self.roles_v3_client = mock_admin_mgr.return_value.roles_v3_client
 
         self.set_roles(['admin', 'member'], [])
 
diff --git a/patrole_tempest_plugin/tests/unit/test_hacking.py b/patrole_tempest_plugin/tests/unit/test_hacking.py
new file mode 100644
index 0000000..021602b
--- /dev/null
+++ b/patrole_tempest_plugin/tests/unit/test_hacking.py
@@ -0,0 +1,258 @@
+# 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.tests import base
+
+from patrole_tempest_plugin.hacking import checks
+
+
+class RBACHackingTestCase(base.TestCase):
+
+    def test_import_no_clients_in_api_tests(self):
+        for client in checks.PYTHON_CLIENTS:
+            import_string = "import " + client + "client"
+            self.assertTrue(checks.import_no_clients_in_api_tests(
+                import_string,
+                "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+            self.assertFalse(checks.import_no_clients_in_api_tests(
+                import_string,
+                "./patrole_tempest_plugin/tests/scenario/fake_test.py"))
+            self.assertFalse(checks.import_no_clients_in_api_tests(
+                import_string,
+                "./patrole_tempest_plugin/tests/unit/fake_test.py"))
+            self.assertFalse(checks.import_no_clients_in_api_tests(
+                import_string,
+                "./patrole_tempest_plugin/fake_test.py"))
+
+    def test_no_setup_teardown_class_for_tests(self):
+        self.assertTrue(checks.no_setup_teardown_class_for_tests(
+            "  def setUpClass(cls):",
+            "./patrole_tempest_plugin/tests/fake_test.py"))
+        self.assertIsNone(checks.no_setup_teardown_class_for_tests(
+            "  def setUpClass(cls): # noqa",
+            "./patrole_tempest_plugin/tests/fake_test.py"))
+        self.assertTrue(checks.no_setup_teardown_class_for_tests(
+            "  def setUpClass(cls):",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+        self.assertTrue(checks.no_setup_teardown_class_for_tests(
+            "  def setUpClass(cls):",
+            "./patrole_tempest_plugin/tests/scenario/fake_test.py"))
+        self.assertTrue(checks.no_setup_teardown_class_for_tests(
+            "  def tearDownClass(cls):",
+            "./patrole_tempest_plugin/tests/fake_test.py"))
+        self.assertIsNone(checks.no_setup_teardown_class_for_tests(
+            "  def tearDownClass(cls): # noqa",
+            "./patrole_tempest_plugin/tests/fake_test.py"))
+        self.assertTrue(checks.no_setup_teardown_class_for_tests(
+            "  def tearDownClass(cls):",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+        self.assertTrue(checks.no_setup_teardown_class_for_tests(
+            "  def tearDownClass(cls):",
+            "./patrole_tempest_plugin/tests/scenario/fake_test.py"))
+
+    def test_no_vi_headers(self):
+        self.assertTrue(checks.no_vi_headers(
+            "# vim: tabstop=4", 1, range(250)))
+        self.assertTrue(checks.no_vi_headers(
+            "# vim: tabstop=4", 249, range(250)))
+
+    def test_service_tags_not_in_module_path(self):
+        self.assertTrue(checks.service_tags_not_in_module_path(
+            "@test.services('volume')",
+            "./patrole_tempest_plugin/tests/api/volume/fake_test_rbac.py"))
+        self.assertFalse(checks.service_tags_not_in_module_path(
+            "@test.services('image')",
+            "./patrole_tempest_plugin/tests/api/volume/fake_test_rbac.py"))
+
+    def test_no_hyphen_at_end_of_rand_name(self):
+        self.assertIsNone(checks.no_hyphen_at_end_of_rand_name(
+            "data_utils.rand_name('test')",
+            "./patrole_tempest_plugin/tests/fake_test.py"))
+        self.assertIsNone(checks.no_hyphen_at_end_of_rand_name(
+            "data_utils.rand_name('test')",
+            "./patrole_tempest_plugin/tests/api/compute/fake_test_rbac.py"))
+        self.assertIsNone(checks.no_hyphen_at_end_of_rand_name(
+            "data_utils.rand_name('test')",
+            "./patrole_tempest_plugin/tests/scenario/fake_test.py"))
+        self.assertIsNone(checks.no_hyphen_at_end_of_rand_name(
+            "data_utils.rand_name('test')",
+            "./patrole_tempest_plugin/tests/unit/fake_test.py"))
+        self.assertTrue(checks.no_hyphen_at_end_of_rand_name(
+            "data_utils.rand_name('test-')",
+            "./patrole_tempest_plugin/tests/fake_test.py"))
+        self.assertTrue(checks.no_hyphen_at_end_of_rand_name(
+            "data_utils.rand_name('test-')",
+            "./patrole_tempest_plugin/tests/api/compute/fake_test_rbac.py"))
+        self.assertTrue(checks.no_hyphen_at_end_of_rand_name(
+            "data_utils.rand_name('test-')",
+            "./patrole_tempest_plugin/tests/scenario/fake_test.py"))
+        self.assertTrue(checks.no_hyphen_at_end_of_rand_name(
+            "data_utils.rand_name('test-')",
+            "./patrole_tempest_plugin/tests/unit/fake_test.py"))
+
+    def test_no_mutable_default_args(self):
+        self.assertEqual(0, len(list(checks.no_mutable_default_args(
+            "  def test_function(test_param_1, test_param_2"))))
+        self.assertEqual(1, len(list(checks.no_mutable_default_args(
+            "  def test_function(test_param_1, test_param_2={}"))))
+
+    def test_no_testtools_skip_decorator(self):
+        self.assertEqual(1, len(list(checks.no_testtools_skip_decorator(
+            " @testtools.skip('Bug')"))))
+        self.assertEqual(0, len(list(checks.no_testtools_skip_decorator(
+            " @testtools.skipTest('reason')"))))
+        self.assertEqual(0, len(list(checks.no_testtools_skip_decorator(
+            " @testtools.skipUnless(reason, 'message')"))))
+        self.assertEqual(0, len(list(checks.no_testtools_skip_decorator(
+            " @testtools.skipIf(reason, 'message')"))))
+
+    def test_use_rand_uuid_instead_of_uuid4(self):
+        self.assertTrue(checks.use_rand_uuid_instead_of_uuid4(
+            "new_uuid = uuid.uuid4()",
+            "./patrole_tempest_plugin/tests/fake_test.py"))
+        self.assertTrue(checks.use_rand_uuid_instead_of_uuid4(
+            "new_hex_uuid = uuid.uuid4().hex",
+            "./patrole_tempest_plugin/tests/fake_test.py"))
+        self.assertIsNotNone(checks.use_rand_uuid_instead_of_uuid4(
+            "new_uuid = data_utils.rand_uuid()",
+            "./patrole_tempest_plugin/tests/fake_test.py"))
+        self.assertIsNotNone(checks.use_rand_uuid_instead_of_uuid4(
+            "new_hex_uuid = data_utils.rand_uuid_hex()",
+            "./patrole_tempest_plugin/tests/fake_test.py"))
+
+    def _test_no_rbac_rule_validation_decorator(
+            self, filename, with_other_decorators=True,
+            with_rbac_decorator=True, expected_success=True):
+        other_decorators = [
+            "@decorators.idempotent_id(123)",
+            "@decorators.attr(type=['slow'])",
+            "@test.requires_ext(extension='ext', service='svc')"
+        ]
+
+        if with_other_decorators:
+            # Include multiple decorators to verify that this check works with
+            # arbitrarily many decorators. These insert decorators above the
+            # rbac_rule_validation decorator.
+            for decorator in other_decorators:
+                self.assertIsNone(checks.no_rbac_rule_validation_decorator(
+                    " %s" % decorator, filename))
+        if with_rbac_decorator:
+            self.assertIsNone(checks.no_rbac_rule_validation_decorator(
+                " @rbac_rule_validation.action('rule')",
+                filename))
+        if with_other_decorators:
+            # Include multiple decorators to verify that this check works with
+            # arbitrarily many decorators. These insert decorators between
+            # the test and the @rbac_rule_validation decorator.
+            for decorator in other_decorators:
+                self.assertIsNone(checks.no_rbac_rule_validation_decorator(
+                    " %s" % decorator, filename))
+        final_result = checks.no_rbac_rule_validation_decorator(
+            " def test_rbac_test",
+            filename)
+        if expected_success:
+            self.assertIsNone(final_result)
+        else:
+            self.assertIsInstance(final_result, tuple)
+            self.assertFalse(final_result[0])
+
+    def test_no_rbac_rule_validation_decorator(self):
+        self._test_no_rbac_rule_validation_decorator(
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py")
+        self._test_no_rbac_rule_validation_decorator(
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py",
+            False)
+        self._test_no_rbac_rule_validation_decorator(
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py",
+            with_other_decorators=True, with_rbac_decorator=False,
+            expected_success=False)
+        self._test_no_rbac_rule_validation_decorator(
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py",
+            with_other_decorators=False, with_rbac_decorator=False,
+            expected_success=False)
+
+        self._test_no_rbac_rule_validation_decorator(
+            "./patrole_tempest_plugin/tests/scenario/fake_test.py")
+        self._test_no_rbac_rule_validation_decorator(
+            "./patrole_tempest_plugin/tests/scenario/fake_test.py",
+            False)
+        self._test_no_rbac_rule_validation_decorator(
+            "./patrole_tempest_plugin/tests/scenario/fake_test.py",
+            with_other_decorators=True, with_rbac_decorator=False,
+            expected_success=False)
+        self._test_no_rbac_rule_validation_decorator(
+            "./patrole_tempest_plugin/tests/scenario/fake_test.py",
+            with_other_decorators=False, with_rbac_decorator=False,
+            expected_success=False)
+
+    def test_no_rbac_suffix_in_test_filename(self):
+        self.assertFalse(checks.no_rbac_suffix_in_test_filename(
+            "./patrole_tempest_plugin/tests/fake_test.py"))
+        self.assertFalse(checks.no_rbac_suffix_in_test_filename(
+            "./patrole_tempest_plugin/tests/scenario/fake_test.py"))
+        self.assertFalse(checks.no_rbac_suffix_in_test_filename(
+            "./patrole_tempest_plugin/tests/unit/fake_test.py"))
+        self.assertFalse(checks.no_rbac_suffix_in_test_filename(
+            "./patrole_tempest_plugin/tests/api/fake_rbac_base.py"))
+        self.assertFalse(checks.no_rbac_suffix_in_test_filename(
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+        self.assertTrue(checks.no_rbac_suffix_in_test_filename(
+            "./patrole_tempest_plugin/tests/api/fake_test.py"))
+
+    def test_no_rbac_test_suffix_in_test_class_name(self):
+        self.assertFalse(checks.no_rbac_test_suffix_in_test_class_name(
+            "class FakeTest",
+            "./patrole_tempest_plugin/tests/fake_test.py"))
+        self.assertFalse(checks.no_rbac_test_suffix_in_test_class_name(
+            "class FakeTest",
+            "./patrole_tempest_plugin/tests/scenario/fake_test.py"))
+        self.assertFalse(checks.no_rbac_test_suffix_in_test_class_name(
+            "class FakeTest",
+            "./patrole_tempest_plugin/tests/unit/fake_test.py"))
+        self.assertFalse(checks.no_rbac_test_suffix_in_test_class_name(
+            "class FakeTest",
+            "./patrole_tempest_plugin/tests/api/fake_rbac_base.py"))
+        self.assertFalse(checks.no_rbac_test_suffix_in_test_class_name(
+            "class FakeRbacTest",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+        self.assertTrue(checks.no_rbac_test_suffix_in_test_class_name(
+            "class FakeTest",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+
+    def test_no_client_alias_in_test_cases(self):
+        self.assertFalse(checks.no_client_alias_in_test_cases(
+            "  self.client",
+            "./patrole_tempest_plugin/tests/fake_test.py"))
+        self.assertFalse(checks.no_client_alias_in_test_cases(
+            "  cls.client",
+            "./patrole_tempest_plugin/tests/fake_test.py"))
+        self.assertFalse(checks.no_client_alias_in_test_cases(
+            "  self.client",
+            "./patrole_tempest_plugin/tests/unit/fake_test.py"))
+        self.assertFalse(checks.no_client_alias_in_test_cases(
+            "  cls.client",
+            "./patrole_tempest_plugin/tests/unit/fake_test.py"))
+        self.assertFalse(checks.no_client_alias_in_test_cases(
+            "  self.client",
+            "./patrole_tempest_plugin/tests/scenario/fake_test.py"))
+        self.assertFalse(checks.no_client_alias_in_test_cases(
+            "  cls.client",
+            "./patrole_tempest_plugin/tests/scenario/fake_test.py"))
+        self.assertTrue(checks.no_client_alias_in_test_cases(
+            "  self.client",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+        self.assertTrue(checks.no_client_alias_in_test_cases(
+            "  cls.client",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
diff --git a/patrole_tempest_plugin/tests/unit/test_patrole.py b/patrole_tempest_plugin/tests/unit/test_patrole.py
index 58aff05..9b8e88c 100644
--- a/patrole_tempest_plugin/tests/unit/test_patrole.py
+++ b/patrole_tempest_plugin/tests/unit/test_patrole.py
@@ -14,16 +14,23 @@
 # under the License.
 
 """
-test_patrole
-----------------------------------
-
 Tests for `patrole` module.
 """
 
+from tempest import config
+
 from patrole_tempest_plugin.tests.unit import base
 
+CONF = config.CONF
+
 
 class TestPatrole(base.TestCase):
 
-    def test_something(self):
-        pass
+    def test_rbac_group_backwards_compatability(self):
+        """Validate that the deprecated group [rbac] is available and has the
+        same options and option values as [patrole] group, which is current.
+        """
+        self.assertTrue(hasattr(CONF, 'patrole'))
+        self.assertTrue(hasattr(CONF, 'rbac'))
+        # Validate that both groups are identical.
+        self.assertEqual(CONF.patrole.items(), CONF.rbac.items())
diff --git a/patrole_tempest_plugin/tests/unit/test_rbac_policy_parser.py b/patrole_tempest_plugin/tests/unit/test_policy_authority.py
similarity index 74%
rename from patrole_tempest_plugin/tests/unit/test_rbac_policy_parser.py
rename to patrole_tempest_plugin/tests/unit/test_policy_authority.py
index 36fa045..2a8da9d 100644
--- a/patrole_tempest_plugin/tests/unit/test_rbac_policy_parser.py
+++ b/patrole_tempest_plugin/tests/unit/test_policy_authority.py
@@ -20,13 +20,14 @@
 from tempest import config
 from tempest.tests import base
 
+from patrole_tempest_plugin import policy_authority
 from patrole_tempest_plugin import rbac_exceptions
-from patrole_tempest_plugin import rbac_policy_parser
+from patrole_tempest_plugin.tests.unit import fixtures
 
 CONF = config.CONF
 
 
-class RbacPolicyTest(base.TestCase):
+class PolicyAuthorityTest(base.TestCase):
 
     services = {
         'services': [
@@ -39,9 +40,9 @@
     }
 
     def setUp(self):
-        super(RbacPolicyTest, self).setUp()
-        self.patchobject(rbac_policy_parser, 'credentials')
-        m_creds = self.patchobject(rbac_policy_parser, 'clients')
+        super(PolicyAuthorityTest, self).setUp()
+        self.patchobject(policy_authority, 'credentials')
+        m_creds = self.patchobject(policy_authority, 'clients')
         m_creds.Manager().identity_services_client.list_services.\
             return_value = self.services
         m_creds.Manager().identity_services_v3_client.list_services.\
@@ -63,36 +64,29 @@
         self.conf_policy_path = os.path.join(
             current_directory, 'resources', '%s.json')
 
-        CONF.set_override(
-            'custom_policy_files', [self.conf_policy_path], group='rbac')
-        self.addCleanup(CONF.clear_override, 'custom_policy_files',
-                        group='rbac')
+        self.useFixture(fixtures.ConfPatcher(
+            custom_policy_files=[self.conf_policy_path], group='patrole'))
+        self.useFixture(fixtures.ConfPatcher(
+            api_v3=True, api_v2=False, group='identity-feature-enabled'))
 
         # Guarantee a blank slate for each test.
         for attr in ('available_services', 'policy_files'):
-            if attr in dir(rbac_policy_parser.RbacPolicyParser):
-                delattr(rbac_policy_parser.RbacPolicyParser, attr)
-
-        # TODO(fm577c): Use fixture for setting/clearing CONF.
-        CONF.set_override('api_v3', True, group='identity-feature-enabled')
-        self.addCleanup(CONF.clear_override, 'api_v2',
-                        group='identity-feature-enabled')
-        self.addCleanup(CONF.clear_override, 'api_v3',
-                        group='identity-feature-enabled')
+            if attr in dir(policy_authority.PolicyAuthority):
+                delattr(policy_authority.PolicyAuthority, attr)
 
     def _get_fake_policy_rule(self, name, rule):
-        fake_rule = mock.Mock(check=rule)
+        fake_rule = mock.Mock(check=rule, __name__='foo')
         fake_rule.name = name
         return fake_rule
 
-    @mock.patch.object(rbac_policy_parser, 'LOG', autospec=True)
+    @mock.patch.object(policy_authority, 'LOG', autospec=True)
     def test_custom_policy(self, m_log):
         default_roles = ['zero', 'one', 'two', 'three', 'four',
                          'five', 'six', 'seven', 'eight', 'nine']
 
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
-        parser = rbac_policy_parser.RbacPolicyParser(
+        authority = policy_authority.PolicyAuthority(
             test_tenant_id, test_user_id, "custom_rbac_policy")
 
         expected = {
@@ -107,14 +101,14 @@
 
         for rule, role_list in expected.items():
             for role in role_list:
-                self.assertTrue(parser.allowed(rule, role))
+                self.assertTrue(authority.allowed(rule, role))
             for role in set(default_roles) - set(role_list):
-                self.assertFalse(parser.allowed(rule, role))
+                self.assertFalse(authority.allowed(rule, role))
 
     def test_admin_policy_file_with_admin_role(self):
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
-        parser = rbac_policy_parser.RbacPolicyParser(
+        authority = policy_authority.PolicyAuthority(
             test_tenant_id, test_user_id, "admin_rbac_policy")
 
         role = 'admin'
@@ -124,17 +118,17 @@
         disallowed_rules = ['non_admin_rule']
 
         for rule in allowed_rules:
-            allowed = parser.allowed(rule, role)
+            allowed = authority.allowed(rule, role)
             self.assertTrue(allowed)
 
         for rule in disallowed_rules:
-            allowed = parser.allowed(rule, role)
+            allowed = authority.allowed(rule, role)
             self.assertFalse(allowed)
 
     def test_admin_policy_file_with_member_role(self):
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
-        parser = rbac_policy_parser.RbacPolicyParser(
+        authority = policy_authority.PolicyAuthority(
             test_tenant_id, test_user_id, "admin_rbac_policy")
 
         role = 'Member'
@@ -145,17 +139,17 @@
             'admin_rule', 'is_admin_rule', 'alt_admin_rule']
 
         for rule in allowed_rules:
-            allowed = parser.allowed(rule, role)
+            allowed = authority.allowed(rule, role)
             self.assertTrue(allowed)
 
         for rule in disallowed_rules:
-            allowed = parser.allowed(rule, role)
+            allowed = authority.allowed(rule, role)
             self.assertFalse(allowed)
 
     def test_alt_admin_policy_file_with_context_is_admin(self):
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
-        parser = rbac_policy_parser.RbacPolicyParser(
+        authority = policy_authority.PolicyAuthority(
             test_tenant_id, test_user_id, "alt_admin_rbac_policy")
 
         role = 'fake_admin'
@@ -163,11 +157,11 @@
         disallowed_rules = ['admin_rule']
 
         for rule in allowed_rules:
-            allowed = parser.allowed(rule, role)
+            allowed = authority.allowed(rule, role)
             self.assertTrue(allowed)
 
         for rule in disallowed_rules:
-            allowed = parser.allowed(rule, role)
+            allowed = authority.allowed(rule, role)
             self.assertFalse(allowed)
 
         role = 'super_admin'
@@ -175,11 +169,11 @@
         disallowed_rules = ['non_admin_rule']
 
         for rule in allowed_rules:
-            allowed = parser.allowed(rule, role)
+            allowed = authority.allowed(rule, role)
             self.assertTrue(allowed)
 
         for rule in disallowed_rules:
-            allowed = parser.allowed(rule, role)
+            allowed = authority.allowed(rule, role)
             self.assertFalse(allowed)
 
     def test_tenant_user_policy(self):
@@ -191,28 +185,28 @@
         """
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
-        parser = rbac_policy_parser.RbacPolicyParser(
+        authority = policy_authority.PolicyAuthority(
             test_tenant_id, test_user_id, "tenant_rbac_policy")
 
         # Check whether Member role can perform expected actions.
         allowed_rules = ['rule1', 'rule2', 'rule3', 'rule4']
         for rule in allowed_rules:
-            allowed = parser.allowed(rule, 'Member')
+            allowed = authority.allowed(rule, 'Member')
             self.assertTrue(allowed)
 
         disallowed_rules = ['admin_tenant_rule', 'admin_user_rule']
         for disallowed_rule in disallowed_rules:
-            self.assertFalse(parser.allowed(disallowed_rule, 'Member'))
+            self.assertFalse(authority.allowed(disallowed_rule, 'Member'))
 
         # Check whether admin role can perform expected actions.
         allowed_rules.extend(disallowed_rules)
         for rule in allowed_rules:
-            allowed = parser.allowed(rule, 'admin')
+            allowed = authority.allowed(rule, 'admin')
             self.assertTrue(allowed)
 
         # Check whether _try_rule is called with the correct target dictionary.
         with mock.patch.object(
-            parser, '_try_rule', return_value=True, autospec=True) \
+            authority, '_try_rule', return_value=True, autospec=True) \
             as mock_try_rule:
 
             expected_target = {
@@ -232,20 +226,20 @@
             }
 
             for rule in allowed_rules:
-                allowed = parser.allowed(rule, 'Member')
+                allowed = authority.allowed(rule, 'Member')
                 self.assertTrue(allowed)
                 mock_try_rule.assert_called_once_with(
                     rule, expected_target, expected_access_data, mock.ANY)
                 mock_try_rule.reset_mock()
 
-    @mock.patch.object(rbac_policy_parser, 'LOG', autospec=True)
+    @mock.patch.object(policy_authority, 'LOG', autospec=True)
     def test_invalid_service_raises_exception(self, m_log):
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
         service = 'invalid_service'
 
         self.assertRaises(rbac_exceptions.RbacInvalidService,
-                          rbac_policy_parser.RbacPolicyParser,
+                          policy_authority.PolicyAuthority,
                           test_tenant_id,
                           test_user_id,
                           service)
@@ -253,25 +247,25 @@
         m_log.debug.assert_called_once_with(
             '%s is NOT a valid service.', service)
 
-    @mock.patch.object(rbac_policy_parser, 'LOG', autospec=True)
+    @mock.patch.object(policy_authority, 'LOG', autospec=True)
     def test_service_is_none_raises_exception(self, m_log):
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
         service = None
 
         self.assertRaises(rbac_exceptions.RbacInvalidService,
-                          rbac_policy_parser.RbacPolicyParser,
+                          policy_authority.PolicyAuthority,
                           test_tenant_id,
                           test_user_id,
                           service)
 
         m_log.debug.assert_called_once_with('%s is NOT a valid service.', None)
 
-    @mock.patch.object(rbac_policy_parser, 'LOG', autospec=True)
+    @mock.patch.object(policy_authority, 'LOG', autospec=True)
     def test_invalid_policy_rule_throws_rbac_parsing_exception(self, m_log):
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
-        parser = rbac_policy_parser.RbacPolicyParser(
+        authority = policy_authority.PolicyAuthority(
             test_tenant_id, test_user_id, "custom_rbac_policy")
 
         fake_rule = 'fake_rule'
@@ -279,18 +273,19 @@
                            .format(fake_rule, self.custom_policy_file)
 
         e = self.assertRaises(rbac_exceptions.RbacParsingException,
-                              parser.allowed, fake_rule, None)
+                              authority.allowed, fake_rule, None)
         self.assertIn(expected_message, str(e))
         m_log.debug.assert_called_once_with(expected_message)
 
-    @mock.patch.object(rbac_policy_parser, 'LOG', autospec=True)
+    @mock.patch.object(policy_authority, 'LOG', autospec=True)
     def test_unknown_exception_throws_rbac_parsing_exception(self, m_log):
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
 
-        parser = rbac_policy_parser.RbacPolicyParser(
+        authority = policy_authority.PolicyAuthority(
             test_tenant_id, test_user_id, "custom_rbac_policy")
-        parser.rules = mock.MagicMock(
+        authority.rules = mock.MagicMock(
+            __name__='foo',
             **{'__getitem__.return_value.side_effect': Exception(
                mock.sentinel.error)})
 
@@ -299,11 +294,11 @@
                                                       self.custom_policy_file)
 
         e = self.assertRaises(rbac_exceptions.RbacParsingException,
-                              parser.allowed, mock.sentinel.rule, None)
+                              authority.allowed, mock.sentinel.rule, None)
         self.assertIn(expected_message, str(e))
         m_log.debug.assert_called_once_with(expected_message)
 
-    @mock.patch.object(rbac_policy_parser, 'stevedore', autospec=True)
+    @mock.patch.object(policy_authority, 'stevedore', autospec=True)
     def test_get_policy_data_from_file_and_from_code(self, mock_stevedore):
         fake_policy_rules = [
             self._get_fake_policy_rule('code_policy_action_1',
@@ -314,7 +309,7 @@
                                        'rule:code_rule_3'),
         ]
 
-        mock_manager = mock.Mock(obj=fake_policy_rules)
+        mock_manager = mock.Mock(obj=fake_policy_rules, __name__='foo')
         mock_manager.configure_mock(name='fake_service')
         mock_stevedore.named.NamedExtensionManager.return_value = [
             mock_manager
@@ -322,10 +317,10 @@
 
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
-        parser = rbac_policy_parser.RbacPolicyParser(
+        authority = policy_authority.PolicyAuthority(
             test_tenant_id, test_user_id, "tenant_rbac_policy")
 
-        policy_data = parser._get_policy_data('fake_service')
+        policy_data = authority._get_policy_data('fake_service')
         self.assertIsInstance(policy_data, str)
 
         actual_policy_data = json.loads(policy_data)
@@ -343,7 +338,7 @@
 
         self.assertEqual(expected_policy_data, actual_policy_data)
 
-    @mock.patch.object(rbac_policy_parser, 'stevedore', autospec=True)
+    @mock.patch.object(policy_authority, 'stevedore', autospec=True)
     def test_get_policy_data_from_file_and_from_code_with_overwrite(
             self, mock_stevedore):
         # The custom policy file should overwrite default rules rule1 and rule2
@@ -355,7 +350,7 @@
                                        'rule:code_rule_3'),
         ]
 
-        mock_manager = mock.Mock(obj=fake_policy_rules)
+        mock_manager = mock.Mock(obj=fake_policy_rules, __name__='foo')
         mock_manager.configure_mock(name='fake_service')
         mock_stevedore.named.NamedExtensionManager.return_value = [
             mock_manager
@@ -364,9 +359,9 @@
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
 
-        parser = rbac_policy_parser.RbacPolicyParser(
+        authority = policy_authority.PolicyAuthority(
             test_tenant_id, test_user_id, 'tenant_rbac_policy')
-        policy_data = parser._get_policy_data('fake_service')
+        policy_data = authority._get_policy_data('fake_service')
         self.assertIsInstance(policy_data, str)
 
         actual_policy_data = json.loads(policy_data)
@@ -382,29 +377,29 @@
 
         self.assertEqual(expected_policy_data, actual_policy_data)
 
-    @mock.patch.object(rbac_policy_parser, 'stevedore', autospec=True)
+    @mock.patch.object(policy_authority, 'stevedore', autospec=True)
     def test_get_policy_data_cannot_find_policy(self, mock_stevedore):
         mock_stevedore.named.NamedExtensionManager.return_value = None
         e = self.assertRaises(rbac_exceptions.RbacParsingException,
-                              rbac_policy_parser.RbacPolicyParser,
+                              policy_authority.PolicyAuthority,
                               None, None, 'test_service')
 
         expected_error = \
             'Policy file for {0} service neither found in code '\
             'nor at {1}.'.format(
                 'test_service',
-                [CONF.rbac.custom_policy_files[0] % 'test_service'])
+                [CONF.patrole.custom_policy_files[0] % 'test_service'])
 
         self.assertIn(expected_error, str(e))
 
-    @mock.patch.object(rbac_policy_parser, 'json', autospec=True)
-    @mock.patch.object(rbac_policy_parser, 'stevedore', autospec=True)
+    @mock.patch.object(policy_authority, 'json', autospec=True)
+    @mock.patch.object(policy_authority, 'stevedore', autospec=True)
     def test_get_policy_data_without_valid_policy(self, mock_stevedore,
                                                   mock_json):
-        test_policy_action = mock.Mock(check='rule:bar')
+        test_policy_action = mock.Mock(check='rule:bar', __name__='foo')
         test_policy_action.configure_mock(name='foo')
 
-        test_policy = mock.Mock(obj=[test_policy_action])
+        test_policy = mock.Mock(obj=[test_policy_action], __name__='foo')
         test_policy.configure_mock(name='test_service')
 
         mock_stevedore.named.NamedExtensionManager\
@@ -413,7 +408,7 @@
         mock_json.dumps.side_effect = ValueError
 
         e = self.assertRaises(rbac_exceptions.RbacParsingException,
-                              rbac_policy_parser.RbacPolicyParser,
+                              policy_authority.PolicyAuthority,
                               None, None, 'test_service')
 
         expected_error = "Policy file for {0} service is invalid."\
@@ -427,39 +422,39 @@
             invoke_on_load=True,
             warn_on_missing_entrypoint=False)
 
-    @mock.patch.object(rbac_policy_parser, 'json', autospec=True)
-    @mock.patch.object(rbac_policy_parser, 'stevedore', autospec=True)
+    @mock.patch.object(policy_authority, 'json', autospec=True)
+    @mock.patch.object(policy_authority, 'stevedore', autospec=True)
     def test_get_policy_data_from_file_not_json(self, mock_stevedore,
                                                 mock_json):
         mock_stevedore.named.NamedExtensionManager.return_value = None
         mock_json.loads.side_effect = ValueError
         e = self.assertRaises(rbac_exceptions.RbacParsingException,
-                              rbac_policy_parser.RbacPolicyParser,
+                              policy_authority.PolicyAuthority,
                               None, None, 'tenant_rbac_policy')
 
         expected_error = (
             'Policy file for {0} service neither found in code nor at {1}.'
-            .format('tenant_rbac_policy', [CONF.rbac.custom_policy_files[0]
+            .format('tenant_rbac_policy', [CONF.patrole.custom_policy_files[0]
                                            % 'tenant_rbac_policy']))
         self.assertIn(expected_error, str(e))
 
     def test_discover_policy_files(self):
-        policy_parser = rbac_policy_parser.RbacPolicyParser(
+        policy_parser = policy_authority.PolicyAuthority(
             None, None, 'tenant_rbac_policy')
 
         # Ensure that "policy_files" is set at class and instance levels.
         self.assertIn('policy_files',
-                      dir(rbac_policy_parser.RbacPolicyParser))
+                      dir(policy_authority.PolicyAuthority))
         self.assertIn('policy_files', dir(policy_parser))
         self.assertIn('tenant_rbac_policy', policy_parser.policy_files)
         self.assertEqual(self.conf_policy_path % 'tenant_rbac_policy',
                          policy_parser.policy_files['tenant_rbac_policy'])
 
-    @mock.patch.object(rbac_policy_parser, 'policy', autospec=True)
-    @mock.patch.object(rbac_policy_parser.RbacPolicyParser, '_get_policy_data',
+    @mock.patch.object(policy_authority, 'policy', autospec=True)
+    @mock.patch.object(policy_authority.PolicyAuthority, '_get_policy_data',
                        autospec=True)
-    @mock.patch.object(rbac_policy_parser, 'clients', autospec=True)
-    @mock.patch.object(rbac_policy_parser, 'os', autospec=True)
+    @mock.patch.object(policy_authority, 'clients', autospec=True)
+    @mock.patch.object(policy_authority, 'os', autospec=True)
     def test_discover_policy_files_with_many_invalid_one_valid(self, m_os,
                                                                m_creds, *args):
         # Only the 3rd path is valid.
@@ -471,16 +466,16 @@
                 'services': [{'name': 'test_service'}]}
 
         # The expected policy will be 'baz/test_service'.
-        CONF.set_override(
-            'custom_policy_files', ['foo/%s', 'bar/%s', 'baz/%s'],
-            group='rbac')
+        self.useFixture(fixtures.ConfPatcher(
+            custom_policy_files=['foo/%s', 'bar/%s', 'baz/%s'],
+            group='patrole'))
 
-        policy_parser = rbac_policy_parser.RbacPolicyParser(
+        policy_parser = policy_authority.PolicyAuthority(
             None, None, 'test_service')
 
         # Ensure that "policy_files" is set at class and instance levels.
         self.assertIn('policy_files',
-                      dir(rbac_policy_parser.RbacPolicyParser))
+                      dir(policy_authority.PolicyAuthority))
         self.assertIn('policy_files', dir(policy_parser))
         self.assertIn('test_service', policy_parser.policy_files)
         self.assertEqual('baz/test_service',
@@ -492,19 +487,19 @@
                           [self.conf_policy_path % 'test_service'])
 
         e = self.assertRaises(rbac_exceptions.RbacParsingException,
-                              rbac_policy_parser.RbacPolicyParser,
+                              policy_authority.PolicyAuthority,
                               None, None, 'test_service')
         self.assertIn(expected_error, str(e))
 
         self.assertIn('policy_files',
-                      dir(rbac_policy_parser.RbacPolicyParser))
+                      dir(policy_authority.PolicyAuthority))
         self.assertNotIn(
             'test_service',
-            rbac_policy_parser.RbacPolicyParser.policy_files.keys())
+            policy_authority.PolicyAuthority.policy_files.keys())
 
     def _test_validate_service(self, v2_services, v3_services,
                                expected_failure=False, expected_services=None):
-        with mock.patch.object(rbac_policy_parser, 'clients') as m_creds:
+        with mock.patch.object(policy_authority, 'clients') as m_creds:
             m_creds.Manager().identity_services_client.list_services.\
                 return_value = v2_services
             m_creds.Manager().identity_services_v3_client.list_services.\
@@ -513,15 +508,15 @@
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
 
-        mock_os = self.patchobject(rbac_policy_parser, 'os')
+        mock_os = self.patchobject(policy_authority, 'os')
         mock_os.path.join.return_value = self.admin_policy_file
 
         if not expected_services:
             expected_services = [s['name'] for s in self.services['services']]
 
         # Guarantee a blank slate for this test.
-        if hasattr(rbac_policy_parser.RbacPolicyParser, 'available_services'):
-            delattr(rbac_policy_parser.RbacPolicyParser,
+        if hasattr(policy_authority.PolicyAuthority, 'available_services'):
+            delattr(policy_authority.PolicyAuthority,
                     'available_services')
 
         if expected_failure:
@@ -530,10 +525,10 @@
             expected_exception = 'invalid_service is NOT a valid service'
             with self.assertRaisesRegex(rbac_exceptions.RbacInvalidService,
                                         expected_exception):
-                rbac_policy_parser.RbacPolicyParser(
+                policy_authority.PolicyAuthority(
                     test_tenant_id, test_user_id, "INVALID_SERVICE")
         else:
-            policy_parser = rbac_policy_parser.RbacPolicyParser(
+            policy_parser = policy_authority.PolicyAuthority(
                 test_tenant_id, test_user_id, "tenant_rbac_policy")
 
         # Check that the attribute is available at object and class levels.
@@ -542,11 +537,11 @@
             self.assertTrue(hasattr(policy_parser, 'available_services'))
             self.assertEqual(expected_services,
                              policy_parser.available_services)
-        self.assertTrue(hasattr(rbac_policy_parser.RbacPolicyParser,
+        self.assertTrue(hasattr(policy_authority.PolicyAuthority,
                                 'available_services'))
         self.assertEqual(
             expected_services,
-            rbac_policy_parser.RbacPolicyParser.available_services)
+            policy_authority.PolicyAuthority.available_services)
 
     def test_validate_service(self):
         """Positive test case to ensure ``validate_service`` works.
@@ -556,16 +551,16 @@
             2) Identity v2 API enabled.
             3) Both are enabled.
         """
-        CONF.set_override('api_v2', True, group='identity-feature-enabled')
-        CONF.set_override('api_v3', False, group='identity-feature-enabled')
+        self.useFixture(fixtures.ConfPatcher(
+            api_v2=True, api_v3=False, group='identity-feature-enabled'))
         self._test_validate_service(self.services, [], False)
 
-        CONF.set_override('api_v2', False, group='identity-feature-enabled')
-        CONF.set_override('api_v3', True, group='identity-feature-enabled')
+        self.useFixture(fixtures.ConfPatcher(
+            api_v2=False, api_v3=True, group='identity-feature-enabled'))
         self._test_validate_service([], self.services, False)
 
-        CONF.set_override('api_v2', True, group='identity-feature-enabled')
-        CONF.set_override('api_v3', True, group='identity-feature-enabled')
+        self.useFixture(fixtures.ConfPatcher(
+            api_v2=True, api_v3=True, group='identity-feature-enabled'))
         self._test_validate_service(self.services, self.services, False)
 
     def test_validate_service_except_invalid_service(self):
@@ -577,18 +572,18 @@
             3) Both are enabled.
             4) Neither are enabled.
         """
-        CONF.set_override('api_v2', True, group='identity-feature-enabled')
-        CONF.set_override('api_v3', False, group='identity-feature-enabled')
+        self.useFixture(fixtures.ConfPatcher(
+            api_v2=True, api_v3=False, group='identity-feature-enabled'))
         self._test_validate_service(self.services, [], True)
 
-        CONF.set_override('api_v2', False, group='identity-feature-enabled')
-        CONF.set_override('api_v3', True, group='identity-feature-enabled')
+        self.useFixture(fixtures.ConfPatcher(
+            api_v2=False, api_v3=True, group='identity-feature-enabled'))
         self._test_validate_service([], self.services, True)
 
-        CONF.set_override('api_v2', True, group='identity-feature-enabled')
-        CONF.set_override('api_v3', True, group='identity-feature-enabled')
+        self.useFixture(fixtures.ConfPatcher(
+            api_v2=True, api_v3=True, group='identity-feature-enabled'))
         self._test_validate_service(self.services, self.services, True)
 
-        CONF.set_override('api_v2', False, group='identity-feature-enabled')
-        CONF.set_override('api_v3', False, group='identity-feature-enabled')
+        self.useFixture(fixtures.ConfPatcher(
+            api_v2=False, api_v3=False, group='identity-feature-enabled'))
         self._test_validate_service([], [], True, [])
diff --git a/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py b/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py
index a9acf1c..94a2306 100644
--- a/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py
+++ b/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py
@@ -37,12 +37,15 @@
         self.mock_args.os_primary.credentials.user_id = \
             mock.sentinel.user_id
 
-        CONF.set_override('rbac_test_role', 'Member', group='rbac')
-        self.addCleanup(CONF.clear_override, 'rbac_test_role', group='rbac')
+        CONF.set_override('rbac_test_role', 'Member', group='patrole')
+        self.addCleanup(CONF.clear_override, 'rbac_test_role', group='patrole')
+
+        self.mock_rbaclog = mock.patch.object(
+            rbac_rv.RBACLOG, 'info', autospec=False).start()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
-    def test_rule_validation_have_permission_no_exc(self, mock_policy,
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rule_validation_have_permission_no_exc(self, mock_authority,
                                                     mock_log):
         """Test that having permission and no exception thrown is success.
 
@@ -50,10 +53,11 @@
         """
         decorator = rbac_rv.action(mock.sentinel.service, mock.sentinel.action)
 
-        mock_function = mock.Mock()
+        mock_function = mock.Mock(__name__='foo')
         wrapper = decorator(mock_function)
 
-        mock_policy.RbacPolicyParser.return_value.allowed.return_value = True
+        mock_authority.PolicyAuthority.return_value.allowed\
+            .return_value = True
 
         result = wrapper(self.mock_args)
 
@@ -62,8 +66,8 @@
         mock_log.error.assert_not_called()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
-    def test_rule_validation_lack_permission_throw_exc(self, mock_policy,
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rule_validation_lack_permission_throw_exc(self, mock_authority,
                                                        mock_log):
         """Test that having no permission and exception thrown is success.
 
@@ -71,11 +75,12 @@
         """
         decorator = rbac_rv.action(mock.sentinel.service, mock.sentinel.action)
 
-        mock_function = mock.Mock()
+        mock_function = mock.Mock(__name__='foo')
         mock_function.side_effect = exceptions.Forbidden
         wrapper = decorator(mock_function)
 
-        mock_policy.RbacPolicyParser.return_value.allowed.return_value = False
+        mock_authority.PolicyAuthority.return_value.allowed\
+            .return_value = False
 
         result = wrapper(self.mock_args)
 
@@ -84,8 +89,9 @@
         mock_log.error.assert_not_called()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
-    def test_rule_validation_forbidden_negative(self, mock_policy, mock_log):
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rule_validation_forbidden_negative(self, mock_authority,
+                                                mock_log):
         """Test Forbidden error is thrown and have permission fails.
 
         Negative test case: if Forbidden is thrown and the user should be
@@ -93,11 +99,12 @@
         raised.
         """
         decorator = rbac_rv.action(mock.sentinel.service, mock.sentinel.action)
-        mock_function = mock.Mock()
+        mock_function = mock.Mock(__name__='foo')
         mock_function.side_effect = exceptions.Forbidden
         wrapper = decorator(mock_function)
 
-        mock_policy.RbacPolicyParser.return_value.allowed.return_value = True
+        mock_authority.PolicyAuthority.return_value.allowed\
+            .return_value = True
 
         e = self.assertRaises(exceptions.Forbidden, wrapper, self.mock_args)
         self.assertIn(
@@ -107,21 +114,21 @@
                                                " perform sentinel.action.")
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
-    def test_rule_validation_rbac_malformed_response_positive(self,
-                                                              mock_policy,
-                                                              mock_log):
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rule_validation_rbac_malformed_response_positive(
+            self, mock_authority_authority, mock_log):
         """Test RbacMalformedResponse error is thrown without permission passes.
 
         Positive test case: if RbacMalformedResponse is thrown and the user is
         not allowed to perform the action, then this is a success.
         """
         decorator = rbac_rv.action(mock.sentinel.service, mock.sentinel.action)
-        mock_function = mock.Mock()
+        mock_function = mock.Mock(__name__='foo')
         mock_function.side_effect = rbac_exceptions.RbacMalformedResponse
         wrapper = decorator(mock_function)
 
-        mock_policy.RbacPolicyParser.return_value.allowed.return_value = False
+        (mock_authority_authority.PolicyAuthority.return_value.allowed
+            .return_value) = False
 
         result = wrapper(self.mock_args)
 
@@ -130,21 +137,21 @@
         mock_log.warning.assert_not_called()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
-    def test_rule_validation_rbac_malformed_response_negative(self,
-                                                              mock_policy,
-                                                              mock_log):
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rule_validation_rbac_malformed_response_negative(
+            self, mock_authority_authority, mock_log):
         """Test RbacMalformedResponse error is thrown with permission fails.
 
         Negative test case: if RbacMalformedResponse is thrown and the user is
         allowed to perform the action, then this is an expected failure.
         """
         decorator = rbac_rv.action(mock.sentinel.service, mock.sentinel.action)
-        mock_function = mock.Mock()
+        mock_function = mock.Mock(__name__='foo')
         mock_function.side_effect = rbac_exceptions.RbacMalformedResponse
         wrapper = decorator(mock_function)
 
-        mock_policy.RbacPolicyParser.return_value.allowed.return_value = True
+        (mock_authority_authority.PolicyAuthority.return_value.allowed
+            .return_value) = True
 
         e = self.assertRaises(exceptions.Forbidden, wrapper, self.mock_args)
         self.assertIn(
@@ -155,21 +162,21 @@
                                                " perform sentinel.action.")
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
-    def test_rule_validation_rbac_conflicting_policies_positive(self,
-                                                                mock_policy,
-                                                                mock_log):
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rule_validation_rbac_conflicting_policies_positive(
+            self, mock_authority_authority, mock_log):
         """Test RbacConflictingPolicies error is thrown without permission passes.
 
         Positive test case: if RbacConflictingPolicies is thrown and the user
         is not allowed to perform the action, then this is a success.
         """
         decorator = rbac_rv.action(mock.sentinel.service, mock.sentinel.action)
-        mock_function = mock.Mock()
+        mock_function = mock.Mock(__name__='foo')
         mock_function.side_effect = rbac_exceptions.RbacConflictingPolicies
         wrapper = decorator(mock_function)
 
-        mock_policy.RbacPolicyParser.return_value.allowed.return_value = False
+        (mock_authority_authority.PolicyAuthority.return_value.allowed
+            .return_value) = False
 
         result = wrapper(self.mock_args)
 
@@ -178,9 +185,9 @@
         mock_log.warning.assert_not_called()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
     def test_rule_validation_rbac_conflicting_policies_negative(self,
-                                                                mock_policy,
+                                                                mock_authority,
                                                                 mock_log):
         """Test RbacConflictingPolicies error is thrown with permission fails.
 
@@ -188,11 +195,12 @@
         is allowed to perform the action, then this is an expected failure.
         """
         decorator = rbac_rv.action(mock.sentinel.service, mock.sentinel.action)
-        mock_function = mock.Mock()
+        mock_function = mock.Mock(__name__='foo')
         mock_function.side_effect = rbac_exceptions.RbacConflictingPolicies
         wrapper = decorator(mock_function)
 
-        mock_policy.RbacPolicyParser.return_value.allowed.return_value = True
+        mock_authority.PolicyAuthority.return_value.allowed\
+            .return_value = True
 
         e = self.assertRaises(exceptions.Forbidden, wrapper, self.mock_args)
         self.assertIn(
@@ -203,8 +211,8 @@
                                                " perform sentinel.action.")
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
-    def test_expect_not_found_but_raises_forbidden(self, mock_policy,
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_expect_not_found_but_raises_forbidden(self, mock_authority,
                                                    mock_log):
         """Test that expecting 404 but getting 403 works for all scenarios.
 
@@ -217,17 +225,16 @@
         decorator = rbac_rv.action(mock.sentinel.service,
                                    mock.sentinel.action,
                                    expected_error_code=404)
-        mock_function = mock.Mock()
+        mock_function = mock.Mock(__name__='foo')
         mock_function.side_effect = exceptions.Forbidden('Random message.')
         wrapper = decorator(mock_function)
 
-        expected_error = "Forbidden\nDetails: Random message. An unexpected "\
-                         "exception has occurred: Expected exception was "\
-                         "NotFound, which was not thrown."
+        expected_error = "An unexpected exception has occurred during test: "\
+            "foo, Exception was: Forbidden\nDetails: Random message."
 
-        for permission in [True, False]:
-            mock_policy.RbacPolicyParser.return_value.allowed.return_value =\
-                permission
+        for allowed in [True, False]:
+            mock_authority.PolicyAuthority.return_value.allowed.\
+                return_value = allowed
 
             e = self.assertRaises(exceptions.Forbidden, wrapper,
                                   self.mock_args)
@@ -236,8 +243,9 @@
             mock_log.error.reset_mock()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
-    def test_expect_not_found_and_raise_not_found(self, mock_policy, mock_log):
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_expect_not_found_and_raise_not_found(self, mock_authority,
+                                                  mock_log):
         """Test that expecting 404 and getting 404 works for all scenarios.
 
         Tests the following scenarios:
@@ -250,7 +258,7 @@
         decorator = rbac_rv.action(mock.sentinel.service,
                                    mock.sentinel.action,
                                    expected_error_code=404)
-        mock_function = mock.Mock()
+        mock_function = mock.Mock(__name__='foo')
         mock_function.side_effect = exceptions.NotFound
         wrapper = decorator(mock_function)
 
@@ -258,9 +266,9 @@
             "Role Member was not allowed to perform sentinel.action.", None
         ]
 
-        for pos, permission in enumerate([True, False]):
-            mock_policy.RbacPolicyParser.return_value.allowed.return_value =\
-                permission
+        for pos, allowed in enumerate([True, False]):
+            mock_authority.PolicyAuthority.return_value.allowed\
+                .return_value = allowed
 
             expected_error = expected_errors[pos]
 
@@ -283,8 +291,8 @@
             mock_log.error.reset_mock()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
-    def test_rule_validation_overpermission_negative(self, mock_policy,
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rule_validation_overpermission_negative(self, mock_authority,
                                                      mock_log):
         """Test that OverPermission is correctly handled.
 
@@ -293,45 +301,46 @@
         """
         decorator = rbac_rv.action(mock.sentinel.service, mock.sentinel.action)
 
-        mock_function = mock.Mock()
+        mock_function = mock.Mock(__name__='foo')
         wrapper = decorator(mock_function)
 
-        mock_policy.RbacPolicyParser.return_value.allowed.return_value = False
+        mock_authority.PolicyAuthority.return_value.allowed\
+            .return_value = False
 
         e = self.assertRaises(rbac_exceptions.RbacOverPermission, wrapper,
                               self.mock_args)
         self.assertIn(("OverPermission: Role Member was allowed to perform "
-                      "sentinel.action"), e.__str__())
+                       "sentinel.action"), e.__str__())
         mock_log.error.assert_called_once_with(
             'Role %s was allowed to perform %s', 'Member',
             mock.sentinel.action)
 
-    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
     def test_invalid_policy_rule_throws_parsing_exception(
-            self, mock_rbac_policy_parser):
+            self, mock_authority_authority):
         """Test that invalid policy action causes test to be skipped."""
-        CONF.set_override('strict_policy_check', True, group='rbac')
+        CONF.set_override('strict_policy_check', True, group='patrole')
         self.addCleanup(CONF.clear_override, 'strict_policy_check',
-                        group='rbac')
+                        group='patrole')
 
-        mock_rbac_policy_parser.RbacPolicyParser.return_value.allowed.\
+        mock_authority_authority.PolicyAuthority.return_value.allowed.\
             side_effect = rbac_exceptions.RbacParsingException
 
         decorator = rbac_rv.action(mock.sentinel.service,
                                    mock.sentinel.policy_rule)
-        wrapper = decorator(mock.Mock())
+        wrapper = decorator(mock.Mock(__name__='foo'))
 
         e = self.assertRaises(rbac_exceptions.RbacParsingException, wrapper,
                               self.mock_args)
         self.assertEqual('Attempted to test an invalid policy file or action',
                          str(e))
 
-        mock_rbac_policy_parser.RbacPolicyParser.assert_called_once_with(
+        mock_authority_authority.PolicyAuthority.assert_called_once_with(
             mock.sentinel.project_id, mock.sentinel.user_id,
             mock.sentinel.service, extra_target_data={})
 
-    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
-    def test_get_exception_type_404(self, mock_policy):
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_get_exception_type_404(self, _):
         """Test that getting a 404 exception type returns NotFound."""
         expected_exception = exceptions.NotFound
         expected_irregular_msg = ("NotFound exception was caught for policy "
@@ -344,8 +353,8 @@
         self.assertEqual(expected_exception, actual_exception)
         self.assertEqual(expected_irregular_msg, actual_irregular_msg)
 
-    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
-    def test_get_exception_type_403(self, mock_policy):
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_get_exception_type_403(self, _):
         """Test that getting a 404 exception type returns Forbidden."""
         expected_exception = exceptions.Forbidden
         expected_irregular_msg = None
@@ -356,10 +365,9 @@
         self.assertEqual(expected_exception, actual_exception)
         self.assertEqual(expected_irregular_msg, actual_irregular_msg)
 
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
-    def test_exception_thrown_when_type_is_not_int(self, mock_policy,
-                                                   mock_log):
+    def test_exception_thrown_when_type_is_not_int(self, mock_log, _):
         """Test that non-integer exception type raises error."""
         self.assertRaises(rbac_exceptions.RbacInvalidErrorCode,
                           rbac_rv._get_exception_type, "403")
@@ -368,10 +376,9 @@
                                                "code. Currently supported "
                                                "codes: [403, 404]")
 
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
-    def test_exception_thrown_when_type_is_403_or_404(self, mock_policy,
-                                                      mock_log):
+    def test_exception_thrown_when_type_is_403_or_404(self, mock_log, _):
         """Test that unsupported exceptions throw error."""
         invalid_exceptions = [200, 400, 500]
         for exc in invalid_exceptions:
@@ -382,3 +389,51 @@
                 "codes: [403, 404]")
 
             mock_log.error.reset_mock()
+
+    @mock.patch.object(rbac_rv, 'RBACLOG', autospec=True)
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rbac_report_logging_disabled(self, mock_authority, mock_rbaclog):
+        """Test case to ensure that we DON'T write logs when
+        enable_reporting is False
+        """
+        CONF.set_override('enable_reporting', False, group='patrole_log')
+        self.addCleanup(CONF.clear_override,
+                        'enable_reporting', group='patrole_log')
+
+        decorator = rbac_rv.action(mock.sentinel.service, mock.sentinel.action)
+
+        mock_function = mock.Mock(__name__='foo-nolog')
+        wrapper = decorator(mock_function)
+
+        mock_authority.PolicyAuthority.return_value.allowed.return_value = True
+
+        wrapper(self.mock_args)
+
+        self.assertFalse(mock_rbaclog.info.called)
+
+    @mock.patch.object(rbac_rv, 'RBACLOG', autospec=True)
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rbac_report_logging_enabled(self, mock_authority, mock_rbaclog):
+        """Test case to ensure that we DO write logs when
+        enable_reporting is True
+        """
+        CONF.set_override('enable_reporting', True, group='patrole_log')
+        self.addCleanup(CONF.clear_override,
+                        'enable_reporting', group='patrole_log')
+
+        decorator = rbac_rv.action(mock.sentinel.service, mock.sentinel.action)
+
+        mock_function = mock.Mock(__name__='foo-log')
+        wrapper = decorator(mock_function)
+
+        mock_authority.PolicyAuthority.return_value.allowed.return_value = True
+
+        wrapper(self.mock_args)
+
+        mock_rbaclog.info.assert_called_once_with(
+            "[Service]: %s, [Test]: %s, [Rule]: %s, "
+            "[Expected]: %s, [Actual]: %s",
+            mock.sentinel.service, 'foo-log',
+            mock.sentinel.action,
+            "Allowed",
+            "Allowed")
diff --git a/patrole_tempest_plugin/tests/unit/test_rbac_utils.py b/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
index 540283a..87adff0 100644
--- a/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
+++ b/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
@@ -35,7 +35,7 @@
     def test_switch_role_with_missing_admin_role(self):
         self.rbac_utils.set_roles('member')
         error_re = (
-            'Roles defined by `\[rbac\] rbac_test_role` and `\[identity\] '
+            'Roles defined by `\[patrole\] rbac_test_role` and `\[identity\] '
             'admin_role` must be defined in the system.')
         self.assertRaisesRegex(rbac_exceptions.RbacResourceSetupFailed,
                                error_re, self.rbac_utils.switch_role)
@@ -43,7 +43,7 @@
     def test_switch_role_with_missing_rbac_role(self):
         self.rbac_utils.set_roles('admin')
         error_re = (
-            'Roles defined by `\[rbac\] rbac_test_role` and `\[identity\] '
+            'Roles defined by `\[patrole\] rbac_test_role` and `\[identity\] '
             'admin_role` must be defined in the system.')
         self.assertRaisesRegex(rbac_exceptions.RbacResourceSetupFailed,
                                error_re, self.rbac_utils.switch_role)
diff --git a/releasenotes/notes/deprecate-rbac-group-148e222913dc74cc.yaml b/releasenotes/notes/deprecate-rbac-group-148e222913dc74cc.yaml
new file mode 100644
index 0000000..1b1a5ac
--- /dev/null
+++ b/releasenotes/notes/deprecate-rbac-group-148e222913dc74cc.yaml
@@ -0,0 +1,6 @@
+---
+deprecations:
+  - |
+    The ``[rbac]`` configuration group has been deprecated and will be removed
+    in the next release. Use ``[patrole]`` group instead, which has the exact
+    same options.
diff --git a/releasenotes/notes/extended-availability-zone-policies-2ec19e8bbb9ce158.yaml b/releasenotes/notes/extended-availability-zone-policies-2ec19e8bbb9ce158.yaml
new file mode 100644
index 0000000..a796946
--- /dev/null
+++ b/releasenotes/notes/extended-availability-zone-policies-2ec19e8bbb9ce158.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Add RBAC tests for APIs that enforce
+    "os_compute_api:os-extended-availability-zone".
diff --git a/releasenotes/notes/flavor-manage-rbac-tests-eb78439316d67ab2.yaml b/releasenotes/notes/flavor-manage-rbac-tests-eb78439316d67ab2.yaml
new file mode 100644
index 0000000..0fbf24f
--- /dev/null
+++ b/releasenotes/notes/flavor-manage-rbac-tests-eb78439316d67ab2.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Add test coverage for the os-flavor-manage compute API, which includes
+    tests for the following policy actions:
+
+      * "os_compute_api:os-flavor-manage:create"
+      * "os_compute_api:os-flavor-manage:delete"
diff --git a/releasenotes/notes/flavor-rxtx-d7aadbb32a9f232c.yaml b/releasenotes/notes/flavor-rxtx-d7aadbb32a9f232c.yaml
new file mode 100644
index 0000000..083d9b0
--- /dev/null
+++ b/releasenotes/notes/flavor-rxtx-d7aadbb32a9f232c.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    test_flavor_rxtx_rbac now offers complete
+    coverage for the os-flavor-rxtx policy.
diff --git a/releasenotes/notes/keypairs-c8355d9496f83f9f.yaml b/releasenotes/notes/keypairs-c8355d9496f83f9f.yaml
new file mode 100644
index 0000000..0580c0e
--- /dev/null
+++ b/releasenotes/notes/keypairs-c8355d9496f83f9f.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Adds tests to see if key_name is returned in server
+    response to test_server_misc_policy_actions_rbac.
diff --git a/releasenotes/notes/rbac-per-test-log-071a530e957c1c26.yaml b/releasenotes/notes/rbac-per-test-log-071a530e957c1c26.yaml
new file mode 100644
index 0000000..b1d400c
--- /dev/null
+++ b/releasenotes/notes/rbac-per-test-log-071a530e957c1c26.yaml
@@ -0,0 +1,28 @@
+---
+features:
+  - |
+    Added in a new logging feature which logs the result of each Patrole test
+
+    The format of the new log output is:
+
+      "[Service]: %s, [Test]: %s, [Rule]: %s, [Expected]: %s, [Actual]: %s"
+
+      where each "%s" is a string that contains:
+
+      * [Service] - The openstack service being tested (Nova, Neutron, etc)
+      * [Test] - The name of the test function being invoked (eg: test_list_aggregate_rbac)
+      * [Rule] - The name of the rule the Patrole test is testing (eg: os_compute_api:os-aggregates)
+      * [Expected] - The expected outcome (one of Allowed/Denied)
+      * [Actual] - The actual outcome from the Patrole test (one of Allowed/Denied/Error)
+
+    This logging feature has two config variables:
+
+      These variables are part of a new config group ``patrole_log``
+
+      * enable_reporting:
+          This enables or disables the enhanced rbac reporting
+      * report_log_name:
+          This variable specifies the name of the log file to write
+      * report_log_path:
+          This variable specifies the path (relative or absolute)
+          of the log file to write
diff --git a/releasenotes/notes/rbac-tests-for-compute-extended-volumes-7f3ccab122d22737.yaml b/releasenotes/notes/rbac-tests-for-compute-extended-volumes-7f3ccab122d22737.yaml
new file mode 100644
index 0000000..f7eb02d
--- /dev/null
+++ b/releasenotes/notes/rbac-tests-for-compute-extended-volumes-7f3ccab122d22737.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add RBAC tests for os-extended-volumes:volumes_attached policies, which
+    validate that "os-extended-volumes:volumes_attached" is returned in the
+    response body.
diff --git a/releasenotes/notes/start-of-pike-support-360e27b4d192e3d2.yaml b/releasenotes/notes/start-of-pike-support-360e27b4d192e3d2.yaml
new file mode 100644
index 0000000..50e9159
--- /dev/null
+++ b/releasenotes/notes/start-of-pike-support-360e27b4d192e3d2.yaml
@@ -0,0 +1,10 @@
+---
+prelude: >
+    This release marks the start of support for the Pike release in Patrole.
+other:
+    - OpenStack Releases supported after this release are **Pike**.
+
+      The release under current development of this tag is Queens, meaning
+      that every Patrole commit is also tested against master during the Queens
+      cycle. However, this does not necessarily mean that using Patrole as of
+      this tag will work against a Queens (or future release) cloud.
diff --git a/requirements.txt b/requirements.txt
index cd6a577..abccb62 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,9 +3,8 @@
 # process, which may cause wedges in the gate later.
 hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
 pbr!=2.1.0,>=2.0.0 # Apache-2.0
-urllib3>=1.21.1 # MIT
-oslo.log>=3.22.0 # Apache-2.0
-oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
+oslo.log>=3.30.0 # Apache-2.0
+oslo.config>=4.6.0 # Apache-2.0
 oslo.policy>=1.23.0 # Apache-2.0
 tempest>=16.1.0 # Apache-2.0
 stevedore>=1.20.0 # Apache-2.0
diff --git a/test-requirements.txt b/test-requirements.txt
index 8a97494..dc2fec9 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -4,14 +4,14 @@
 hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
 
 sphinx>=1.6.2 # BSD
-openstackdocstheme>=1.16.0 # Apache-2.0
-reno!=2.3.1,>=1.8.0 # Apache-2.0
+openstackdocstheme>=1.17.0 # Apache-2.0
+reno>=2.5.0 # Apache-2.0
 fixtures>=3.0.0 # Apache-2.0/BSD
-mock>=2.0 # BSD
+mock>=2.0.0 # BSD
 coverage!=4.4,>=4.0 # Apache-2.0
-nose # LGPL
-nosexcover # BSD
+nose>=1.3.7 # LGPL
+nosexcover>=1.0.10 # BSD
 oslotest>=1.10.0 # Apache-2.0
 oslo.policy>=1.23.0 # Apache-2.0
-oslo.log>=3.22.0 # Apache-2.0
+oslo.log>=3.30.0 # Apache-2.0
 tempest>=16.1.0 # Apache-2.0
diff --git a/tox.ini b/tox.ini
index 41e893c..f1b5b86 100644
--- a/tox.ini
+++ b/tox.ini
@@ -59,7 +59,11 @@
 enable-extensions = H106,H203,H904
 show-source = True
 # E123, E125 skipped as they are invalid PEP-8.
-ignore = E123,E125
+#
+# H405 is another one that is good as a guideline, but sometimes
+# multiline doc strings just don't have a natural summary
+# line. Rejecting code for this reason is wrong.
+ignore = E123,E125,H405
 builtins = _
 exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build