Merge "Update documentation with rbac_utils details"
diff --git a/.gitignore b/.gitignore
index 963e589..b77e7f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,6 +53,10 @@
 *~
 .*.swp
 .*sw?
+*.idea
 
 # Files created by releasenotes build
-releasenotes/build
\ No newline at end of file
+releasenotes/build
+
+# Misc
+.stestr
diff --git a/.mailmap b/.mailmap
index 516ae6f..47612b3 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,3 +1,5 @@
 # Format is:
 # <preferred e-mail> <other e-mail 1>
 # <preferred e-mail> <other e-mail 2>
+Felipe Monteiro <felipe.carneiro.monteiro@gmail.com> <fm577c@att.com>
+Felipe Monteiro <felipe.carneiro.monteiro@gmail.com> <felipe.monteiro@att.com>
diff --git a/.stestr.conf b/.stestr.conf
new file mode 100644
index 0000000..76a6ac5
--- /dev/null
+++ b/.stestr.conf
@@ -0,0 +1,3 @@
+[DEFAULT]
+test_path=./patrole_tempest_plugin/tests/unit
+group_regex=([^\.]*\.)*
diff --git a/.testr.conf b/.testr.conf
deleted file mode 100644
index 87d049d..0000000
--- a/.testr.conf
+++ /dev/null
@@ -1,7 +0,0 @@
-[DEFAULT]
-test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
-             OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
-             OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
-             ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./patrole_tempest_plugin/tests/unit} $LISTOPT $IDOPTION
-test_id_option=--load-list $IDFILE
-test_list_option=--list
diff --git a/.zuul.yaml b/.zuul.yaml
new file mode 100644
index 0000000..313ce49
--- /dev/null
+++ b/.zuul.yaml
@@ -0,0 +1,80 @@
+- job:
+    name: patrole-dsvm-base
+    parent: legacy-dsvm-base
+    timeout: 7800
+    irrelevant-files:
+      - ^(test-|)requirements.txt$
+      - ^.*\.rst$
+      - ^doc/.*
+      - ^patrole/patrole_tempest_plugin/tests/unit/.*$
+      - ^releasenotes/.*
+      - ^setup.cfg$
+    required-projects:
+      - openstack-infra/devstack-gate
+      - openstack/patrole
+      - openstack/tempest
+
+- job:
+    name: patrole-dsvm-base-multinode
+    parent: legacy-dsvm-base-multinode
+    timeout: 7800
+    irrelevant-files:
+      - ^(test-|)requirements.txt$
+      - ^.*\.rst$
+      - ^doc/.*
+      - ^patrole/patrole_tempest_plugin/tests/unit/.*$
+      - ^releasenotes/.*
+      - ^setup.cfg$
+    required-projects:
+      - openstack-infra/devstack-gate
+      - openstack/patrole
+      - openstack/tempest
+
+- job:
+    name: tempest-dsvm-patrole-admin
+    parent: patrole-dsvm-base
+    run: playbooks/legacy/tempest-dsvm-patrole-admin/run.yaml
+    post-run: playbooks/legacy/tempest-dsvm-patrole-admin/post.yaml
+
+- job:
+    name: tempest-dsvm-patrole-member
+    parent: patrole-dsvm-base
+    run: playbooks/legacy/tempest-dsvm-patrole-member/run.yaml
+    post-run: playbooks/legacy/tempest-dsvm-patrole-member/post.yaml
+
+- job:
+    name: tempest-dsvm-patrole-multinode-admin
+    parent: patrole-dsvm-base-multinode
+    run: playbooks/legacy/tempest-dsvm-patrole-multinode-admin/run.yaml
+    post-run: playbooks/legacy/tempest-dsvm-patrole-multinode-admin/post.yaml
+    voting: false
+    nodeset: legacy-ubuntu-xenial-2-node
+
+- job:
+    name: tempest-dsvm-patrole-multinode-member
+    parent: patrole-dsvm-base-multinode
+    run: playbooks/legacy/tempest-dsvm-patrole-multinode-member/run.yaml
+    post-run: playbooks/legacy/tempest-dsvm-patrole-multinode-member/post.yaml
+    voting: false
+    nodeset: legacy-ubuntu-xenial-2-node
+
+- job:
+    name: tempest-dsvm-patrole-py35-member
+    parent: patrole-dsvm-base
+    run: playbooks/legacy/tempest-dsvm-patrole-py35-member/run.yaml
+    post-run: playbooks/legacy/tempest-dsvm-patrole-py35-member/post.yaml
+
+- project:
+    name: openstack/patrole
+    check:
+      jobs:
+        - tempest-dsvm-patrole-admin
+        - tempest-dsvm-patrole-member
+        - tempest-dsvm-patrole-py35-member
+        - tempest-dsvm-patrole-multinode-admin
+        - tempest-dsvm-patrole-multinode-member
+    gate:
+      jobs:
+        - tempest-dsvm-patrole-admin
+        - tempest-dsvm-patrole-member
+        - tempest-dsvm-patrole-py35-member
diff --git a/README.rst b/README.rst
index 6110dda..f4ab65c 100644
--- a/README.rst
+++ b/README.rst
@@ -16,6 +16,35 @@
 Patrole currently offers testing for the following OpenStack services: Nova,
 Neutron, Glance, Cinder and Keystone.
 
+Patrole is currently undergoing heavy development. As more projects move
+toward policy in code, Patrole will align its testing with the appropriate
+documentation.
+
+Design Principles
+-----------------
+
+Patrole borrows some design principles from Tempest, but not all, as its
+testing scope is confined to policies.
+
+* *Stability*. Patrole uses OpenStack public interfaces. Tests in Patrole
+  should only touch public OpenStack APIs.
+* *Atomicity*. Patrole tests should be atomic: they should test policies in
+  isolation. Unlike Tempest, a Patrole test strives to only call a single
+  endpoint at a time.
+* *Holistic coverage*. Patrole strives for complete coverage of the OpenStack
+  API. Additionally, Patrole strives to test the API-to-policy mapping
+  contained in each project's policy in code documentation.
+* *Self-contained*. Patrole should attempt to clean up after itself; whenever
+  possible we should tear down resources when done.
+
+  .. note::
+
+      Patrole modifies roles dynamically in the background, which affects
+      pre-provisioned credentials. Work is currently underway to clean up
+      modifications made to pre-provisioned credentials.
+
+* *Self-tested*. Patrole should be self-tested.
+
 Features
 --------
 * Validation of default policy definitions located in policy.json files.
diff --git a/devstack/plugin.sh b/devstack/plugin.sh
index 1066136..10d13f6 100644
--- a/devstack/plugin.sh
+++ b/devstack/plugin.sh
@@ -18,9 +18,8 @@
             RBAC_TEST_ROLE="Member"
         fi
 
-        iniset $TEMPEST_CONFIG rbac enable_rbac True
-        iniset $TEMPEST_CONFIG rbac rbac_test_role $RBAC_TEST_ROLE
-        iniset $TEMPEST_CONFIG rbac strict_policy_check False
+        iniset $TEMPEST_CONFIG patrole enable_rbac True
+        iniset $TEMPEST_CONFIG patrole rbac_test_role $RBAC_TEST_ROLE
     fi
 }
 
diff --git a/doc/source/usage.rst b/doc/source/usage.rst
index dff43f2..14c2cc7 100644
--- a/doc/source/usage.rst
+++ b/doc/source/usage.rst
@@ -4,8 +4,8 @@
 Usage
 ========
 
-RBAC (API) Tests
-================
+Patrole (API) Tests
+===================
 
 If Patrole is installed correctly, then the RBAC tests can be executed
 from inside the tempest root directory as follows::
@@ -28,7 +28,7 @@
 ..
 
 To change the role that the patrole tests are being run as, edit
-``rbac_test_role`` in the ``rbac`` section of tempest.conf: ::
+``rbac_test_role`` in the ``patrole`` section of tempest.conf: ::
 
     [patrole]
     rbac_test_role = Member
diff --git a/etc/patrole.conf.sample b/etc/patrole.conf.sample
index 370ca8d..cafdf8a 100644
--- a/etc/patrole.conf.sample
+++ b/etc/patrole.conf.sample
@@ -14,11 +14,17 @@
 # Enables RBAC tests. (boolean value)
 #enable_rbac = true
 
-# If true, throws RbacParsingException for policies which
+# DEPRECATED: If true, throws RbacParsingException for policies which
 # don't exist or are not included in the service's policy file. If
 # false, throws
 # skipException. (boolean value)
-#strict_policy_check = false
+# This option is deprecated for removal.
+# Its value may be silently ignored in the future.
+# Reason: This option allows for the possibility
+# of false positives. As a testing framework, Patrole should fail any
+# test that
+# passes in an invalid policy.
+#strict_policy_check = true
 
 # List of the paths to search for policy files. Each
 # policy path assumes that the service name is included in the path
@@ -32,46 +38,6 @@
 #  (list value)
 #custom_policy_files = /etc/%s/policy.json
 
