Merge "Nova test for Volume client"
diff --git a/HACKING.rst b/HACKING.rst
index bb337b0..b1d730d 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -1,6 +1,62 @@
 Patrole Style Commandments
 ==========================
 
-Read the OpenStack Style Commandments: `<http://docs.openstack.org/developer/hacking/>`__
+- Step 1: Read the OpenStack Style Commandments: `<http://docs.openstack.org/developer/hacking/>`__
+- Step 2: Review Tempest's Style Commandments: `<https://docs.openstack.org/developer/tempest/HACKING.html>`__
+- Step 3: Read on
 
-Also review Tempest's Style Commandments: `<https://docs.openstack.org/developer/tempest/HACKING.html>`__
+Patrole Specific Commandments
+------------------------------
+
+Patrole borrows the following commandments from Tempest; refer to
+`Tempest's Commandments <https://docs.openstack.org/developer/tempest/HACKING.html>`__
+for more information:
+
+.. note::
+
+    The original Tempest Commandments do not include Patrole-specific paths.
+    Patrole-specific paths replace the Tempest-specific paths within Patrole's
+    hacking checks.
+..
+
+- [T102] Cannot import OpenStack python clients in patrole_tempest_plugin/tests/api
+- [T105] Tests cannot use setUpClass/tearDownClass
+- [T106] vim configuration should not be kept in source files.
+- [T107] Check that a service tag isn't in the module path
+- [T108] Check no hyphen at the end of rand_name() argument
+- [T109] Cannot use testtools.skip decorator; instead use
+         decorators.skip_because from tempest.lib
+- [T113] Check that tests use data_utils.rand_uuid() instead of uuid.uuid4()
+- [N322] Method's default argument shouldn't be mutable
+
+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)
+- [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'
+
+Role Switching
+--------------
+
+Correct role switching is vital to correct RBAC testing within Patrole. If a
+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.
+
+.. note::
+
+    Switching back to the admin role for setup and clean up is automatically
+    performed. Toggling ``switch_role`` with ``toggle_rbac_role=False`` within
+    the context of a test should *never* be performed and doing so will likely
+    result in an error being thrown.
+..
+
+Patrole does not have a hacking check for role switching, but does use a
+built-in mechanism for verifying that role switching is being correctly
+executed across tests. If a test does not call ``switch_role`` with
+``toggle_rbac_role=True``, then an ``RbacResourceSetupFailed`` exception
+will be raised.
diff --git a/README.rst b/README.rst
index c527c3b..ad8add0 100644
--- a/README.rst
+++ b/README.rst
@@ -24,11 +24,21 @@
 Currently, Patrole supports policies contained in code and in policy.json files.
 If both exist, the policy actions in the policy.json are prioritized.
 
+Stable Interface
+----------------
+Patrole offers a stable interface that is guaranteed to be backwards compatible and
+can be directly consumed by other projects. Currently, rbac_exceptions.py and
+rbac_policy_parser.py are guaranteed to be stable.
+
+Release Versioning
+------------------
+`Patrole Release Notes <https://docs.openstack.org/releasenotes/patrole/>`_ show
+what changes have been released.
+
 .. _test-flows:
 
 Test Flows
 ----------
-
 There are several possible test flows.
 
 If the ``rbac_test_role`` is allowed to access the endpoint:
@@ -47,7 +57,7 @@
 
     Certain services like Neutron *intentionally* raise a 404 instead of a 403
     for security concerns. Patrole accomodates this behavior by anticipating
-    a 404 instead of a 403, if specified through a special argument. For more
+    a 404 instead of a 403, using the ``expected_exception`` argument. For more
     information about Neutron's policy enforcement, see:
     `<https://docs.openstack.org/developer/neutron/devref/policy.html#request-authorization>`__.
 
@@ -70,7 +80,6 @@
 
 Test Execution Workflow
 -----------------------
-
 The workflow is as follows:
 
 #. Each test uses the ``rbac_rule_validation.action`` decorator, like below: ::
diff --git a/contrib/post_test_hook.sh b/contrib/post_test_hook.sh
index db48fc2..e934cc4 100644
--- a/contrib/post_test_hook.sh
+++ b/contrib/post_test_hook.sh
@@ -28,7 +28,6 @@
 TEMPEST_COMMAND="sudo -H -u tempest tox"
 
 DEVSTACK_GATE_TEMPEST_REGEX="(?!.*\[.*\bslow\b.*\])(^patrole_tempest_plugin\.tests\.api)"
-DEVSTACK_GATE_TEMPEST_HEAT_REGEX="(?!.*\[.*\bslow\b.*\])(^patrole_tempest_plugin\.tests\.api\.orchestration)"
 DEVSTACK_MULTINODE_GATE_TEMPEST_REGEX="(?=.*\[.*\bslow\b.*\])(^patrole_tempest_plugin\.tests\.api)"
 
 # Import devstack function 'iniset'.
@@ -61,9 +60,7 @@
     # Set strict_policy_check=False under [rbac] section in tempest.conf
     iniset $TEMPEST_CONFIG rbac strict_policy_check False
     # Set additional, necessary CONF values
-    iniset $TEMPEST_CONFIG auth use_dynamic_credentials True
     iniset $TEMPEST_CONFIG auth tempest_roles Member
-    iniset $TEMPEST_CONFIG identity auth_version v3
 }
 
 function run_tests() {
@@ -78,8 +75,6 @@
 
     if [[ "$TYPE" == "multinode" ]]; then
         $TEMPEST_COMMAND -eall-plugin -- $DEVSTACK_MULTINODE_GATE_TEMPEST_REGEX --concurrency=$TEMPEST_CONCURRENCY
-    elif [[ "$TYPE" == "heat" ]]; then
-        $TEMPEST_COMMAND -eall-plugin -- $DEVSTACK_GATE_TEMPEST_HEAT_REGEX --concurrency=$TEMPEST_CONCURRENCY
     else
         $TEMPEST_COMMAND -eall-plugin -- $DEVSTACK_GATE_TEMPEST_REGEX --concurrency=$TEMPEST_CONCURRENCY
     fi
diff --git a/doc/source/installation.rst b/doc/source/installation.rst
index b532d63..e342dd8 100644
--- a/doc/source/installation.rst
+++ b/doc/source/installation.rst
@@ -50,3 +50,13 @@
     # not found in the policy.json. Otherwise, they throw a
     # skipException.
     strict_policy_check = False
+
+    # The following config options set the location of the service's
+    # policy file. For services that have their policy in code (e.g.,
+    # Nova), this would be the location of a custom policy.json, if
+    # one exists.
+    cinder_policy_file = /etc/cinder/policy.json
+    glance_policy_file = /etc/glance/policy.json
+    keystone_policy_file = /etc/keystone/policy.json
+    neutron_policy_file = /etc/neutron/policy.json
+    nova_policy_file = /etc/nova/policy.json
diff --git a/patrole_tempest_plugin/config.py b/patrole_tempest_plugin/config.py
index dfb6cef..cb00269 100644
--- a/patrole_tempest_plugin/config.py
+++ b/patrole_tempest_plugin/config.py
@@ -30,5 +30,20 @@
                 default=False,
                 help="If true, throws RbacParsingException for"
                      " policies which don't exist. If false, "
-                     "throws skipException.")
+                     "throws skipException."),
+    cfg.StrOpt('cinder_policy_file',
+               default='/etc/cinder/policy.json',
+               help="Location of the neutron policy file."),
+    cfg.StrOpt('glance_policy_file',
+               default='/etc/glance/policy.json',
+               help="Location of the glance policy file."),
+    cfg.StrOpt('keystone_policy_file',
+               default='/etc/keystone/policy.json',
+               help="Location of the keystone policy file."),
+    cfg.StrOpt('neutron_policy_file',
+               default='/etc/neutron/policy.json',
+               help="Location of the neutron policy file."),
+    cfg.StrOpt('nova_policy_file',
+               default='/etc/nova/policy.json',
+               help="Location of the nova policy file.")
 ]
diff --git a/patrole_tempest_plugin/tests/api/orchestration/__init__.py b/patrole_tempest_plugin/hacking/__init__.py
similarity index 100%
rename from patrole_tempest_plugin/tests/api/orchestration/__init__.py
rename to patrole_tempest_plugin/hacking/__init__.py
diff --git a/patrole_tempest_plugin/hacking/checks.py b/patrole_tempest_plugin/hacking/checks.py
new file mode 100644
index 0000000..e7e5cb0
--- /dev/null
+++ b/patrole_tempest_plugin/hacking/checks.py
@@ -0,0 +1,216 @@
+# Copyright 2013 IBM Corp.
+# 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.
+
+import os
+import re
+
+import pep8
+
+
+PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'neutron',
+                  'ironic', 'heat', 'sahara']
+
+PYTHON_CLIENT_RE = re.compile('import (%s)client' % '|'.join(PYTHON_CLIENTS))
+TEST_DEFINITION = re.compile(r'^\s*def test.*')
+SETUP_TEARDOWN_CLASS_DEFINITION = re.compile(r'^\s+def (setUp|tearDown)Class')
+SCENARIO_DECORATOR = re.compile(r'\s*@.*services\((.*)\)')
+VI_HEADER_RE = re.compile(r"^#\s+vim?:.+")
+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\((.*)\)')
+IDEMPOTENT_ID_DECORATOR = re.compile(r'\s*@decorators\.idempotent_id\((.*)\)')
+
+previous_decorator = None
+
+
+def import_no_clients_in_api_tests(physical_line, filename):
+    """Check for client imports from patrole_tempest_plugin/tests/api
+
+    T102: Cannot import OpenStack python clients
+    """
+    if "patrole_tempest_plugin/tests/api" in filename:
+        res = PYTHON_CLIENT_RE.match(physical_line)
+        if res:
+            return (physical_line.find(res.group(1)),
+                    ("T102: python clients import not allowed "
+                     "in patrole_tempest_plugin/tests/api/* or "
+                     "patrole_tempest_plugin/tests/scenario/* tests"))
+
+
+def no_setup_teardown_class_for_tests(physical_line, filename):
+    """Check that tests do not use setUpClass/tearDownClass
+
+    T105: Tests cannot use setUpClass/tearDownClass
+    """
+    if pep8.noqa(physical_line):
+        return
+
+    if SETUP_TEARDOWN_CLASS_DEFINITION.match(physical_line):
+        return (physical_line.find('def'),
+                "T105: (setUp|tearDown)Class can not be used in tests")
+
+
+def no_vi_headers(physical_line, line_number, lines):
+    """Check for vi editor configuration in source files.
+
+    By default vi modelines can only appear in the first or
+    last 5 lines of a source file.
+
+    T106
+    """
+    # NOTE(gilliard): line_number is 1-indexed
+    if line_number <= 5 or line_number > len(lines) - 5:
+        if VI_HEADER_RE.match(physical_line):
+            return 0, "T106: Don't put vi configuration in source files"
+
+
+def service_tags_not_in_module_path(physical_line, filename):
+    """Check that a service tag isn't in the module path
+
+    A service tag should only be added if the service name isn't already in
+    the module path.
+
+    T107
+    """
+    matches = SCENARIO_DECORATOR.match(physical_line)
+    if matches:
+        services = matches.group(1).split(',')
+        for service in services:
+            service_name = service.strip().strip("'")
+            modulepath = os.path.split(filename)[0]
+            if service_name in modulepath:
+                return (physical_line.find(service_name),
+                        "T107: service tag should not be in path")
+
+
+def no_hyphen_at_end_of_rand_name(logical_line, filename):
+    """Check no hyphen at the end of rand_name() argument
+
+    T108
+    """
+    msg = "T108: hyphen should not be specified at the end of rand_name()"
+    if RAND_NAME_HYPHEN_RE.match(logical_line):
+        return 0, msg
+
+
+def no_mutable_default_args(logical_line):
+    """Check that mutable object isn't used as default argument
+
+    N322: Method's default argument shouldn't be mutable
+    """
+    msg = "N322: Method's default argument shouldn't be mutable!"
+    if MUTABLE_DEFAULT_ARGS.match(logical_line):
+        yield (0, msg)
+
+
+def no_testtools_skip_decorator(logical_line):
+    """Check that methods do not have the testtools.skip decorator
+
+    T109
+    """
+    if TESTTOOLS_SKIP_DECORATOR.match(logical_line):
+        yield (0, "T109: Cannot use testtools.skip decorator; instead use "
+               "decorators.skip_because from tempest.lib")
+
+
+def use_rand_uuid_instead_of_uuid4(logical_line, filename):
+    """Check that tests use data_utils.rand_uuid() instead of uuid.uuid4()
+
+    T113
+    """
+    if 'uuid.uuid4()' not in logical_line:
+        return
+
+    msg = ("T113: Tests should use data_utils.rand_uuid()/rand_uuid_hex() "
+           "instead of uuid.uuid4()/uuid.uuid4().hex")
+    yield (0, msg)
+
+
+def no_rbac_rule_validation_decorator(physical_line, filename,
+                                      previous_logical):
+    """Check that each test has the ``rbac_rule_validation.action`` decorator.
+
+    Checks whether the test function has "@rbac_rule_validation.action"
+    above it; otherwise checks that it has "@decorators.idempotent_id" above
+    it and "@rbac_rule_validation.action" above that.
+
+    Assumes that ``rbac_rule_validation.action`` decorator is either the first
+    or second decorator above the test function; otherwise this check fails.
+
+    P100
+    """
+    global previous_decorator
+
+    if "patrole_tempest_plugin/tests/api" in filename:
+
+        if IDEMPOTENT_ID_DECORATOR.match(physical_line):
+            previous_decorator = previous_logical
+            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")
+
+
+def no_rbac_suffix_in_test_filename(physical_line, filename, previous_logical):
+    """Check that RBAC filenames end with "_rbac" suffix.
+
+    P101
+    """
+    if "patrole_tempest_plugin/tests/api" in filename:
+
+        if filename.endswith('rbac_base.py'):
+            return
+
+        if not filename.endswith('_rbac.py'):
+            return 0, "RBAC test filenames must end in _rbac suffix"
+
+
+def no_rbac_test_suffix_in_test_class_name(physical_line, filename,
+                                           previous_logical):
+    """Check that RBAC class names end with "RbacTest"
+
+    P102
+    """
+    if "patrole_tempest_plugin/tests/api" in filename:
+
+        if filename.endswith('rbac_base.py'):
+            return
+
+        if CLASS.match(physical_line):
+            if not RBAC_CLASS_NAME_RE.match(physical_line):
+                return 0, "RBAC test class names must end in 'RbacTest'"
+
+
+def factory(register):
+    register(import_no_clients_in_api_tests)
+    register(no_setup_teardown_class_for_tests)
+    register(no_vi_headers)
+    register(no_hyphen_at_end_of_rand_name)
+    register(no_mutable_default_args)
+    register(no_testtools_skip_decorator)
+    register(use_rand_uuid_instead_of_uuid4)
+    register(service_tags_not_in_module_path)
+    register(no_rbac_rule_validation_decorator)
+    register(no_rbac_suffix_in_test_filename)
+    register(no_rbac_test_suffix_in_test_class_name)
diff --git a/patrole_tempest_plugin/rbac_auth.py b/patrole_tempest_plugin/rbac_auth.py
deleted file mode 100644
index ae497f3..0000000
--- a/patrole_tempest_plugin/rbac_auth.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# 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.
-
-import testtools
-
-from oslo_log import log as logging
-
-from tempest import config
-
-from patrole_tempest_plugin import rbac_exceptions
-from patrole_tempest_plugin import rbac_policy_parser
-
-LOG = logging.getLogger(__name__)
-CONF = config.CONF
-
-
-class RbacAuthority(object):
-    def __init__(self, project_id, user_id, service, extra_target_data):
-        self.policy_parser = rbac_policy_parser.RbacPolicyParser(
-            project_id, user_id, service, extra_target_data=extra_target_data)
-
-    def get_permission(self, rule_name, role):
-        try:
-            is_allowed = self.policy_parser.allowed(rule_name, role)
-            if is_allowed:
-                LOG.debug("[Action]: %s, [Role]: %s is allowed!", rule_name,
-                          role)
-            else:
-                LOG.debug("[Action]: %s, [Role]: %s is NOT allowed!",
-                          rule_name, role)
-            return is_allowed
-        except rbac_exceptions.RbacParsingException as e:
-            if CONF.rbac.strict_policy_check:
-                raise e
-            else:
-                raise testtools.TestCase.skipException(str(e))
-        return False
diff --git a/patrole_tempest_plugin/rbac_policy_parser.py b/patrole_tempest_plugin/rbac_policy_parser.py
index 2686736..d4e989b 100644
--- a/patrole_tempest_plugin/rbac_policy_parser.py
+++ b/patrole_tempest_plugin/rbac_policy_parser.py
@@ -40,8 +40,7 @@
     each role, whether a given rule is allowed using oslo policy.
     """
 
-    def __init__(self, project_id, user_id, service=None, path=None,
-                 extra_target_data={}):
+    def __init__(self, project_id, user_id, service, extra_target_data=None):
         """Initialization of Rbac Policy Parser.
 
         Parses a policy file to create a dictionary, mapping policy actions to
@@ -64,6 +63,9 @@
         :param path: type string
         """
 
+        if extra_target_data is None:
+            extra_target_data = {}
+
         # First check if the service is valid
         service = service.lower().strip() if service else None
         self.admin_mgr = credentials.AdminManager()
@@ -76,7 +78,12 @@
 
         # Use default path in /etc/<service_name/policy.json if no path
         # is provided.
-        self.path = path or os.path.join('/etc', service, 'policy.json')
+        path = getattr(CONF.rbac, '%s_policy_file' % str(service), None)
+        if not path:
+            LOG.info("No config option found for %s,"
+                     " using default path" % str(service))
+            path = os.path.join('/etc', service, 'policy.json')
+        self.path = path
         self.rules = policy.Rules.load(self._get_policy_data(service),
                                        'default')
         self.project_id = project_id
@@ -98,12 +105,18 @@
 
         # Check whether policy file exists.
         if os.path.isfile(self.path):
-            with open(self.path, 'r') as policy_file:
-                file_policy_data = policy_file.read()
             try:
+                with open(self.path, 'r') as policy_file:
+                    file_policy_data = policy_file.read()
                 file_policy_data = json.loads(file_policy_data)
-            except ValueError:
-                file_policy_data = None
+            except (IOError, ValueError) as e:
+                msg = "Failed to read policy file for service. "
+                if isinstance(e, IOError):
+                    msg += "Please check that policy path exists."
+                else:
+                    msg += "JSON may be improperly formatted."
+                LOG.debug(msg)
+                file_policy_data = {}
 
         # Check whether policy actions are defined in code. Nova and Keystone,
         # for example, define their default policy actions in code.
@@ -157,7 +170,7 @@
             return self._allowed(
                 access=self._get_access_token(role),
                 apply_rule='context_is_admin')
-        return role == 'admin'
+        return role == CONF.identity.admin_role
 
     def _get_access_token(self, role):
         access_token = {
diff --git a/patrole_tempest_plugin/rbac_rule_validation.py b/patrole_tempest_plugin/rbac_rule_validation.py
index 6a5ed5e..d77b2d6 100644
--- a/patrole_tempest_plugin/rbac_rule_validation.py
+++ b/patrole_tempest_plugin/rbac_rule_validation.py
@@ -15,6 +15,7 @@
 
 import logging
 import sys
+import testtools
 
 import six
 
@@ -22,15 +23,15 @@
 from tempest.lib import exceptions
 from tempest import test
 
-from patrole_tempest_plugin import rbac_auth
 from patrole_tempest_plugin import rbac_exceptions
+from patrole_tempest_plugin import rbac_policy_parser
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
 
 
-def action(service, rule, admin_only=False, expected_error_code=403,
-           extra_target_data={}):
+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 which allows for positive and negative RBAC testing. Given
@@ -61,53 +62,49 @@
     :raises Forbidden: for bullet (2) above.
     :raises RbacOverPermission: for bullet (3) above.
     """
