Merge "Add Shelve/Unshelve policy tests"
diff --git a/doc/source/conf.py b/doc/source/conf.py
index ddb1d45..88c1bea 100755
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -22,10 +22,15 @@
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
 extensions = [
     'sphinx.ext.autodoc',
-    #'sphinx.ext.intersphinx',
-    'openstackdocstheme'
+    'sphinx.ext.todo',
+    'sphinx.ext.viewcode',
+    'openstackdocstheme',
+    'oslo_config.sphinxconfiggen',
 ]
 
+config_generator_config_file = '../../etc/config-generator.patrole.conf'
+sample_config_basename = '_static/patrole'
+
 # autodoc generation is a bit aggressive and a nuisance when doing heavy
 # text edit cycles.
 # execute "export SPHINX_DEBUG=1" in your terminal to disable
@@ -55,7 +60,7 @@
 # The theme to use for HTML and HTML Help pages.  Major themes that come with
 # Sphinx are currently 'default' and 'sphinxdoc'.
 # html_theme_path = ["."]
-# html_static_path = ['static']
+html_static_path = ['_static']
 html_theme = 'openstackdocs'
 
 # openstackdocstheme options
diff --git a/doc/source/sampleconf.rst b/doc/source/sampleconf.rst
index 94ebc4d..ee848b5 100644
--- a/doc/source/sampleconf.rst
+++ b/doc/source/sampleconf.rst
@@ -3,49 +3,16 @@
 Sample Configuration File
 ==========================
 
-The following is a sample Patrole configuration for adaptation and use.
+The following is a sample Patrole configuration for adaptation and use. It is
+auto-generated from Patrole when this documentation is built, so
+if you are having issues with an option, please compare your version of
+Patrole with the version of this documentation.
 
-.. code-block:: ini
+Note that the Patrole configuration options actually live inside the Tempest
+configuration file; at runtime, Tempest populates its own configuration
+file with Patrole groups and options, assuming that Patrole is correctly
+installed and recognized as a plugin.
 
-    [patrole]
+The sample configuration can also be viewed in `file form <_static/patrole.conf.sample>`_.
 
