Merge "Remove duplicate test_unmanage_volume test"
diff --git a/.zuul.yaml b/.zuul.yaml
index 2619ed7..1eab464 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -1,7 +1,7 @@
 - job:
     name: patrole-base
     parent: devstack-tempest
-    description: Patrole base job for admin and Member roles.
+    description: Patrole base job for admin and member roles.
     required-projects:
       - name: openstack/tempest
       - name: openstack/patrole
@@ -54,7 +54,7 @@
 - job:
     name: patrole-member
     parent: patrole-base
-    description: Patrole job for Member role.
+    description: Patrole job for member role.
     # This currently works from stable/pike onward.
     branches:
       - master
@@ -62,7 +62,7 @@
       - stable/pike
     vars:
       devstack_localrc:
-        RBAC_TEST_ROLE: Member
+        RBAC_TEST_ROLE: member
 
 - job:
     name: patrole-member-queens
@@ -93,12 +93,12 @@
 - job:
     name: patrole-py35-member
     parent: patrole-base
-    description: Patrole py3 job for Member role.
+    description: Patrole py35 job for member role.
     vars:
       devstack_localrc:
-        # Use Member for py3 because arguably negative testing is more
+        # Use member for py35 because arguably negative testing is more
         # important than admin, which is already covered by patrole-admin job.
-        RBAC_TEST_ROLE: Member
+        RBAC_TEST_ROLE: member
         USE_PYTHON3: true
       devstack_services:
         s-account: false
diff --git a/devstack/plugin.sh b/devstack/plugin.sh
index d56c963..bd0068b 100644
--- a/devstack/plugin.sh
+++ b/devstack/plugin.sh
@@ -13,16 +13,13 @@
 function install_patrole_tempest_plugin {
     setup_package $PATROLE_DIR -e
 
-    if [[ "$RBAC_TEST_ROLE" == "member" ]]; then
-        RBAC_TEST_ROLE="Member"
-    fi
-
-    iniset $TEMPEST_CONFIG patrole enable_rbac True
-    iniset $TEMPEST_CONFIG patrole rbac_test_role $RBAC_TEST_ROLE
-
     if [[ ${DEVSTACK_SERIES} == 'pike' ]]; then
+        if [[ "$RBAC_TEST_ROLE" == "member" ]]; then
+            RBAC_TEST_ROLE="Member"
+        fi
+
         # Policies used by Patrole testing that were changed in a backwards-incompatible way.
-        # TODO(fmontei): Remove these once stable/pike becomes EOL.
+        # TODO(felipemonteiro): Remove these once stable/pike becomes EOL.
         iniset $TEMPEST_CONFIG policy-feature-enabled create_port_fixed_ips_ip_address_policy False
         iniset $TEMPEST_CONFIG policy-feature-enabled update_port_fixed_ips_ip_address_policy False
         iniset $TEMPEST_CONFIG policy-feature-enabled limits_extension_used_limits_policy False
@@ -30,6 +27,15 @@
         iniset $TEMPEST_CONFIG policy-feature-enabled volume_extension_volume_actions_reserve_policy False
         iniset $TEMPEST_CONFIG policy-feature-enabled volume_extension_volume_actions_unreserve_policy False
     fi
+
+    if [[ ${DEVSTACK_SERIES} == 'queens' ]]; then
+        if [[ "$RBAC_TEST_ROLE" == "member" ]]; then
+            RBAC_TEST_ROLE="Member"
+        fi
+    fi
+
+    iniset $TEMPEST_CONFIG patrole enable_rbac True
+    iniset $TEMPEST_CONFIG patrole rbac_test_role $RBAC_TEST_ROLE
 }
 
 if is_service_enabled tempest; then
diff --git a/doc/source/framework/policy_authority.rst b/doc/source/framework/policy_authority.rst
index 7cd4421..822c7b6 100644
--- a/doc/source/framework/policy_authority.rst
+++ b/doc/source/framework/policy_authority.rst
@@ -9,7 +9,8 @@
 This module is only called for calculating the "Expected" result if
 ``[patrole] test_custom_requirements`` is ``False``.
 
-Using the Policy Authority Module, policy verification is performed by:
+Using the :class:`~patrole_tempest_plugin.policy_authority.PolicyAuthority`
+class, policy verification is performed by:
 
 #. Pooling together the default `in-code` policy rules.
 #. Overriding the defaults with custom policy rules located in a policy.json,
@@ -22,9 +23,40 @@
 #. Performing a call with all necessary data to ``oslo.policy`` and returning
    the expected result back to ``rbac_rule_validation`` decorator.
 