+
+    if extra_target_data is None:
+        extra_target_data = {}
+
     def decorator(func):
+        role = CONF.rbac.rbac_test_role
+
         def wrapper(*args, **kwargs):
-            try:
-                caller_ref = None
-                if args and isinstance(args[0], test.BaseTestCase):
-                    caller_ref = args[0]
-                project_id = caller_ref.auth_provider.credentials.project_id
-                user_id = caller_ref.auth_provider.credentials.user_id
-            except AttributeError as e:
-                msg = ("{0}: project_id/user_id not found in "
-                       "cls.auth_provider.credentials".format(e))
-                LOG.error(msg)
-                raise rbac_exceptions.RbacResourceSetupFailed(msg)
+            if args and isinstance(args[0], test.BaseTestCase):
+                test_obj = args[0]
+            else:
+                raise rbac_exceptions.RbacResourceSetupFailed(
+                    '`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 = CONF.rbac.rbac_test_role == 'admin'
+                allowed = CONF.rbac.rbac_test_role == CONF.identity.admin_role
             else:
-                authority = rbac_auth.RbacAuthority(
-                    project_id, user_id, service,
-                    _format_extra_target_data(caller_ref, extra_target_data))
-                allowed = authority.get_permission(
-                    rule, CONF.rbac.rbac_test_role)
+                allowed = _is_authorized(test_obj, service, rule,
+                                         extra_target_data)
 
             expected_exception, irregular_msg = _get_exception_type(
                 expected_error_code)
 
             try:
-                func(*args)
+                func(*args, **kwargs)
             except rbac_exceptions.RbacInvalidService as e:
                 msg = ("%s is not a valid service." % service)
                 LOG.error(msg)
                 raise exceptions.NotFound(
-                    "%s RbacInvalidService was: %s" %
-                    (msg, e))
+                    "%s RbacInvalidService was: %s" % (msg, e))
             except (expected_exception, rbac_exceptions.RbacActionFailed) as e:
                 if irregular_msg:
                     LOG.warning(irregular_msg.format(rule, service))
                 if allowed:
                     msg = ("Role %s was not allowed to perform %s." %
-                           (CONF.rbac.rbac_test_role, rule))
+                           (role, rule))
                     LOG.error(msg)
                     raise exceptions.Forbidden(
-                        "%s exception was: %s" %
-                        (msg, e))
+                        "%s exception was: %s" % (msg, e))
             except Exception as e:
                 exc_info = sys.exc_info()
                 error_details = exc_info[1].__str__()
@@ -119,21 +116,58 @@
             else:
                 if not allowed:
                     LOG.error("Role %s was allowed to perform %s" %
-                              (CONF.rbac.rbac_test_role, rule))
+                              (role, rule))
                     raise rbac_exceptions.RbacOverPermission(
                         "OverPermission: Role %s was allowed to perform %s" %
-                        (CONF.rbac.rbac_test_role, rule))
+                        (role, rule))
             finally:
-                caller_ref.rbac_utils.switch_role(caller_ref,
-                                                  toggle_rbac_role=False)
-        return wrapper
+                test_obj.rbac_utils.switch_role(test_obj,
+                                                toggle_rbac_role=False)
+
+        _wrapper = testtools.testcase.attr(role)(wrapper)
+        return _wrapper
     return decorator
 
 
+def _is_authorized(test_obj, service, rule_name, extra_target_data):
+    try:
+        project_id = test_obj.auth_provider.credentials.project_id
+        user_id = test_obj.auth_provider.credentials.user_id
+    except AttributeError as e:
+        msg = ("{0}: project_id/user_id not found in "
+               "cls.auth_provider.credentials".format(e))
+        LOG.error(msg)
+        raise rbac_exceptions.RbacResourceSetupFailed(msg)
+
+    try:
+        role = CONF.rbac.rbac_test_role
+        formatted_target_data = _format_extra_target_data(
+            test_obj, extra_target_data)
+        policy_parser = rbac_policy_parser.RbacPolicyParser(
+            project_id, user_id, service,
+            extra_target_data=formatted_target_data)
+        is_allowed = policy_parser.allowed(rule_name, role)
+
+        if is_allowed:
+            LOG.debug("[Action]: %s, [Role]: %s is allowed!", rule_name,
+                      role)
+        else:
+            LOG.debug("[Action]: %s, [Role]: %s is NOT allowed!",
+                      rule_name, role)
+        return is_allowed
+    except rbac_exceptions.RbacParsingException as e:
+        if CONF.rbac.strict_policy_check:
+            raise e
+        else:
+            raise testtools.TestCase.skipException(str(e))
+    return False
+
+
 def _get_exception_type(expected_error_code):
     expected_exception = None
     irregular_msg = None
     supported_error_codes = [403, 404]
+
     if expected_error_code == 403:
         expected_exception = exceptions.Forbidden
     elif expected_error_code == 404:
diff --git a/patrole_tempest_plugin/rbac_utils.py b/patrole_tempest_plugin/rbac_utils.py
index d952014..4cddb8d 100644
--- a/patrole_tempest_plugin/rbac_utils.py
+++ b/patrole_tempest_plugin/rbac_utils.py
@@ -20,6 +20,7 @@
 import oslo_utils.uuidutils as uuid_utils
 import six
 
+from tempest.common import credentials_factory as credentials
 from tempest import config
 
 from patrole_tempest_plugin import rbac_exceptions
@@ -56,10 +57,12 @@
         self.token = test_obj.auth_provider.get_token()
         self.identity_version = test_obj.get_identity_version()
 
-        if self.identity_version.endswith('v3'):
-            self.roles_client = test_obj.os_admin.roles_v3_client
-        else:
-            self.roles_client = test_obj.os_admin.roles_client
+        if not credentials.is_admin_available(
+                identity_version=self.identity_version):
+            msg = "Missing Identity Admin API credentials in configuration."
+            raise rbac_exceptions.RbacResourceSetupFailed(msg)
+
+        self.roles_client = test_obj.os_admin.roles_v3_client
 
         LOG.debug('Switching role to: %s', toggle_rbac_role)
 
@@ -157,7 +160,7 @@
         for role in available_roles['roles']:
             if role['name'] == CONF.rbac.rbac_test_role:
                 rbac_role_id = role['id']
-            if role['name'] == 'admin':
+            if role['name'] == CONF.identity.admin_role:
                 admin_role_id = role['id']
 
         if not admin_role_id or not rbac_role_id:
diff --git a/patrole_tempest_plugin/tests/api/compute/rbac_base.py b/patrole_tempest_plugin/tests/api/compute/rbac_base.py
index b2f830e..bc02dbf 100644
--- a/patrole_tempest_plugin/tests/api/compute/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/compute/rbac_base.py
@@ -35,7 +35,7 @@
     @classmethod
     def setup_clients(cls):
         super(BaseV2ComputeRbacTest, cls).setup_clients()
-        cls.auth_provider = cls.os.auth_provider
+        cls.auth_provider = cls.os_primary.auth_provider
         cls.rbac_utils = rbac_utils()
         cls.rbac_utils.switch_role(cls, toggle_rbac_role=False)
 
diff --git a/patrole_tempest_plugin/tests/api/compute/test_aggregates_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_aggregates_rbac.py
index a180bc4..db504c0 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_aggregates_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_aggregates_rbac.py
@@ -13,30 +13,28 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-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 import test
 
 from patrole_tempest_plugin import rbac_rule_validation
 from patrole_tempest_plugin.tests.api.compute import rbac_base
 
-CONF = config.CONF
-
 
 class AggregatesRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
     @classmethod
     def skip_checks(cls):
         super(AggregatesRbacTest, cls).skip_checks()
-        if not CONF.compute_feature_enabled.api_extensions:
-            raise cls.skipException(
-                '%s skipped as no compute extensions enabled' % cls.__name__)
+        if not test.is_extension_enabled('os-aggregates', 'compute'):
+            msg = "%s skipped as os-aggregates not enabled." % cls.__name__
+            raise cls.skipException(msg)
 
     @classmethod
     def setup_clients(cls):
         super(AggregatesRbacTest, cls).setup_clients()
-        cls.hosts_client = cls.os.hosts_client
+        cls.hosts_client = cls.os_primary.hosts_client
 
     def _create_aggregate(self):
         agg_name = data_utils.rand_name('aggregate')
diff --git a/patrole_tempest_plugin/tests/api/compute/test_attach_interfaces_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_attach_interfaces_rbac.py
index 7241c98..90b60c9 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_attach_interfaces_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_attach_interfaces_rbac.py
@@ -17,6 +17,7 @@
 from tempest import config
 from tempest.lib.common.utils import test_utils
 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
@@ -34,9 +35,10 @@
     @classmethod
     def skip_checks(cls):
         super(AttachInterfacesRbacTest, cls).skip_checks()