-# DEPRECATED: Location of the Cinder policy file. Assumed to be on
-# the same host as Patrole. (string value)
-# This option is deprecated for removal.
-# Its value may be silently ignored in the future.
-# Reason: It is better to use `custom_policy_files` which supports any
-# OpenStack service.
-#cinder_policy_file = /etc/cinder/policy.json
-
-# DEPRECATED: Location of the Glance policy file. Assumed to be on
-# the same host as Patrole. (string value)
-# This option is deprecated for removal.
-# Its value may be silently ignored in the future.
-# Reason: It is better to use `custom_policy_files` which supports any
-# OpenStack service.
-#glance_policy_file = /etc/glance/policy.json
-
-# DEPRECATED: Location of the custom Keystone policy file. Assumed to
-# be on the same host as Patrole. (string value)
-# This option is deprecated for removal.
-# Its value may be silently ignored in the future.
-# Reason: It is better to use `custom_policy_files` which supports any
-# OpenStack service.
-#keystone_policy_file = /etc/keystone/policy.json
-
-# DEPRECATED: Location of the Neutron policy file. Assumed to be on
-# the same host as Patrole. (string value)
-# This option is deprecated for removal.
-# Its value may be silently ignored in the future.
-# Reason: It is better to use `custom_policy_files` which supports any
-# OpenStack service.
-#neutron_policy_file = /etc/neutron/policy.json
-
-# DEPRECATED: Location of the custom Nova policy file. Assumed to be
-# on the same host as Patrole. (string value)
-# This option is deprecated for removal.
-# Its value may be silently ignored in the future.
-# Reason: It is better to use `custom_policy_files` which supports any
-# OpenStack service.
-#nova_policy_file = /etc/nova/policy.json
-
 #
 # This option determines whether Patrole should run against a
 # `custom_requirements_file` which defines RBAC requirements. The
@@ -146,131 +112,3 @@
 # is logged. This is combined withreport_log_name to generate the full
 # path. (string value)
 #report_log_path = .