-    # The role that you want the RBAC tests to use for RBAC testing
-    # This needs to be edited to run the test as a different role.
-    rbac_test_role = Member
-
-    # Enables RBAC Tempest tests if set to True. Otherwise, they are
-    # skipped.
-    enable_rbac = True
-
-    # If set to True, tests throw a RbacParsingException for policies
-    # not found in the policy file. Otherwise, they throw a skipException.
-    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.
-    custom_policy_files = /etc/nova/policy.json,/etc/neutron/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.
-    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:
-    custom_requirements_file = patrole/requirements.txt
-
-    # DEPRECATED: The following config options set the location of the service's
-    # policy file. For services that have their policy in code (e.g., Nova),
-    # this would be the location of a custom policy.json, if one exists.
-    cinder_policy_file = /etc/cinder/policy.json
-    glance_policy_file = /etc/glance/policy.json
-    keystone_policy_file = /etc/keystone/policy.json
-    neutron_policy_file = /etc/neutron/policy.json
-    nova_policy_file = /etc/nova/policy.json
+.. literalinclude:: _static/patrole.conf.sample
diff --git a/etc/config-generator.patrole.conf b/etc/config-generator.patrole.conf
new file mode 100644
index 0000000..8988ae0
--- /dev/null
+++ b/etc/config-generator.patrole.conf
@@ -0,0 +1,3 @@
+[DEFAULT]
+output_file = etc/patrole.conf.sample
+namespace = patrole.config
diff --git a/etc/patrole.conf.sample b/etc/patrole.conf.sample
new file mode 100644
index 0000000..370ca8d
--- /dev/null
+++ b/etc/patrole.conf.sample
@@ -0,0 +1,276 @@
+[DEFAULT]
+
+
+[patrole]
+
+#
+# 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>
+
+
+[patrole_log]
+
+#
+# From patrole.config
+#
+
+# Enables reporting on RBAC expected and actual test results for each
+# Patrole test (boolean value)
+#enable_reporting = false
+
+# Name of file where output from 'enable_reporting' is logged. Note
+# that this file is recreated on each invocation of patrole (string
+# value)
+#report_log_name = patrole.log
+
+# Path (relative or absolute) where the output from 'enable_reporting'
+# 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 fcf29af..d309d60 100644
--- a/patrole_tempest_plugin/config.py
+++ b/patrole_tempest_plugin/config.py
@@ -46,46 +46,6 @@
 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.
 """),
-    cfg.StrOpt('cinder_policy_file',
-               default='/etc/cinder/policy.json',
-               help="""Location of the Cinder policy file. Assumed to be on
-the same host as Patrole.""",
-               deprecated_group='rbac',
-               deprecated_for_removal=True,
-               deprecated_reason="It is better to use `custom_policy_files` "
-                                 "which supports any OpenStack service."),
-    cfg.StrOpt('glance_policy_file',
-               default='/etc/glance/policy.json',
-               help="""Location of the Glance policy file. Assumed to be on
-the same host as Patrole.""",
-               deprecated_group='rbac',
-               deprecated_for_removal=True,
-               deprecated_reason="It is better to use `custom_policy_files` "
-                                 "which supports any OpenStack service."),
-    cfg.StrOpt('keystone_policy_file',
-               default='/etc/keystone/policy.json',
-               help="""Location of the custom Keystone policy file. Assumed to
-be on the same host as Patrole.""",
-               deprecated_group='rbac',
-               deprecated_for_removal=True,
-               deprecated_reason="It is better to use `custom_policy_files` "
-                                 "which supports any OpenStack service."),
-    cfg.StrOpt('neutron_policy_file',
-               default='/etc/neutron/policy.json',
-               help="""Location of the Neutron policy file. Assumed to be on
-the same host as Patrole.""",
-               deprecated_group='rbac',
-               deprecated_for_removal=True,
-               deprecated_reason="It is better to use `custom_policy_files` "
-                                 "which supports any OpenStack service."),
-    cfg.StrOpt('nova_policy_file',
-               default='/etc/nova/policy.json',
-               help="""Location of the custom Nova policy file. Assumed to be
-on the same host as Patrole.""",
-               deprecated_group='rbac',
-               deprecated_for_removal=True,
-               deprecated_reason="It is better to use `custom_policy_files` "
-                                 "which supports any OpenStack service."),
     cfg.BoolOpt('test_custom_requirements',
                 default=False,
                 deprecated_group='rbac',
@@ -167,3 +127,18 @@
                     "'enable_reporting' is logged. This is combined with"
                     "report_log_name to generate the full path."),
 ]
+
+
+def list_opts():
+    """Return a list of oslo.config options available.
+
+    The purpose of this is to allow tools like the Oslo sample config file
+    generator to discover the options exposed to users.
+    """
+    opt_list = [
+        (patrole_group, PatroleGroup),
+        (patrole_log_group, PatroleLogGroup),
+        (rbac_group, PatroleGroup)
+    ]
+
+    return opt_list
diff --git a/patrole_tempest_plugin/policy_authority.py b/patrole_tempest_plugin/policy_authority.py
index 0a1aa13..3f4236b 100644
--- a/patrole_tempest_plugin/policy_authority.py
+++ b/patrole_tempest_plugin/policy_authority.py
@@ -107,12 +107,10 @@
 
         # Prioritize dynamically searching for policy files over relying on
         # deprecated service-specific policy file locations.
+        self.path = None
         if CONF.patrole.custom_policy_files:
             self.discover_policy_files()
             self.path = self.policy_files.get(service)
-        else:
-            self.path = getattr(CONF.patrole, '%s_policy_file' % str(service),
-                                None)
 
         self.rules = policy.Rules.load(self._get_policy_data(service),
                                        'default')
diff --git a/patrole_tempest_plugin/rbac_rule_validation.py b/patrole_tempest_plugin/rbac_rule_validation.py
index 540d006..927c803 100644
--- a/patrole_tempest_plugin/rbac_rule_validation.py
+++ b/patrole_tempest_plugin/rbac_rule_validation.py
@@ -17,6 +17,7 @@
 import sys
 import testtools
 
+from oslo_utils import excutils
 import six
 
 from tempest import config
@@ -158,14 +159,14 @@
                     raise exceptions.Forbidden(
                         "%s Exception was: %s" % (msg, e))
             except Exception as e:
-                exc_info = sys.exc_info()
-                error_details = exc_info[1].__str__()
-                msg = ("An unexpected exception has occurred during test: %s. "
-                       "Exception was: %s"
-                       % (test_func.__name__, error_details))
-                test_status = ('Error, %s' % (error_details))
-                LOG.error(msg)
-                six.reraise(exc_info[0], exc_info[0](msg), exc_info[2])
+                with excutils.save_and_reraise_exception():
+                    exc_info = sys.exc_info()
+                    error_details = six.text_type(exc_info[1])
+                    msg = ("An unexpected exception has occurred during test: "
+                           "%s. Exception was: %s" % (test_func.__name__,
+                                                      error_details))
+                    test_status = 'Error, %s' % (error_details)
+                    LOG.error(msg)
             else:
                 if not allowed:
                     LOG.error("Role %s was allowed to perform %s",
diff --git a/patrole_tempest_plugin/tests/api/compute/test_floating_ips_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_floating_ips_rbac.py
index 290e523..8ab5a51 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_floating_ips_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_floating_ips_rbac.py
@@ -15,6 +15,7 @@
 
 from tempest.common import utils
 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
@@ -47,3 +48,39 @@
     def test_list_floating_ips(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.floating_ips_client.list_floating_ips()['floating_ips']
+
+    @decorators.idempotent_id('bebe52b3-5269-4e72-80c8-5a4a39c3bfa6')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-floating-ips")
+    def test_show_floating_ip(self):
+        body = self.floating_ips_client.create_floating_ip(
+            pool=CONF.network.floating_network_name)['floating_ip']
+        self.addCleanup(
+            self.floating_ips_client.delete_floating_ip, body['id'])
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.floating_ips_client.show_floating_ip(body['id'])['floating_ip']
+
+    @decorators.idempotent_id('2bfb8745-c329-4ee9-95f6-c165a1989dbf')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-floating-ips")
+    def test_create_floating_ips(self):
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        body = self.floating_ips_client.create_floating_ip(
+            pool=CONF.network.floating_network_name)['floating_ip']
+        self.addCleanup(
+            self.floating_ips_client.delete_floating_ip, body['id'])
+
+    @decorators.idempotent_id('d3028373-5027-4e7a-b761-01c515403ecb')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-floating-ips")
+    def test_delete_floating_ip(self):
+        body = self.floating_ips_client.create_floating_ip(
+            pool=CONF.network.floating_network_name)['floating_ip']
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.floating_ips_client.delete_floating_ip, body['id'])
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.floating_ips_client.delete_floating_ip(body['id'])
diff --git a/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py
index e315623..0ba1282 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.common import image as common_image
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
@@ -49,10 +50,10 @@
     @classmethod
     def setup_clients(cls):
         super(ImagesRbacTest, cls).setup_clients()
-        if CONF.image_feature_enabled.api_v1:
-            cls.glance_image_client = cls.os_primary.image_client
-        elif CONF.image_feature_enabled.api_v2:
+        if CONF.image_feature_enabled.api_v2:
             cls.glance_image_client = cls.os_primary.image_client_v2
+        elif CONF.image_feature_enabled.api_v1:
+            cls.glance_image_client = cls.os_primary.image_client
         else:
             raise lib_exc.InvalidConfiguration(
                 'Either api_v1 or api_v2 must be True in '
@@ -61,8 +62,11 @@
     @classmethod
     def resource_setup(cls):
         super(ImagesRbacTest, cls).resource_setup()
-        cls.image = cls.glance_image_client.create_image(
-            name=data_utils.rand_name(cls.__name__ + '-image'))
+        params = {'name': data_utils.rand_name(cls.__name__ + '-image')}
+        if CONF.image_feature_enabled.api_v1:
+            params = {'headers': common_image.image_meta_to_headers(**params)}
+
+        cls.image = cls.glance_image_client.create_image(**params)
         cls.addClassResourceCleanup(
             cls.glance_image_client.wait_for_resource_deletion,
             cls.image['id'])
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py
index f15dd78..48df4a3 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py
@@ -419,6 +419,21 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.servers_client.rescue_server(self.server['id'])
 
+    @decorators.idempotent_id('ac2d956f-d6a3-4184-b814-b44d05c9574c')
+    @utils.requires_ext(extension='os-rescue', service='compute')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-rescue")
+    def test_unrescue_server(self):
+        """Test unrescue server, part of os-rescue."""
+        self.servers_client.rescue_server(self.server['id'])
+        waiters.wait_for_server_status(
+            self.os_admin.servers_client, self.server['id'], 'RESCUE')
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.servers_client.unrescue_server(self.server['id'])
+        # `setUp` will wait for the server to reach 'ACTIVE' for next test.
+
     @utils.requires_ext(extension='os-server-diagnostics', service='compute')
     @rbac_rule_validation.action(
         service="nova",
@@ -508,8 +523,8 @@
     def test_resume_server(self):
         """Test resume server, part of os-suspend-server."""
         self.servers_client.suspend_server(self.server['id'])
-        waiters.wait_for_server_status(self.servers_client, self.server['id'],
-                                       'SUSPENDED')
+        waiters.wait_for_server_status(
+            self.os_admin.servers_client, self.server['id'], 'SUSPENDED')
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.servers_client.resume_server(self.server['id'])
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 466fb0c..88c5d82 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
@@ -69,7 +69,9 @@
             self.admin_volumes_client, volume_id, 'available')
 
     @utils.services('compute')
-    @rbac_rule_validation.action(service="cinder", rule="volume:attach")
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_actions:attach")
     @decorators.idempotent_id('f97b10e4-2eed-4f8b-8632-71c02cb9fe42')
     def test_attach_volume_to_instance(self):
         server = self._create_server()
@@ -78,7 +80,9 @@
 
     @utils.services('compute')
     @decorators.attr(type='slow')
-    @rbac_rule_validation.action(service="cinder", rule="volume:detach")
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_actions:detach")
     @decorators.idempotent_id('5a042f6a-688b-42e6-a02e-fe5c47b89b07')
     def test_detach_volume_from_instance(self):
         server = self._create_server()
@@ -141,15 +145,17 @@
                                                 bootable=True)
 
     @decorators.idempotent_id('41566922-75a1-4484-99c7-9c8782ee99ac')
-    @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:reserve_volume")
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_actions:reserve")
     def test_volume_reserve(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.volumes_client.reserve_volume(self.volume['id'])
 
     @decorators.idempotent_id('e5fa9564-77d9-4e57-b0c0-3e0ae4d08535')
-    @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:unreserve_volume")
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:volume_actions:unreserve")
     def test_volume_unreserve(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.volumes_client.unreserve_volume(self.volume['id'])
diff --git a/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py b/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py
index 3065cfe..82f0428 100644
--- a/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py
+++ b/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py
@@ -203,17 +203,16 @@
         def test_policy(*args):
             raise exceptions.Forbidden('Test message')
 
-        error_re = ("An unexpected exception has occurred during test: "
-                    "test_policy. Exception was: Forbidden\nDetails: Test "
-                    "message")
+        error_msg = ("An unexpected exception has occurred during test: "
+                     "test_policy. Exception was: Forbidden\nDetails: Test "
+                     "message")
 
         for allowed in [True, False]:
             mock_authority.PolicyAuthority.return_value.allowed.\
                 return_value = allowed
-
-            self.assertRaisesRegex(exceptions.Forbidden, '.* ' + error_re,
+            self.assertRaisesRegex(exceptions.Forbidden, 'Test message',
                                    test_policy, self.mock_test_args)
-            self.assertIn(error_re, mock_log.error.mock_calls[0][1][0])
+            self.assertIn(error_msg, mock_log.error.mock_calls[0][1][0])
             mock_log.error.reset_mock()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
diff --git a/releasenotes/notes/remove-named-policy-files-134f3045502e9ce9.yaml b/releasenotes/notes/remove-named-policy-files-134f3045502e9ce9.yaml
new file mode 100644
index 0000000..00245d7
--- /dev/null
+++ b/releasenotes/notes/remove-named-policy-files-134f3045502e9ce9.yaml
@@ -0,0 +1,13 @@
+---
+deprecations:
+  - |
+    Removed the following deprecated Patrole configuration options:
+
+        * cinder_policy_file
+        * glance_policy_file
+        * keystone_policy_file
+        * neutron_policy_file
+        * nova_policy_file
+
+    To specify the location of a custom policy file, use
+    ``[patrole] custom_policy_files`` instead.
diff --git a/setup.cfg b/setup.cfg
index b3f4312..d805f62 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -54,3 +54,5 @@
 [entry_points]
 tempest.test_plugins =
     patrole_tempest_plugin = patrole_tempest_plugin.plugin:PatroleTempestPlugin
+oslo.config.opts =
+    patrole.config = patrole_tempest_plugin.config:list_opts
diff --git a/tox.ini b/tox.ini
index 320eb4e..e95cadf 100644
--- a/tox.ini
+++ b/tox.ini
@@ -55,6 +55,9 @@
 [testenv:debug]
 commands = oslo_debug_helper -t patrole_tempest_plugin/tests {posargs}
 
+[testenv:genconfig]
+commands = oslo-config-generator --config-file etc/config-generator.patrole.conf
+
 [flake8]
 # [H106] Don’t put vim configuration in source files.
 # [H203] Use assertIs(Not)None to check for None.