+When to use
+-----------
+
+This :class:`~patrole_tempest_plugin.rbac_authority.RbacAuthority` class
+can be used to validate the default OpenStack policy configuration. It
+is recommended that this approach be used for RBAC validation for clouds that
+use little to no policy customizations or overrides.
+
+This validation approach should be used when:
+
+* Validating the out-of-the-box policy-in-code OpenStack policy configuration.
+
+  It is important that the default OpenStack policy configuration be validated
+  before deploying OpenStack into production. Bugs exist in software and the
+  earlier they can be caught and prevented (via CI/CD, for example), the
+  better. Patrole continues to be used to identify default policy bugs
+  across OpenStack services.
+
+* Validating policy reliably and accurately.
+
+  Relying on ``oslo.policy`` to compute the expected test results provides
+  accurate tests, without the hassle of having to reinvent the wheel. Since
+  OpenStack APIs use ``oslo.policy`` for policy enforcement, it makes sense
+  to compute expected results by using the same library, ensuring test
+  reliability.
+
+* Continuously validating policy changes to OpenStack projects under
+  development by gating them against Patrole CI/CD jobs run by `Zuul`_.
+
+.. _Zuul: https://docs.openstack.org/infra/zuul/
+
 Implementation
 --------------
 
 .. automodule:: patrole_tempest_plugin.policy_authority
    :members:
-   :special-members:
+   :undoc-members:
diff --git a/doc/source/framework/rbac_authority.rst b/doc/source/framework/rbac_authority.rst
new file mode 100644
index 0000000..84c372b
--- /dev/null
+++ b/doc/source/framework/rbac_authority.rst
@@ -0,0 +1,37 @@
+.. rbac-authority:
+
+RBAC Authority Module
+=====================
+
+Overview
+--------
+
+This module implements an abstract class that is implemented by the classes
+below. Each implementation is used by the :ref:`rbac-validation` framework
+to determine each expected test result.
+
+:ref:`policy-authority`
+-----------------------
+
+The *default* :class:`~patrole_tempest_plugin.rbac_authority.RbacAuthority`
+implementation class which is used for policy validation. Uses ``oslo.policy``
+to determine the expected test result.
+
+All Patrole `Zuul`_ gates use this
+:class:`~patrole_tempest_plugin.rbac_authority.RbacAuthority` class by default.
+
+.. _Zuul: https://docs.openstack.org/infra/zuul/
+
+:ref:`requirements-authority`
+-----------------------------
+
+Optional :class:`~patrole_tempest_plugin.rbac_authority.RbacAuthority`
+implementation class which is used for policy validation. It uses a high-level
+requirements-driven approach to validating RBAC in Patrole.
+
+Implementation
+--------------
+
+.. automodule:: patrole_tempest_plugin.rbac_authority
+   :members:
+   :undoc-members:
diff --git a/doc/source/framework/requirements_authority.rst b/doc/source/framework/requirements_authority.rst
new file mode 100644
index 0000000..6c4fcc0
--- /dev/null
+++ b/doc/source/framework/requirements_authority.rst
@@ -0,0 +1,105 @@
+.. _requirements-authority:
+
+Requirements Authority Module
+=============================
+
+Overview
+--------
+
+Requirements-driven approach to declaring the expected RBAC test results
+referenced by Patrole. Uses a high-level YAML syntax to crystallize policy
+requirements concisely and unambiguously.
+
+.. note::
+
+    The :ref:`custom-requirements-file` is required to use this validation
+    approach and, currently, must be manually generated.
+
+This validation approach can be toggled on by setting the
+``[patrole].test_custom_requirements`` configuration option to ``True``;
+see :ref:`patrole-configuration` for more information.
+
+When to use
+-----------
+
+This :class:`~patrole_tempest_plugin.rbac_authority.RbacAuthority` class
+can be used to achieve a requirements-driven approach to validating an
+OpenStack cloud's RBAC implementation. Using this approach, Patrole computes
+expected test results by performing lookups against a
+:ref:`custom-requirements-file` which precisely defines the cloud's RBAC
+requirements.
+
+Using a high-level declarative language, the requirements are captured
+unambiguously in the :ref:`custom-requirements-file`, allowing operators to
+validate their requirements against their OpenStack cloud.
+
+This validation approach should be used when:
+
+* The cloud has heavily customized policy files that require careful validation
+  against one's requirements.
+
+  Heavily customized policy files can contain relatively nuanced/technical
+  syntax that impinges upon the goal of using a clear and concise syntax
+  present in the :ref:`custom-requirements-file` to drive RBAC validation.
+
+* The cloud has non-OpenStack services that require RBAC validation but which
+  don't leverage the ``oslo.policy`` framework.
+
+  Services like `Contrail`_ that are present in an OpenStack-based cloud that
+  interface with OpenStack services like Neutron also require RBAC validation.
+  The requirements-driven approach to RBAC validation is framework-agnostic
+  and so can work with any policy engine.
+
+* Expected results are captured as clear-cut, unambiguous requirements.
+
+  Validating a cloud's RBAC against high-level, clear-cut requirements is
+  a valid use case. Relying on ``oslo.policy`` validating customized policy
+  files is not sufficient to satisfy this use case.
+
+As mentioned above, the trade-off with this approach is having to manually
+generate the :ref:`custom-requirements-file`. There is currently no
+tooling to automatically do this.
+
+.. _Contrail: https://github.com/Juniper/contrail-controller/wiki/RBAC
+
+.. _custom-requirements-file:
+
+Custom Requirements File
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+File path of the YAML file that defines your RBAC requirements. This
+file must be located on the same host that Patrole runs on. The YAML
+file should be written as follows:
+
+.. code-block:: yaml
+
+    <service_foo>:
+      <api_action_a>:
+        - <allowed_role_1>
+        - <allowed_role_2>
+        - <allowed_role_3>
+      <api_action_b>:
+        - <allowed_role_2>
+        - <allowed_role_4>
+    <service_bar>:
+      <api_action_c>:
+        - <allowed_role_3>
+
+Where:
+
+service = the service that is being tested (Cinder, Nova, etc.).
+
+api_action = the policy action that is being tested. Examples:
+
+* volume:create
+* os_compute_api:servers:start
+* add_image
+
+allowed_role = the ``oslo.policy`` role that is allowed to perform the API.
+
+Implementation
+--------------
+
+.. automodule:: patrole_tempest_plugin.requirements_authority
+   :members:
+   :undoc-members:
diff --git a/doc/source/index.rst b/doc/source/index.rst
index d964845..8368262 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -47,7 +47,9 @@
 
    framework/overview
    framework/rbac_validation