-
-
-[rbac]
-# This group is deprecated and will be removed in the next release.
-# Use the [patrole] group instead.
-
-#
-# From patrole.config
-#
-
-# The current RBAC role against which to run Patrole
-# tests. (string value)
-#rbac_test_role = admin
-
-# Enables RBAC tests. (boolean value)
-#enable_rbac = true
-
-# If true, throws RbacParsingException for policies which
-# don't exist or are not included in the service's policy file. If
-# false, throws
-# skipException. (boolean value)
-#strict_policy_check = false
-
-# List of the paths to search for policy files. Each
-# policy path assumes that the service name is included in the path
-# once. Also
-# assumes Patrole is on the same host as the policy files. The paths
-# should be
-# ordered by precedence, with high-priority paths before low-priority
-# paths. The
-# first path that is found to contain the service's policy file will
-# be used.
-#  (list value)
-#custom_policy_files = /etc/%s/policy.json
-
-# DEPRECATED: Location of the Cinder policy file. Assumed to be on
-# the same host as Patrole. (string value)
-# This option is deprecated for removal.
-# Its value may be silently ignored in the future.
-# Reason: It is better to use `custom_policy_files` which supports any
-# OpenStack service.
-#cinder_policy_file = /etc/cinder/policy.json
-
-# DEPRECATED: Location of the Glance policy file. Assumed to be on
-# the same host as Patrole. (string value)
-# This option is deprecated for removal.
-# Its value may be silently ignored in the future.
-# Reason: It is better to use `custom_policy_files` which supports any
-# OpenStack service.
-#glance_policy_file = /etc/glance/policy.json
-
-# DEPRECATED: Location of the custom Keystone policy file. Assumed to
-# be on the same host as Patrole. (string value)
-# This option is deprecated for removal.
-# Its value may be silently ignored in the future.
-# Reason: It is better to use `custom_policy_files` which supports any
-# OpenStack service.
-#keystone_policy_file = /etc/keystone/policy.json
-
-# DEPRECATED: Location of the Neutron policy file. Assumed to be on
-# the same host as Patrole. (string value)
-# This option is deprecated for removal.
-# Its value may be silently ignored in the future.
-# Reason: It is better to use `custom_policy_files` which supports any
-# OpenStack service.
-#neutron_policy_file = /etc/neutron/policy.json
-
-# DEPRECATED: Location of the custom Nova policy file. Assumed to be
-# on the same host as Patrole. (string value)
-# This option is deprecated for removal.
-# Its value may be silently ignored in the future.
-# Reason: It is better to use `custom_policy_files` which supports any
-# OpenStack service.
-#nova_policy_file = /etc/nova/policy.json
-
-#
-# 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
-# is in accordance to requirements. The idea is that the
-# `custom_requirements_file` perfectly defines what the RBAC
-# requirements are.
-#
-# Here are the possible outcomes when running the Patrole tests
-# against
-# a `custom_requirements_file`:
-#
-# YAML definition: allowed
-# test run: allowed
-# test result: pass
-#
-# YAML definition: allowed
-# test run: not allowed
-# test result: fail (under-permission)
-#
-# YAML definition: not allowed
-# test run: allowed
-# test result: fail (over-permission)
-#  (boolean value)
-#test_custom_requirements = false
-
-#
-# 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:
-#
-# ```
-# <service>:
-#   <api_action>:
-#     - <allowed_role>
-#     - <allowed_role>
-#     - <allowed_role>
-#   <api_action>:
-#     - <allowed_role>
-#     - <allowed_role>
-# <service>
-#   <api_action>:
-#     - <allowed_role>
-# ```
-# 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 Keystone role that is allowed to perform the API
-#  (string value)
-#custom_requirements_file = <None>
diff --git a/patrole_tempest_plugin/config.py b/patrole_tempest_plugin/config.py
index d309d60..8ac2a20 100644
--- a/patrole_tempest_plugin/config.py
+++ b/patrole_tempest_plugin/config.py
@@ -22,16 +22,17 @@
 PatroleGroup = [
     cfg.StrOpt('rbac_test_role',
                default='admin',
-               deprecated_group='rbac',
                help="""The current RBAC role against which to run Patrole
 tests."""),
     cfg.BoolOpt('enable_rbac',
                 default=True,
-                deprecated_group='rbac',
                 help="Enables RBAC tests."),
     cfg.BoolOpt('strict_policy_check',
-                default=False,
-                deprecated_group='rbac',
+                default=True,
+                deprecated_for_removal=True,
+                deprecated_reason="""This option allows for the possibility
+of false positives. As a testing framework, Patrole should fail any test that
+passes in an invalid policy.""",
                 help="""If true, throws RbacParsingException for policies which
 don't exist or are not included in the service's policy file. If false, throws
 skipException."""),
@@ -39,7 +40,6 @@
     # other hosts. It may be possible to leverage the v3 identity policy API.
     cfg.ListOpt('custom_policy_files',
                 default=['/etc/%s/policy.json'],
-                deprecated_group='rbac',
                 help="""List of the paths to search for policy files. Each
 policy path assumes that the service name is included in the path once. Also
 assumes Patrole is on the same host as the policy files. The paths should be
@@ -48,7 +48,6 @@
 """),
     cfg.BoolOpt('test_custom_requirements',
                 default=False,
-                deprecated_group='rbac',
                 help="""
 This option determines whether Patrole should run against a
 `custom_requirements_file` which defines RBAC requirements. The
@@ -72,7 +71,6 @@
 test result: fail (over-permission)
 """),
     cfg.StrOpt('custom_requirements_file',
-               deprecated_group='rbac',
                help="""
 File path of the yaml file that defines your RBAC requirements. This
 file must be located on the same host that Patrole runs on. The yaml
@@ -102,12 +100,6 @@
 ]
 
 
-rbac_group = cfg.OptGroup(name='rbac',
-                          title='RBAC testing options',
-                          help="This group is deprecated and will be removed "
-                               "in the next release. Use the [patrole] group "
-                               "instead.")
-
 patrole_log_group = cfg.OptGroup(
     name='patrole_log', title='Patrole Logging Options')
 
@@ -137,8 +129,7 @@
     """
     opt_list = [
         (patrole_group, PatroleGroup),
-        (patrole_log_group, PatroleLogGroup),
-        (rbac_group, PatroleGroup)
+        (patrole_log_group, PatroleLogGroup)
     ]
 
     return opt_list
diff --git a/patrole_tempest_plugin/plugin.py b/patrole_tempest_plugin/plugin.py
index b7717ea..a214892 100644
--- a/patrole_tempest_plugin/plugin.py
+++ b/patrole_tempest_plugin/plugin.py
@@ -62,12 +62,6 @@
         RBACLOG.addHandler(rbac_report_handler)
 
     def register_opts(self, conf):
-        # TODO(fmontei): Remove ``rbac_group`` in a future release as it is
-        # currently deprecated.
-        config.register_opt_group(
-            conf,
-            project_config.rbac_group,
-            project_config.PatroleGroup)
         config.register_opt_group(
             conf,
             project_config.patrole_group,
diff --git a/patrole_tempest_plugin/rbac_utils.py b/patrole_tempest_plugin/rbac_utils.py
index 9fa3740..2bb9eed 100644
--- a/patrole_tempest_plugin/rbac_utils.py
+++ b/patrole_tempest_plugin/rbac_utils.py
@@ -107,7 +107,7 @@
             # passing the second boundary before attempting to authenticate.
             # Only sleep if a token revocation occurred as a result of role
             # switching. This will optimize test runtime in the case where
-            # ``[identity] admin_role`` == ``[rbac] rbac_test_role``.
+            # ``[identity] admin_role`` == ``[patrole] rbac_test_role``.
             if not role_already_present:
                 time.sleep(1)
             test_obj.os_primary.auth_provider.set_auth()
diff --git a/patrole_tempest_plugin/tests/api/compute/test_fixed_ips_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_fixed_ips_rbac.py
new file mode 100644
index 0000000..dd32187
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/compute/test_fixed_ips_rbac.py
@@ -0,0 +1,78 @@
+#    Copyright 2017 NEC 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 patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.compute import rbac_base
+
+CONF = config.CONF
+
+
+class FixedIpsRbacTest(rbac_base.BaseV2ComputeRbacTest):
+
+    # Tests will fail with a 404 starting from microversion 2.36:
+    # See the following link for details:
+    # https://developer.openstack.org/api-ref/compute/#fixed-ips-os-fixed-ips-deprecated
+    max_microversion = '2.35'
+
+    @classmethod
+    def skip_checks(cls):
+        super(FixedIpsRbacTest, cls).skip_checks()
+        if CONF.service_available.neutron:
+            msg = ("%s skipped as neutron is available" % cls.__name__)
+            raise cls.skipException(msg)
+
+    @classmethod
+    def resource_setup(cls):
+        super(FixedIpsRbacTest, cls).resource_setup()
+        server = cls.create_test_server(wait_until='ACTIVE')
+        server = cls.servers_client.show_server(server['id'])['server']
+        cls.ip = None
+        for ip_set in server['addresses']:
+            for ip in server['addresses'][ip_set]:
+                if ip['OS-EXT-IPS:type'] == 'fixed':
+                    cls.ip = ip['addr']
+                    break
+            if cls.ip:
+                break
+        if cls.ip is None:
+            raise cls.skipException("No fixed ip found for server: %s"
+                                    % server['id'])
+
+    @decorators.idempotent_id('c89391f7-4844-4a70-a116-37c1336efb99')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-fixed-ips")
+    def test_show_fixed_ip_details(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.fixed_ips_client.show_fixed_ip(self.ip)
+
+    @decorators.idempotent_id('f0314501-735d-4315-9856-959e01e82f0d')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-fixed-ips")
+    def test_set_reserve(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.fixed_ips_client.reserve_fixed_ip(self.ip, reserve="None")
+
+    @decorators.idempotent_id('866a6fdc-a237-4502-9bf2-52fe82aba356')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-fixed-ips")
+    def test_set_unreserve(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.fixed_ips_client.reserve_fixed_ip(self.ip, unreserve="None")
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 be5cedb..2bc267b 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
@@ -87,11 +87,42 @@
             waiters.wait_for_server_status(self.os_admin.servers_client,
                                            self.server_id, 'SHELVED')
 
+    def _pause_server(self):
+        self.servers_client.pause_server(self.server_id)
+        self.addCleanup(self._cleanup_server_actions,
+                        self.servers_client.unpause_server,
+                        self.server_id)
+        waiters.wait_for_server_status(
+            self.os_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']
         if server['status'] != 'ACTIVE':
             function(server_id, **kwargs)
 
+    @decorators.idempotent_id('117f4ff2-8544-437b-824f-5e41cb6640ee')
+    @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+                          'Pause is not available.')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-pause-server:pause")
+    def test_pause_server(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self._pause_server()
+
+    @decorators.idempotent_id('087008cf-82fa-4eeb-ae8b-32c4126456ad')
+    @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+                          'Pause is not available.')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-pause-server:unpause")
+    def test_unpause_server(self):
+        self._pause_server()
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.servers_client.unpause_server(self.server_id)
+        waiters.wait_for_server_status(
+            self.os_admin.servers_client, self.server_id, 'ACTIVE')
+
     @rbac_rule_validation.action(
         service="nova",
         rule="os_compute_api:servers:stop")
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_auth_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_auth_rbac.py
index bc096ce..6a26f2b 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_auth_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_auth_rbac.py
@@ -26,8 +26,8 @@
     https://github.com/openstack/keystone/blob/master/keystone/common/policies/auth.py
     """
 
-    # TODO(felipemonteiro): Add tests for identity:get_auth_catalog and
-    # identity:get_auth_domains once the endpoints are implemented in Tempest's
+    # TODO(felipemonteiro): Add tests for identity:get_auth_catalog
+    # once the endpoints are implemented in Tempest's
     # identity v3 client.
 
     @decorators.idempotent_id('2a9fbf7f-6feb-4161-ae4b-faf7d6421b1a')
@@ -36,3 +36,10 @@
     def test_list_auth_projects(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.identity_client.list_auth_projects()['projects']
+
+    @decorators.idempotent_id('6a40af0d-7265-4657-b6b2-87a2828e263e')
+    @rbac_rule_validation.action(service="keystone",
+                                 rule="identity:get_auth_domains")
+    def test_list_auth_domain(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.identity_client.list_auth_domains()
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 82feff9..3639520 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
@@ -127,3 +127,11 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.trusts_client.show_trust_role(
             self.trust['id'], self.delegated_role_id)['role']
+
+    @decorators.idempotent_id('0184e0fb-641e-4b52-ab73-81c1ce6ca5c1')
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:get_trust")
+    def test_show_trust(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.trusts_client.show_trust(self.trust['id'])
diff --git a/patrole_tempest_plugin/tests/api/network/test_routers_rbac.py b/patrole_tempest_plugin/tests/api/network/test_routers_rbac.py
index c3ed9e1..fff2ada 100644
--- a/patrole_tempest_plugin/tests/api/network/test_routers_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_routers_rbac.py
@@ -176,6 +176,27 @@
             raise rbac_exceptions.RbacMalformedResponse(
                 attribute='distributed')
 
+    @decorators.idempotent_id('defc502c-4159-4824-b4d9-3cdcc39015b2')
+    @utils.requires_ext(extension='l3-ha', service='network')
+    @rbac_rule_validation.action(service="neutron",
+                                 rule="get_router:ha")
+    def test_show_high_availability_router(self):
+        """GET high-availability router
+
+        RBAC test for the neutron get_router:ha policy
+        """
+        router = self.routers_client.create_router(ha=True)['router']
+        self.addCleanup(self.routers_client.delete_router, router['id'])
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        retrieved_fields = self.routers_client.show_router(
+            router['id'], fields=['ha'])['router']
+
+        # Rather than throwing a 403, the field is not present, so raise exc.
+        if 'ha' not in retrieved_fields:
+            raise rbac_exceptions.RbacMalformedResponse(
+                attribute='ha')
+
     @rbac_rule_validation.action(
         service="neutron", rule="update_router")
     @decorators.idempotent_id('3d182f4e-0023-4218-9aa0-ea2b0ae0bd7a')
diff --git a/patrole_tempest_plugin/tests/api/volume/rbac_base.py b/patrole_tempest_plugin/tests/api/volume/rbac_base.py
index f4531df..7e2ebad 100644
--- a/patrole_tempest_plugin/tests/api/volume/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/volume/rbac_base.py
@@ -22,6 +22,12 @@
 
 
 class BaseVolumeRbacTest(vol_base.BaseVolumeTest):
+    # NOTE(felipemonteiro): Patrole currently only tests the v3 Cinder API
+    # because it is the current API and because policy enforcement does not
+    # change between API major versions. So, it is not necessary to specify
+    # the `_api_version` in any test class. However, specify microversions in
+    # subclasses if necessary.
+    _api_version = 3
 
     @classmethod
     def skip_checks(cls):
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 74ffe60..cfca14e 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_capabilities_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_capabilities_rbac.py
@@ -20,18 +20,18 @@
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
 
-class CapabilitiesRbacTest(rbac_base.BaseVolumeRbacTest):
+class CapabilitiesV3RbacTest(rbac_base.BaseVolumeRbacTest):
 
     @classmethod
     def skip_checks(cls):
-        super(CapabilitiesRbacTest, cls).skip_checks()
+        super(CapabilitiesV3RbacTest, cls).skip_checks()
         if not utils.is_extension_enabled('capabilities', 'volume'):
             msg = "%s skipped as capabilities not enabled." % cls.__name__
             raise cls.skipException(msg)
 
     @classmethod
     def setup_clients(cls):
-        super(CapabilitiesRbacTest, cls).setup_clients()
+        super(CapabilitiesV3RbacTest, cls).setup_clients()
         cls.capabilities_client = cls.os_primary.volume_capabilities_v2_client
         cls.hosts_client = cls.os_primary.volume_hosts_v2_client
 
@@ -42,7 +42,3 @@
         host = self.hosts_client.list_hosts()['hosts'][0]['host_name']
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.capabilities_client.show_backend_capabilities(host)
-
-
-class CapabilitiesV3RbacTest(CapabilitiesRbacTest):
-    _api_version = 3
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
index 2cae0bd..a78585f 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_encryption_types_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_encryption_types_rbac.py
@@ -20,18 +20,18 @@
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
 
-class EncryptionTypesRbacTest(rbac_base.BaseVolumeRbacTest):
+class EncryptionTypesV3RbacTest(rbac_base.BaseVolumeRbacTest):
 
     @classmethod
     def skip_checks(cls):
-        super(EncryptionTypesRbacTest, cls).skip_checks()
+        super(EncryptionTypesV3RbacTest, cls).skip_checks()
         if not utils.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()
+        super(EncryptionTypesV3RbacTest, cls).setup_clients()
         cls.encryption_types_client = cls.os_primary.encryption_types_v2_client
 
     def _create_volume_type_encryption(self):
@@ -82,7 +82,3 @@
         vol_type_id = self._create_volume_type_encryption()
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.encryption_types_client.show_encryption_type(vol_type_id)
-
-
-class EncryptionTypesV3RbacTest(EncryptionTypesRbacTest):
-    _api_version = 3
diff --git a/patrole_tempest_plugin/tests/api/volume/test_groups_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_groups_rbac.py
index 20f20a5..e1c0910 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_groups_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_groups_rbac.py
@@ -24,10 +24,17 @@
 
 
 class GroupsV3RbacTest(rbac_base.BaseVolumeRbacTest):
-    _api_version = 3
     min_microversion = '3.14'
     max_microversion = 'latest'
 
+    credentials = ['primary', 'admin']
+
+    @classmethod
+    def setup_clients(cls):
+        super(GroupsV3RbacTest, cls).setup_clients()
+        cls.admin_groups_client = cls.os_admin.groups_v3_client
+        cls.admin_volumes_client = cls.os_admin.volumes_v3_client
+
     def setUp(self):
         super(GroupsV3RbacTest, self).setUp()
         self.volume_type_id = self.create_volume_type()['id']
@@ -38,20 +45,25 @@
             self.__class__.__name__ + '-Group')
         group = self.groups_client.create_group(name=group_name, **kwargs)[
             'group']
-        waiters.wait_for_volume_resource_status(
-            self.groups_client, group['id'], 'available')
-
         if ignore_notfound:
             self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                             self._delete_group, group['id'])
         else:
             self.addCleanup(self._delete_group, group['id'])
-
+        waiters.wait_for_volume_resource_status(
+            self.admin_groups_client, group['id'], 'available')
         return group
 
-    def _delete_group(self, group_id, delete_volumes=True):
-        self.groups_client.delete_group(group_id, delete_volumes)
-        self.groups_client.wait_for_resource_deletion(group_id)
+    def _delete_group(self, group_id):
+        self.groups_client.delete_group(group_id, delete_volumes=True)
+        self.admin_groups_client.wait_for_resource_deletion(group_id)
+
+        vols = self.admin_volumes_client.list_volumes(
+            detail=True, params={'all_tenants': True})['volumes']
+        for vol in vols:
+            if vol['group_id'] == group_id:
+                self.admin_volumes_client.wait_for_resource_deletion(
+                    vol['id'])
 
     @decorators.idempotent_id('43235328-66ae-424f-bc7f-f709c0ca268c')
     @rbac_rule_validation.action(
@@ -112,11 +124,10 @@
                                    volume_types=[self.volume_type_id])
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.groups_client.delete_group(group['id'])
+        self._delete_group(group['id'])
 
 
 class GroupTypesV3RbacTest(rbac_base.BaseVolumeRbacTest):
-    _api_version = 3
     min_microversion = '3.11'
     max_microversion = 'latest'
 
@@ -148,7 +159,21 @@
         service="cinder",
         rule="group:group_types_manage")
     def test_delete_group_type(self):
-        goup_type = self.create_group_type(ignore_notfound=True)
+        group_type = self.create_group_type(ignore_notfound=True)
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.group_types_client.delete_group_type(goup_type['id'])
+        self.group_types_client.delete_group_type(group_type['id'])
+
+    @decorators.idempotent_id('8d9e2831-24c3-47b7-a76a-2e563287f12f')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="group:access_group_types_specs")
+    def test_show_group_type(self):
+        group_type = self.create_group_type()
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        resp_body = \
+            self.group_types_client.show_group_type(
+                group_type['id'])['group_type']
+        if 'group_specs' not in resp_body:
+            raise rbac_exceptions.RbacMalformedResponse(
+                attribute='group_specs')
diff --git a/patrole_tempest_plugin/tests/api/volume/test_limits_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_limits_rbac.py
new file mode 100644
index 0000000..fa92cad
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/volume/test_limits_rbac.py
@@ -0,0 +1,30 @@
+# 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.volume import rbac_base
+
+
+class LimitsV3RbacTest(rbac_base.BaseVolumeRbacTest):
+    _api_version = 3
+
+    @decorators.idempotent_id('dab04510-5b86-4479-a633-6e496ff405af')
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="limits_extension:used_limits")
+    def test_show_limits(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.volume_limits_client.show_limits()
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 2327de8..3ac59be 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_qos_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_qos_rbac.py
@@ -22,13 +22,12 @@
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
 
-class VolumeQOSRbacTest(rbac_base.BaseVolumeRbacTest):
-
+class VolumeQOSV3RbacTest(rbac_base.BaseVolumeRbacTest):
     credentials = ['primary', 'admin']
 
     @classmethod
     def setup_clients(cls):
-        super(VolumeQOSRbacTest, cls).setup_clients()
+        super(VolumeQOSV3RbacTest, cls).setup_clients()
         cls.qos_client = cls.os_primary.volume_qos_v2_client
         cls.admin_qos_client = cls.os_admin.volume_qos_v2_client
 
@@ -146,7 +145,3 @@
         self.qos_client.disassociate_all_qos(qos['id'])
         waiters.wait_for_qos_operations(self.admin_qos_client, qos['id'],
                                         'disassociate-all')
-
-
-class VolumeQOSV3RbacTest(VolumeQOSRbacTest):
-    _api_version = 3
diff --git a/patrole_tempest_plugin/tests/api/volume/test_quota_classes_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_quota_classes_rbac.py
index d016498..a81f1b9 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_quota_classes_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_quota_classes_rbac.py
@@ -21,11 +21,11 @@
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
 
-class QuotaClassesRbacTest(rbac_base.BaseVolumeRbacTest):
+class QuotaClassesV3RbacTest(rbac_base.BaseVolumeRbacTest):
 
     @classmethod
     def skip_checks(cls):
-        super(QuotaClassesRbacTest, cls).skip_checks()
+        super(QuotaClassesV3RbacTest, cls).skip_checks()
         if not utils.is_extension_enabled('os-quota-class-sets', 'volume'):
             msg = ("%s skipped as os-quota-class-sets not enabled."
                    % cls.__name__)
@@ -33,7 +33,7 @@
 
     @classmethod
     def setup_clients(cls):
-        super(QuotaClassesRbacTest, cls).setup_clients()
+        super(QuotaClassesV3RbacTest, cls).setup_clients()
         cls.quota_classes_client = cls.os_primary.quota_classes_client
         cls.quota_name = data_utils.rand_name(cls.__name__ + '-QuotaClass')
 
@@ -56,7 +56,3 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.quota_classes_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 25562e8..8fded0a 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
@@ -20,18 +20,18 @@
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
 
-class SchedulerStatsRbacTest(rbac_base.BaseVolumeRbacTest):
+class SchedulerStatsV3RbacTest(rbac_base.BaseVolumeRbacTest):
 
     @classmethod
     def skip_checks(cls):
-        super(SchedulerStatsRbacTest, cls).skip_checks()
+        super(SchedulerStatsV3RbacTest, cls).skip_checks()
         if not utils.is_extension_enabled('scheduler-stats', 'volume'):
             msg = "%s skipped as scheduler-stats not enabled." % cls.__name__
             raise cls.skipException(msg)
 
     @classmethod
     def setup_clients(cls):
-        super(SchedulerStatsRbacTest, cls).setup_clients()
+        super(SchedulerStatsV3RbacTest, cls).setup_clients()
         cls.scheduler_stats_client =\
             cls.os_primary.volume_scheduler_stats_v2_client
 
@@ -42,7 +42,3 @@
     def test_list_back_end_storage_pools(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.scheduler_stats_client.list_pools()
-
-
-class SchedulerStatsV3RbacTest(SchedulerStatsRbacTest):
-    _api_version = 3
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 fc39f4a..96243d8 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
@@ -22,17 +22,17 @@
 CONF = config.CONF
 
 
-class SnapshotsActionsRbacTest(rbac_base.BaseVolumeRbacTest):
+class SnapshotsActionsV3RbacTest(rbac_base.BaseVolumeRbacTest):
 
     @classmethod
     def skip_checks(cls):
-        super(SnapshotsActionsRbacTest, cls).skip_checks()
+        super(SnapshotsActionsV3RbacTest, cls).skip_checks()
         if not CONF.volume_feature_enabled.snapshot:
             raise cls.skipException("Cinder snapshot feature disabled")
 
     @classmethod
     def resource_setup(cls):
-        super(SnapshotsActionsRbacTest, cls).resource_setup()
+        super(SnapshotsActionsV3RbacTest, cls).resource_setup()
         cls.volume = cls.create_volume()
         cls.snapshot = cls.create_snapshot(volume_id=cls.volume['id'])
         cls.snapshot_id = cls.snapshot['id']
@@ -57,7 +57,3 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.snapshots_client.force_delete_snapshot(temp_snapshot['id'])
         self.snapshots_client.wait_for_resource_deletion(temp_snapshot['id'])
-
-
-class SnapshotsActionsV3RbacTest(SnapshotsActionsRbacTest):
-    _api_version = 3
diff --git a/patrole_tempest_plugin/tests/api/volume/test_snapshots_metadata_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_snapshots_metadata_rbac.py
index 3737212..1f82671 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_snapshots_metadata_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_snapshots_metadata_rbac.py
@@ -22,17 +22,17 @@
 CONF = config.CONF
 
 
-class SnapshotMetadataRbacTest(rbac_base.BaseVolumeRbacTest):
+class SnapshotMetadataV3RbacTest(rbac_base.BaseVolumeRbacTest):
 
     @classmethod
     def skip_checks(cls):
-        super(SnapshotMetadataRbacTest, cls).skip_checks()
+        super(SnapshotMetadataV3RbacTest, cls).skip_checks()
         if not CONF.volume_feature_enabled.snapshot:
             raise cls.skipException("Cinder snapshot feature disabled")
 
     @classmethod
     def resource_setup(cls):
-        super(SnapshotMetadataRbacTest, cls).resource_setup()
+        super(SnapshotMetadataV3RbacTest, cls).resource_setup()
         cls.volume = cls.create_volume()
         # Create a snapshot
         cls.snapshot = cls.create_snapshot(volume_id=cls.volume['id'])
@@ -118,7 +118,3 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.snapshots_client.delete_snapshot_metadata_item(
             self.snapshot['id'], "key1")
-
-
-class SnapshotMetadataV3RbacTest(SnapshotMetadataRbacTest):
-    _api_version = 3
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 fddaee4..bac9189 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
@@ -25,7 +25,6 @@
 
 
 class MessagesV3RbacTest(rbac_base.BaseVolumeRbacTest):
-    _api_version = 3
     min_microversion = '3.3'
     max_microversion = 'latest'
 
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 88c5d82..e9ebb99 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
@@ -27,19 +27,18 @@
 CONF = config.CONF
 
 
-class VolumesActionsRbacTest(rbac_base.BaseVolumeRbacTest):
-
+class VolumesActionsV3RbacTest(rbac_base.BaseVolumeRbacTest):
     credentials = ['primary', 'admin']
 
     @classmethod
     def setup_clients(cls):
-        super(VolumesActionsRbacTest, cls).setup_clients()
+        super(VolumesActionsV3RbacTest, cls).setup_clients()
         cls.admin_image_client = cls.os_admin.image_client_v2
         cls.admin_volumes_client = cls.os_admin.volumes_client_latest
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesActionsRbacTest, cls).resource_setup()
+        super(VolumesActionsV3RbacTest, cls).resource_setup()
         cls.volume = cls.create_volume()
 
     def _create_server(self):
@@ -217,10 +216,6 @@
                                                 volume['id'], 'available')
 
 
-class VolumesActionsV3RbacTest(VolumesActionsRbacTest):
-    _api_version = 3
-
-
 class VolumesActionsV310RbacTest(rbac_base.BaseVolumeRbacTest):
     _api_version = 3
     min_microversion = '3.10'
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_basic_crud_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_basic_crud_rbac.py
index 3f5227e..244f333 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_basic_crud_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_basic_crud_rbac.py
@@ -20,11 +20,11 @@
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
 
-class VolumesBasicCrudRbacTest(rbac_base.BaseVolumeRbacTest):
+class VolumesBasicCrudV3RbacTest(rbac_base.BaseVolumeRbacTest):
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesBasicCrudRbacTest, cls).resource_setup()
+        super(VolumesBasicCrudV3RbacTest, cls).resource_setup()
         cls.volume = cls.create_volume()
 
     @rbac_rule_validation.action(service="cinder",
@@ -70,7 +70,3 @@
     def test_volume_list_image_metadata(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.volumes_client.list_volumes(detail=True)
-
-
-class VolumesBasicCrudV3RbacTest(VolumesBasicCrudRbacTest):
-    _api_version = 3
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_hosts_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_hosts_rbac.py
index ee0a0be..726f84e 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_hosts_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_hosts_rbac.py
@@ -19,7 +19,7 @@
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
 
-class VolumeHostsRbacTest(rbac_base.BaseVolumeRbacTest):
+class VolumeHostsV3RbacTest(rbac_base.BaseVolumeRbacTest):
 
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume_extension:hosts")
@@ -28,6 +28,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.volume_hosts_client.list_hosts()
 
+    @decorators.skip_because(bug="1732808")
     @decorators.idempotent_id('9ddf321e-788f-4787-b8cc-dfa59e264143')
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume_extension:hosts")
@@ -39,7 +40,3 @@
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.volume_hosts_client.show_host(host_names[0])
-
-
-class VolumeHostsV3RbacTest(VolumeHostsRbacTest):
-    _api_version = 3
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_metadata_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_metadata_rbac.py
index f9114a8..5866934 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
@@ -14,6 +14,7 @@
 #    under the License.
 
 from tempest import config
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
@@ -22,21 +23,21 @@
 CONF = config.CONF
 
 
-class VolumeMetadataRbacTest(rbac_base.BaseVolumeRbacTest):
+class VolumeMetadataV3RbacTest(rbac_base.BaseVolumeRbacTest):
 
     @classmethod
     def resource_setup(cls):
-        super(VolumeMetadataRbacTest, cls).resource_setup()
+        super(VolumeMetadataV3RbacTest, cls).resource_setup()
         cls.volume = cls.create_volume()
         cls.image_id = CONF.compute.image_ref
 
     def setUp(self):
-        super(VolumeMetadataRbacTest, self).setUp()
+        super(VolumeMetadataV3RbacTest, self).setUp()
         self._add_metadata(self.volume)
 
     def tearDown(self):
         self.volumes_client.update_volume_metadata(self.volume['id'], {})
-        super(VolumeMetadataRbacTest, self).tearDown()
+        super(VolumeMetadataV3RbacTest, self).tearDown()
 
     def _add_metadata(self, volume):
         # Create metadata for the volume
@@ -66,17 +67,6 @@
         self.volumes_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.volumes_client.update_volume_image_metadata(
-            self.volume['id'], image_id=self.image_id)
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.volumes_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')
@@ -98,12 +88,25 @@
     @decorators.idempotent_id('a9d9e825-5ea3-42e6-96f3-7ac4e97b2ed0')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume:update_volume_metadata")
+        rule="volume_extension:volume_image_metadata")
     def test_update_volume_image_metadata(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.volumes_client.update_volume_image_metadata(
             self.volume['id'], image_id=self.image_id)
+        self.addCleanup(self.volumes_client.delete_volume_image_metadata,
+                        self.volume['id'], 'image_id')
 
+    @decorators.idempotent_id('a41c8eed-2051-4a25-b401-df036faacbdc')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_image_metadata")
+    def test_delete_volume_image_metadata(self):
+        self.volumes_client.update_volume_image_metadata(
+            self.volume['id'], image_id=self.image_id)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.volumes_client.delete_volume_image_metadata,
+                        self.volume['id'], 'image_id')
 
-class VolumeMetadataV3RbacTest(VolumeMetadataRbacTest):
-    _api_version = 3
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.volumes_client.delete_volume_image_metadata(self.volume['id'],
+                                                         'image_id')
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 851d468..6a79345 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
@@ -18,40 +18,72 @@
 from patrole_tempest_plugin import rbac_rule_validation
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
+QUOTA_KEYS = ['gigabytes', 'snapshots', 'volumes', 'backups',
+              'backup_gigabytes', 'per_volume_gigabytes']
 
-class VolumeQuotasRbacTest(rbac_base.BaseVolumeRbacTest):
+
+class VolumeQuotasV3RbacTest(rbac_base.BaseVolumeRbacTest):
 
     @classmethod
     def setup_credentials(cls):
-        super(VolumeQuotasRbacTest, cls).setup_credentials()
+        super(VolumeQuotasV3RbacTest, cls).setup_credentials()
         cls.demo_tenant_id = cls.os_primary.credentials.tenant_id
 
     @classmethod
     def setup_clients(cls):
-        super(VolumeQuotasRbacTest, cls).setup_clients()
+        super(VolumeQuotasV3RbacTest, cls).setup_clients()
         cls.quotas_client = cls.os_primary.volume_quotas_v2_client
 
+    def _restore_default_quota_set(self):
+        default_quota_set = self.quotas_client.show_default_quota_set(
+            self.demo_tenant_id)['quota_set']
+        cleanup_quota_set = dict(
+            (k, v) for k, v in default_quota_set.items()
+            if k in QUOTA_KEYS)
+        self.addCleanup(self.quotas_client.update_quota_set,
+                        self.demo_tenant_id, **cleanup_quota_set)
+
+    @decorators.idempotent_id('427c9f0c-982e-403d-ae45-c05f4d6322ff')
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="volume_extension:quotas:show")
+    def test_list_quotas(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.quotas_client.show_quota_set(self.demo_tenant_id)
+
+    @decorators.idempotent_id('e47cf444-2753-4983-be6d-fc0d6523720f')
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="volume_extension:quotas:show")
+    def test_list_quotas_usage_true(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.quotas_client.show_quota_set(self.demo_tenant_id,
+                                          params={'usage': True})
+
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume_extension:quotas:show")
     @decorators.idempotent_id('b3c7177e-b6b1-4d0f-810a-fc95606964dd')
     def test_list_default_quotas(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.quotas_client.show_default_quota_set(
-            self.demo_tenant_id)['quota_set']
+            self.demo_tenant_id)
 
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume_extension:quotas:update")
     @decorators.idempotent_id('60f8f421-1630-4953-b449-b22af32265c7')
-    def test_update_all_quota_resources_for_tenant(self):
+    def test_update_quota_set(self):
+        self._restore_default_quota_set()
         new_quota_set = {'gigabytes': 1009,
                          'volumes': 11,
                          'snapshots': 11}
-        # Update limits for all quota resources
+        # Update limits for all quota resources.
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.quotas_client.update_quota_set(
-            self.demo_tenant_id,
-            **new_quota_set)['quota_set']
+            self.demo_tenant_id, **new_quota_set)
 
+    @decorators.idempotent_id('329bdb88-5132-4810-b1fc-350d181577e3')
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="volume_extension:quotas:delete")
+    def test_delete_quota_set(self):
+        self._restore_default_quota_set()
 
-class VolumeQuotasV3RbacTest(VolumeQuotasRbacTest):
-    _api_version = 3
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.quotas_client.delete_quota_set(self.demo_tenant_id)
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
index 63978aa..d36fb5a 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_services_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_services_rbac.py
@@ -20,7 +20,7 @@
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
 
-class VolumeServicesRbacTest(rbac_base.BaseVolumeRbacTest):
+class VolumeServicesV3RbacTest(rbac_base.BaseVolumeRbacTest):
 
     # TODO(felipemonteiro): Implement a test to cover the policy action,
     # "volume_extension:services:update", once the Tempest client endpoint
@@ -28,14 +28,14 @@
 
     @classmethod
     def skip_checks(cls):
-        super(VolumeServicesRbacTest, cls).skip_checks()
+        super(VolumeServicesV3RbacTest, cls).skip_checks()
         if not utils.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()
+        super(VolumeServicesV3RbacTest, cls).setup_clients()
         cls.services_client = cls.os_primary.volume_services_v2_client
 
     @decorators.idempotent_id('b9134f01-97c0-4abd-9455-fe2f03e3f966')
@@ -45,7 +45,3 @@
     def test_list_services(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.services_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 656a2e6..9640dc6 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
@@ -21,19 +21,18 @@
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
 
-class VolumesTransfersRbacTest(rbac_base.BaseVolumeRbacTest):
-
+class VolumesTransfersV3RbacTest(rbac_base.BaseVolumeRbacTest):
     credentials = ['primary', 'admin']
 
     @classmethod
     def setup_clients(cls):
-        super(VolumesTransfersRbacTest, cls).setup_clients()
+        super(VolumesTransfersV3RbacTest, cls).setup_clients()
         cls.transfers_client = cls.os_primary.volume_transfers_v2_client
         cls.admin_volumes_client = cls.os_admin.volumes_client_latest
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesTransfersRbacTest, cls).resource_setup()
+        super(VolumesTransfersV3RbacTest, cls).resource_setup()
         cls.volume = cls.create_volume()
 
     def _delete_transfer(self, transfer):
@@ -89,7 +88,3 @@
         transfer = self._create_transfer()
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.transfers_client.delete_volume_transfer(transfer['id'])
-
-
-class VolumesTransfersV3RbacTest(VolumesTransfersRbacTest):
-    _api_version = 3
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_types_access_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_types_access_rbac.py
index 773df2b..f4aeee8 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_types_access_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_types_access_rbac.py
@@ -22,7 +22,6 @@
 
 
 class VolumeTypesAccessRbacTest(rbac_base.BaseVolumeRbacTest):
-    _api_version = 3
 
     @classmethod
     def skip_checks(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 aa02316..2abfd32 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
@@ -23,7 +23,6 @@
 
 
 class VolumeTypesExtraSpecsRbacTest(rbac_base.BaseVolumeRbacTest):
-    _api_version = 3
 
     @classmethod
     def skip_checks(cls):
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 d10c876..51ee925 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
@@ -29,24 +29,23 @@
 CONF = config.CONF
 
 
-class VolumesBackupsRbacTest(rbac_base.BaseVolumeRbacTest):
-
+class VolumesBackupsV3RbacTest(rbac_base.BaseVolumeRbacTest):
     credentials = ['primary', 'admin']
 
     @classmethod
     def skip_checks(cls):
-        super(VolumesBackupsRbacTest, cls).skip_checks()
+        super(VolumesBackupsV3RbacTest, cls).skip_checks()
         if not CONF.volume_feature_enabled.backup:
             raise cls.skipException("Cinder backup feature disabled")
 
     @classmethod
     def setup_clients(cls):
-        super(VolumesBackupsRbacTest, cls).setup_clients()
+        super(VolumesBackupsV3RbacTest, cls).setup_clients()
         cls.admin_backups_client = cls.os_admin.backups_v2_client
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesBackupsRbacTest, cls).resource_setup()
+        super(VolumesBackupsV3RbacTest, cls).resource_setup()
         cls.volume = cls.create_volume()
 
     def _decode_url(self, backup_url):
@@ -168,10 +167,6 @@
         self.addCleanup(self.backups_client.delete_backup, import_backup['id'])
 
 
-class VolumesBackupsV3RbacTest(VolumesBackupsRbacTest):
-    _api_version = 3
-
-
 class VolumesBackupsV318RbacTest(rbac_base.BaseVolumeRbacTest):
     _api_version = 3
     # The minimum microversion for showing 'os-backup-project-attr:project_id'
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volumes_extend_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volumes_extend_rbac.py
index 205be9e..8a34923 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volumes_extend_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volumes_extend_rbac.py
@@ -23,18 +23,17 @@
 CONF = config.CONF
 
 
-class VolumesExtendRbacTest(rbac_base.BaseVolumeRbacTest):
-
+class VolumesExtendV3RbacTest(rbac_base.BaseVolumeRbacTest):
     credentials = ['primary', 'admin']
 
     @classmethod
     def setup_clients(cls):
-        super(VolumesExtendRbacTest, cls).setup_clients()
+        super(VolumesExtendV3RbacTest, cls).setup_clients()
         cls.admin_volumes_client = cls.os_admin.volumes_client_latest
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesExtendRbacTest, cls).resource_setup()
+        super(VolumesExtendV3RbacTest, cls).resource_setup()
         # Create a test shared volume for tests
         cls.volume = cls.create_volume()
 
@@ -48,7 +47,3 @@
                                           new_size=extend_size)
         waiters.wait_for_volume_resource_status(
             self.admin_volumes_client, self.volume['id'], 'available')
-
-
-class VolumesExtendV3RbacTest(VolumesExtendRbacTest):
-    _api_version = 3
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
index dab796d..1365b79 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volumes_manage_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volumes_manage_rbac.py
@@ -25,13 +25,12 @@
 CONF = config.CONF
 
 
-class VolumesManageRbacTest(rbac_base.BaseVolumeRbacTest):
-
+class VolumesManageV3RbacTest(rbac_base.BaseVolumeRbacTest):
     credentials = ['primary', 'admin']
 
     @classmethod
     def skip_checks(cls):
-        super(VolumesManageRbacTest, cls).skip_checks()
+        super(VolumesManageV3RbacTest, cls).skip_checks()
 
         if not CONF.volume_feature_enabled.manage_volume:
             raise cls.skipException("Manage volume tests are disabled")
@@ -42,7 +41,7 @@
 
     @classmethod
     def setup_clients(cls):
-        super(VolumesManageRbacTest, cls).setup_clients()
+        super(VolumesManageV3RbacTest, cls).setup_clients()
         cls.volume_manage_client = cls.os_primary.volume_manage_v2_client
         cls.admin_volumes_client = cls.os_admin.volumes_client_latest
 
@@ -110,7 +109,3 @@
         # 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_snapshots_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volumes_snapshots_rbac.py
index 249b88b..7491820 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volumes_snapshots_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volumes_snapshots_rbac.py
@@ -23,24 +23,23 @@
 CONF = config.CONF
 
 
-class VolumesSnapshotRbacTest(rbac_base.BaseVolumeRbacTest):
-
+class VolumesSnapshotV3RbacTest(rbac_base.BaseVolumeRbacTest):
     credentials = ['primary', 'admin']
 
     @classmethod
     def skip_checks(cls):
-        super(VolumesSnapshotRbacTest, cls).skip_checks()
+        super(VolumesSnapshotV3RbacTest, cls).skip_checks()
         if not CONF.volume_feature_enabled.snapshot:
             raise cls.skipException("Cinder volume snapshots are disabled")
 
     @classmethod
     def setup_clients(cls):
-        super(VolumesSnapshotRbacTest, cls).setup_clients()
+        super(VolumesSnapshotV3RbacTest, cls).setup_clients()
         cls.admin_snapshots_client = cls.os_admin.snapshots_v2_client
 
     @classmethod
     def resource_setup(cls):
-        super(VolumesSnapshotRbacTest, cls).resource_setup()
+        super(VolumesSnapshotV3RbacTest, cls).resource_setup()
         # Create a test shared volume for tests
         cls.volume = cls.create_volume()
         # Create a test shared snapshot for tests
@@ -107,7 +106,3 @@
         self.snapshots_client.delete_snapshot(temp_snapshot['id'])
         self.admin_snapshots_client.wait_for_resource_deletion(
             temp_snapshot['id'])
-
-
-class VolumesSnapshotV3RbacTest(VolumesSnapshotRbacTest):
-    _api_version = 3
diff --git a/patrole_tempest_plugin/tests/unit/test_patrole.py b/patrole_tempest_plugin/tests/unit/test_patrole.py
deleted file mode 100644
index 9b8e88c..0000000
--- a/patrole_tempest_plugin/tests/unit/test_patrole.py
+++ /dev/null
@@ -1,36 +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.
-
-"""
-Tests for `patrole` module.
-"""
-
-from tempest import config
-
-from patrole_tempest_plugin.tests.unit import base
-
-CONF = config.CONF
-
-
-class TestPatrole(base.TestCase):
-
-    def test_rbac_group_backwards_compatability(self):
-        """Validate that the deprecated group [rbac] is available and has the
-        same options and option values as [patrole] group, which is current.
-        """
-        self.assertTrue(hasattr(CONF, 'patrole'))
-        self.assertTrue(hasattr(CONF, 'rbac'))
-        # Validate that both groups are identical.
-        self.assertEqual(CONF.patrole.items(), CONF.rbac.items())
diff --git a/playbooks/legacy/tempest-dsvm-patrole-admin/post.yaml b/playbooks/legacy/tempest-dsvm-patrole-admin/post.yaml
new file mode 100644
index 0000000..dac8753
--- /dev/null
+++ b/playbooks/legacy/tempest-dsvm-patrole-admin/post.yaml
@@ -0,0 +1,80 @@
+- hosts: primary
+  tasks:
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=**/*nose_results.html
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=**/*testr_results.html.gz
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=/.testrepository/tmp*
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=**/*testrepository.subunit.gz
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}/tox'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=/.tox/*/log/*
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=/logs/**
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
diff --git a/playbooks/legacy/tempest-dsvm-patrole-admin/run.yaml b/playbooks/legacy/tempest-dsvm-patrole-admin/run.yaml
new file mode 100644
index 0000000..57f208d
--- /dev/null
+++ b/playbooks/legacy/tempest-dsvm-patrole-admin/run.yaml
@@ -0,0 +1,60 @@
+- hosts: all
+  name: Autoconverted job legacy-tempest-dsvm-patrole-admin from old job gate-tempest-dsvm-patrole-admin-ubuntu-xenial
+  tasks:
+
+    - name: Ensure legacy workspace directory
+      file:
+        path: '{{ ansible_user_dir }}/workspace'
+        state: directory
+
+    - shell:
+        cmd: |
+          set -e
+          set -x
+          cat > clonemap.yaml << EOF
+          clonemap:
+            - name: openstack-infra/devstack-gate
+              dest: devstack-gate
+          EOF
+          /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \
+              git://git.openstack.org \
+              openstack-infra/devstack-gate
+        executable: /bin/bash
+        chdir: '{{ ansible_user_dir }}/workspace'
+      environment: '{{ zuul | zuul_legacy_vars }}'
+
+    - shell:
+        cmd: |
+          set -e
+          set -x
+          cat << 'EOF' >>"/tmp/dg-local.conf"
+          [[local|localrc]]
+          enable_plugin patrole git://git.openstack.org/openstack/patrole
+          TEMPEST_PLUGINS='/opt/stack/new/patrole'
+          # Needed by Patrole devstack plugin
+          RBAC_TEST_ROLE=admin
+          EOF
+        executable: /bin/bash
+        chdir: '{{ ansible_user_dir }}/workspace'
+      environment: '{{ zuul | zuul_legacy_vars }}'
+
+    - shell:
+        cmd: |
+          set -e
+          set -x
+          export PYTHONUNBUFFERED=true
+          export DEVSTACK_GATE_TEMPEST=1
+          export DEVSTACK_GATE_NEUTRON=1
+          export DEVSTACK_GATE_TEMPEST_REGEX='(?!.*\[.*\bslow\b.*\])(^patrole_tempest_plugin\.tests\.api)'
+          export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1
+          export TEMPEST_CONCURRENCY=2
+          export PROJECTS="openstack/patrole $PROJECTS"
+          export BRANCH_OVERRIDE=default
+          if [ "$BRANCH_OVERRIDE" != "default" ] ; then
+              export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE
+          fi
+          cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
+          ./safe-devstack-vm-gate-wrap.sh
+        executable: /bin/bash
+        chdir: '{{ ansible_user_dir }}/workspace'
+      environment: '{{ zuul | zuul_legacy_vars }}'
diff --git a/playbooks/legacy/tempest-dsvm-patrole-member/post.yaml b/playbooks/legacy/tempest-dsvm-patrole-member/post.yaml
new file mode 100644
index 0000000..dac8753
--- /dev/null
+++ b/playbooks/legacy/tempest-dsvm-patrole-member/post.yaml
@@ -0,0 +1,80 @@
+- hosts: primary
+  tasks:
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=**/*nose_results.html
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=**/*testr_results.html.gz
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=/.testrepository/tmp*
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=**/*testrepository.subunit.gz
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}/tox'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=/.tox/*/log/*
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=/logs/**
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
diff --git a/playbooks/legacy/tempest-dsvm-patrole-member/run.yaml b/playbooks/legacy/tempest-dsvm-patrole-member/run.yaml
new file mode 100644
index 0000000..b95467f
--- /dev/null
+++ b/playbooks/legacy/tempest-dsvm-patrole-member/run.yaml
@@ -0,0 +1,61 @@
+- hosts: all
+  name: Autoconverted job legacy-tempest-dsvm-patrole-member from old job gate-tempest-dsvm-patrole-member-ubuntu-xenial
+  tasks:
+
+    - name: Ensure legacy workspace directory
+      file:
+        path: '{{ ansible_user_dir }}/workspace'
+        state: directory
+
+    - shell:
+        cmd: |
+          set -e
+          set -x
+          cat > clonemap.yaml << EOF
+          clonemap:
+            - name: openstack-infra/devstack-gate
+              dest: devstack-gate
+          EOF
+          /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \
+              git://git.openstack.org \
+              openstack-infra/devstack-gate
+        executable: /bin/bash
+        chdir: '{{ ansible_user_dir }}/workspace'
+      environment: '{{ zuul | zuul_legacy_vars }}'
+
+    - shell:
+        cmd: |
+          set -e
+          set -x
+          cat << 'EOF' >>"/tmp/dg-local.conf"
+          [[local|localrc]]
+          enable_plugin patrole git://git.openstack.org/openstack/patrole
+          TEMPEST_PLUGINS='/opt/stack/new/patrole'
+          # Needed by Patrole devstack plugin
+          RBAC_TEST_ROLE=member
+          EOF
+        executable: /bin/bash
+        chdir: '{{ ansible_user_dir }}/workspace'
+      environment: '{{ zuul | zuul_legacy_vars }}'
+
+    - shell:
+        cmd: |
+          set -e
+          set -x
+          export PYTHONUNBUFFERED=true
+          export DEVSTACK_GATE_TEMPEST=1
+          export DEVSTACK_GATE_NEUTRON=1
+          export DEVSTACK_GATE_TEMPEST_REGEX='(?!.*\[.*\bslow\b.*\])(^patrole_tempest_plugin\.tests\.api)'
+          export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1
+          export TEMPEST_CONCURRENCY=2
+          export PROJECTS="openstack/patrole $PROJECTS"
+          export BRANCH_OVERRIDE=default
+          if [ "$BRANCH_OVERRIDE" != "default" ] ; then
+              export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE
+          fi
+          cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
+          ./safe-devstack-vm-gate-wrap.sh
+        executable: /bin/bash
+        chdir: '{{ ansible_user_dir }}/workspace'
+      environment: '{{ zuul | zuul_legacy_vars }}'
+
diff --git a/playbooks/legacy/tempest-dsvm-patrole-multinode-admin/post.yaml b/playbooks/legacy/tempest-dsvm-patrole-multinode-admin/post.yaml
new file mode 100644
index 0000000..dac8753
--- /dev/null
+++ b/playbooks/legacy/tempest-dsvm-patrole-multinode-admin/post.yaml
@@ -0,0 +1,80 @@
+- hosts: primary
+  tasks:
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=**/*nose_results.html
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=**/*testr_results.html.gz
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=/.testrepository/tmp*
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=**/*testrepository.subunit.gz
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}/tox'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=/.tox/*/log/*
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=/logs/**
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
diff --git a/playbooks/legacy/tempest-dsvm-patrole-multinode-admin/run.yaml b/playbooks/legacy/tempest-dsvm-patrole-multinode-admin/run.yaml
new file mode 100644
index 0000000..bece4e2
--- /dev/null
+++ b/playbooks/legacy/tempest-dsvm-patrole-multinode-admin/run.yaml
@@ -0,0 +1,63 @@
+- hosts: primary
+  name: Autoconverted job legacy-tempest-dsvm-patrole-multinode-admin from old job
+    gate-tempest-dsvm-patrole-multinode-admin-ubuntu-xenial-nv
+  tasks:
+
+    - name: Ensure legacy workspace directory
+      file:
+        path: '{{ ansible_user_dir }}/workspace'
+        state: directory
+
+    - shell:
+        cmd: |
+          set -e
+          set -x
+          cat > clonemap.yaml << EOF
+          clonemap:
+            - name: openstack-infra/devstack-gate
+              dest: devstack-gate
+          EOF
+          /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \
+              git://git.openstack.org \
+              openstack-infra/devstack-gate
+        executable: /bin/bash
+        chdir: '{{ ansible_user_dir }}/workspace'
+      environment: '{{ zuul | zuul_legacy_vars }}'
+
+    - shell:
+        cmd: |
+          set -e
+          set -x
+          cat << 'EOF' >>"/tmp/dg-local.conf"
+          [[local|localrc]]
+          enable_plugin patrole git://git.openstack.org/openstack/patrole
+          TEMPEST_PLUGINS='/opt/stack/new/patrole'
+          # Needed by Patrole devstack plugin
+          RBAC_TEST_ROLE=admin
+          EOF
+        executable: /bin/bash
+        chdir: '{{ ansible_user_dir }}/workspace'
+      environment: '{{ zuul | zuul_legacy_vars }}'
+
+    - shell:
+        cmd: |
+          set -e
+          set -x
+          export PYTHONUNBUFFERED=true
+          # Ensure that tempest set up is executed, but do not automatically
+          # execute tempest tests; they are executed in post_test_hook.
+          export DEVSTACK_GATE_TEMPEST=1
+          export DEVSTACK_GATE_NEUTRON=1
+          export DEVSTACK_GATE_TOPOLOGY="multinode"
+          export DEVSTACK_GATE_TEMPEST_REGEX='(?=.*\[.*\bslow\b.*\])(^patrole_tempest_plugin\.tests\.api)'
+          export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1
+          export PROJECTS="openstack/patrole $PROJECTS"
+          export BRANCH_OVERRIDE=default
+          if [ "$BRANCH_OVERRIDE" != "default" ] ; then
+              export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE
+          fi
+          cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
+          ./safe-devstack-vm-gate-wrap.sh
+        executable: /bin/bash
+        chdir: '{{ ansible_user_dir }}/workspace'
+      environment: '{{ zuul | zuul_legacy_vars }}'
diff --git a/playbooks/legacy/tempest-dsvm-patrole-multinode-member/post.yaml b/playbooks/legacy/tempest-dsvm-patrole-multinode-member/post.yaml
new file mode 100644
index 0000000..dac8753
--- /dev/null
+++ b/playbooks/legacy/tempest-dsvm-patrole-multinode-member/post.yaml
@@ -0,0 +1,80 @@
+- hosts: primary
+  tasks:
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=**/*nose_results.html
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=**/*testr_results.html.gz
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=/.testrepository/tmp*
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=**/*testrepository.subunit.gz
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}/tox'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=/.tox/*/log/*
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=/logs/**
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
diff --git a/playbooks/legacy/tempest-dsvm-patrole-multinode-member/run.yaml b/playbooks/legacy/tempest-dsvm-patrole-multinode-member/run.yaml
new file mode 100644
index 0000000..4c7b70f
--- /dev/null
+++ b/playbooks/legacy/tempest-dsvm-patrole-multinode-member/run.yaml
@@ -0,0 +1,63 @@
+- hosts: primary
+  name: Autoconverted job legacy-tempest-dsvm-patrole-multinode-member from old job
+    gate-tempest-dsvm-patrole-multinode-member-ubuntu-xenial-nv
+  tasks:
+
+    - name: Ensure legacy workspace directory
+      file:
+        path: '{{ ansible_user_dir }}/workspace'
+        state: directory
+
+    - shell:
+        cmd: |
+          set -e
+          set -x
+          cat > clonemap.yaml << EOF
+          clonemap:
+            - name: openstack-infra/devstack-gate
+              dest: devstack-gate
+          EOF
+          /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \
+              git://git.openstack.org \
+              openstack-infra/devstack-gate
+        executable: /bin/bash
+        chdir: '{{ ansible_user_dir }}/workspace'
+      environment: '{{ zuul | zuul_legacy_vars }}'
+
+    - shell:
+        cmd: |
+          set -e
+          set -x
+          cat << 'EOF' >>"/tmp/dg-local.conf"
+          [[local|localrc]]
+          enable_plugin patrole git://git.openstack.org/openstack/patrole
+          TEMPEST_PLUGINS='/opt/stack/new/patrole'
+          # Needed by Patrole devstack plugin
+          RBAC_TEST_ROLE=member
+          EOF
+        executable: /bin/bash
+        chdir: '{{ ansible_user_dir }}/workspace'
+      environment: '{{ zuul | zuul_legacy_vars }}'
+
+    - shell:
+        cmd: |
+          set -e
+          set -x
+          export PYTHONUNBUFFERED=true
+          # Ensure that tempest set up is executed, but do not automatically
+          # execute tempest tests; they are executed in post_test_hook.
+          export DEVSTACK_GATE_TEMPEST=1
+          export DEVSTACK_GATE_NEUTRON=1
+          export DEVSTACK_GATE_TOPOLOGY="multinode"
+          export DEVSTACK_GATE_TEMPEST_REGEX='(?=.*\[.*\bslow\b.*\])(^patrole_tempest_plugin\.tests\.api)'
+          export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1
+          export PROJECTS="openstack/patrole $PROJECTS"
+          export BRANCH_OVERRIDE=default
+          if [ "$BRANCH_OVERRIDE" != "default" ] ; then
+              export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE
+          fi
+          cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
+          ./safe-devstack-vm-gate-wrap.sh
+        executable: /bin/bash
+        chdir: '{{ ansible_user_dir }}/workspace'
+      environment: '{{ zuul | zuul_legacy_vars }}'
diff --git a/playbooks/legacy/tempest-dsvm-patrole-py35-member/post.yaml b/playbooks/legacy/tempest-dsvm-patrole-py35-member/post.yaml
new file mode 100644
index 0000000..dac8753
--- /dev/null
+++ b/playbooks/legacy/tempest-dsvm-patrole-py35-member/post.yaml
@@ -0,0 +1,80 @@
+- hosts: primary
+  tasks:
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=**/*nose_results.html
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=**/*testr_results.html.gz
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=/.testrepository/tmp*
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=**/*testrepository.subunit.gz
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}/tox'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=/.tox/*/log/*
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
+
+    - name: Copy files from {{ ansible_user_dir }}/workspace/ on node
+      synchronize:
+        src: '{{ ansible_user_dir }}/workspace/'
+        dest: '{{ zuul.executor.log_root }}'
+        mode: pull
+        copy_links: true
+        verify_host: true
+        rsync_opts:
+          - --include=/logs/**
+          - --include=*/
+          - --exclude=*
+          - --prune-empty-dirs
diff --git a/playbooks/legacy/tempest-dsvm-patrole-py35-member/run.yaml b/playbooks/legacy/tempest-dsvm-patrole-py35-member/run.yaml
new file mode 100644
index 0000000..e895702
--- /dev/null
+++ b/playbooks/legacy/tempest-dsvm-patrole-py35-member/run.yaml
@@ -0,0 +1,70 @@
+- hosts: all
+  name: Autoconverted job legacy-tempest-dsvm-patrole-py35-member from old job gate-tempest-dsvm-patrole-py35-member-ubuntu-xenial
+  tasks:
+
+    - name: Ensure legacy workspace directory
+      file:
+        path: '{{ ansible_user_dir }}/workspace'
+        state: directory
+
+    - shell:
+        cmd: |
+          set -e
+          set -x
+          cat > clonemap.yaml << EOF
+          clonemap:
+            - name: openstack-infra/devstack-gate
+              dest: devstack-gate
+          EOF
+          /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \
+              git://git.openstack.org \
+              openstack-infra/devstack-gate
+        executable: /bin/bash
+        chdir: '{{ ansible_user_dir }}/workspace'
+      environment: '{{ zuul | zuul_legacy_vars }}'
+
+    - shell:
+        cmd: |
+          set -e
+          set -x
+          cat << 'EOF' >>"/tmp/dg-local.conf"
+          [[local|localrc]]
+          enable_plugin patrole git://git.openstack.org/openstack/patrole
+          TEMPEST_PLUGINS='/opt/stack/new/patrole'
+          # Needed by Patrole devstack plugin
+          RBAC_TEST_ROLE=member
+          # Swift is not ready for python3 yet
+          disable_service s-account
+          disable_service s-container
+          disable_service s-object
+          disable_service s-proxy
+          # Without Swift, c-bak cannot run (in the Gate at least)
+          disable_service c-bak
+          EOF
+        executable: /bin/bash
+        chdir: '{{ ansible_user_dir }}/workspace'
+      environment: '{{ zuul | zuul_legacy_vars }}'
+
+    - shell:
+        cmd: |
+          set -e
+          set -x
+          export PYTHONUNBUFFERED=true
+          export DEVSTACK_GATE_USE_PYTHON3=True
+          # Ensure that tempest set up is executed, but do not automatically
+          # execute tempest tests; they are executed in post_test_hook.
+          export DEVSTACK_GATE_TEMPEST=1
+          export DEVSTACK_GATE_NEUTRON=1
+          export DEVSTACK_GATE_TEMPEST_REGEX='(?!.*\[.*\bslow\b.*\])(^patrole_tempest_plugin\.tests\.api)'
+          export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1
+          export TEMPEST_CONCURRENCY=2
+          export PROJECTS="openstack/patrole $PROJECTS"
+          export BRANCH_OVERRIDE=default
+          if [ "$BRANCH_OVERRIDE" != "default" ] ; then
+              export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE
+          fi
+          cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
+          ./safe-devstack-vm-gate-wrap.sh
+        executable: /bin/bash
+        chdir: '{{ ansible_user_dir }}/workspace'
+      environment: '{{ zuul | zuul_legacy_vars }}'
diff --git a/releasenotes/notes/deprecate-strict-policy-enforce-option-e15d2be4e753608e.yaml b/releasenotes/notes/deprecate-strict-policy-enforce-option-e15d2be4e753608e.yaml
new file mode 100644
index 0000000..4f56dd8
--- /dev/null
+++ b/releasenotes/notes/deprecate-strict-policy-enforce-option-e15d2be4e753608e.yaml
@@ -0,0 +1,10 @@
+---
+deprecations:
+  - |
+    The configuration option ``[patrole] strict_policy_check`` is deprecated
+    and will be removed in the Rocky release cycle.
+other:
+  - |
+    The default value for ``[patrole] strict_policy_check`` has been changed
+    to ``True`` because a Patrole test should always fail if the policy action
+    is invalid, to avoid false positives.
diff --git a/releasenotes/notes/remove-rbac-config-group-097c200f3db99fad.yaml b/releasenotes/notes/remove-rbac-config-group-097c200f3db99fad.yaml
new file mode 100644
index 0000000..fba7dd3
--- /dev/null
+++ b/releasenotes/notes/remove-rbac-config-group-097c200f3db99fad.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+  - |
+    The ``[rbac]`` config group has been removed. Use the ``[patrole]`` group
+    instead which contains the exact same options.
diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py
index 7444c35..1aeff4b 100644
--- a/releasenotes/source/conf.py
+++ b/releasenotes/source/conf.py
@@ -58,15 +58,13 @@
 project = u'Patrole Release Notes'
 copyright = u'2017, Patrole Developers'
 
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-from patrole_tempest_plugin.version import version_info as patrole_version
+# Release do not need a version number in the title, they
+# cover multiple versions.
+
 # The full version, including alpha/beta/rc tags.
-release = patrole_version.version_string_with_vcs()
+release = ''
 # The short X.Y version.
-version = patrole_version.canonical_version_string()
+version = ''
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/requirements.txt b/requirements.txt
index 0e46596..94f3073 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,7 +4,7 @@
 hacking>=1.0.0 # Apache-2.0
 pbr!=2.1.0,>=2.0.0 # Apache-2.0
 oslo.log>=3.30.0 # Apache-2.0
-oslo.config>=4.6.0 # Apache-2.0
+oslo.config>=5.1.0 # Apache-2.0
 oslo.policy>=1.23.0 # Apache-2.0
-tempest>=16.1.0 # Apache-2.0
+tempest>=17.1.0 # Apache-2.0
 stevedore>=1.20.0 # Apache-2.0
diff --git a/test-requirements.txt b/test-requirements.txt
index 1953685..a8a3044 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -14,4 +14,4 @@
 oslotest>=1.10.0 # Apache-2.0
 oslo.policy>=1.23.0 # Apache-2.0
 oslo.log>=3.30.0 # Apache-2.0
-tempest>=16.1.0 # Apache-2.0
+tempest>=17.1.0 # Apache-2.0
diff --git a/tox.ini b/tox.ini
index e95cadf..d7801db 100644
--- a/tox.ini
+++ b/tox.ini
@@ -12,13 +12,13 @@
    LANGUAGE=en_US
    LC_ALL=en_US.utf-8
    PYTHONWARNINGS=default::DeprecationWarning
-passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH OS_TEST_PATH http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY
+passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY
 whitelist_externals = find
 deps = -r{toxinidir}/requirements.txt
        -r{toxinidir}/test-requirements.txt
 commands =
     find . -type f -name "*.pyc" -delete
-    ostestr {posargs}
+    stestr --test-path ./patrole_tempest_plugin/tests/unit run {posargs}
 
 [testenv:pep8]
 commands = flake8 {posargs}