-        if not CONF.compute_feature_enabled.api_extensions:
-            raise cls.skipException(
-                '%s skipped as no compute extensions enabled' % cls.__name__)
+        if not test.is_extension_enabled('os-attach-interfaces', 'compute'):
+            msg = ("%s skipped as os-attach-interfaces not "
+                   "enabled." % cls.__name__)
+            raise cls.skipException(msg)
         if not CONF.compute_feature_enabled.interface_attach:
             raise cls.skipException(
                 "%s skipped as interface attachment is not available"
diff --git a/patrole_tempest_plugin/tests/api/compute/test_availability_zone_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_availability_zone_rbac.py
index c466ad2..0743d7c 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_availability_zone_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_availability_zone_rbac.py
@@ -11,34 +11,38 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import config
 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
 
-CONF = config.CONF
-
 
 class NovaAvailabilityZoneRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
     @classmethod
     def skip_checks(cls):
         super(NovaAvailabilityZoneRbacTest, cls).skip_checks()
-        if not CONF.compute_feature_enabled.api_extensions:
-            raise cls.skipException(
-                '%s skipped as no compute extensions enabled' % cls.__name__)
+        if not test.is_extension_enabled('os-availability-zone', 'compute'):
+            msg = ("%s skipped as os-availability-zone not "
+                   "enabled." % cls.__name__)
+            raise cls.skipException(msg)
+
+    @classmethod
+    def setup_clients(cls):
+        super(NovaAvailabilityZoneRbacTest, cls).setup_clients()
+        cls.client = cls.availability_zone_client
 
     @rbac_rule_validation.action(service="nova", rule="os_compute_api:"
                                  "os-availability-zone:list")
     @decorators.idempotent_id('cd34e7ea-d26e-4fa3-a8d0-f8883726ce3d')
     def test_get_availability_zone_list_rbac(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.os.availability_zone_client.list_availability_zones()
+        self.client.list_availability_zones()
 
     @rbac_rule_validation.action(service="nova", rule="os_compute_api:"
                                  "os-availability-zone:detail")
     @decorators.idempotent_id('2f61c191-6ece-4f21-b487-39d749e3d38e')
     def test_get_availability_zone_list_detail_rbac(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.os.availability_zone_client.list_availability_zones(detail=True)
+        self.client.list_availability_zones(detail=True)
diff --git a/patrole_tempest_plugin/tests/api/compute/test_floating_ip_pools_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_floating_ip_pools_rbac.py
index 6d78878..75cc8f4 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_floating_ip_pools_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_floating_ip_pools_rbac.py
@@ -25,15 +25,16 @@
 
 class FloatingIpPoolsRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
-    # Tests will fail with a 404 starting from microversion 2.36, according to:
-    # https://developer.openstack.org/api-ref/
-    # compute/?expanded=list-floating-ip-pools-detail
+    # Tests will fail with a 404 starting from microversion 2.36:
+    # See the following link for details:
+    # https://developer.openstack.org/api-ref/compute/#floating-ip-pools-os-floating-ip-pools-deprecated
+    min_microversion = '2.10'
     max_microversion = '2.35'
 
     @classmethod
     def setup_clients(cls):
         super(FloatingIpPoolsRbacTest, cls).setup_clients()
-        cls.client = cls.os.floating_ip_pools_client
+        cls.client = cls.os_primary.floating_ip_pools_client
 
     @classmethod
     def skip_checks(cls):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_floating_ips_bulk_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_floating_ips_bulk_rbac.py
index ab9d551..bff0612 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_floating_ips_bulk_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_floating_ips_bulk_rbac.py
@@ -25,15 +25,16 @@
 
 class FloatingIpsBulkRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
-    # Tests will fail with a 404 starting from microversion 2.36, according to:
-    # https://developer.openstack.org/api-ref/
-    # compute/?expanded=list-floating-ips-detail
+    # Tests will fail with a 404 starting from microversion 2.36:
+    # See the following link for details:
+    # https://developer.openstack.org/api-ref/compute/#floating-ips-bulk-os-floating-ips-bulk-deprecated
+    min_microversion = '2.10'
     max_microversion = '2.35'
 
     @classmethod
     def setup_clients(cls):
         super(FloatingIpsBulkRbacTest, cls).setup_clients()
-        cls.client = cls.os.floating_ips_bulk_client
+        cls.client = cls.os_primary.floating_ips_bulk_client
 
     @classmethod
     def skip_checks(cls):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_floating_ips_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_floating_ips_rbac.py
index 784a82d..0bd00a4 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_floating_ips_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_floating_ips_rbac.py
@@ -25,9 +25,10 @@
 
 class FloatingIpsRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
-    # Tests will fail with a 404 starting from microversion 2.36, according to:
-    # https://developer.openstack.org/api-ref/
-    # compute/?expanded=list-floating-ip-addresses-detail
+    # Tests will fail with a 404 starting from microversion 2.36:
+    # See the following link for details:
+    # https://developer.openstack.org/api-ref/compute/#floating-ips-os-floating-ips-deprecated
+    min_microversion = '2.10'
     max_microversion = '2.35'
 
     @classmethod
diff --git a/patrole_tempest_plugin/tests/api/compute/test_hosts_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_hosts_rbac.py
index 53e3a70..0b7f2d1 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_hosts_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_hosts_rbac.py
@@ -13,28 +13,26 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import config
 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
 
-CONF = config.CONF
-
 
 class HostsRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
     @classmethod
     def setup_clients(cls):
         super(HostsRbacTest, cls).setup_clients()
-        cls.client = cls.os.hosts_client
+        cls.client = cls.os_primary.hosts_client
 
     @classmethod
     def skip_checks(cls):
         super(HostsRbacTest, cls).skip_checks()
-        if not CONF.compute_feature_enabled.api_extensions:
-            raise cls.skipException(
-                '%s skipped as no compute extensions enabled' % cls.__name__)
+        if not test.is_extension_enabled('os-hosts', 'compute'):
+            msg = "%s skipped as os-hosts not enabled." % cls.__name__
+            raise cls.skipException(msg)
 
     @decorators.idempotent_id('035b7935-2fae-4218-8d37-27fa83097494')
     @rbac_rule_validation.action(
diff --git a/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py
index 3a4f2d6..b848509 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py
@@ -48,7 +48,7 @@
     def setup_clients(cls):
         super(ImagesV235RbacTest, cls).setup_clients()
         cls.client = cls.compute_images_client
-        cls.glance_image_client = cls.os.image_client_v2
+        cls.glance_image_client = cls.os_primary.image_client_v2
 
     @classmethod
     def resource_setup(cls):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_ips_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_ips_rbac.py
index 96fa243..600cfbe 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_ips_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_ips_rbac.py
@@ -15,6 +15,7 @@
 
 from tempest import config
 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
@@ -32,9 +33,9 @@
     @classmethod
     def skip_checks(cls):
         super(IpsRbacTest, cls).skip_checks()
-        if not CONF.compute_feature_enabled.api_extensions:
-            raise cls.skipException(
-                '%s skipped as no compute extensions enabled' % cls.__name__)
+        if not test.is_extension_enabled('os-ips', 'compute'):
+            msg = "%s skipped as os-ips not enabled." % cls.__name__
+            raise cls.skipException(msg)
         if not CONF.service_available.neutron:
             raise cls.skipException(
                 '%s skipped as Neutron is required' % cls.__name__)
diff --git a/patrole_tempest_plugin/tests/api/compute/test_limits_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_limits_rbac.py
index eb0e686..f16a635 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_limits_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_limits_rbac.py
@@ -11,14 +11,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import config
 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
 
-CONF = config.CONF
-
 
 class LimitsRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
@@ -30,9 +28,9 @@
     @classmethod
     def skip_checks(cls):
         super(LimitsRbacTest, cls).skip_checks()
-        if not CONF.compute_feature_enabled.api_extensions:
-            raise cls.skipException(
-                '%s skipped as no compute extensions enabled' % cls.__name__)
+        if not test.is_extension_enabled('os-limits', 'compute'):
+            msg = "%s skipped as os-limits not enabled." % cls.__name__
+            raise cls.skipException(msg)
 
     @rbac_rule_validation.action(service="nova",
                                  rule="os_compute_api:limits")
diff --git a/patrole_tempest_plugin/tests/api/compute/test_migrations_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_migrations_rbac.py
index fda9071..b6c6c5f 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_migrations_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_migrations_rbac.py
@@ -13,14 +13,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import config
 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
 
-CONF = config.CONF
-
 
 class MigrationsRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
@@ -32,9 +30,9 @@
     @classmethod
     def skip_checks(cls):
         super(MigrationsRbacTest, cls).skip_checks()
-        if not CONF.compute_feature_enabled.api_extensions:
-            raise cls.skipException(
-                '%s skipped as no compute extensions enabled' % cls.__name__)
+        if not test.is_extension_enabled('os-migrations', 'compute'):
+            msg = "%s skipped as os-migrations not enabled." % cls.__name__
+            raise cls.skipException(msg)
 
     @decorators.idempotent_id('5795231c-3729-448c-a072-9a225db1a328')
     @rbac_rule_validation.action(
diff --git a/patrole_tempest_plugin/tests/api/compute/test_quota_sets_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_quota_sets_rbac.py
index 826ae8d..f6706a7 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_quota_sets_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_quota_sets_rbac.py
@@ -28,7 +28,7 @@
     def setup_clients(cls):
         super(QuotaSetsRbacTest, cls).setup_clients()
         cls.client = cls.quotas_client
-        cls.projects_client = cls.os.projects_client
+        cls.projects_client = cls.os_primary.projects_client
 
     @classmethod
     def skip_checks(cls):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_rescue_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_rescue_rbac.py
index acf9bd6..277ba1b 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_rescue_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_rescue_rbac.py
@@ -13,14 +13,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import config
 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
 
-CONF = config.CONF
-
 
 class RescueRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
@@ -32,9 +30,9 @@
     @classmethod
     def skip_checks(cls):
         super(RescueRbacTest, cls).skip_checks()
-        if not CONF.compute_feature_enabled.api_extensions:
-            raise cls.skipException(
-                '%s skipped as no compute extensions enabled' % cls.__name__)
+        if not test.is_extension_enabled('os-rescue', 'compute'):
+            msg = "%s skipped as os-rescue not enabled." % cls.__name__
+            raise cls.skipException(msg)
 
     @classmethod
     def resource_setup(cls):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_actions_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_actions_rbac.py
index 81266af..86b650e 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_actions_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_actions_rbac.py
@@ -301,12 +301,9 @@
 
 class ServerActionsV216RbacTest(rbac_base.BaseV2ComputeRbacTest):
 
-    # This class has test case(s) that requires at least version 2.16.
-    #
+    # This class has test case(s) that requires at least microversion 2.16.
     # See the following link for details:
-    # http://developer.openstack.org/
-    # api-ref-compute-v2.1.html#show-server-details
-
+    # http://developer.openstack.org/api-ref-compute-v2.1.html#show-server-details
     min_microversion = '2.16'
     max_microversion = 'latest'
 
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_diagnostics_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_diagnostics_rbac.py
index dd810a5..2a309e3 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_diagnostics_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_diagnostics_rbac.py
@@ -13,14 +13,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import config
 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
 
-CONF = config.CONF
-
 
 class ServerDiagnosticsRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
@@ -32,9 +30,10 @@
     @classmethod
     def skip_checks(cls):
         super(ServerDiagnosticsRbacTest, cls).skip_checks()
-        if not CONF.compute_feature_enabled.api_extensions:
-            raise cls.skipException(
-                '%s skipped as no compute extensions enabled' % cls.__name__)
+        if not test.is_extension_enabled('os-server-diagnostics', 'compute'):
+            msg = ("%s skipped as os-server-diagnostics not "
+                   "enabled." % cls.__name__)
+            raise cls.skipException(msg)
 
     @classmethod
     def resource_setup(cls):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_groups_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_groups_rbac.py
index b89750d..a75f550 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_groups_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_groups_rbac.py
@@ -13,14 +13,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import config
 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
 
-CONF = config.CONF
-
 
 class ServerGroupsRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
@@ -32,9 +30,9 @@
     @classmethod
     def skip_checks(cls):
         super(ServerGroupsRbacTest, cls).skip_checks()
-        if not CONF.compute_feature_enabled.api_extensions:
-            raise cls.skipException(
-                '%s skipped as no compute extensions enabled' % cls.__name__)
+        if not test.is_extension_enabled('os-server-groups', 'compute'):
+            msg = "%s skipped as os-server-groups not enabled." % cls.__name__
+            raise cls.skipException(msg)
 
     @classmethod
     def resource_setup(cls):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_migrations_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_migrations_rbac.py
index e5b1ea0..38ef552 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_migrations_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_migrations_rbac.py
@@ -43,8 +43,8 @@
     def setup_clients(cls):
         super(MigrateServerV225RbacTest, cls).setup_clients()
         cls.client = cls.servers_client
-        cls.admin_servers_client = cls.os_adm.servers_client
-        cls.hosts_client = cls.os.hosts_client
+        cls.admin_servers_client = cls.os_admin.servers_client
+        cls.hosts_client = cls.os_primary.hosts_client
 
     def _get_server_details(self, server_id):
         body = self.client.show_server(server_id)['server']
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 4157c05..b71887c 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
@@ -37,8 +37,8 @@
     def setup_clients(cls):
         super(ComputeServersRbacTest, cls).setup_clients()
         cls.client = cls.servers_client
-        cls.networks_client = cls.os.networks_client
-        cls.subnets_client = cls.os.subnets_client
+        cls.networks_client = cls.os_primary.networks_client
+        cls.subnets_client = cls.os_primary.subnets_client
 
     @classmethod
     def resource_setup(cls):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_usage_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_usage_rbac.py
index 4493826..8f836a6 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_usage_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_usage_rbac.py
@@ -13,14 +13,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import config
 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
 
-CONF = config.CONF
-
 
 class ServerUsageRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
@@ -32,9 +30,9 @@
     @classmethod
     def skip_checks(cls):
         super(ServerUsageRbacTest, cls).skip_checks()
-        if not CONF.compute_feature_enabled.api_extensions:
-            raise cls.skipException(
-                '%s skipped as no compute extensions enabled' % cls.__name__)
+        if not test.is_extension_enabled('OS-SRV-USG', 'compute'):
+            msg = "%s skipped as OS-SRV-USG not enabled." % cls.__name__
+            raise cls.skipException(msg)
 
     @classmethod
     def resource_setup(cls):
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 ba33a0f..160affd 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
@@ -31,7 +31,7 @@
     def setup_clients(cls):
         super(ServerVolumeAttachmentRbacTest, cls).setup_clients()
         cls.client = cls.servers_client
-        cls.volumes_client = cls.os.volumes_client
+        cls.volumes_client = cls.os_primary.volumes_client
 
     @classmethod
     def skip_checks(cls):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_simple_tenant_usage_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_simple_tenant_usage_rbac.py
index 93c179e..5ecc32a 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_simple_tenant_usage_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_simple_tenant_usage_rbac.py
@@ -13,28 +13,27 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import config
 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
 
-CONF = config.CONF
-
 
 class SimpleTenantUsageRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
     @classmethod
     def setup_clients(cls):
         super(SimpleTenantUsageRbacTest, cls).setup_clients()
-        cls.client = cls.os.tenant_usages_client
+        cls.client = cls.os_primary.tenant_usages_client
 
     @classmethod
     def skip_checks(cls):
         super(SimpleTenantUsageRbacTest, cls).skip_checks()
-        if not CONF.compute_feature_enabled.api_extensions:
-            raise cls.skipException(
-                '%s skipped as no compute extensions enabled' % cls.__name__)
+        if not test.is_extension_enabled('os-simple-tenant-usage', 'compute'):
+            msg = ("%s skipped as os-simple-tenant-usage not "
+                   "enabled." % cls.__name__)
+            raise cls.skipException(msg)
 
     @rbac_rule_validation.action(
         service="nova",
diff --git a/patrole_tempest_plugin/tests/api/compute/test_tenant_networks_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_tenant_networks_rbac.py
index 15a385a..ee2b9a2 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_tenant_networks_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_tenant_networks_rbac.py
@@ -26,15 +26,16 @@
 
 class TenantNetworksRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
-    # Tests will fail with a 404 starting from microversion 2.36, according to:
-    # https://developer.openstack.org/api-ref/
-    # compute/?expanded=list-project-networks-detail
+    # Tests will fail with a 404 starting from microversion 2.36.
+    # See the following link for details:
+    # https://developer.openstack.org/api-ref/compute/#project-networks-os-tenant-networks-deprecated
+    min_microversion = '2.10'
     max_microversion = '2.35'
 
     @classmethod
     def setup_clients(cls):
         super(TenantNetworksRbacTest, cls).setup_clients()
-        cls.client = cls.os.tenant_networks_client
+        cls.client = cls.os_primary.tenant_networks_client
 
     @classmethod
     def skip_checks(cls):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/rbac_base.py b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
similarity index 63%
rename from patrole_tempest_plugin/tests/api/identity/v3/rbac_base.py
rename to patrole_tempest_plugin/tests/api/identity/rbac_base.py
index 31533a3..b37477e 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
@@ -12,6 +12,7 @@
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 #    License for the specific language governing permissions and limitations
 #    under the License.
+from oslo_log import log as logging
 
 from tempest.api.identity import base
 from tempest import config
@@ -21,55 +22,218 @@
 from patrole_tempest_plugin.rbac_utils import rbac_utils
 
 CONF = config.CONF
+LOG = logging.getLogger(__name__)
 
 
-class BaseIdentityV3RbacTest(base.BaseIdentityV3Test):
+class BaseIdentityRbacTest(base.BaseIdentityTest):
 
     credentials = ['admin', 'primary']
 
     @classmethod
     def skip_checks(cls):
-        super(BaseIdentityV3RbacTest, cls).skip_checks()
+        super(BaseIdentityRbacTest, cls).skip_checks()
         if not CONF.rbac.enable_rbac:
             raise cls.skipException(
                 "%s skipped as RBAC testing not enabled" % cls.__name__)
 
     @classmethod
     def setup_clients(cls):
-        super(BaseIdentityV3RbacTest, cls).setup_clients()
-        cls.auth_provider = cls.os.auth_provider
+        super(BaseIdentityRbacTest, cls).setup_clients()
+        cls.auth_provider = cls.os_primary.auth_provider
 
         cls.rbac_utils = rbac_utils()
         cls.rbac_utils.switch_role(cls, toggle_rbac_role=False)
 
-        cls.creds_client = cls.os.credentials_client
-        cls.consumers_client = cls.os.oauth_consumers_client
-        cls.domains_client = cls.os.domains_client
-        cls.endpoints_client = cls.os.endpoints_v3_client
-        cls.groups_client = cls.os.groups_client
-        cls.projects_client = cls.os.projects_client
-        cls.policies_client = cls.os.policies_client
-        cls.regions_client = cls.os.regions_client
-        cls.role_assignments_client = cls.os.role_assignments_client
-        cls.roles_client = cls.os.roles_v3_client
-        cls.services_client = cls.os.identity_services_v3_client
-        cls.trusts_client = cls.os.trusts_client
-        cls.users_client = cls.os.users_v3_client
+    @classmethod
+    def resource_setup(cls):
+        super(BaseIdentityRbacTest, cls).resource_setup()
+        cls.endpoints = []
+        cls.roles = []
+        cls.services = []
+        cls.users = []
+
+    @classmethod
+    def resource_cleanup(cls):
+        for endpoint in cls.endpoints:
+            test_utils.call_and_ignore_notfound_exc(
+                cls.endpoints_client.delete_endpoint, endpoint['id'])
+
+        for role in cls.roles:
+            test_utils.call_and_ignore_notfound_exc(
+                cls.roles_client.delete_role, role['id'])
+
+        for service in cls.services:
+            test_utils.call_and_ignore_notfound_exc(
+                cls.services_client.delete_service, service['id'])
+
+        for user in cls.users:
+            test_utils.call_and_ignore_notfound_exc(
+                cls.users_client.delete_user, user['id'])
+
+        super(BaseIdentityRbacTest, cls).resource_cleanup()
+
+    @classmethod
+    def setup_test_endpoint(cls, service=None):
+        """Creates a service and an endpoint for test."""
+        interface = 'public'
+        url = data_utils.rand_url()
+        region_name = data_utils.rand_name('region')
+        # Endpoint creation requires a service
+        if service is None:
+            service = cls.setup_test_service()
+        params = {
+            'service_id': service['id'],
+            'region': region_name,
+            'interface': interface
+        }
+        if cls.identity_version == 'v2':
+            params['publicurl'] = url
+        elif cls.identity_version == 'v3':
+            params['url'] = url
+        else:
+            LOG.debug("Keystone version is invalid."
+                      " Please enter a valid version number.")
+            raise KeyError
+
+        endpoint = cls.endpoints_client.create_endpoint(**params)['endpoint']
+        cls.endpoints.append(endpoint)
+
+        return endpoint
+
+    @classmethod
+    def setup_test_role(cls):
+        """Set up a test role."""
+        name = data_utils.rand_name('test_role')
+        role = cls.roles_client.create_role(name=name)['role']
+        cls.roles.append(role)
+
+        return role
+
+    @classmethod
+    def setup_test_service(cls):
+        """Setup a test service."""
+        name = data_utils.rand_name('service')
+        serv_type = data_utils.rand_name('type')
+        desc = data_utils.rand_name('description')
+
+        service = cls.services_client.create_service(
+            name=name,
+            type=serv_type,
+            description=desc)
+
+        if cls.identity_version == 'v2':
+            service = service['OS-KSADM:service']
+        elif cls.identity_version == 'v3':
+            service = service['service']
+        else:
+            LOG.debug("Keystone version is invalid."
+                      " Please enter a valid version number.")
+            raise KeyError
+
+        cls.services.append(service)
+
+        return service
+
+    @classmethod
+    def setup_test_user(cls, password=None, **kwargs):
+        """Set up a test user."""
+        username = data_utils.rand_name('test_user')
+        email = username + '@testmail.tm'
+
+        user = cls.users_client.create_user(
+            name=username,
+            email=email,
+            password=password,
+            **kwargs)['user']
+        cls.users.append(user)
+
+        return user
+
+
+class BaseIdentityV2AdminRbacTest(BaseIdentityRbacTest):
+    """Base test class for the Identity v2 admin API.
+
+    Keystone's v2 API is split into two APIs: an admin and non-admin API. RBAC
+    testing is only provided for the admin API. Instead of policy enforcement,
+    these APIs execute ``self.assert_admin(request)``, which checks that the
+    request object has ``context_is_admin``. For more details, see the
+    implementation of ``assert_admin`` in ``keystone.common.wsgi``.
+    """
+
+    identity_version = 'v2'
+
+    @classmethod
+    def skip_checks(cls):
+        super(BaseIdentityV2AdminRbacTest, cls).skip_checks()
+        if not CONF.identity_feature_enabled.api_v2_admin:
+            raise cls.skipException('Identity v2 admin not available')
+
+    @classmethod
+    def setup_clients(cls):
+        super(BaseIdentityV2AdminRbacTest, cls).setup_clients()
+        cls.client = cls.os_primary.identity_client
+        cls.endpoints_client = cls.os_primary.endpoints_client
+        cls.roles_client = cls.os_primary.roles_client
+        cls.services_client = cls.os_primary.identity_services_client
+        cls.tenants_client = cls.os_primary.tenants_client
+        cls.token_client = cls.os_primary.token_client
+        cls.users_client = cls.os_primary.users_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(BaseIdentityV2AdminRbacTest, cls).resource_setup()
+        cls.tenants = []
+
+    @classmethod
+    def resource_cleanup(cls):
+        for tenant in cls.tenants:
+            test_utils.call_and_ignore_notfound_exc(
+                cls.tenants_client.delete_tenant, tenant['id'])
+
+        super(BaseIdentityV2AdminRbacTest, cls).resource_cleanup()
+
+    @classmethod
+    def setup_test_tenant(cls):
+        """Set up a test tenant."""
+        name = data_utils.rand_name('test_tenant')
+        tenant = cls.tenants_client.create_tenant(
+            name=name,
+            description=data_utils.rand_name('desc'))['tenant']
+        cls.tenants.append(tenant)
+        return tenant
+
+
+class BaseIdentityV3RbacTest(BaseIdentityRbacTest):
+
+    identity_version = 'v3'
+
+    @classmethod
+    def setup_clients(cls):
+        super(BaseIdentityV3RbacTest, cls).setup_clients()
+        cls.creds_client = cls.os_primary.credentials_client
+        cls.consumers_client = cls.os_primary.oauth_consumers_client
+        cls.domains_client = cls.os_primary.domains_client
+        cls.endpoints_client = cls.os_primary.endpoints_v3_client
+        cls.groups_client = cls.os_primary.groups_client
+        cls.projects_client = cls.os_primary.projects_client
+        cls.policies_client = cls.os_primary.policies_client
+        cls.regions_client = cls.os_primary.regions_client
+        cls.role_assignments_client = cls.os_primary.role_assignments_client
+        cls.roles_client = cls.os_primary.roles_v3_client
+        cls.services_client = cls.os_primary.identity_services_v3_client
+        cls.trusts_client = cls.os_primary.trusts_client
+        cls.users_client = cls.os_primary.users_v3_client
 
     @classmethod
     def resource_setup(cls):
         super(BaseIdentityV3RbacTest, cls).resource_setup()
         cls.credentials = []
         cls.domains = []
-        cls.endpoints = []
         cls.groups = []
         cls.policies = []
         cls.projects = []
         cls.regions = []
-        cls.roles = []
-        cls.services = []
         cls.trusts = []
-        cls.users = []
 
     @classmethod
     def resource_cleanup(cls):
@@ -85,10 +249,6 @@
             test_utils.call_and_ignore_notfound_exc(
                 cls.domains_client.delete_domain, domain['id'])
 
-        for endpoint in cls.endpoints:
-            test_utils.call_and_ignore_notfound_exc(
-                cls.endpoints_client.delete_endpoint, endpoint['id'])
-
         for group in cls.groups:
             test_utils.call_and_ignore_notfound_exc(
                 cls.groups_client.delete_group, group['id'])
@@ -105,22 +265,10 @@
             test_utils.call_and_ignore_notfound_exc(
                 cls.regions_client.delete_region, region['id'])
 
-        for role in cls.roles:
-            test_utils.call_and_ignore_notfound_exc(
-                cls.roles_client.delete_role, role['id'])
-
-        for service in cls.services:
-            test_utils.call_and_ignore_notfound_exc(
-                cls.services_client.delete_service, service['id'])
-
         for trust in cls.trusts:
             test_utils.call_and_ignore_notfound_exc(
                 cls.trusts_client.delete_trust, trust['id'])
 
-        for user in cls.users:
-            test_utils.call_and_ignore_notfound_exc(
-                cls.users_client.delete_user, user['id'])
-
         super(BaseIdentityV3RbacTest, cls).resource_cleanup()
 
     @classmethod
@@ -150,23 +298,6 @@
         return domain
 
     @classmethod
-    def setup_test_endpoint(cls, service=None):
-        """Creates a service and an endpoint for test."""
-        interface = 'public'
-        url = data_utils.rand_url()
-        # Endpoint creation requires a service
-        if service is None:
-            service = cls.setup_test_service()
-
-        endpoint = cls.endpoints_client.create_endpoint(
-            service_id=service['id'],
-            interface=interface,
-            url=url)['endpoint']
-        cls.endpoints.append(endpoint)
-
-        return endpoint
-
-    @classmethod
     def setup_test_group(cls):
         """Creates a group for test."""
         name = data_utils.rand_name('test_group')
@@ -211,30 +342,6 @@
         return region
 
     @classmethod
-    def setup_test_role(cls):
-        """Set up a test role."""
-        name = data_utils.rand_name('test_role')
-        role = cls.roles_client.create_role(name=name)['role']
-        cls.roles.append(role)
-
-        return role
-
-    @classmethod
-    def setup_test_service(cls):
-        """Setup a test service."""
-        name = data_utils.rand_name('service')
-        serv_type = data_utils.rand_name('type')
-        desc = data_utils.rand_name('description')
-
-        service = cls.services_client.create_service(
-            name=name,
-            type=serv_type,
-            description=desc)['service']
-        cls.services.append(service)
-
-        return service
-
-    @classmethod
     def setup_test_trust(cls, trustee_user_id, trustor_user_id, **kwargs):
         """Setup a test trust."""
         trust = cls.trusts_client.create_trust(
@@ -243,18 +350,3 @@
         cls.trusts.append(trust)
 
         return trust
-
-    @classmethod
-    def setup_test_user(cls, password=None, **kwargs):
-        """Set up a test user."""
-        username = data_utils.rand_name('test_user')
-        email = username + '@testmail.tm'
-
-        user = cls.users_client.create_user(
-            name=username,
-            email=email,
-            password=password,
-            **kwargs)['user']
-        cls.users.append(user)
-
-        return user
diff --git a/patrole_tempest_plugin/tests/api/identity/v2/rbac_base.py b/patrole_tempest_plugin/tests/api/identity/v2/rbac_base.py
deleted file mode 100644
index 5e5d918..0000000
--- a/patrole_tempest_plugin/tests/api/identity/v2/rbac_base.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# 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.api.identity import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
-
-from patrole_tempest_plugin.rbac_utils import rbac_utils
-
-CONF = config.CONF
-
-
-class BaseIdentityV2RbacTest(base.BaseIdentityV2Test):
-
-    credentials = ['admin', 'primary']
-
-    @classmethod
-    def skip_checks(cls):
-        super(BaseIdentityV2RbacTest, cls).skip_checks()
-        if not CONF.rbac.enable_rbac:
-            raise cls.skipException(
-                "%s skipped as RBAC testing not enabled" % cls.__name__)
-
-    @classmethod
-    def setup_clients(cls):
-        super(BaseIdentityV2RbacTest, cls).setup_clients()
-        cls.auth_provider = cls.os.auth_provider
-
-        cls.rbac_utils = rbac_utils()
-        cls.rbac_utils.switch_role(cls, toggle_rbac_role=False)
-
-        cls.client = cls.os.identity_client
-        cls.endpoints_client = cls.os.endpoints_client
-        cls.roles_client = cls.os.roles_client
-        cls.services_client = cls.os.identity_services_client
-        cls.tenants_client = cls.os.tenants_client
-        cls.token_client = cls.os.token_client
-        cls.users_client = cls.os.users_client
-
-    def _create_service(self):
-        name = data_utils.rand_name('service')
-        type = data_utils.rand_name('type')
-
-        self.service = self.services_client.create_service(
-            name=name, type=type,
-            description="description")
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.services_client.delete_service,
-                        self.service['OS-KSADM:service']['id'])
-        return self.service
-
-    def _create_user(self, name=None, email=None, password=None, **kwargs):
-        """Set up a test user."""
-        if name is None:
-            name = data_utils.rand_name('test_user')
-        if email is None:
-            email = name + '@testmail.tm'
-        if password is None:
-            password = data_utils.rand_password()
-        user = self.users_client.create_user(
-            name=name, email=email, password=password, **kwargs)['user']
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.users_client.delete_user,
-                        user['id'])
-        return user
-
-    def _create_tenant(self):
-        """Set up a test tenant."""
-        name = data_utils.rand_name('test_tenant')
-        tenant = self.tenants_client.create_tenant(
-            name=name,
-            description=data_utils.rand_name('desc'))['tenant']
-        # Delete the tenant at the end of the test
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.tenants_client.delete_tenant,
-                        tenant['id'])
-        return tenant
diff --git a/patrole_tempest_plugin/tests/api/identity/v2/test_endpoints_rbac.py b/patrole_tempest_plugin/tests/api/identity/v2/test_endpoints_rbac.py
index b8677cf..0a9feef 100644
--- a/patrole_tempest_plugin/tests/api/identity/v2/test_endpoints_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v2/test_endpoints_rbac.py
@@ -13,48 +13,15 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-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 patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v2 import rbac_base
-
-CONF = config.CONF
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
-class IdentityEndpointsV2RbacTest(rbac_base.BaseIdentityV2RbacTest):
-
-    @classmethod
-    def setup_clients(cls):
-        super(IdentityEndpointsV2RbacTest, cls).setup_clients()
-        cls.endpoints_client = cls.os.endpoints_client
-
-    @classmethod
-    def resource_setup(cls):
-        super(IdentityEndpointsV2RbacTest, cls).resource_setup()
-        cls.region = data_utils.rand_name('region')
-        cls.public_url = data_utils.rand_url()
-        cls.admin_url = data_utils.rand_url()
-        cls.internal_url = data_utils.rand_url()
-
-    def _create_endpoint(self):
-        self._create_service()
-        endpoint = self.endpoints_client.create_endpoint(
-            service_id=self.service['OS-KSADM:service']['id'],
-            region=self.region,
-            publicurl=self.public_url,
-            adminurl=self.admin_url,
-            internalurl=self.internal_url
-        )
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.endpoints_client.delete_endpoint,
-                        endpoint['endpoint']['id'])
-        return endpoint
+class IdentityEndpointsV2AdminRbacTest(rbac_base.BaseIdentityV2AdminRbacTest):
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_endpoint",
                                  admin_only=True)
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd124')
     def test_create_endpoint(self):
@@ -65,10 +32,9 @@
         """
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._create_endpoint()
+        self.setup_test_endpoint()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_endpoint",
                                  admin_only=True)
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd125')
     def test_delete_endpoint(self):
@@ -78,12 +44,11 @@
         RBAC test for Identity v2 delete_endpoint
         """
 
-        endpoint = self._create_endpoint()
+        endpoint = self.setup_test_endpoint()
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.endpoints_client.delete_endpoint(endpoint['endpoint']['id'])
+        self.endpoints_client.delete_endpoint(endpoint['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_endpoints",
                                  admin_only=True)
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd126')
     def test_list_endpoints(self):
diff --git a/patrole_tempest_plugin/tests/api/identity/v2/test_projects_rbac.py b/patrole_tempest_plugin/tests/api/identity/v2/test_projects_rbac.py
index 6853b64..9a4363d 100644
--- a/patrole_tempest_plugin/tests/api/identity/v2/test_projects_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v2/test_projects_rbac.py
@@ -16,16 +16,16 @@
 from tempest import config
 from tempest.lib import decorators
 
+from patrole_tempest_plugin import rbac_exceptions
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v2 import rbac_base
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 CONF = config.CONF
 
 
-class IdentityProjectV2RbacTest(rbac_base.BaseIdentityV2RbacTest):
+class IdentityProjectV2AdminRbacTest(rbac_base.BaseIdentityV2AdminRbacTest):
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_project",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-b348-080044d0d904')
     def test_create_project(self):
@@ -36,10 +36,9 @@
         """
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._create_tenant()
+        self.setup_test_tenant()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_project",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-b348-080044d0d905')
     def test_update_project(self):
@@ -48,14 +47,13 @@
 
         RBAC test for Identity 2.0 update_tenant
         """
-        tenant = self._create_tenant()
+        tenant = self.setup_test_tenant()
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.tenants_client.update_tenant(tenant['id'],
                                           description="Changed description")
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_project",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-b348-080044d0d906')
     def test_delete_project(self):
@@ -64,13 +62,12 @@
 
         RBAC test for Identity 2.0 delete_tenant
         """
-        tenant = self._create_tenant()
+        tenant = self.setup_test_tenant()
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.tenants_client.delete_tenant(tenant['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_project",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-b348-080044d0d907')
     def test_get_project(self):
@@ -80,26 +77,12 @@
         RBAC test for Identity 2.0 show_tenant
         """
 
-        tenant = self._create_tenant()
+        tenant = self.setup_test_tenant()
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.tenants_client.show_tenant(tenant['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_projects",
-                                 admin_only=True)
-    @decorators.idempotent_id('0f148510-63bf-11e6-b348-080044d0d908')
-    def test_get_all_projects(self):
-
-        """List All Projects Test
-
-        RBAC test for Identity 2.0 list_tenants
-        """
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.tenants_client.list_tenants()
-
-    @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_user_projects",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-b348-080044d0d909')
     def test_list_project_users(self):
@@ -108,7 +91,41 @@
 
         RBAC test for Identity 2.0 list_tenant_users
         """
-        tenant = self._create_tenant()
+        tenant = self.setup_test_tenant()
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.tenants_client.list_tenant_users(tenant['id'])
+
+    @rbac_rule_validation.action(service="keystone",
+                                 admin_only=True)
+    @decorators.idempotent_id('0f148510-63bf-11e6-b348-080044d0d908')
+    def test_list_all_projects(self):
+
+        """List All Projects Test
+
+        RBAC test for Identity 2.0 list_tenants (admin endpoint)
+
+        There are two separate APIs for listing tenants in the Keystone
+        v2 API: one for admin and one for non-admin. The ``os_admin`` client
+        calls the admin endpoint and the ``os_primary`` client calls the
+        non-admin endpoint. To ensure that the admin endpoint only returns
+        admin-scoped tenants, raise ``RbacActionFailed`` exception otherwise.
+        """
+        tenants_client = self.os_admin.tenants_client if \
+            CONF.identity.admin_role == CONF.rbac.rbac_test_role else \
+            self.os_primary.tenants_client
+        admin_tenant_id = self.os_admin.auth_provider.credentials.project_id
+        non_admin_tenant_id = self.auth_provider.credentials.project_id
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        tenants = tenants_client.list_tenants()['tenants']
+
+        tenant_ids = [t['id'] for t in tenants]
+        if admin_tenant_id not in tenant_ids:
+            raise rbac_exceptions.RbacActionFailed(
+                "The admin tenant id was not returned by the call to "
+                "``list_tenants``.")
+        if non_admin_tenant_id in tenant_ids:
+            raise rbac_exceptions.RbacActionFailed(
+                "The non-admin tenant id was returned by the call to "
+                "``list_tenants``.")
diff --git a/patrole_tempest_plugin/tests/api/identity/v2/test_roles_rbac.py b/patrole_tempest_plugin/tests/api/identity/v2/test_roles_rbac.py
index 9dd90e1..9d80469 100644
--- a/patrole_tempest_plugin/tests/api/identity/v2/test_roles_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v2/test_roles_rbac.py
@@ -13,35 +13,24 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-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 patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v2 import rbac_base
-
-CONF = config.CONF
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
-class IdentityRoleV2RbacTest(rbac_base.BaseIdentityV2RbacTest):
+class IdentityRolesV2AdminRbacTest(rbac_base.BaseIdentityV2AdminRbacTest):
 
     @classmethod
     def setup_clients(cls):
-        super(IdentityRoleV2RbacTest, cls).setup_clients()
-        cls.roles_client = cls.os.roles_client
-
-    def _create_role(self):
-        role = self.roles_client.create_role(
-            name=data_utils.rand_name('test_role'))['role']
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.roles_client.delete_role, role['id'])
-        return role
+        super(IdentityRolesV2AdminRbacTest, cls).setup_clients()
+        cls.roles_client = cls.os_primary.roles_client
 
     def _create_tenant_user_and_role(self):
-        tenant = self._create_tenant()
-        user = self._create_user(tenantid=tenant['id'])
-        role = self._create_role()
+        tenant = self.setup_test_tenant()
+        user = self.setup_test_user(tenantid=tenant['id'])
+        role = self.setup_test_role()
         return tenant, user, role
 
     def _create_role_on_project(self, tenant, user, role):
@@ -53,7 +42,6 @@
             tenant['id'], user['id'], role['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_role",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-8674-080044d0d904')
     def test_create_role(self):
@@ -64,10 +52,9 @@
         """
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._create_role()
+        self.setup_test_role()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_role",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-8674-080044d0d905')
     def test_delete_role(self):
@@ -76,13 +63,12 @@
 
         RBAC test for Identity v2 delete_role
         """
-        role = self._create_role()
+        role = self.setup_test_role()
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.roles_client.delete_role(role['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_role",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-8674-080044d0d906')
     def test_show_role(self):
@@ -91,13 +77,12 @@
 
         RBAC test for Identity v2 show_role
         """
-        role = self._create_role()
+        role = self.setup_test_role()
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.roles_client.show_role(role['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_roles",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-8674-080044d0d907')
     def test_list_roles(self):
@@ -110,7 +95,6 @@
         self.roles_client.list_roles()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:add_role_to_user",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-8674-080044d0d908')
     def test_create_role_on_project(self):
@@ -124,7 +108,6 @@
         self._create_role_on_project(tenant, user, role)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:remove_role_from_user",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-8674-080044d0d909')
     def test_delete_role_from_user_on_project(self):
@@ -141,7 +124,6 @@
             tenant['id'], user['id'], role['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_user_roles",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-8674-080044d0d90a')
     def test_list_user_roles_on_project(self):
@@ -150,8 +132,8 @@
 
         RBAC test for Identity v2 list_user_roles_on_project
         """
-        tenant = self._create_tenant()
-        user = self._create_user(tenantid=tenant['id'])
+        tenant = self.setup_test_tenant()
+        user = self.setup_test_user(tenantid=tenant['id'])
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.roles_client.list_user_roles_on_project(
diff --git a/patrole_tempest_plugin/tests/api/identity/v2/test_services_rbac.py b/patrole_tempest_plugin/tests/api/identity/v2/test_services_rbac.py
index a371bbc..8419ec9 100644
--- a/patrole_tempest_plugin/tests/api/identity/v2/test_services_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v2/test_services_rbac.py
@@ -13,24 +13,15 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import config
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v2 import rbac_base
-
-CONF = config.CONF
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
-class IdentityServicesV2RbacTest(rbac_base.BaseIdentityV2RbacTest):
-
-    @classmethod
-    def setup_clients(cls):
-        super(IdentityServicesV2RbacTest, cls).setup_clients()
-        cls.services_client = cls.os.identity_services_client
+class IdentityServicesV2AdminRbacTest(rbac_base.BaseIdentityV2AdminRbacTest):
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_service",
                                  admin_only=True)
     @decorators.idempotent_id('370050f6-d271-4fb4-abc5-4de1d6dfbad2')
     def test_create_service(self):
@@ -39,10 +30,9 @@
         RBAC test for Identity v2 create_service
         """
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._create_service()
+        self.setup_test_service()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_service",
                                  admin_only=True)
     @decorators.idempotent_id('f6c64fc3-6a1f-423e-af91-e411add3a384')
     def test_delete_service(self):
@@ -50,13 +40,12 @@
 
         RBAC test for Identity v2 delete_service
         """
-        service_id = self._create_service()['OS-KSADM:service']['id']
+        service_id = self.setup_test_service()['id']
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.services_client.delete_service(service_id)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_service",
                                  admin_only=True)
     @decorators.idempotent_id('504d62bb-97d7-445e-9d6d-b1945a7c9e08')
     def test_show_service(self):
@@ -64,13 +53,12 @@
 
         RBAC test for Identity v2 show_service
         """
-        service_id = self._create_service()['OS-KSADM:service']['id']
+        service_id = self.setup_test_service()['id']
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.services_client.show_service(service_id)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_services",
                                  admin_only=True)
     @decorators.idempotent_id('d7dc461d-51ad-48e0-9cd2-33add1b88de9')
     def test_list_services(self):
diff --git a/patrole_tempest_plugin/tests/api/identity/v2/test_users_rbac.py b/patrole_tempest_plugin/tests/api/identity/v2/test_users_rbac.py
index 48f3d11..dfe6e71 100644
--- a/patrole_tempest_plugin/tests/api/identity/v2/test_users_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v2/test_users_rbac.py
@@ -17,51 +17,46 @@
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v2 import rbac_base
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
-class IdentityUserV2RbacTest(rbac_base.BaseIdentityV2RbacTest):
+class IdentityUsersV2AdminRbacTest(rbac_base.BaseIdentityV2AdminRbacTest):
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_user",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-1342-080044d0d904')
     def test_create_user(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._create_user()
+        self.setup_test_user()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_user",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-1342-080044d0d905')
     def test_update_user(self):
-        user = self._create_user()
+        user = self.setup_test_user()
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.users_client.update_user(user['id'], email="changedUser@xyz.com")
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:set_user_enabled",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-1342-080044d0d9a1')
     def test_update_user_enabled(self):
-        user = self._create_user()
+        user = self.setup_test_user()
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.users_client.update_user_enabled(user['id'], enabled=True)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_user",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-1342-080044d0d906')
     def test_delete_user(self):
-        user = self._create_user()
+        user = self.setup_test_user()
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.users_client.delete_user(user['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_users",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-1342-080044d0d907')
     def test_list_users(self):
@@ -69,21 +64,19 @@
         self.users_client.list_users()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_user",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-1342-080044d0d908')
     def test_show_user(self):
-        user = self._create_user()
+        user = self.setup_test_user()
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.users_client.show_user(user['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:change_password",
                                  admin_only=True)
     @decorators.idempotent_id('0f148510-63bf-11e6-1342-080044d0d909')
     def test_update_user_password(self):
-        user = self._create_user()
+        user = self.setup_test_user()
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.users_client.update_user_password(
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_credentials_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_credentials_rbac.py
index 42f2c01..995c3b0 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_credentials_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_credentials_rbac.py
@@ -17,7 +17,7 @@
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v3 import rbac_base
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
 class IdentityCredentialsV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_domains_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_domains_rbac.py
index ba5d5e0..b45c091 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_domains_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_domains_rbac.py
@@ -17,7 +17,7 @@
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v3 import rbac_base
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
 class IdentityDomainsV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_endpoint_filter_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_endpoint_filter_rbac.py
new file mode 100644
index 0000000..7e844e7
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_endpoint_filter_rbac.py
@@ -0,0 +1,91 @@
+# 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.common.utils import test_utils
+from tempest.lib import decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.identity import rbac_base
+
+
+class IdentityEndpointsFilterV3RbacTest(
+        rbac_base.BaseIdentityV3RbacTest):
+
+    @classmethod
+    def setup_clients(cls):
+        super(IdentityEndpointsFilterV3RbacTest, cls).setup_clients()
+        cls.ep_api_client = cls.os_primary.endpoint_filter_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(IdentityEndpointsFilterV3RbacTest, cls).resource_setup()
+        cls.project = cls.setup_test_project()
+        cls.service = cls.setup_test_service()
+        cls.endpoint = cls.setup_test_endpoint(service=cls.service)
+
+    def _add_endpoint_to_project(self):
+        # Adding and cleaning up endpoints to projects
+        self.ep_api_client.add_endpoint_to_project(
+            self.project['id'], self.endpoint['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ep_api_client.delete_endpoint_from_project,
+                        self.project['id'], self.endpoint['id'])
+
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:add_endpoint_to_project")
+    @decorators.idempotent_id('9199ec13-816d-4efe-b8b1-e1cd026b9747')
+    def test_add_endpoint_to_project(self):
+        # Adding endpoints to projects
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self._add_endpoint_to_project()
+
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:list_projects_for_endpoint")
+    @decorators.idempotent_id('f53dca42-ec8a-48e9-924b-0bbe6c99727f')
+    def test_list_projects_for_endpoint(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.ep_api_client.list_projects_for_endpoint(
+            self.endpoint['id'])
+
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:check_endpoint_in_project")
+    @decorators.idempotent_id('0c1425eb-833c-4aa1-a21d-52ffa41fdc6a')
+    def test_check_endpoint_in_project(self):
+        self._add_endpoint_to_project()
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.ep_api_client.check_endpoint_in_project(
+            self.project['id'], self.endpoint['id'])
+
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:list_endpoints_for_project")
+    @decorators.idempotent_id('5d86c659-c6ad-41e0-854e-3823e95c7cc2')
+    def test_list_endpoints_in_project(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.ep_api_client.list_endpoints_in_project(
+            self.project['id'])
+
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:remove_endpoint_from_project")
+    @decorators.idempotent_id('b4e21c10-4f47-427b-9b8a-f5b5601adfda')
+    def test_remove_endpoint_from_project(self):
+        self._add_endpoint_to_project()
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.ep_api_client.delete_endpoint_from_project(
+            self.project['id'], self.endpoint['id'])
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_endpoints_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_endpoints_rbac.py
index eabebb6..2659bae 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_endpoints_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_endpoints_rbac.py
@@ -17,7 +17,7 @@
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v3 import rbac_base
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
 class IdentityEndpointsV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_groups_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_groups_rbac.py
index 3cc71a6..74402d5 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_groups_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_groups_rbac.py
@@ -17,7 +17,7 @@
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v3 import rbac_base
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
 class IdentityGroupsV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_oauth_consumers_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_oauth_consumers_rbac.py
index f331cff..fab5a6e 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_oauth_consumers_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_oauth_consumers_rbac.py
@@ -18,7 +18,7 @@
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v3 import rbac_base
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
 class IdentityConsumersV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_policies_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_policies_rbac.py
index 8f11e30..da587d9 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_policies_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_policies_rbac.py
@@ -17,7 +17,7 @@
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v3 import rbac_base
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
 class IdentityPoliciesV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_projects_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_projects_rbac.py
index 325b987..0c65602 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_projects_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_projects_rbac.py
@@ -17,7 +17,7 @@
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v3 import rbac_base
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
 class IdentityProjectV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_regions_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_regions_rbac.py
index b35facd..541e8af 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_regions_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_regions_rbac.py
@@ -17,7 +17,7 @@
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v3 import rbac_base
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
 class IdentityRegionsV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_role_assignments_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_role_assignments_rbac.py
index 2dd0ff5..6fe6c29 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_role_assignments_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_role_assignments_rbac.py
@@ -16,7 +16,7 @@
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v3 import rbac_base
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
 class IdentityRoleAssignmentsV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_roles_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_roles_rbac.py
index 2676bf9..0458e16 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_roles_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_roles_rbac.py
@@ -18,7 +18,7 @@
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v3 import rbac_base
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
 class IdentityRolesV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_services_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_services_rbac.py
index c02b471..2f0d1ac 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_services_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_services_rbac.py
@@ -17,7 +17,7 @@
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v3 import rbac_base
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
 class IdentitySericesV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_trusts_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_trusts_rbac.py
index 622b330..763c407 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_trusts_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_trusts_rbac.py
@@ -19,7 +19,7 @@
 from tempest import test
 
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v3 import rbac_base
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 CONF = config.CONF
 
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 956727b..d3b3e4c 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
@@ -17,14 +17,14 @@
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.identity.v3 import rbac_base
+from patrole_tempest_plugin.tests.api.identity import rbac_base
 
 
-class IdentityUserV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
+class IdentityUserV3AdminRbacTest(rbac_base.BaseIdentityV3RbacTest):
 
     @classmethod
     def resource_setup(cls):
-        super(IdentityUserV3RbacTest, cls).resource_setup()
+        super(IdentityUserV3AdminRbacTest, cls).resource_setup()
         cls.default_user_id = cls.auth_provider.credentials.user_id
 
     @rbac_rule_validation.action(service="keystone",
diff --git a/patrole_tempest_plugin/tests/api/image/rbac_base.py b/patrole_tempest_plugin/tests/api/image/rbac_base.py
index 7266079..2a45ccb 100644
--- a/patrole_tempest_plugin/tests/api/image/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/image/rbac_base.py
@@ -33,7 +33,7 @@
     @classmethod
     def setup_clients(cls):
         super(BaseV1ImageRbacTest, cls).setup_clients()
-        cls.auth_provider = cls.os.auth_provider
+        cls.auth_provider = cls.os_primary.auth_provider
         cls.rbac_utils = rbac_utils()
         cls.rbac_utils.switch_role(cls, toggle_rbac_role=False)
 
@@ -52,6 +52,6 @@
     @classmethod
     def setup_clients(cls):
         super(BaseV2ImageRbacTest, cls).setup_clients()
-        cls.auth_provider = cls.os.auth_provider
+        cls.auth_provider = cls.os_primary.auth_provider
         cls.rbac_utils = rbac_utils()
         cls.rbac_utils.switch_role(cls, toggle_rbac_role=False)
diff --git a/patrole_tempest_plugin/tests/api/image/v1/test_images_member_rbac.py b/patrole_tempest_plugin/tests/api/image/v1/test_images_member_rbac.py
index 643ca73..8015277 100644
--- a/patrole_tempest_plugin/tests/api/image/v1/test_images_member_rbac.py
+++ b/patrole_tempest_plugin/tests/api/image/v1/test_images_member_rbac.py
@@ -29,7 +29,7 @@
     @classmethod
     def setup_clients(cls):
         super(ImagesMemberRbacTest, cls).setup_clients()
-        cls.image_member_client = cls.os.image_member_client
+        cls.image_member_client = cls.os_primary.image_member_client
 
     @classmethod
     def resource_setup(cls):
diff --git a/patrole_tempest_plugin/tests/api/image/v2/test_image_resource_types_rbac.py b/patrole_tempest_plugin/tests/api/image/v2/test_image_resource_types_rbac.py
index 94c704f..552a137 100644
--- a/patrole_tempest_plugin/tests/api/image/v2/test_image_resource_types_rbac.py
+++ b/patrole_tempest_plugin/tests/api/image/v2/test_image_resource_types_rbac.py
@@ -23,6 +23,21 @@
 
 class ImageResourceTypesRbacTest(rbac_base.BaseV2ImageRbacTest):
 
+    @classmethod
+    def resource_setup(cls):
+        super(ImageResourceTypesRbacTest, cls).resource_setup()
+        cls.namespace_name = data_utils.rand_name('test-ns')
+        cls.namespaces_client.create_namespace(
+            namespace=cls.namespace_name,
+            protected=False)
+
+    @classmethod
+    def resource_cleanup(cls):
+        test_utils.call_and_ignore_notfound_exc(
+            cls.namespaces_client.delete_namespace,
+            cls.namespace_name)
+        super(ImageResourceTypesRbacTest, cls).resource_setup()
+
     @rbac_rule_validation.action(service="glance",
                                  rule="list_metadef_resource_types")
     @decorators.idempotent_id('0416fc4d-cfdc-447b-88b6-d9f1dd0382f7')
@@ -42,15 +57,15 @@
 
         RBAC test for the glance get_metadef_resource_type policy.
         """
-        namespace_name = data_utils.rand_name('test-ns')
-        self.namespaces_client.create_namespace(
-            namespace=namespace_name,
-            protected=False)
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.namespaces_client.delete_namespace,
-            namespace_name)
-
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.resource_types_client.list_resource_type_association(
-            namespace_name)
+            self.namespace_name)
+
+    @rbac_rule_validation.action(service="glance",
+                                 rule="add_metadef_resource_type_association")
+    @decorators.idempotent_id('ef9fbc60-3e28-4164-a25c-d30d892f7939')
+    def test_add_metadef_resource_type(self):
+        type_name = data_utils.rand_name()
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.resource_types_client.create_resource_type_association(
+            self.namespace_name, name=type_name)
diff --git a/patrole_tempest_plugin/tests/api/image/v2/test_images_member_rbac.py b/patrole_tempest_plugin/tests/api/image/v2/test_images_member_rbac.py
index 07d4015..a7c59f5 100644
--- a/patrole_tempest_plugin/tests/api/image/v2/test_images_member_rbac.py
+++ b/patrole_tempest_plugin/tests/api/image/v2/test_images_member_rbac.py
@@ -32,8 +32,8 @@
     @classmethod
     def setup_clients(cls):
         super(ImagesMemberRbacTest, cls).setup_clients()
-        cls.image_client = cls.os.image_client_v2
-        cls.image_member_client = cls.os.image_member_client_v2
+        cls.image_client = cls.os_primary.image_client_v2
+        cls.image_member_client = cls.os_primary.image_member_client_v2
 
     @rbac_rule_validation.action(service="glance",
                                  rule="add_member")
diff --git a/patrole_tempest_plugin/tests/api/image/v2/test_images_rbac.py b/patrole_tempest_plugin/tests/api/image/v2/test_images_rbac.py
index fa492bb..880f892 100644
--- a/patrole_tempest_plugin/tests/api/image/v2/test_images_rbac.py
+++ b/patrole_tempest_plugin/tests/api/image/v2/test_images_rbac.py
@@ -27,7 +27,19 @@
     @classmethod
     def setup_clients(cls):
         super(BasicOperationsImagesRbacTest, cls).setup_clients()
-        cls.client = cls.os.image_client_v2
+        cls.client = cls.os_primary.image_client_v2
+
+    def _create_image(self, **kwargs):
+        image_name = data_utils.rand_name('image')
+        image = self.create_image(name=image_name,
+                                  container_format='bare',
+                                  disk_format='raw',
+                                  **kwargs)
+        return image
+
+    def _upload_image(self, image_id):
+        image_file = moves.cStringIO(data_utils.random_bytes())
+        return self.client.store_image_file(image_id, image_file)
 
     @rbac_rule_validation.action(service="glance",
                                  rule="add_image")
@@ -38,14 +50,8 @@
 
         RBAC test for the glance create_image endpoint
         """
-        uuid = '00000000-1111-2222-3333-444455556666'
-        image_name = data_utils.rand_name('image')
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.create_image(name=image_name,
-                          container_format='bare',
-                          disk_format='raw',
-                          visibility='private',
-                          ramdisk_id=uuid)
+        self._create_image()
 
     @rbac_rule_validation.action(service="glance",
                                  rule="upload_image")
@@ -56,18 +62,25 @@
 
         RBAC test for the glance upload_image endpoint
         """
-        uuid = '00000000-1111-2222-3333-444455556666'
-        image_name = data_utils.rand_name('image')
-        body = self.create_image(name=image_name,
-                                 container_format='bare',
-                                 disk_format='raw',
-                                 visibility='private',
-                                 ramdisk_id=uuid)
+        image = self._create_image()
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        # Try uploading an image file
-        image_file = moves.cStringIO(data_utils.random_bytes())
-        self.client.store_image_file(body['id'], image_file)
+        self._upload_image(image['id'])
+
+    @decorators.idempotent_id('f0c268f3-cb51-49aa-9bd5-d30cf647322f')
+    @rbac_rule_validation.action(service="glance",
+                                 rule="download_image")
+    def test_download_image(self):
+
+        """Download Image Test
+
+        RBAC test for the glance download_image endpoint
+        """
+        image = self._create_image()
+        self._upload_image(image['id'])
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.show_image_file(image['id'])
 
     @rbac_rule_validation.action(service="glance",
                                  rule="delete_image")
@@ -78,16 +91,11 @@
 
         RBAC test for the glance delete_image endpoint
         """
-        image_name = data_utils.rand_name('image')
-        body = self.create_image(name=image_name,
-                                 container_format='bare',
-                                 disk_format='raw',
-                                 visibility='public')
-        image_id = body.get('id')
-        # Toggle role and delete created image
+        image = self._create_image()
+
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.delete_image(image_id)
-        self.client.wait_for_resource_deletion(image_id)
+        self.client.delete_image(image['id'])
+        self.client.wait_for_resource_deletion(image['id'])
 
     @rbac_rule_validation.action(service="glance",
                                  rule="get_image")
@@ -98,16 +106,10 @@
 
         RBAC test for the glance create_image endpoint
         """
+        image = self._create_image()
 
-        image_name = data_utils.rand_name('image')
-        body = self.create_image(name=image_name,
-                                 container_format='bare',
-                                 disk_format='raw',
-                                 visibility='private')
-        image_id = body.get('id')
-        # Toggle role and get created image
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.show_image(image_id)
+        self.client.show_image(image['id'])
 
     @rbac_rule_validation.action(service="glance",
                                  rule="get_images")
@@ -118,10 +120,8 @@
 
         RBAC test for the glance list_images endpoint
         """
-
-        # Toggle role and get created image
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.list_images()
+        self.client.list_images()['images']
 
     @rbac_rule_validation.action(service="glance",
                                  rule="modify_image")
@@ -132,22 +132,42 @@
 
         RBAC test for the glance update_image endpoint
         """
-        image_name = data_utils.rand_name('image')
-        body = self.create_image(name=image_name,
-                                 container_format='bare',
-                                 disk_format='raw',
-                                 visibility='private')
-        image_id = body.get('id')
+        image = self._create_image()
 
-        # Now try uploading an image file
-        image_file = moves.cStringIO(data_utils.random_bytes())
-        self.client.store_image_file(image_id, image_file)
-
-        # Toggle role and update created image
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        new_image_name = data_utils.rand_name('new-image')
-        body = self.client.update_image(image_id, [
-            dict(replace='/name', value=new_image_name)])
+        updated_image_name = data_utils.rand_name('image')
+        self.client.update_image(image['id'], [
+            dict(replace='/name', value=updated_image_name)])
+
+    @decorators.idempotent_id('244050d9-1b9a-446a-b3c5-f26f3ba8eb75')
+    @rbac_rule_validation.action(service="glance",
+                                 rule="modify_image")
+    def test_create_image_tag(self):
+
+        """Create image tag
+
+        RBAC test for the glance add_image_tag endpoint
+        """
+        image = self._create_image()
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.add_image_tag(image['id'], data_utils.rand_name('tag'))
+
+    @decorators.idempotent_id('c4a0bf9c-b78b-48c6-a31f-72c95f943c6e')
+    @rbac_rule_validation.action(service="glance",
+                                 rule="modify_image")
+    def test_delete_image_tag(self):
+
+        """Delete image tag
+
+        RBAC test for the glance delete_image_tag endpoint
+        """
+        image = self._create_image()
+        tag_name = data_utils.rand_name('tag')
+        self.client.add_image_tag(image['id'], tag_name)
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.delete_image_tag(image['id'], tag_name)
 
     @rbac_rule_validation.action(service="glance",
                                  rule="publicize_image")
@@ -158,12 +178,8 @@
 
         RBAC test for the glance publicize_image endpoint
         """
-        image_name = data_utils.rand_name('image')
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.create_image(name=image_name,
-                          container_format='bare',
-                          disk_format='raw',
-                          visibility='public')
+        self._create_image(visibility='public')
 
     @rbac_rule_validation.action(service="glance",
                                  rule="deactivate")
@@ -174,20 +190,11 @@
 
         RBAC test for the glance deactivate_image endpoint
         """
-        uuid = '00000000-1111-2222-3333-444455556666'
-        image_name = data_utils.rand_name('image')
-        body = self.create_image(name=image_name,
-                                 container_format='bare',
-                                 disk_format='raw',
-                                 visibility='private',
-                                 ramdisk_id=uuid)
-        image_id = body.get('id')
-        # Now try uploading an image file
-        image_file = moves.cStringIO(data_utils.random_bytes())
-        self.client.store_image_file(image_id=image_id, data=image_file)
-        # Toggling role and deacivate image
+        image = self._create_image()
+        self._upload_image(image['id'])
+
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.deactivate_image(image_id)
+        self.client.deactivate_image(image['id'])
 
     @rbac_rule_validation.action(service="glance",
                                  rule="reactivate")
@@ -198,18 +205,8 @@
 
         RBAC test for the glance reactivate_image endpoint
         """
-        uuid = '00000000-1111-2222-3333-444455556666'
-        image_name = data_utils.rand_name('image')
-        body = self.create_image(name=image_name,
-                                 container_format='bare',
-                                 disk_format='raw',
-                                 visibility='private',
-                                 ramdisk_id=uuid)
+        image = self._create_image()
+        self._upload_image(image['id'])
 
-        # Now try uploading an image file
-        image_id = body.get('id')
-        image_file = moves.cStringIO(data_utils.random_bytes())
-        self.client.store_image_file(image_id=image_id, data=image_file)
-        # Toggling role and reactivate image
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.reactivate_image(image_id)
+        self.client.reactivate_image(image['id'])
diff --git a/patrole_tempest_plugin/tests/api/network/rbac_base.py b/patrole_tempest_plugin/tests/api/network/rbac_base.py
index 6e7898f..629f0ca 100644
--- a/patrole_tempest_plugin/tests/api/network/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/network/rbac_base.py
@@ -35,6 +35,6 @@
     @classmethod
     def setup_clients(cls):
         super(BaseNetworkRbacTest, cls).setup_clients()
-        cls.auth_provider = cls.os.auth_provider
+        cls.auth_provider = cls.os_primary.auth_provider
         cls.rbac_utils = rbac_utils()
         cls.rbac_utils.switch_role(cls, toggle_rbac_role=False)
diff --git a/patrole_tempest_plugin/tests/api/network/test_metering_label_rules_rbac.py b/patrole_tempest_plugin/tests/api/network/test_metering_label_rules_rbac.py
index 2f55e30..2efb3fe 100644
--- a/patrole_tempest_plugin/tests/api/network/test_metering_label_rules_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_metering_label_rules_rbac.py
@@ -39,8 +39,9 @@
     @classmethod
     def setup_clients(cls):
         super(MeteringLabelRulesRbacTest, cls).setup_clients()
-        cls.metering_labels_client = cls.os.metering_labels_client
-        cls.metering_label_rules_client = cls.os.metering_label_rules_client
+        cls.metering_labels_client = cls.os_primary.metering_labels_client
+        cls.metering_label_rules_client = \
+            cls.os_primary.metering_label_rules_client
 
     @classmethod
     def resource_setup(cls):
diff --git a/patrole_tempest_plugin/tests/api/network/test_metering_labels_rbac.py b/patrole_tempest_plugin/tests/api/network/test_metering_labels_rbac.py
index 091e31d..9aabfa0 100644
--- a/patrole_tempest_plugin/tests/api/network/test_metering_labels_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_metering_labels_rbac.py
@@ -37,7 +37,7 @@
     @classmethod
     def setup_clients(cls):
         super(MeteringLabelsRbacTest, cls).setup_clients()
-        cls.metering_labels_client = cls.os.metering_labels_client
+        cls.metering_labels_client = cls.os_primary.metering_labels_client
 
     def _create_metering_label(self):
         body = self.metering_labels_client.create_metering_label(
diff --git a/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py b/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py
index 9752db6..133ea74 100644
--- a/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py
@@ -30,12 +30,6 @@
 class RbacNetworksTest(base.BaseNetworkRbacTest):
 
     @classmethod
-    def setup_clients(cls):
-        super(RbacNetworksTest, cls).setup_clients()
-        cls.networks_client = cls.os.networks_client
-        cls.subnet_client = cls.os.subnets_client
-
-    @classmethod
     def resource_setup(cls):
         super(RbacNetworksTest, cls).resource_setup()
 
diff --git a/patrole_tempest_plugin/tests/api/orchestration/rbac_base.py b/patrole_tempest_plugin/tests/api/orchestration/rbac_base.py
deleted file mode 100644
index b426ecd..0000000
--- a/patrole_tempest_plugin/tests/api/orchestration/rbac_base.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2017 AT&T Corporation.
-#    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.api.orchestration import base as heat_base
-from tempest import config
-
-from patrole_tempest_plugin.rbac_utils import rbac_utils
-
-CONF = config.CONF
-
-
-class BaseOrchestrationRbacTest(heat_base.BaseOrchestrationTest):
-
-    credentials = ['admin', 'primary']
-
-    @classmethod
-    def skip_checks(cls):
-        super(BaseOrchestrationRbacTest, cls).skip_checks()
-        if not CONF.rbac.enable_rbac:
-            raise cls.skipException(
-                "%s skipped as RBAC Flag not enabled" % cls.__name__)
-
-    @classmethod
-    def setup_clients(cls):
-        super(BaseOrchestrationRbacTest, cls).setup_clients()
-        cls.auth_provider = cls.os.auth_provider
-        cls.rbac_utils = rbac_utils()
-        cls.rbac_utils.switch_role(cls, toggle_rbac_role=False)
diff --git a/patrole_tempest_plugin/tests/api/orchestration/test_resource_types_rbac.py b/patrole_tempest_plugin/tests/api/orchestration/test_resource_types_rbac.py
deleted file mode 100644
index a348512..0000000
--- a/patrole_tempest_plugin/tests/api/orchestration/test_resource_types_rbac.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# 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 patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.orchestration import rbac_base
-
-
-class ResourceTypesRbacTest(rbac_base.BaseOrchestrationRbacTest):
-
-    @classmethod
-    def setup_clients(cls):
-        super(ResourceTypesRbacTest, cls).setup_clients()
-        cls.client = cls.orchestration_client
-
-    @classmethod
-    def resource_setup(cls):
-        super(ResourceTypesRbacTest, cls).resource_setup()
-
-        cls.resource_types = cls.client.list_resource_types()['resource_types']
-
-        # There should always be several resource types on a system. But just
-        # in case there are none, skip these tests, as that implies the system
-        # is misconfigured.
-        if cls.resource_types:
-            cls.resource_type_name = cls.resource_types[0]
-        else:
-            raise cls.skipException('No resource types found.')
-
-    @decorators.idempotent_id('56c06e92-df96-47b5-bcf2-0104e74e2511')
-    @rbac_rule_validation.action(service="heat",
-                                 rule="stacks:list_resource_types")
-    def test_list_resource_types(self):
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.list_resource_types()['resource_types']
-
-    @decorators.idempotent_id('8b0290f9-0d53-479e-8e4d-3d865b0107a4')
-    @rbac_rule_validation.action(service="heat",
-                                 rule="stacks:generate_template")
-    def test_show_resource_type_template(self):
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.show_resource_type_template(self.resource_type_name)
-
-    @decorators.idempotent_id('2cdcd47f-6abe-43af-b736-c188df27dd38')
-    @rbac_rule_validation.action(service="heat",
-                                 rule="stacks:resource_schema")
-    def test_show_resource_type_schema(self):
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.show_resource_type(self.resource_type_name)[
-            'resource_type']
diff --git a/patrole_tempest_plugin/tests/api/orchestration/test_soft_config_rbac.py b/patrole_tempest_plugin/tests/api/orchestration/test_soft_config_rbac.py
deleted file mode 100644
index d219ace..0000000
--- a/patrole_tempest_plugin/tests/api/orchestration/test_soft_config_rbac.py
+++ /dev/null
@@ -1,138 +0,0 @@
-# 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.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.orchestration import rbac_base
-
-
-class TestRbacSoftwareConfig(rbac_base.BaseOrchestrationRbacTest):
-
-    def setUp(self):
-        super(TestRbacSoftwareConfig, self).setUp()
-        self.config = self._config_create('a')
-        self._deployment_create(self.config['id'])
-
-    @rbac_rule_validation.action(service="heat",
-                                 rule="software_configs:show")
-    @decorators.idempotent_id('b2e7c98c-e17b-4f37-82f3-5d21eff86e79')
-    def test_get_software_config(self):
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.show_software_config(self.config['id'])
-
-    @rbac_rule_validation.action(service="heat",
-                                 rule="software_deployments:metadata")
-    @decorators.idempotent_id('defa34ab-9d1f-4b14-8613-34e964c0c478')
-    def test_get_deployment_metadata(self):
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.show_software_deployment_metadata(self.server_id)
-
-    @rbac_rule_validation.action(service="heat",
-                                 rule="software_deployments:index")
-    @decorators.idempotent_id('2a4dcb91-1803-4749-9cb7-5b69ba668b18')
-    def test_get_deployment_list(self):
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.list_software_deployments()
-
-    @rbac_rule_validation.action(service="heat",
-                                 rule="software_deployments:show")
-    @decorators.idempotent_id('d4e627bc-88a3-4189-8092-151f22ed989d')
-    def test_software_show_deployment(self):
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.show_software_deployment(self.deployment_id)
-
-    @rbac_rule_validation.action(service="heat",
-                                 rule="software_deployments:update")
-    @decorators.idempotent_id('90e8958c-6fa7-4515-b6d7-6d6952979f8c')
-    def test_software_deployment_update(self):
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        new_action = data_utils.rand_name('ACTION')
-        new_status = data_utils.rand_name('STATUS')
-        new_reason = data_utils.rand_name('REASON')
-        self.client.update_software_deploy(self.deployment_id,
-                                           self.server_id,
-                                           self.config['id'],
-                                           new_action, new_status,
-                                           self.input_values,
-                                           self.output_values,
-                                           new_reason,
-                                           self.signal_transport)
-
-    @rbac_rule_validation.action(service="heat",
-                                 rule="software_deployments:create")
-    @decorators.idempotent_id('9175fe7b-4210-4c1d-acbb-954998a9fd77')
-    def test_software_deployment_create(self):
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._deployment_create(self.config['id'])
-
-    @rbac_rule_validation.action(service="heat",
-                                 rule="software_deployments:delete")
-    @decorators.idempotent_id('20f4683d-7316-4d88-a6ea-1ee6013da908')
-    def test_software_deployment_delete(self):
-        deploy_id = self._deployment_create(self.config['id'])
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.delete_software_deploy(deploy_id)
-
-    @rbac_rule_validation.action(service="heat",
-                                 rule="software_configs:create")
-    @decorators.idempotent_id('c8fb1c73-fcb6-46c2-9510-8ef0083c9620')
-    def test_config_create(self):
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._config_create('e')
-
-    @rbac_rule_validation.action(service="heat",
-                                 rule="software_configs:delete")
-    @decorators.idempotent_id('f4f784ea-9878-4306-bc5f-041ba5307ce5')
-    def test_config_delete(self):
-        configuration = self._config_create('d')
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.delete_software_config(configuration['id'])
-
-    def _config_create(self, suffix):
-        configuration = {'group': 'script',
-                         'inputs': [],
-                         'outputs': [],
-                         'options': {}}
-        configuration['name'] = 'heat_soft_config_%s' % suffix
-        configuration['config'] = '#!/bin/bash echo init-%s' % suffix
-        api_config = self.client.create_software_config(**configuration)
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.client.delete_software_config,
-            api_config['software_config']['id'])
-        configuration['id'] = api_config['software_config']['id']
-        return configuration
-
-    def _deployment_create(self, config_id):
-        self.server_id = data_utils.rand_name('dummy-server')
-        self.action = 'ACTION_0'
-        self.status = 'STATUS_0'
-        self.input_values = {}
-        self.output_values = []
-        self.status_reason = 'REASON_0'
-        self.signal_transport = 'NO_SIGNAL'
-        self.deployment = self.client.create_software_deploy(
-            self.server_id, config_id, self.action, self.status,
-            self.input_values, self.output_values, self.status_reason,
-            self.signal_transport)
-        self.addCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            self.client.delete_software_deploy,
-            self.deployment['software_deployment']['id'])
-        self.deployment_id = self.deployment['software_deployment']['id']
-        return self.deployment_id
diff --git a/patrole_tempest_plugin/tests/api/volume/rbac_base.py b/patrole_tempest_plugin/tests/api/volume/rbac_base.py
index 79b9f0d..ccadad1 100644
--- a/patrole_tempest_plugin/tests/api/volume/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/volume/rbac_base.py
@@ -35,15 +35,18 @@
     @classmethod
     def setup_clients(cls):
         super(BaseVolumeRbacTest, cls).setup_clients()
-        cls.auth_provider = cls.os.auth_provider
+        cls.auth_provider = cls.os_primary.auth_provider
 
         cls.rbac_utils = rbac_utils()
         cls.rbac_utils.switch_role(cls, toggle_rbac_role=False)
 
         version_checker = {
-            1: [cls.os.volume_hosts_client, cls.os.volume_types_client],
-            2: [cls.os.volume_hosts_v2_client, cls.os.volume_types_v2_client],
-            3: [cls.os.volume_hosts_v2_client, cls.os.volume_types_v2_client]
+            1: [cls.os_primary.volume_hosts_client,
+                cls.os_primary.volume_types_client],
+            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]
diff --git a/patrole_tempest_plugin/tests/api/volume/test_capabilities_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_capabilities_rbac.py
index 28506c0..9d76ef3 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_capabilities_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_capabilities_rbac.py
@@ -32,8 +32,8 @@
     @classmethod
     def setup_clients(cls):
         super(CapabilitiesRbacTest, cls).setup_clients()
-        cls.client = cls.os.volume_capabilities_v2_client
-        cls.hosts_client = cls.os.volume_hosts_v2_client
+        cls.client = cls.os_primary.volume_capabilities_v2_client
+        cls.hosts_client = cls.os_primary.volume_hosts_v2_client
 
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume_extension:capabilities")
diff --git a/patrole_tempest_plugin/tests/api/volume/test_encryption_types_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_encryption_types_rbac.py
new file mode 100644
index 0000000..d829591
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/volume/test_encryption_types_rbac.py
@@ -0,0 +1,86 @@
+# 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.volume import rbac_base
+
+
+class EncryptionTypesRbacTest(rbac_base.BaseVolumeRbacTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(EncryptionTypesRbacTest, cls).skip_checks()
+        if not test.is_extension_enabled('encryption', 'volume'):
+            msg = "%s skipped as encryption not enabled." % cls.__name__
+            raise cls.skipException(msg)
+
+    @classmethod
+    def setup_clients(cls):
+        super(EncryptionTypesRbacTest, cls).setup_clients()
+        cls.client = cls.os_primary.encryption_types_v2_client
+
+    def _create_volume_type_encryption(self):
+        vol_type_id = self.create_volume_type()['id']
+        self.client.create_encryption_type(
+            vol_type_id,
+            provider="nova.volume.encryptors.luks.LuksEncryptor",
+            control_location="front-end")['encryption']
+        return vol_type_id
+
+    @decorators.idempotent_id('ffd94ce5-c24b-4b6c-84c9-c5aad8c3010c')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_type_encryption")
+    def test_create_volume_type_encryption(self):
+        vol_type_id = self.create_volume_type()['id']
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.create_encryption_type(
+            vol_type_id,
+            provider="nova.volume.encryptors.luks.LuksEncryptor",
+            control_location="front-end")['encryption']
+
+    @decorators.idempotent_id('6599e72e-acef-4c0d-a9b2-463fca30d1da')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_type_encryption")
+    def test_delete_volume_type_encryption(self):
+        vol_type_id = self._create_volume_type_encryption()
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.delete_encryption_type(vol_type_id)
+
+    @decorators.idempotent_id('42da9fec-32fd-4dca-9242-8a53b2fed25a')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_type_encryption")
+    def test_update_volume_type_encryption(self):
+        vol_type_id = self._create_volume_type_encryption()
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.update_encryption_type(vol_type_id,
+                                           control_location="front-end")
+
+    @decorators.idempotent_id('1381a3dc-248f-4282-b231-c9399018c804')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_type_encryption")
+    def test_show_volume_type_encryption(self):
+        vol_type_id = self._create_volume_type_encryption()
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.show_encryption_type(vol_type_id)
+
+
+class EncryptionTypesV3RbacTest(EncryptionTypesRbacTest):
+    _api_version = 3
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 c3cca0d..4638e78 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_qos_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_qos_rbac.py
@@ -26,8 +26,8 @@
     @classmethod
     def setup_clients(cls):
         super(VolumeQOSRbacTest, cls).setup_clients()
-        cls.auth_provider = cls.os.auth_provider
-        cls.client = cls.os.volume_qos_v2_client
+        cls.auth_provider = cls.os_primary.auth_provider
+        cls.client = cls.os_primary.volume_qos_v2_client
 
     def _create_test_qos_specs(self, name=None, consumer=None, **kwargs):
         """Create a test Qos-Specs."""
diff --git a/patrole_tempest_plugin/tests/api/volume/test_quota_classes_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_quota_classes_rbac.py
new file mode 100644
index 0000000..44c0d3e
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/volume/test_quota_classes_rbac.py
@@ -0,0 +1,60 @@
+# 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.common.utils import data_utils
+from tempest.lib import decorators
+from tempest import test
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.volume import rbac_base
+
+
+class QuotaClassesRbacTest(rbac_base.BaseVolumeRbacTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(QuotaClassesRbacTest, cls).skip_checks()
+        if not test.is_extension_enabled('os-quota-class-sets', 'volume'):
+            msg = ("%s skipped as os-quota-class-sets not enabled."
+                   % cls.__name__)
+            raise cls.skipException(msg)
+
+    @classmethod
+    def setup_clients(cls):
+        super(QuotaClassesRbacTest, cls).setup_clients()
+        cls.client = cls.os_primary.quota_classes_client
+        cls.quota_name = data_utils.rand_name(cls.__name__ + '-QuotaClass')
+
+    @decorators.idempotent_id('1a060def-2b43-4534-97f5-5eadbbe8c726')
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="volume_extension:quota_classes")
+    def test_show_quota_class_set(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.show_quota_class_set(self.quota_name)['quota_class_set']
+
+    @decorators.idempotent_id('72159478-23a7-4c75-989f-6bac609eca62')
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="volume_extension:quota_classes")
+    def test_update_quota_class_set(self):
+        quota_class_set = self.client.show_quota_class_set(self.quota_name)[
+            'quota_class_set']
+        quota_class_set.pop('id')
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.update_quota_class_set(self.quota_name, **quota_class_set)
+
+
+class QuotaClassesV3RbacTest(QuotaClassesRbacTest):
+    _api_version = 3
diff --git a/patrole_tempest_plugin/tests/api/volume/test_scheduler_stats_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_scheduler_stats_rbac.py
index bf6290a..5a36709 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_scheduler_stats_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_scheduler_stats_rbac.py
@@ -32,7 +32,7 @@
     @classmethod
     def setup_clients(cls):
         super(SchedulerStatsRbacTest, cls).setup_clients()
-        cls.client = cls.os.volume_scheduler_stats_v2_client
+        cls.client = cls.os_primary.volume_scheduler_stats_v2_client
 
     @rbac_rule_validation.action(
         service="cinder",
diff --git a/patrole_tempest_plugin/tests/api/volume/test_snapshots_actions_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_snapshots_actions_rbac.py
index b070a10..5a4e246 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_snapshots_actions_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_snapshots_actions_rbac.py
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslo_log import log as logging
-
 from tempest import config
 from tempest.lib import decorators
 
@@ -22,7 +20,6 @@
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
 CONF = config.CONF
-LOG = logging.getLogger(__name__)
 
 
 class SnapshotsActionsRbacTest(rbac_base.BaseVolumeRbacTest):
@@ -36,14 +33,12 @@
     @classmethod
     def setup_clients(cls):
         super(SnapshotsActionsRbacTest, cls).setup_clients()
-        cls.client = cls.os.snapshots_client
+        cls.client = cls.snapshots_client
 
     @classmethod
     def resource_setup(cls):
         super(SnapshotsActionsRbacTest, cls).resource_setup()
-        # Create a volume
         cls.volume = cls.create_volume()
-        # Create a snapshot
         cls.snapshot = cls.create_snapshot(volume_id=cls.volume['id'])
         cls.snapshot_id = cls.snapshot['id']
 
@@ -52,22 +47,17 @@
         rule="volume_extension:snapshot_admin_actions:reset_status")
     @decorators.idempotent_id('ea430145-34ef-408d-b678-95d5ae5f46eb')
     def test_reset_snapshot_status(self):
-        # Reset snapshot status to error
         status = 'error'
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.\
-            reset_snapshot_status(self.snapshot['id'], status)
+        self.client.reset_snapshot_status(self.snapshot['id'], status)
 
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_admin_actions:force_delete")
+        rule="volume_extension:snapshot_admin_actions:force_delete")
     @decorators.idempotent_id('a8b0f7d8-4c00-4645-b8d5-33ab4eecc6cb')
     def test_snapshot_force_delete(self):
-        # Test force delete of snapshot
-        # Create snapshot,
-        # and force delete temp snapshot
         temp_snapshot = self.create_snapshot(self.volume['id'])
-        # Force delete the snapshot
+
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.client.force_delete_snapshot(temp_snapshot['id'])
         self.client.wait_for_resource_deletion(temp_snapshot['id'])
diff --git a/patrole_tempest_plugin/tests/api/volume/test_user_messages_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_user_messages_rbac.py
index b1e5cba..60484f1 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_user_messages_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_user_messages_rbac.py
@@ -32,7 +32,7 @@
     @classmethod
     def setup_clients(cls):
         super(MessagesV3RbacTest, cls).setup_clients()
-        cls.client = cls.os.volume_v3_messages_client
+        cls.client = cls.os_primary.volume_v3_messages_client
 
     def _create_user_message(self):
         """Trigger a 'no valid host' situation to generate a message."""
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_actions_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_actions_rbac.py
index 70c73fc..16e77ed 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_actions_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_actions_rbac.py
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import testtools
-
 from tempest.common import compute
 from tempest.common import waiters
 from tempest import config
@@ -31,10 +29,17 @@
 
 class VolumesActionsRbacTest(rbac_base.BaseVolumeRbacTest):
 
+    # TODO(felipemonteiro): "volume_extension:volume_actions:upload_public"
+    # test can be created once volumes v3 client is created in Tempest.
+
     @classmethod
     def setup_clients(cls):
         super(VolumesActionsRbacTest, cls).setup_clients()
-        cls.client = cls.os.volumes_client
+        cls.client = cls.volumes_client
+        if CONF.image_feature_enabled.api_v1:
+            cls.image_client = cls.os_primary.image_client
+        elif CONF.image_feature_enabled.api_v2:
+            cls.image_client = cls.os_primary.image_client_v2
 
     @classmethod
     def resource_setup(cls):
@@ -42,10 +47,11 @@
         cls.volume = cls.create_volume()
 
     def _create_server(self):
-        body, _ = compute.create_test_server(self.os, wait_until='ACTIVE')
+        server, _ = compute.create_test_server(
+            self.os_primary, wait_until='ACTIVE')
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.servers_client.delete_server, body['id'])
-        return body
+                        self.servers_client.delete_server, server['id'])
+        return server
 
     def _attach_volume(self, server):
         self.servers_client.attach_volume(
@@ -60,52 +66,139 @@
         waiters.wait_for_volume_resource_status(
             self.client, self.volume['id'], 'available')
 
-    @testtools.skipUnless(CONF.service_available.nova,
-                          "Nova is required to create a server")
+    @test.services('compute')
     @rbac_rule_validation.action(service="cinder", rule="volume:attach")
     @decorators.idempotent_id('f97b10e4-2eed-4f8b-8632-71c02cb9fe42')
     def test_attach_volume_to_instance(self):
         server = self._create_server()
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        # Attach the volume
         self._attach_volume(server)
 
-    @test.attr(type="slow")
+    @test.attr(type=["slow"])
+    @test.services('compute')
     @rbac_rule_validation.action(service="cinder", rule="volume:detach")
     @decorators.idempotent_id('5a042f6a-688b-42e6-a02e-fe5c47b89b07')
     def test_detach_volume_from_instance(self):
-        # Attach the volume
         server = self._create_server()
         self._attach_volume(server)
+
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        # Detach the volume
         self._detach_volume()
 
-    @testtools.skipIf(True, "Patrole bug #1672799")
-    @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:copy_volume_to_image")
+    @test.services('image')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_actions:upload_image")
     @decorators.idempotent_id('b0d0da46-903c-4445-893e-20e680d68b50')
     def test_volume_upload(self):
-        self.image_client = self.os.image_client
-        image_name = data_utils.rand_name('image')
+        # TODO(felipemonteiro): The ``upload_volume`` endpoint also enforces
+        # "volume:copy_volume_to_image" but is not currently contained in
+        # Cinder's policy.json.
+        image_name = data_utils.rand_name(self.__class__.__name__ + '-Image')
+
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         body = self.client.upload_volume(
-            self.volume['id'], image_name=image_name,
+            self.volume['id'], image_name=image_name, visibility="private",
             disk_format=CONF.volume.disk_format)['os-volume_upload_image']
+        image_id = body["image_id"]
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.image_client.delete_image,
-                        body['image_id'])
-        waiters.wait_for_volume_resource_status(
-            self.client, self.volume['id'], 'available')
+                        image_id)
+        waiters.wait_for_image_status(self.image_client, image_id, 'active')
+        waiters.wait_for_volume_resource_status(self.os_admin.volumes_client,
+                                                self.volume['id'], 'available')
 
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume:update_readonly_flag")
     @decorators.idempotent_id('2750717a-f250-4e41-9e09-02624aad6ff8')
     def test_volume_readonly_update(self):
-        volume = self.create_volume()
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        # Update volume readonly
-        self.client.update_volume_readonly(volume['id'], readonly=True)
+
+        self.client.update_volume_readonly(self.volume['id'], readonly=True)
+        self.addCleanup(self.client.update_volume_readonly,
+                        self.volume['id'], readonly=False)
+
+    @decorators.idempotent_id('72bab13c-dfaf-4b6d-a132-c83a85fb1776')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_unmanage")
+    def test_unmanage_volume(self):
+        volume = self.create_volume()
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.unmanage_volume(volume['id'])
+
+    @decorators.idempotent_id('59b783c0-f4ef-430c-8a90-1bad97d4ec5c')
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="volume:update")
+    def test_volume_set_bootable(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.set_bootable_volume(self.volume['id'], bootable=True)
+
+    @decorators.idempotent_id('41566922-75a1-4484-99c7-9c8782ee99ac')
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="volume:reserve_volume")
+    def test_volume_reserve(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.reserve_volume(self.volume['id'])
+
+    @decorators.idempotent_id('e5fa9564-77d9-4e57-b0c0-3e0ae4d08535')
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="volume:unreserve_volume")
+    def test_volume_unreserve(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.unreserve_volume(self.volume['id'])
+
+    @decorators.idempotent_id('c015c82f-7010-48cc-bd71-4ef542046f20')
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="volume:retype")
+    def test_volume_retype(self):
+        volume = self.create_volume()
+        vol_type = self.create_volume_type()['name']
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.retype_volume(volume['id'], new_type=vol_type)
+
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_admin_actions:reset_status")
+    @decorators.idempotent_id('4b3dad7d-0e73-4839-8781-796dd3d7af1d')
+    def test_volume_reset_status(self):
+        volume = self.create_volume()
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.reset_volume_status(volume['id'], status='error')
+
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_admin_actions:force_delete")
+    @decorators.idempotent_id('a312a937-6abf-4b91-a950-747086cbce48')
+    def test_volume_force_delete(self):
+        volume = self.create_volume()
+        self.client.reset_volume_status(volume['id'], status='error')
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.force_delete_volume(volume['id'])
+        self.client.wait_for_resource_deletion(volume['id'])
+
+    @decorators.idempotent_id('48bd302b-950a-4830-840c-3158246ecdcc')
+    @test.services('compute')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_admin_actions:force_detach")
+    def test_force_detach_volume_from_instance(self):
+        server = self._create_server()
+        self._attach_volume(server)
+        attachment = self.volumes_client.show_volume(
+            self.volume['id'])['volume']['attachments'][0]
+
+        # Reset volume's status to error.
+        self.volumes_client.reset_volume_status(self.volume['id'],
+                                                status='error')
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.volumes_client.force_detach_volume(
+            self.volume['id'], connector=None,
+            attachment_id=attachment['attachment_id'])
 
 
 class VolumesActionsV3RbacTest(VolumesActionsRbacTest):
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 1c73e68..d07e5c6 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
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslo_log import log as logging
-
 from tempest import config
 from tempest.lib import decorators
 
@@ -22,62 +20,95 @@
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
 CONF = config.CONF
-LOG = logging.getLogger(__name__)
 
 
 class VolumeMetadataRbacTest(rbac_base.BaseVolumeRbacTest):
     @classmethod
     def setup_clients(cls):
         super(VolumeMetadataRbacTest, cls).setup_clients()
-        cls.client = cls.os.volumes_client
+        cls.client = cls.volumes_client
+        if CONF.image_feature_enabled.api_v1:
+            cls.image_client = cls.os_primary.image_client
+        elif CONF.image_feature_enabled.api_v2:
+            cls.image_client = cls.os_primary.image_client_v2
+        cls.image_id = CONF.compute.image_ref
 
-    def _add_metadata(self, volume):
+    @classmethod
+    def resource_setup(cls):
+        super(VolumeMetadataRbacTest, cls).resource_setup()
+        cls.volume = cls.create_volume()
+        cls._add_metadata(cls.volume)
+        cls.image_id = CONF.compute.image_ref
+
+    @classmethod
+    def _add_metadata(cls, volume):
         # Create metadata for the volume
         metadata = {"key1": "value1",
                     "key2": "value2",
                     "key3": "value3",
                     "key4": "<value&special_chars>"}
-        self.volumes_client.create_volume_metadata(volume['id'],
-                                                   metadata)['metadata']
+        cls.client.create_volume_metadata(cls.volume['id'],
+                                          metadata)['metadata']
 
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume:update_volume_metadata")
     @decorators.idempotent_id('232bbb8b-4c29-44dc-9077-b1398c20b738')
     def test_create_volume_metadata(self):
-        volume = self.create_volume()
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self._add_metadata(volume)
+        self._add_metadata(self.volume)
 
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume:get")
     @decorators.idempotent_id('87ea37d9-23ab-47b2-a59c-16fc4d2c6dfa')
     def test_get_volume_metadata(self):
-        volume = self.create_volume()
-        self._add_metadata(volume)
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.volumes_client.show_volume_metadata(volume['id'])['metadata']
+        self.client.show_volume_metadata(self.volume['id'])['metadata']
 
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume:delete_volume_metadata")
     @decorators.idempotent_id('7498dfc1-9db2-4423-ad20-e6dcb25d1beb')
-    def test_delete_volume_metadata(self):
-        volume = self.create_volume()
-        self._add_metadata(volume)
+    def test_delete_volume_metadata_item(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.volumes_client.delete_volume_metadata_item(volume['id'],
-                                                        "key1")
+        self.client.delete_volume_metadata_item(self.volume['id'], "key1")
+
+    @decorators.idempotent_id('a41c8eed-2051-4a25-b401-df036faacbdc')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume:delete_volume_metadata")
+    def test_delete_volume_image_metadata(self):
+        self.client.update_volume_image_metadata(self.volume['id'],
+                                                 image_id=self.image_id)
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.delete_volume_image_metadata(self.volume['id'], 'image_id')
 
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume:update_volume_metadata")
     @decorators.idempotent_id('8ce2ff80-99ba-49ae-9bb1-7e96729ee5af')
-    def test_update_volume_metadata(self):
-        volume = self.create_volume()
-        self._add_metadata(volume)
+    def test_update_volume_metadata_item(self):
         # Metadata to update
         update_item = {"key3": "value3_update"}
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.volumes_client.update_volume_metadata_item(
-            volume['id'], "key3", update_item)['meta']
+        self.client.update_volume_metadata_item(self.volume['id'], "key3",
+                                                update_item)['meta']
+
+    @decorators.idempotent_id('a231b445-97a5-4657-b05f-245895e88da9')
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="volume:update_volume_metadata")
+    def test_update_volume_metadata(self):
+        # Metadata to update
+        update = {"key1": "value1",
+                  "key3": "value3"}
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.update_volume_metadata(self.volume['id'], update)
+
+    @decorators.idempotent_id('a9d9e825-5ea3-42e6-96f3-7ac4e97b2ed0')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume:update_volume_metadata")
+    def test_update_volume_image_metadata(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.update_volume_image_metadata(self.volume['id'],
+                                                 image_id=self.image_id)
 
 
 class VolumeMetadataV3RbacTest(VolumeMetadataRbacTest):
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_quotas_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_quotas_rbac.py
index a104782..0321e13 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_quotas_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_quotas_rbac.py
@@ -32,12 +32,12 @@
     @classmethod
     def setup_credentials(cls):
         super(VolumeQuotasRbacTest, cls).setup_credentials()
-        cls.demo_tenant_id = cls.os.credentials.tenant_id
+        cls.demo_tenant_id = cls.os_primary.credentials.tenant_id
 
     @classmethod
     def setup_clients(cls):
         super(VolumeQuotasRbacTest, cls).setup_clients()
-        cls.client = cls.os.volume_quotas_client
+        cls.client = cls.os_primary.volume_quotas_client
 
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume_extension:quotas:show")
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_services_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_services_rbac.py
new file mode 100644
index 0000000..c1d8a65
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_services_rbac.py
@@ -0,0 +1,51 @@
+# 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.volume import rbac_base
+
+
+class VolumeServicesRbacTest(rbac_base.BaseVolumeRbacTest):
+
+    # TODO(felipemonteiro): Implement a test to cover the policy action,
+    # "volume_extension:services:update", once the Tempest client endpoint
+    # is implemented.
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumeServicesRbacTest, cls).skip_checks()
+        if not test.is_extension_enabled('os-services', 'volume'):
+            msg = "%s skipped as os-services not enabled." % cls.__name__
+            raise cls.skipException(msg)
+
+    @classmethod
+    def setup_clients(cls):
+        super(VolumeServicesRbacTest, cls).setup_clients()
+        cls.client = cls.os_primary.volume_services_v2_client
+
+    @decorators.idempotent_id('b9134f01-97c0-4abd-9455-fe2f03e3f966')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:services:index")
+    def test_list_services(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.client.list_services()['services']
+
+
+class VolumeServicesV3RbacTest(VolumeServicesRbacTest):
+    _api_version = 3
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_transfers_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_transfers_rbac.py
index cb40c50..057379b 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_transfers_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_transfers_rbac.py
@@ -28,8 +28,8 @@
     @classmethod
     def setup_clients(cls):
         super(VolumesTransfersRbacTest, cls).setup_clients()
-        cls.client = cls.os.volume_transfers_v2_client
-        cls.adm_volumes_client = cls.os_adm.volumes_v2_client
+        cls.client = cls.os_primary.volume_transfers_v2_client
+        cls.adm_volumes_client = cls.os_admin.volumes_v2_client
 
     @classmethod
     def resource_setup(cls):
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_types_extra_specs_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_types_extra_specs_rbac.py
index 33bc5ae..94199b5 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_types_extra_specs_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_types_extra_specs_rbac.py
@@ -13,7 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
@@ -22,21 +21,11 @@
 
 class VolumeTypesExtraSpecsRbacTest(rbac_base.BaseVolumeRbacTest):
 
-    def _create_volume_type(self, name=None, **kwargs):
-        """Create a test volume-type"""
-        name = name or data_utils.rand_name(
-            self.__class__.__name__ + '-volume-type')
-        volume_type = self.volume_types_client.create_volume_type(
-            name=name, **kwargs)['volume_type']
-        self.addCleanup(self.volume_types_client.delete_volume_type,
-                        volume_type['id'])
-        return volume_type
-
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume_extension:types_extra_specs")
     @decorators.idempotent_id('eea40251-990b-49b0-99ae-10e4585b479b')
     def test_create_volume_type_extra_specs(self):
-        vol_type = self._create_volume_type()
+        vol_type = self.create_volume_type()
         # List Volume types extra specs.
         extra_specs = {"spec1": "val1"}
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
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 6a3367a..8c04a8d 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
@@ -65,7 +65,7 @@
         backup.update(changes)
         return self._encode_backup(backup)
 
-    @test.attr(type="slow")
+    @test.attr(type=["slow"])
     @rbac_rule_validation.action(service="cinder",
                                  rule="backup:create")
     @decorators.idempotent_id('6887ec94-0bcf-4ab7-b30f-3808a4b5a2a5')
@@ -73,7 +73,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self._create_backup(volume_id=self.volume['id'])
 
-    @test.attr(type="slow")
+    @test.attr(type=["slow"])
     @rbac_rule_validation.action(service="cinder",
                                  rule="backup:get")
     @decorators.idempotent_id('abd92bdd-b0fb-4dc4-9cfc-de9e968f8c8a')
@@ -91,7 +91,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.backups_client.list_backups()
 
-    @test.attr(type="slow")
+    @test.attr(type=["slow"])
     @rbac_rule_validation.action(service="cinder",
                                  rule="backup:restore")
     @decorators.idempotent_id('9c794bf9-2446-4f41-8fe0-80b71e757f9d')
@@ -104,7 +104,7 @@
         waiters.wait_for_volume_resource_status(
             self.backups_client, restore['backup_id'], 'available')
 
-    @test.attr(type="slow")
+    @test.attr(type=["slow"])
     @rbac_rule_validation.action(service="cinder",
                                  rule="backup:delete")
     @decorators.idempotent_id('d5d0c6a2-413d-437e-a73f-4bf2b41a20ed')
@@ -116,7 +116,7 @@
         self.backups_client.delete_backup(backup['id'])
         self.backups_client.wait_for_resource_deletion(backup['id'])
 
-    @test.attr(type='slow')
+    @test.attr(type=["slow"])
     @rbac_rule_validation.action(service="cinder",
                                  rule="backup:backup-export")
     @decorators.idempotent_id('e984ec8d-e8eb-485c-98bc-f1856020303c')
@@ -128,7 +128,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.backups_client.export_backup(backup['id'])['backup-record']
 
-    @test.attr(type='slow')
+    @test.attr(type=["slow"])
     @rbac_rule_validation.action(service="cinder",
                                  rule="backup:backup-import")
     @decorators.idempotent_id('1e70f039-4556-44cc-9cc1-edf2b7ed648b')
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volumes_manage_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volumes_manage_rbac.py
new file mode 100644
index 0000000..7a9d7ba
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/volume/test_volumes_manage_rbac.py
@@ -0,0 +1,113 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.common import waiters
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.volume import rbac_base
+
+CONF = config.CONF
+
+
+class VolumesManageRbacTest(rbac_base.BaseVolumeRbacTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(VolumesManageRbacTest, cls).skip_checks()
+
+        if not CONF.volume_feature_enabled.manage_volume:
+            raise cls.skipException("Manage volume tests are disabled")
+
+        if len(CONF.volume.manage_volume_ref) != 2:
+            raise cls.skipException("Manage volume ref is not correctly "
+                                    "configured")
+
+    @classmethod
+    def setup_clients(cls):
+        super(VolumesManageRbacTest, cls).setup_clients()
+        cls.volume_manage_client = cls.os_primary.volume_manage_v2_client
+
+    def _manage_volume(self, org_volume):
+        # Manage volume
+        new_volume_name = data_utils.rand_name(
+            self.__class__.__name__ + '-volume')
+
+        new_volume_ref = {
+            'name': new_volume_name,
+            'host': org_volume['os-vol-host-attr:host'],
+            'ref': {CONF.volume.manage_volume_ref[0]:
+                    CONF.volume.manage_volume_ref[1] % org_volume['id']},
+            'volume_type': org_volume['volume_type'],
+            'availability_zone': org_volume['availability_zone']}
+
+        new_volume_id = self.volume_manage_client.manage_volume(
+            **new_volume_ref)['volume']['id']
+
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                new_volume_id, 'available')
+        self.addCleanup(self.delete_volume,
+                        self.volumes_client, new_volume_id)
+
+    def _unmanage_volume(self, volume):
+        self.volumes_client.unmanage_volume(volume['id'])
+        self.volumes_client.wait_for_resource_deletion(volume['id'])
+
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_manage")
+    @decorators.idempotent_id('114f9708-939b-407e-aeac-d21ebfabaad3')
+    def test_volume_manage(self):
+        volume_id = self.create_volume()['id']
+        volume = self.volumes_client.show_volume(volume_id)['volume']
+
+        # By default, the volume is managed after creation.  We need to
+        # unmanage the volume first before testing manage volume.
+        self._unmanage_volume(volume)
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        try:
+            self._manage_volume(volume)
+        except exceptions.Forbidden as e:
+            # Since the test role under test does not have permission to
+            # manage the volume, Forbidden exception is thrown and the
+            # manageable list will not be cleaned up. Therefore, we need to
+            # re-manage the volume at the end of the test case for proper
+            # resource clean up.
+            self.addCleanup(self._manage_volume, volume)
+            raise exceptions.Forbidden(e)
+
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_unmanage")
+    @decorators.idempotent_id('d5d72abe-60bc-45ac-a8f2-c21b24f0b5d6')
+    def test_volume_unmanage(self):
+        volume_id = self.create_volume()['id']
+        volume = self.volumes_client.show_volume(volume_id)['volume']
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self._unmanage_volume(volume)
+
+        # In order to clean up the manageable list, we need to re-manage the
+        # volume after the test.  The _manage_volume method will set up the
+        # proper resource cleanup
+        self.addCleanup(self._manage_volume, volume)
+
+
+class VolumesManageV3RbacTest(VolumesManageRbacTest):
+    _api_version = 3
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volumes_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volumes_rbac.py
deleted file mode 100644
index b10f5b3..0000000
--- a/patrole_tempest_plugin/tests/api/volume/test_volumes_rbac.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# 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 oslo_log import log as logging
-
-from tempest import config
-from tempest.lib import decorators
-
-from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.volume import rbac_base
-
-CONF = config.CONF
-LOG = logging.getLogger(__name__)
-
-
-class VolumesRbacTest(rbac_base.BaseVolumeRbacTest):
-
-    @classmethod
-    def setup_clients(cls):
-        super(VolumesRbacTest, cls).setup_clients()
-        cls.client = cls.volumes_client
-
-    @rbac_rule_validation.action(
-        service="cinder",
-        rule="volume_extension:volume_admin_actions:reset_status")
-    @decorators.idempotent_id('4b3dad7d-0e73-4839-8781-796dd3d7af1d')
-    def test_volume_reset_status(self):
-        volume = self.create_volume()
-        # Test volume reset status : available->error->available
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.reset_volume_status(volume['id'], status='error')
-        self.client.reset_volume_status(volume['id'], status='available')
-
-    @rbac_rule_validation.action(
-        service="cinder",
-        rule="volume_extension:volume_admin_actions:force_delete")
-    @decorators.idempotent_id('a312a937-6abf-4b91-a950-747086cbce48')
-    def test_volume_force_delete_when_volume_is_error(self):
-        volume = self.create_volume()
-        self.client.reset_volume_status(volume['id'], status='error')
-        # Test force delete when status of volume is error
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.client.force_delete_volume(volume['id'])
-        self.client.wait_for_resource_deletion(volume['id'])
-
-
-class VolumesV3RbacTest(VolumesRbacTest):
-    _api_version = 3
diff --git a/patrole_tempest_plugin/tests/unit/test_rbac_policy_parser.py b/patrole_tempest_plugin/tests/unit/test_rbac_policy_parser.py
index 0906222..6889b44 100644
--- a/patrole_tempest_plugin/tests/unit/test_rbac_policy_parser.py
+++ b/patrole_tempest_plugin/tests/unit/test_rbac_policy_parser.py
@@ -32,6 +32,8 @@
         super(RbacPolicyTest, self).setUp()
         self.mock_admin_mgr = mock.patch.object(
             rbac_policy_parser, 'credentials').start()
+        self.mock_path = mock.patch.object(
+            rbac_policy_parser, 'os').start()
 
         current_directory = os.path.dirname(os.path.realpath(__file__))
         self.custom_policy_file = os.path.join(current_directory,
@@ -88,8 +90,9 @@
 
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
+        self.mock_path.path.join.return_value = self.custom_policy_file
         parser = rbac_policy_parser.RbacPolicyParser(
-            test_tenant_id, test_user_id, "test", self.custom_policy_file)
+            test_tenant_id, test_user_id, "test")
 
         expected = {
             'policy_action_1': ['two', 'four', 'six', 'eight'],
@@ -110,8 +113,9 @@
     def test_admin_policy_file_with_admin_role(self):
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
+        self.mock_path.path.join.return_value = self.admin_policy_file
         parser = rbac_policy_parser.RbacPolicyParser(
-            test_tenant_id, test_user_id, "test", self.admin_policy_file)
+            test_tenant_id, test_user_id, "test")
 
         role = 'admin'
         allowed_rules = [
@@ -130,8 +134,9 @@
     def test_admin_policy_file_with_member_role(self):
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
+        self.mock_path.path.join.return_value = self.admin_policy_file
         parser = rbac_policy_parser.RbacPolicyParser(
-            test_tenant_id, test_user_id, "test", self.admin_policy_file)
+            test_tenant_id, test_user_id, "test")
 
         role = 'Member'
         allowed_rules = [
@@ -151,8 +156,9 @@
     def test_admin_policy_file_with_context_is_admin(self):
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
+        self.mock_path.path.join.return_value = self.alt_admin_policy_file
         parser = rbac_policy_parser.RbacPolicyParser(
-            test_tenant_id, test_user_id, "test", self.alt_admin_policy_file)
+            test_tenant_id, test_user_id, "test")
 
         role = 'fake_admin'
         allowed_rules = ['non_admin_rule']
@@ -187,8 +193,9 @@
         """
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
+        self.mock_path.path.join.return_value = self.tenant_policy_file
         parser = rbac_policy_parser.RbacPolicyParser(
-            test_tenant_id, test_user_id, "test", self.tenant_policy_file)
+            test_tenant_id, test_user_id, "test")
 
         # Check whether Member role can perform expected actions.
         allowed_rules = ['rule1', 'rule2', 'rule3', 'rule4']
@@ -268,9 +275,9 @@
     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
-
+        self.mock_path.path.join.return_value = self.custom_policy_file
         parser = rbac_policy_parser.RbacPolicyParser(
-            test_tenant_id, test_user_id, "test", self.custom_policy_file)
+            test_tenant_id, test_user_id, "test")
 
         fake_rule = 'fake_rule'
         expected_message = "Policy action: {0} not found in policy file: {1}."\
@@ -285,9 +292,9 @@
     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
-
+        self.mock_path.path.join.return_value = self.custom_policy_file
         parser = rbac_policy_parser.RbacPolicyParser(
-            test_tenant_id, test_user_id, "test", self.custom_policy_file)
+            test_tenant_id, test_user_id, "test")
         parser.rules = mock.MagicMock(
             **{'__getitem__.return_value.side_effect': Exception(
                mock.sentinel.error)})
@@ -320,9 +327,9 @@
 
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
-
+        self.mock_path.path.join.return_value = self.tenant_policy_file
         parser = rbac_policy_parser.RbacPolicyParser(
-            test_tenant_id, test_user_id, "test", self.tenant_policy_file)
+            test_tenant_id, test_user_id, "test")
 
         policy_data = parser._get_policy_data('fake_service')
 
@@ -364,9 +371,9 @@
 
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
-
+        self.mock_path.path.join.return_value = self.tenant_policy_file
         parser = rbac_policy_parser.RbacPolicyParser(
-            test_tenant_id, test_user_id, "test", self.tenant_policy_file)
+            test_tenant_id, test_user_id, "test")
 
         policy_data = parser._get_policy_data('fake_service')
 
@@ -393,10 +400,10 @@
         mock_creds.AdminManager.return_value.identity_services_v3_client.\
             list_services.return_value = {
                 'services': [{'name': 'test_service'}]}
-
+        self.mock_path.path.join.return_value = '/etc/test_service/policy.json'
         e = self.assertRaises(rbac_exceptions.RbacParsingException,
                               rbac_policy_parser.RbacPolicyParser,
-                              None, None, 'test_service', None)
+                              None, None, 'test_service')
 
         expected_error = \
             'Policy file for {0} service neither found in code '\
@@ -405,14 +412,12 @@
 
         self.assertIn(expected_error, str(e))
 
-    @mock.patch.object(rbac_policy_parser, 'os', autospec=True)
     @mock.patch.object(rbac_policy_parser, 'json', autospec=True)
     @mock.patch.object(rbac_policy_parser, 'credentials', autospec=True)
     @mock.patch.object(rbac_policy_parser, 'stevedore', autospec=True)
     def test_get_policy_data_without_valid_policy(self, mock_stevedore,
-                                                  mock_credentials, mock_json,
-                                                  mock_os):
-        mock_os.path.isfile.return_value = False
+                                                  mock_credentials, mock_json):
+        self.mock_path.path.isfile.return_value = False
 
         test_policy_action = mock.Mock(check='rule:bar')
         test_policy_action.configure_mock(name='foo')
@@ -432,7 +437,7 @@
 
         e = self.assertRaises(rbac_exceptions.RbacParsingException,
                               rbac_policy_parser.RbacPolicyParser,
-                              None, None, 'test_service', None)
+                              None, None, 'test_service')
 
         expected_error = "Policy file for {0} service is invalid."\
                          .format("test_service")
@@ -459,11 +464,10 @@
             }
         mock_stevedore.named.NamedExtensionManager.return_value = None
         mock_json.loads.side_effect = ValueError
-
+        self.mock_path.path.join.return_value = self.tenant_policy_file
         e = self.assertRaises(rbac_exceptions.RbacParsingException,
                               rbac_policy_parser.RbacPolicyParser,
-                              None, None, 'test_service',
-                              self.tenant_policy_file)
+                              None, None, 'test_service')
 
         expected_error = 'Policy file for {0} service neither found in code '\
                          'nor at {1}.'.format('test_service',
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 174945e..41af3b2 100644
--- a/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py
+++ b/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py
@@ -19,7 +19,6 @@
 from tempest import test
 from tempest.tests import base
 
-from patrole_tempest_plugin import rbac_auth
 from patrole_tempest_plugin import rbac_exceptions
 from patrole_tempest_plugin import rbac_rule_validation as rbac_rv
 
@@ -43,8 +42,9 @@
         self.addCleanup(CONF.clear_override, 'rbac_test_role', group='rbac')
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_auth, 'RbacAuthority', autospec=True)
-    def test_rule_validation_have_permission_no_exc(self, mock_auth, mock_log):
+    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
+    def test_rule_validation_have_permission_no_exc(self, mock_policy,
+                                                    mock_log):
         """Test that having permission and no exception thrown is success.
 
         Positive test case success scenario.
@@ -54,9 +54,7 @@
         mock_function = mock.Mock()
         wrapper = decorator(mock_function)
 
-        mock_permission = mock.Mock()
-        mock_permission.get_permission.return_value = True
-        mock_auth.return_value = mock_permission
+        mock_policy.RbacPolicyParser.return_value.allowed.return_value = True
 
         result = wrapper(self.mock_args)
 
@@ -65,8 +63,8 @@
         mock_log.error.assert_not_called()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_auth, 'RbacAuthority', autospec=True)
-    def test_rule_validation_lack_permission_throw_exc(self, mock_auth,
+    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
+    def test_rule_validation_lack_permission_throw_exc(self, mock_policy,
                                                        mock_log):
         """Test that having no permission and exception thrown is success.
 
@@ -78,9 +76,7 @@
         mock_function.side_effect = exceptions.Forbidden
         wrapper = decorator(mock_function)
 
-        mock_permission = mock.Mock()
-        mock_permission.get_permission.return_value = False
-        mock_auth.return_value = mock_permission
+        mock_policy.RbacPolicyParser.return_value.allowed.return_value = False
 
         result = wrapper(self.mock_args)
 
@@ -89,8 +85,8 @@
         mock_log.error.assert_not_called()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_auth, 'RbacAuthority', autospec=True)
-    def test_rule_validation_forbidden_negative(self, mock_auth, mock_log):
+    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
+    def test_rule_validation_forbidden_negative(self, mock_policy, mock_log):
         """Test Forbidden error is thrown and have permission fails.
 
         Negative test case: if Forbidden is thrown and the user should be
@@ -102,9 +98,7 @@
         mock_function.side_effect = exceptions.Forbidden
         wrapper = decorator(mock_function)
 
-        mock_permission = mock.Mock()
-        mock_permission.get_permission.return_value = True
-        mock_auth.return_value = mock_permission
+        mock_policy.RbacPolicyParser.return_value.allowed.return_value = True
 
         e = self.assertRaises(exceptions.Forbidden, wrapper, self.mock_args)
         self.assertIn(
@@ -114,8 +108,8 @@
                                                " perform sentinel.action.")
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_auth, 'RbacAuthority', autospec=True)
-    def test_rule_validation_rbac_action_failed_positive(self, mock_auth,
+    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
+    def test_rule_validation_rbac_action_failed_positive(self, mock_policy,
                                                          mock_log):
         """Test RbacActionFailed error is thrown without permission passes.
 
@@ -127,9 +121,7 @@
         mock_function.side_effect = rbac_exceptions.RbacActionFailed
         wrapper = decorator(mock_function)
 
-        mock_permission = mock.Mock()
-        mock_permission.get_permission.return_value = False
-        mock_auth.return_value = mock_permission
+        mock_policy.RbacPolicyParser.return_value.allowed.return_value = False
 
         result = wrapper(self.mock_args)
 
@@ -138,8 +130,8 @@
         mock_log.warning.assert_not_called()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_auth, 'RbacAuthority', autospec=True)
-    def test_rule_validation_rbac_action_failed_negative(self, mock_auth,
+    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
+    def test_rule_validation_rbac_action_failed_negative(self, mock_policy,
                                                          mock_log):
         """Test RbacActionFailed error is thrown with permission fails.
 
@@ -151,9 +143,7 @@
         mock_function.side_effect = rbac_exceptions.RbacActionFailed
         wrapper = decorator(mock_function)
 
-        mock_permission = mock.Mock()
-        mock_permission.get_permission.return_value = True
-        mock_auth.return_value = mock_permission
+        mock_policy.RbacPolicyParser.return_value.allowed.return_value = True
 
         e = self.assertRaises(exceptions.Forbidden, wrapper, self.mock_args)
         self.assertIn(
@@ -164,8 +154,9 @@
                                                " perform sentinel.action.")
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_auth, 'RbacAuthority', autospec=True)
-    def test_expect_not_found_but_raises_forbidden(self, mock_auth, mock_log):
+    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
+    def test_expect_not_found_but_raises_forbidden(self, mock_policy,
+                                                   mock_log):
         """Test that expecting 404 but getting 403 works for all scenarios.
 
         Tests the following scenarios:
@@ -186,9 +177,8 @@
                          "NotFound, which was not thrown."
 
         for permission in [True, False]:
-            mock_permission = mock.Mock()
-            mock_permission.get_permission.return_value = permission
-            mock_auth.return_value = mock_permission
+            mock_policy.RbacPolicyParser.return_value.allowed.return_value =\
+                permission
 
             e = self.assertRaises(exceptions.Forbidden, wrapper,
                                   self.mock_args)
@@ -197,8 +187,8 @@
             mock_log.error.reset_mock()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_auth, 'RbacAuthority', autospec=True)
-    def test_expect_not_found_and_raise_not_found(self, mock_auth, mock_log):
+    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
+    def test_expect_not_found_and_raise_not_found(self, mock_policy, mock_log):
         """Test that expecting 404 and getting 404 works for all scenarios.
 
         Tests the following scenarios:
@@ -220,9 +210,8 @@
         ]
 
         for pos, permission in enumerate([True, False]):
-            mock_permission = mock.Mock()
-            mock_permission.get_permission.return_value = permission
-            mock_auth.return_value = mock_permission
+            mock_policy.RbacPolicyParser.return_value.allowed.return_value =\
+                permission
 
             expected_error = expected_errors[pos]
 
@@ -245,8 +234,8 @@
             mock_log.error.reset_mock()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_auth, 'RbacAuthority', autospec=True)
-    def test_rule_validation_overpermission_negative(self, mock_auth,
+    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
+    def test_rule_validation_overpermission_negative(self, mock_policy,
                                                      mock_log):
         """Test that OverPermission is correctly handled.
 
@@ -258,9 +247,7 @@
         mock_function = mock.Mock()
         wrapper = decorator(mock_function)
 
-        mock_permission = mock.Mock()
-        mock_permission.get_permission.return_value = False
-        mock_auth.return_value = mock_permission
+        mock_policy.RbacPolicyParser.return_value.allowed.return_value = False
 
         e = self.assertRaises(rbac_exceptions.RbacOverPermission, wrapper,
                               self.mock_args)
@@ -270,7 +257,7 @@
         mock_log.error.assert_called_once_with(
             "Role Member was allowed to perform sentinel.action")
 
-    @mock.patch.object(rbac_auth, 'rbac_policy_parser', autospec=True)
+    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
     def test_invalid_policy_rule_throws_parsing_exception(
             self, mock_rbac_policy_parser):
         """Test that invalid policy action causes test to be skipped."""
@@ -295,8 +282,8 @@
             mock.sentinel.project_id, mock.sentinel.user_id,
             mock.sentinel.service, extra_target_data={})
 
-    @mock.patch.object(rbac_auth, 'RbacAuthority', autospec=True)
-    def test_get_exception_type_404(self, mock_auth):
+    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
+    def test_get_exception_type_404(self, mock_policy):
         """Test that getting a 404 exception type returns NotFound."""
         expected_exception = exceptions.NotFound
         expected_irregular_msg = ("NotFound exception was caught for policy "
@@ -309,8 +296,8 @@
         self.assertEqual(expected_exception, actual_exception)
         self.assertEqual(expected_irregular_msg, actual_irregular_msg)
 
-    @mock.patch.object(rbac_auth, 'RbacAuthority', autospec=True)
-    def test_get_exception_type_403(self, mock_auth):
+    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
+    def test_get_exception_type_403(self, mock_policy):
         """Test that getting a 404 exception type returns Forbidden."""
         expected_exception = exceptions.Forbidden
         expected_irregular_msg = None
@@ -322,8 +309,9 @@
         self.assertEqual(expected_irregular_msg, actual_irregular_msg)
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_auth, 'RbacAuthority', autospec=True)
-    def test_exception_thrown_when_type_is_not_int(self, mock_auth, mock_log):
+    @mock.patch.object(rbac_rv, 'rbac_policy_parser', autospec=True)
+    def test_exception_thrown_when_type_is_not_int(self, mock_policy,
+                                                   mock_log):
         """Test that non-integer exception type raises error."""
         self.assertRaises(rbac_exceptions.RbacInvalidErrorCode,
                           rbac_rv._get_exception_type, "403")
@@ -333,8 +321,8 @@
                                                "codes: [403, 404]")
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_auth, 'RbacAuthority', autospec=True)
-    def test_exception_thrown_when_type_is_403_or_404(self, mock_auth,
+    @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):
         """Test that unsupported exceptions throw error."""
         invalid_exceptions = [200, 400, 500]
diff --git a/patrole_tempest_plugin/tests/unit/test_rbac_utils.py b/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
index 057ce20..a2917cf 100644
--- a/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
+++ b/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
@@ -51,12 +51,15 @@
         self.mock_test_obj.os_admin = mock.Mock(
             **{'roles_v3_client.list_roles.return_value': available_roles})
 
-        CONF.set_override('rbac_test_role', 'Member', group='rbac',
+        CONF.set_override('admin_role', 'admin', group='identity',
                           enforce_type=True)
         CONF.set_override('auth_version', 'v3', group='identity',
                           enforce_type=True)
+        CONF.set_override('rbac_test_role', 'Member', group='rbac',
+                          enforce_type=True)
 
         self.addCleanup(CONF.clear_override, 'rbac_test_role', group='rbac')
+        self.addCleanup(CONF.clear_override, 'admin_role', group='identity')
         self.addCleanup(CONF.clear_override, 'auth_version', group='identity')
         self.addCleanup(mock.patch.stopall)
 
diff --git a/releasenotes/notes/add-force-delete-backup-test-7e896affd1471328.yaml b/releasenotes/notes/add-force-delete-backup-test-7e896affd1471328.yaml
new file mode 100644
index 0000000..3d81baa
--- /dev/null
+++ b/releasenotes/notes/add-force-delete-backup-test-7e896affd1471328.yaml
@@ -0,0 +1,11 @@
+---
+features:
+  - |
+    Added an RBAC test for force-deleting a backup which enforces the cinder
+    policy action: "volume_extension:backup_admin_actions:force_delete".
+fixes:
+  - |
+    Corrected the policy action in the ``rbac_rule_validation`` decorator
+    for the test ``test_snapshot_force_delete`` from
+    "volume_extension:volume_admin_actions:force_delete" to
+    "volume_extension:snapshot_admin_actions:force_delete".
diff --git a/releasenotes/notes/add-metadef-resource-type-7973621c5e8fff7f.yaml b/releasenotes/notes/add-metadef-resource-type-7973621c5e8fff7f.yaml
new file mode 100644
index 0000000..61bec83
--- /dev/null
+++ b/releasenotes/notes/add-metadef-resource-type-7973621c5e8fff7f.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Adds test for glance's
+    add_metadef_resource_type_association policy.
diff --git a/releasenotes/notes/add-quota-classes-tests-3e61e671f6e131df.yaml b/releasenotes/notes/add-quota-classes-tests-3e61e671f6e131df.yaml
new file mode 100644
index 0000000..656f6a1
--- /dev/null
+++ b/releasenotes/notes/add-quota-classes-tests-3e61e671f6e131df.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Add RBAC tests for cinder os-quota-class-sets API, which cover the
+    policy action "volume_extension:quota_classes".
diff --git a/releasenotes/notes/admin-only-identity-v2-admin-6f382e38d7a690a4.yaml b/releasenotes/notes/admin-only-identity-v2-admin-6f382e38d7a690a4.yaml
new file mode 100644
index 0000000..750a9f1
--- /dev/null
+++ b/releasenotes/notes/admin-only-identity-v2-admin-6f382e38d7a690a4.yaml
@@ -0,0 +1,12 @@
+---
+fixes:
+  - |
+    Removed ``rule`` kwarg from ``rbac_rule_validation`` decorator for identity
+    v2 admin tests, because the identity v2 admin API does not do policy
+    enforcement, and instead checks whether the request object has
+    ``context_is_admin``.
+other:
+  - |
+    Updated the class names for identity v2 tests to include the "Admin"
+    substring, to convey the fact that these tests are only intended
+    to test the v2 admin API, not the v2 API.
diff --git a/releasenotes/notes/config-opts-paths-01e2a5096a1579b8.yaml b/releasenotes/notes/config-opts-paths-01e2a5096a1579b8.yaml
new file mode 100644
index 0000000..3e63c9d
--- /dev/null
+++ b/releasenotes/notes/config-opts-paths-01e2a5096a1579b8.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Refactored framework to remove unused "path"
+    argument. Added config options to allow the path
+    to the policy.json files for Nova, Keystone, Cinder,
+    Neutron, and Glance to be configured without needing
+    to manually change code.
diff --git a/releasenotes/notes/encryption-types-c9a2d9a3c1996da4.yaml b/releasenotes/notes/encryption-types-c9a2d9a3c1996da4.yaml
new file mode 100644
index 0000000..7b6ae0c
--- /dev/null
+++ b/releasenotes/notes/encryption-types-c9a2d9a3c1996da4.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Adds RBAC tests for the encryption types client.
diff --git a/releasenotes/notes/endpoint-filter-projects-7f64c88659ef0c30.yaml b/releasenotes/notes/endpoint-filter-projects-7f64c88659ef0c30.yaml
new file mode 100644
index 0000000..1537618
--- /dev/null
+++ b/releasenotes/notes/endpoint-filter-projects-7f64c88659ef0c30.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Adds RBAC tests for the project-related endpoints belonging to the
+    OS-EP-FILTER Keystone v3 extension API.
diff --git a/releasenotes/notes/merge-rbac-auth-with-rbac-rule-validation-5d7c286788a95ee9.yaml b/releasenotes/notes/merge-rbac-auth-with-rbac-rule-validation-5d7c286788a95ee9.yaml
new file mode 100644
index 0000000..b96c73a
--- /dev/null
+++ b/releasenotes/notes/merge-rbac-auth-with-rbac-rule-validation-5d7c286788a95ee9.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Merges `rbac_auth` with `rbac_rule_validation`, because `rbac_auth`
+    decentralized logic from `rbac_rule_validation` without providing any
+    authentication-related utility. This change facilitates code maintenance
+    and code readability.
diff --git a/releasenotes/notes/volume-services-rbac-test-57e69f9952c8746e.yaml b/releasenotes/notes/volume-services-rbac-test-57e69f9952c8746e.yaml
new file mode 100644
index 0000000..5b6f0cd
--- /dev/null
+++ b/releasenotes/notes/volume-services-rbac-test-57e69f9952c8746e.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Added RBAC test for the volume services API, which covers the following
+    policy action: "volume_extension:services:index".
diff --git a/releasenotes/notes/volumes-client-tests-d697a4a75d3e1405.yaml b/releasenotes/notes/volumes-client-tests-d697a4a75d3e1405.yaml
new file mode 100644
index 0000000..5dc9ff3
--- /dev/null
+++ b/releasenotes/notes/volumes-client-tests-d697a4a75d3e1405.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - Added tests for volumes client functions set
+    bootable, reserve, unreserve, and update metadata.
+other:
+  - Renamed update metadata item and delete metadata
+    item tests to accurately reflect what actions are
+    being performed.
diff --git a/requirements.txt b/requirements.txt
index ffc6abe..6d2c7f1 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,7 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-
+hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
 pbr>=1.8 # Apache-2.0
 urllib3>=1.15.1 # MIT
 oslo.log>=3.11.0 # Apache-2.0
diff --git a/tox.ini b/tox.ini
index a004c6e..be35509 100644
--- a/tox.ini
+++ b/tox.ini
@@ -59,3 +59,6 @@
 ignore = E123,E125
 builtins = _
 exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
+
+[hacking]
+local-check-factory = patrole_tempest_plugin.hacking.checks.factory