+   framework/rbac_authority
    framework/policy_authority
+   framework/requirements_authority
    framework/rbac_utils
 
 Indices and tables
diff --git a/etc/patrole.conf.sample b/etc/patrole.conf.sample
index 518d38a..8e7931b 100644
--- a/etc/patrole.conf.sample
+++ b/etc/patrole.conf.sample
@@ -29,9 +29,10 @@
 #
 # This option determines whether Patrole should run against a
 # ``custom_requirements_file`` which defines RBAC requirements. The
-# purpose of setting this flag to True is to verify that RBAC policy
+# purpose of setting this flag to ``True`` is to verify that RBAC
+# policy
 # is in accordance to requirements. The idea is that the
-# ``custom_requirements_file`` perfectly defines what the RBAC
+# ``custom_requirements_file`` precisely defines what the RBAC
 # requirements are.
 #
 # Here are the possible outcomes when running the Patrole tests
@@ -57,28 +58,32 @@
 # file must be located on the same host that Patrole runs on. The YAML
 # file should be written as follows:
 #
-# ```
-# <service_foo>:
-#   <api_action_x>:
-#     - <allowed_role_a>
-#     - <allowed_role_b>
-#     - <allowed_role_c>
-#   <api_action_y>:
-#     - <allowed_role_d>
-#     - <allowed_role_e>
-# <service_bar>:
-#   <api_action_z>:
-#     - <allowed_role_b>
-# ```
+# .. code-block:: yaml
+#
+#     <service_foo>:
+#       <api_action_a>:
+#         - <allowed_role_1>
+#         - <allowed_role_2>
+#         - <allowed_role_3>
+#       <api_action_b>:
+#         - <allowed_role_2>
+#         - <allowed_role_4>
+#     <service_bar>:
+#       <api_action_c>:
+#         - <allowed_role_3>
 #
 # Where:
 #
-# service = the service that is being tested (Cinder, Nova, etc.)
+# service = the service that is being tested (Cinder, Nova, etc.).
+#
 # api_action = the policy action that is being tested. Examples:
-#              - volume:create
-#              - os_compute_api:servers:start
-#              - add_image
-# allowed_role = the Keystone role that is allowed to perform the API.
+#
+# * volume:create
+# * os_compute_api:servers:start
+# * add_image
+#
+# allowed_role = the ``oslo.policy`` role that is allowed to perform
+# the API.
 #  (string value)
 #custom_requirements_file = <None>
 
diff --git a/patrole_tempest_plugin/README.rst b/patrole_tempest_plugin/README.rst
deleted file mode 100644
index d678422..0000000
--- a/patrole_tempest_plugin/README.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-==============================
-Tempest Integration of Patrole
-==============================
-
-This directory contains Tempest tests to cover the Patrole project.
diff --git a/patrole_tempest_plugin/config.py b/patrole_tempest_plugin/config.py
index f379859..ee7a6c5 100644
--- a/patrole_tempest_plugin/config.py
+++ b/patrole_tempest_plugin/config.py
@@ -42,9 +42,9 @@
                 help="""
 This option determines whether Patrole should run against a
 ``custom_requirements_file`` which defines RBAC requirements. The
-purpose of setting this flag to True is to verify that RBAC policy
+purpose of setting this flag to ``True`` is to verify that RBAC policy
 is in accordance to requirements. The idea is that the
-``custom_requirements_file`` perfectly defines what the RBAC requirements are.
+``custom_requirements_file`` precisely defines what the RBAC requirements are.
 
 Here are the possible outcomes when running the Patrole tests against
 a ``custom_requirements_file``:
@@ -67,28 +67,31 @@
 file must be located on the same host that Patrole runs on. The YAML
 file should be written as follows:
 
-```
-<service_foo>:
-  <api_action_x>:
-    - <allowed_role_a>
-    - <allowed_role_b>
-    - <allowed_role_c>
-  <api_action_y>:
-    - <allowed_role_d>
-    - <allowed_role_e>
-<service_bar>:
-  <api_action_z>:
-    - <allowed_role_b>
-```
+.. code-block:: yaml
+
+    <service_foo>:
+      <api_action_a>:
+        - <allowed_role_1>
+        - <allowed_role_2>
+        - <allowed_role_3>
+      <api_action_b>:
+        - <allowed_role_2>
+        - <allowed_role_4>
+    <service_bar>:
+      <api_action_c>:
+        - <allowed_role_3>
 
 Where:
 
-service = the service that is being tested (Cinder, Nova, etc.)
+service = the service that is being tested (Cinder, Nova, etc.).
+
 api_action = the policy action that is being tested. Examples:
-             - volume:create
-             - os_compute_api:servers:start
-             - add_image
-allowed_role = the Keystone role that is allowed to perform the API.
+
+* volume:create
+* os_compute_api:servers:start
+* add_image
+
+allowed_role = the ``oslo.policy`` role that is allowed to perform the API.
 """)
 ]
 
diff --git a/patrole_tempest_plugin/policy_authority.py b/patrole_tempest_plugin/policy_authority.py
index 9499bf6..b813f88 100644
--- a/patrole_tempest_plugin/policy_authority.py
+++ b/patrole_tempest_plugin/policy_authority.py
@@ -156,9 +156,9 @@
     def allowed(self, rule_name, role):
         """Checks if a given rule in a policy is allowed with given role.
 
-        :param string rule_name: Rule to be checked using ``oslo.policy``.
-        :param bool is_admin: Whether admin context is used.
-        :raises RbacParsingException: If `rule_name`` does not exist in the
+        :param string rule_name: Policy name to pass to``oslo.policy``.
+        :param string role: Role to validate for authorization.
+        :raises RbacParsingException: If ``rule_name`` does not exist in the
             cloud (in policy file or among registered in-code policy defaults).
         """
         is_admin_context = self._is_admin_context(role)
diff --git a/patrole_tempest_plugin/rbac_utils.py b/patrole_tempest_plugin/rbac_utils.py
index 2ef88ca..6c40aa1 100644
--- a/patrole_tempest_plugin/rbac_utils.py
+++ b/patrole_tempest_plugin/rbac_utils.py
@@ -147,18 +147,25 @@
             test_obj.os_primary.auth_provider.set_auth()
 
     def _get_roles_by_name(self):
-        available_roles = self.admin_roles_client.list_roles()
-        admin_role_id = rbac_role_id = None
+        available_roles = self.admin_roles_client.list_roles()['roles']
+        role_map = {r['name']: r['id'] for r in available_roles}
+        LOG.debug('Available roles: %s', list(role_map.keys()))
 
-        for role in available_roles['roles']:
-            if role['name'] == CONF.patrole.rbac_test_role:
-                rbac_role_id = role['id']
-            if role['name'] == CONF.identity.admin_role:
-                admin_role_id = role['id']
+        admin_role_id = role_map.get(CONF.identity.admin_role)
+        rbac_role_id = role_map.get(CONF.patrole.rbac_test_role)
 
         if not all([admin_role_id, rbac_role_id]):
-            msg = ("Roles defined by `[patrole] rbac_test_role` and "
-                   "`[identity] admin_role` must be defined in the system.")
+            missing_roles = []
+            msg = ("Could not find `[patrole] rbac_test_role` or "
+                   "`[identity] admin_role`, both of which are required for "
+                   "RBAC testing.")
+            if not admin_role_id:
+                missing_roles.append(CONF.identity.admin_role)
+            if not rbac_role_id:
+                missing_roles.append(CONF.patrole.rbac_test_role)
+            msg += " Following roles were not found: %s." % (
+                ", ".join(missing_roles))
+            msg += " Available roles: %s." % ", ".join(list(role_map.keys()))
             raise rbac_exceptions.RbacResourceSetupFailed(msg)
 
         self.admin_role_id = admin_role_id
@@ -226,4 +233,6 @@
 
     :returns: True if ``rbac_test_role`` is the admin role.
     """
+    # TODO(felipemonteiro): Make this more robust via a context is admin
+    # lookup.
     return CONF.patrole.rbac_test_role == CONF.identity.admin_role
diff --git a/patrole_tempest_plugin/requirements_authority.py b/patrole_tempest_plugin/requirements_authority.py
index 683a7eb..75df9f4 100644
--- a/patrole_tempest_plugin/requirements_authority.py
+++ b/patrole_tempest_plugin/requirements_authority.py
@@ -16,14 +16,17 @@
 
 from oslo_log import log as logging
 
+from tempest import config
 from tempest.lib import exceptions
 
 from patrole_tempest_plugin.rbac_authority import RbacAuthority
 
+CONF = config.CONF
 LOG = logging.getLogger(__name__)
 
 
 class RequirementsParser(object):
+    """A class that parses a custom requirements file."""
     _inner = None
 
     class Inner(object):
@@ -40,6 +43,27 @@
 
     @staticmethod
     def parse(component):
+        """Parses a requirements file with the following format:
+
+        .. code-block:: yaml
+
+            <service_foo>:
+              <api_action_a>:
+                - <allowed_role_1>
+                - <allowed_role_2>
+                - <allowed_role_3>
+              <api_action_b>:
+                - <allowed_role_2>
+                - <allowed_role_4>
+            <service_bar>:
+              <api_action_c>:
+                - <allowed_role_3>
+
+        :param str component: Name of the OpenStack service to be validated.
+        :returns: The dictionary that maps each policy action to the list
+            of allowed roles, for the given ``component``.
+        :rtype: dict
+        """
         try:
             for section in RequirementsParser.Inner._rbac_map:
                 if component in section:
@@ -51,13 +75,39 @@
 
 
 class RequirementsAuthority(RbacAuthority):
+    """A class that uses a custom requirements file to validate RBAC."""
+
     def __init__(self, filepath=None, component=None):
-        if filepath is not None and component is not None:
+        """This class can be used to achieve a requirements-driven approach to
+        validating an OpenStack cloud's RBAC implementation. Using this
+        approach, Patrole computes expected test results by performing lookups
+        against a custom requirements file which precisely defines the cloud's
+        RBAC requirements.
+
+        :param str filepath: Path where the custom requirements file lives.
+            Defaults to ``[patrole].custom_requirements_file``.
+        :param str component: Name of the OpenStack service to be validated.
+        """
+        filepath = filepath or CONF.patrole.custom_requirements_file
+
+        if component is not None:
             self.roles_dict = RequirementsParser(filepath).parse(component)
         else:
             self.roles_dict = None
 
     def allowed(self, rule_name, role):
+        """Checks if a given rule in a policy is allowed with given role.
+
+        :param string rule_name: Rule to be checked using provided requirements
+            file specified by ``[patrole].custom_requirements_file``. Must be
+            a key present in this file, under the appropriate component.
+        :param string role: Role to validate against custom requirements file.
+        :returns: True if ``role`` is allowed to perform ``rule_name``, else
+            False.
+        :rtype: bool
+        :raises KeyError: If ``rule_name`` does not exist among the keyed
+            policy names in the custom requirements file.
+        """
         if self.roles_dict is None:
             raise exceptions.InvalidConfiguration(
                 "Roles dictionary parsed from requirements YAML file is "
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 adb5a6c..1fe52e9 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
@@ -33,6 +33,14 @@
 
 class ServerActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
+    # admin credentials used for waiters which invokes a show API call
+    credentials = ['primary', 'admin']
+
+    @classmethod
+    def setup_clients(cls):
+        super(ServerActionsRbacTest, cls).setup_clients()
+        cls.admin_servers_client = cls.os_admin.servers_client
+
     @classmethod
     def resource_setup(cls):
         super(ServerActionsRbacTest, cls).resource_setup()
@@ -58,17 +66,24 @@
     def _stop_server(self):
         self.servers_client.stop_server(self.server_id)
         waiters.wait_for_server_status(
-            self.servers_client, self.server_id, 'SHUTOFF')
+            self.admin_servers_client, self.server_id, 'SHUTOFF')
 
     def _resize_server(self, flavor):
+        status = self.admin_servers_client. \
+            show_server(self.server_id)['server']['status']
+        if status == 'RESIZED':
+            return
         self.servers_client.resize_server(self.server_id, flavor)
         waiters.wait_for_server_status(
-            self.servers_client, self.server_id, 'VERIFY_RESIZE')
+            self.admin_servers_client, self.server_id, 'VERIFY_RESIZE')
 
     def _confirm_resize_server(self):
-        self.servers_client.confirm_resize_server(self.server_id)
+        status = self.admin_servers_client. \
+            show_server(self.server_id)['server']['status']
+        if status == 'VERIFY_RESIZE':
+            self.servers_client.confirm_resize_server(self.server_id)
         waiters.wait_for_server_status(
-            self.servers_client, self.server_id, 'ACTIVE')
+            self.admin_servers_client, self.server_id, 'ACTIVE')
 
     def _shelve_server(self):
         self.servers_client.shelve_server(self.server_id)
@@ -77,12 +92,13 @@
                         self.server_id)
         offload_time = CONF.compute.shelved_offload_time
         if offload_time >= 0:
-            waiters.wait_for_server_status(self.servers_client,
+            waiters.wait_for_server_status(self.admin_servers_client,
                                            self.server_id,
                                            'SHELVED_OFFLOADED',
                                            extra_timeout=offload_time)
         else:
-            waiters.wait_for_server_status(self.servers_client, self.server_id,
+            waiters.wait_for_server_status(self.admin_servers_client,
+                                           self.server_id,
                                            'SHELVED')
 
     def _pause_server(self):
@@ -91,7 +107,7 @@
                         self.servers_client.unpause_server,
                         self.server_id)
         waiters.wait_for_server_status(
-            self.servers_client, self.server_id, 'PAUSED')
+            self.admin_servers_client, self.server_id, 'PAUSED')
 
     def _cleanup_server_actions(self, function, server_id, **kwargs):
         server = self.servers_client.show_server(server_id)['server']
@@ -179,6 +195,7 @@
         self._resize_server(self.flavor_ref_alt)
         self.addCleanup(self._confirm_resize_server)
         self.addCleanup(self._resize_server, self.flavor_ref)
+        self.addCleanup(self._confirm_resize_server)
 
         with self.rbac_utils.override_role(self):
             self._confirm_resize_server()
@@ -276,6 +293,7 @@
             self.compute_images_client.delete_image, image['id'])
 
     @decorators.idempotent_id('9fdd4630-731c-4f7c-bce5-69fa3792c52a')
+    @decorators.attr(type='slow')
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
                           'Snapshotting not available, backup not possible.')
     @utils.services('image')
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py
index 58a829d..13faca1 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py
@@ -570,8 +570,7 @@
     @classmethod
     def resource_setup(cls):
         def _cleanup_ports(network_id):
-            ports = cls.ports_client.\
-                list_ports(network_id=network_id)['ports']
+            ports = cls.ports_client.list_ports(network_id=network_id)['ports']
             for port in ports:
                 test_utils.call_and_ignore_notfound_exc(
                     cls.ports_client.delete_port,
@@ -666,6 +665,9 @@
             self.interfaces_client, self.server['id'],
             interface['port_id'], 'ACTIVE')
         self.addCleanup(
+            waiters.wait_for_interface_detach, self.interfaces_client,
+            self.server['id'], interface['port_id'])
+        self.addCleanup(
             test_utils.call_and_ignore_notfound_exc,
             self.interfaces_client.delete_interface,
             self.server['id'], interface['port_id'])
@@ -684,6 +686,8 @@
         with self.rbac_utils.override_role(self):
             self.interfaces_client.delete_interface(self.server['id'],
                                                     interface['port_id'])
+        waiters.wait_for_interface_detach(
+            self.interfaces_client, self.server['id'], interface['port_id'])
 
     @decorators.idempotent_id('6886d360-0d86-4760-b1a3-882d81fbebcc')
     @utils.requires_ext(extension='os-ips', service='compute')
@@ -723,47 +727,16 @@
         if interfaces:
             network_id = interfaces[0]['net_id']
         else:
-            network_id = self.interfaces_client.create_interface(
-                self.server['id'])['interfaceAttachment']['net_id']
+            interface = self.interfaces_client.create_interface(
+                self.server['id'])['interfaceAttachment']
+            network_id = interface['net_id']
+            self.addCleanup(
+                waiters.wait_for_interface_detach, self.interfaces_client,
+                self.server['id'], interface['port_id'])
+            self.addCleanup(
+                self.interfaces_client.delete_interface,
+                self.server['id'], interface['port_id'])
 
         with self.rbac_utils.override_role(self):
             self.servers_client.add_fixed_ip(self.server['id'],
                                              networkId=network_id)
-
-
-class VirtualInterfacesRbacTest(rbac_base.BaseV2ComputeRbacTest):
-    # The compute os-virtual-interfaces API is deprecated from the Microversion
-    # 2.44 onward. For more information, see:
-    # https://developer.openstack.org/api-ref/compute/#servers-virtual-interfaces-servers-os-virtual-interfaces-deprecated
-    max_microversion = '2.43'
-
-    @classmethod
-    def setup_credentials(cls):
-        # This test needs a network and a subnet
-        cls.set_network_resources(network=True, subnet=True)
-        super(VirtualInterfacesRbacTest, cls).setup_credentials()
-
-    @classmethod
-    def resource_setup(cls):
-        super(VirtualInterfacesRbacTest, cls).resource_setup()
-        cls.server = cls.create_test_server(wait_until='ACTIVE')
-
-    @rbac_rule_validation.action(
-        service="nova",
-        rule="os_compute_api:os-virtual-interfaces")
-    @decorators.idempotent_id('fc719ae3-0f73-4689-8378-1b841f0f2818')
-    def test_list_virtual_interfaces(self):
-        """Test list virtual interfaces, part of os-virtual-interfaces.
-
-        If Neutron is available, then call the API and expect it to fail
-        with a 400 BadRequest (policy enforcement is done before that happens).
-        """
-        with self.rbac_utils.override_role(self):
-            if CONF.service_available.neutron:
-                msg = ("Listing virtual interfaces is not supported by this "
-                       "cloud.")
-                with self.assertRaisesRegex(lib_exc.BadRequest, msg):
-                    self.servers_client.list_virtual_interfaces(
-                        self.server['id'])
-            else:
-                self.servers_client.list_virtual_interfaces(self.server['id'])
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 b803fe3..a510d1e 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
@@ -25,6 +25,10 @@
 CONF = config.CONF
 
 
+# FIXME(felipemonteiro): `@decorators.attr(type='slow')` are added to tests
+# below to in effect cause the tests to be non-voting in Zuul due to a high
+# rate of spurious failures related to volume attachments. This will be
+# revisited at a later date.
 class ServerVolumeAttachmentRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
     @classmethod
@@ -53,6 +57,7 @@
         with self.rbac_utils.override_role(self):
             self.servers_client.list_volume_attachments(self.server['id'])
 
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="nova",
         rule="os_compute_api:os-volumes-attachments:create")
@@ -61,6 +66,7 @@
         with self.rbac_utils.override_role(self):
             self.attach_volume(self.server, self.volume)
 
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="nova",
         rule="os_compute_api:os-volumes-attachments:show")
diff --git a/patrole_tempest_plugin/tests/api/compute/test_virtual_interfaces_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_virtual_interfaces_rbac.py
new file mode 100644
index 0000000..ae77a34
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/compute/test_virtual_interfaces_rbac.py
@@ -0,0 +1,64 @@
+#    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 import config
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.compute import rbac_base
+
+CONF = config.CONF
+
+
+# TODO(rb560u): Remove this test class once the nova queens branch goes into
+# extended maintenance mode.
+class VirtualInterfacesRbacTest(rbac_base.BaseV2ComputeRbacTest):
+    # The compute os-virtual-interfaces API is deprecated from the Microversion
+    # 2.44 onward. For more information, see:
+    # https://developer.openstack.org/api-ref/compute/#servers-virtual-interfaces-servers-os-virtual-interfaces-deprecated
+    depends_on_nova_network = True
+    max_microversion = '2.43'
+
+    @classmethod
+    def setup_credentials(cls):
+        # This test needs a network and a subnet
+        cls.set_network_resources(network=True, subnet=True)
+        super(VirtualInterfacesRbacTest, cls).setup_credentials()
+
+    @classmethod
+    def resource_setup(cls):
+        super(VirtualInterfacesRbacTest, cls).resource_setup()
+        cls.server = cls.create_test_server(wait_until='ACTIVE')
+
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-virtual-interfaces")
+    @decorators.idempotent_id('fc719ae3-0f73-4689-8378-1b841f0f2818')
+    def test_list_virtual_interfaces(self):
+        """Test list virtual interfaces, part of os-virtual-interfaces.
+
+        If Neutron is available, then call the API and expect it to fail
+        with a 400 BadRequest (policy enforcement is done before that happens).
+        """
+        with self.rbac_utils.override_role(self):
+            if CONF.service_available.neutron:
+                msg = ("Listing virtual interfaces is not supported by this "
+                       "cloud.")
+                with self.assertRaisesRegex(lib_exc.BadRequest, msg):
+                    self.servers_client.list_virtual_interfaces(
+                        self.server['id'])
+            else:
+                self.servers_client.list_virtual_interfaces(self.server['id'])
diff --git a/patrole_tempest_plugin/tests/api/identity/rbac_base.py b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
index 90fa6aa..91b3d1e 100644
--- a/patrole_tempest_plugin/tests/api/identity/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
@@ -118,6 +118,8 @@
     @classmethod
     def setup_clients(cls):
         super(BaseIdentityV3RbacTest, cls).setup_clients()
+        cls.application_credentials_client = \
+            cls.os_primary.application_credentials_client
         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
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_application_credentials_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_application_credentials_rbac.py
new file mode 100644
index 0000000..c7a6033
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_application_credentials_rbac.py
@@ -0,0 +1,85 @@
+# Copyright 2018 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 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 import rbac_base
+
+
+CONF = config.CONF
+
+
+class ApplicationCredentialsV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(ApplicationCredentialsV3RbacTest, cls).skip_checks()
+        if not CONF.identity_feature_enabled.application_credentials:
+            raise cls.skipException("Application credentials are not available"
+                                    " in this environment")
+
+    @classmethod
+    def resource_setup(cls):
+        super(ApplicationCredentialsV3RbacTest, cls).resource_setup()
+        cls.user_id = cls.os_primary.credentials.user_id
+
+    def _create_application_credential(self, name=None, **kwargs):
+        name = name or data_utils.rand_name('application_credential')
+        application_credential = (
+            self.application_credentials_client.create_application_credential(
+                self.user_id, name=name, **kwargs))['application_credential']
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.application_credentials_client.delete_application_credential,
+            self.user_id,
+            application_credential['id'])
+        return application_credential
+
+    @decorators.idempotent_id('b53bee14-e9df-4929-b257-6def76c12e4d')
+    @rbac_rule_validation.action(service="keystone",
+                                 rule="identity:create_application_credential")
+    def test_create_application_credential(self):
+        with self.rbac_utils.override_role(self):
+            self._create_application_credential()
+
+    @decorators.idempotent_id('58b3c3a0-5ad0-44f7-8da7-0736f71f7168')
+    @rbac_rule_validation.action(service="keystone",
+                                 rule="identity:list_application_credentials")
+    def test_list_application_credentials(self):
+        with self.rbac_utils.override_role(self):
+            self.application_credentials_client.list_application_credentials(
+                user_id=self.user_id)
+
+    @decorators.idempotent_id('d7b13968-a8a6-47fd-8e1d-7cc7f565c7f8')
+    @rbac_rule_validation.action(service="keystone",
+                                 rule="identity:get_application_credential")
+    def test_show_application_credential(self):
+        app_cred = self._create_application_credential()
+        with self.rbac_utils.override_role(self):
+            self.application_credentials_client.show_application_credential(
+                user_id=self.user_id, application_credential_id=app_cred['id'])
+
+    @decorators.idempotent_id('521b7c0f-1dd5-47a6-ae95-95c0323d7735')
+    @rbac_rule_validation.action(service="keystone",
+                                 rule="identity:delete_application_credential")
+    def test_delete_application_credential(self):
+        app_cred = self._create_application_credential()
+        with self.rbac_utils.override_role(self):
+            self.application_credentials_client.delete_application_credential(
+                user_id=self.user_id, application_credential_id=app_cred['id'])
diff --git a/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py b/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py
index 84ce2c7..932683d 100644
--- a/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py
@@ -294,63 +294,6 @@
         with self.rbac_utils.override_role(self):
             self.networks_client.delete_network(network['id'])
 
-    @rbac_rule_validation.action(service="neutron",
-                                 rule="create_subnet")
-    @decorators.idempotent_id('44f42aaf-8a9a-4678-868a-b8fe82689554')
-    def test_create_subnet(self):
-
-        """Create Subnet Test
-
-        RBAC test for the neutron create_subnet policy
-        """
-        network = self._create_network()
-
-        with self.rbac_utils.override_role(self):
-            self.create_subnet(network, enable_dhcp=False)
-
-    @rbac_rule_validation.action(service="neutron",
-                                 rule="get_subnet")
-    @decorators.idempotent_id('eb88be84-2465-482b-a40b-5201acb41152')
-    def test_show_subnet(self):
-
-        """Show Subnet Test
-
-        RBAC test for the neutron get_subnet policy
-        """
-        with self.rbac_utils.override_role(self):
-            self.subnets_client.show_subnet(self.subnet['id'])
-
-    @rbac_rule_validation.action(service="neutron",
-                                 rule="update_subnet")
-    @decorators.idempotent_id('1bfeaec5-83b9-4140-8138-93a0a9d04cee')
-    def test_update_subnet(self):
-
-        """Update Subnet Test
-
-        RBAC test for the neutron update_subnet policy
-        """
-        updated_name = data_utils.rand_name(
-            self.__class__.__name__ + '-Network')
-
-        with self.rbac_utils.override_role(self):
-            self.subnets_client.update_subnet(self.subnet['id'],
-                                              name=updated_name)
-
-    @rbac_rule_validation.action(service="neutron",
-                                 rule="delete_subnet")
-    @decorators.idempotent_id('1ad1400f-dc84-4edb-9674-b33bbfb0d3e3')
-    def test_delete_subnet(self):
-
-        """Delete Subnet Test
-
-        RBAC test for the neutron delete_subnet policy
-        """
-        network = self._create_network()
-        subnet = self.create_subnet(network, enable_dhcp=False)
-
-        with self.rbac_utils.override_role(self):
-            self.subnets_client.delete_subnet(subnet['id'])
-
     @utils.requires_ext(extension='dhcp_agent_scheduler', service='network')
     @decorators.idempotent_id('b524f19f-fbb4-4d11-a85d-03bfae17bf0e')
     @rbac_rule_validation.action(service="neutron",
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 d69f1e9..a8c1727 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
@@ -73,6 +73,7 @@
         '"volume_extension:volume_actions:attach" must be available in the '
         'cloud.')
     @utils.services('compute')
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="cinder",
         rule="volume_extension:volume_actions:attach")
diff --git a/patrole_tempest_plugin/tests/unit/test_rbac_utils.py b/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
index 5e730d3..4937318 100644
--- a/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
+++ b/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
@@ -36,17 +36,15 @@
 
     def test_override_role_with_missing_admin_role(self):
         self.rbac_utils.set_roles('member')
-        error_re = (
-            'Roles defined by `\[patrole\] rbac_test_role` and `\[identity\] '
-            'admin_role` must be defined in the system.')
+        error_re = (".*Following roles were not found: admin. Available "
+                    "roles: member.")
         self.assertRaisesRegex(rbac_exceptions.RbacResourceSetupFailed,
                                error_re, self.rbac_utils.override_role)
 
     def test_override_role_with_missing_rbac_role(self):
         self.rbac_utils.set_roles('admin')
-        error_re = (
-            'Roles defined by `\[patrole\] rbac_test_role` and `\[identity\] '
-            'admin_role` must be defined in the system.')
+        error_re = (".*Following roles were not found: member. Available "
+                    "roles: admin.")
         self.assertRaisesRegex(rbac_exceptions.RbacResourceSetupFailed,
                                error_re, self.rbac_utils.override_role)