Merge "Add missing v3 token related testcases"
diff --git a/patrole_tempest_plugin/config.py b/patrole_tempest_plugin/config.py
index 1180836..a6f30e7 100644
--- a/patrole_tempest_plugin/config.py
+++ b/patrole_tempest_plugin/config.py
@@ -21,33 +21,61 @@
RbacGroup = [
cfg.StrOpt('rbac_test_role',
default='admin',
- help="The current RBAC role against which to run"
- " Patrole tests."),
+ help="""The current RBAC role against which to run Patrole
+tests."""),
cfg.BoolOpt('enable_rbac',
default=True,
help="Enables RBAC tests."),
cfg.BoolOpt('strict_policy_check',
default=False,
- help="If true, throws RbacParsingException for"
- " policies which don't exist. If false, "
- "throws skipException."),
+ 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."""),
# TODO(rb560u): There needs to be support for reading these JSON files from
- # other hosts. It may be possible to leverage the v3 identity policy API
+ # other hosts. It may be possible to leverage the v3 identity policy API.
+ cfg.ListOpt('custom_policy_files',
+ default=['/etc/%s/policy.json'],
+ 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
+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 neutron policy file."),
+ help="""Location of the Cinder policy file. Assumed to be on
+the same host as Patrole.""",
+ 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."),
+ help="""Location of the Glance policy file. Assumed to be on
+the same host as Patrole.""",
+ 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 keystone policy file."),
+ help="""Location of the custom Keystone policy file. Assumed to
+be on the same host as Patrole.""",
+ 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."),
+ help="""Location of the Neutron policy file. Assumed to be on
+the same host as Patrole.""",
+ 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 nova policy file."),
+ help="""Location of the custom Nova policy file. Assumed to be
+on the same host as Patrole.""",
+ 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,
help="""
diff --git a/patrole_tempest_plugin/rbac_exceptions.py b/patrole_tempest_plugin/rbac_exceptions.py
index aa3135e..5ccb216 100644
--- a/patrole_tempest_plugin/rbac_exceptions.py
+++ b/patrole_tempest_plugin/rbac_exceptions.py
@@ -16,25 +16,25 @@
from tempest.lib import exceptions
-class RbacActionFailed (exceptions.ClientRestClientException):
+class RbacActionFailed(exceptions.ClientRestClientException):
message = "Rbac action failed"
-class RbacResourceSetupFailed (exceptions.TempestException):
+class RbacResourceSetupFailed(exceptions.TempestException):
message = "Rbac resource setup failed"
-class RbacOverPermission (exceptions.TempestException):
+class RbacOverPermission(exceptions.TempestException):
message = "Action performed that should not be permitted"
-class RbacInvalidService (exceptions.TempestException):
+class RbacInvalidService(exceptions.TempestException):
message = "Attempted to test an invalid service"
-class RbacParsingException (exceptions.TempestException):
+class RbacParsingException(exceptions.TempestException):
message = "Attempted to test an invalid policy file or action"
-class RbacInvalidErrorCode (exceptions.TempestException):
+class RbacInvalidErrorCode(exceptions.TempestException):
message = "Unsupported error code passed in test"
diff --git a/patrole_tempest_plugin/rbac_policy_parser.py b/patrole_tempest_plugin/rbac_policy_parser.py
index 17a626c..41871cf 100644
--- a/patrole_tempest_plugin/rbac_policy_parser.py
+++ b/patrole_tempest_plugin/rbac_policy_parser.py
@@ -58,26 +58,27 @@
the custom policy file over the default policy implementation is
prioritized.
- :param project_id: type uuid
- :param user_id: type uuid
- :param service: type string
- :param path: type string
+ :param uuid project_id: project_id of object performing API call
+ :param uuid user_id: user_id of object performing API call
+ :param string service: service of the policy file
+ :param dict extra_target_data: dictionary containing additional object
+ data needed by oslo.policy to validate generic checks
"""
if extra_target_data is None:
extra_target_data = {}
- # First check if the service is valid.
self.validate_service(service)
- # Use default path in /etc/<service_name/policy.json if no path
- # is provided.
- path = getattr(CONF.rbac, '%s_policy_file' % str(service), None)
- if not path:
- LOG.info("No config option found for %s,"
- " using default path", str(service))
- path = os.path.join('/etc', service, 'policy.json')
- self.path = path
+ # Prioritize dynamically searching for policy files over relying on
+ # deprecated service-specific policy file locations.
+ if CONF.rbac.custom_policy_files:
+ self.discover_policy_files()
+ self.path = self.policy_files.get(service)
+ else:
+ self.path = getattr(CONF.rbac, '%s_policy_file' % str(service),
+ None)
+
self.rules = policy.Rules.load(self._get_policy_data(service),
'default')
self.project_id = project_id
@@ -86,7 +87,7 @@
@classmethod
def validate_service(cls, service):
- """Validate whether the service passed to ``init`` exists."""
+ """Validate whether the service passed to ``__init__`` exists."""
service = service.lower().strip() if service else None
# Cache the list of available services in memory to avoid needlessly
@@ -102,6 +103,19 @@
raise rbac_exceptions.RbacInvalidService(
"%s is NOT a valid service." % service)
+ @classmethod
+ def discover_policy_files(cls):
+ # Dynamically discover the policy file for each service in
+ # ``cls.available_services``. Pick the first ``candidate_path`` found
+ # out of the potential paths in ``CONF.rbac.custom_policy_files``.
+ if not hasattr(cls, 'policy_files'):
+ cls.policy_files = {}
+ for service in cls.available_services:
+ for candidate_path in CONF.rbac.custom_policy_files:
+ if os.path.isfile(candidate_path % service):
+ cls.policy_files.setdefault(service,
+ candidate_path % service)
+
def allowed(self, rule_name, role):
is_admin_context = self._is_admin_context(role)
is_allowed = self._allowed(
@@ -115,8 +129,8 @@
mgr_policy_data = {}
policy_data = {}
- # Check whether policy file exists.
- if os.path.isfile(self.path):
+ # Check whether policy file exists and attempt to read it.
+ if self.path and os.path.isfile(self.path):
try:
with open(self.path, 'r') as policy_file:
file_policy_data = policy_file.read()
@@ -158,8 +172,11 @@
elif mgr_policy_data:
policy_data = mgr_policy_data
else:
- error_message = 'Policy file for {0} service neither found in '\
- 'code nor at {1}.'.format(service, self.path)
+ error_message = (
+ 'Policy file for {0} service neither found in code nor at {1}.'
+ .format(service, [loc % service for loc in
+ CONF.rbac.custom_policy_files])
+ )
raise rbac_exceptions.RbacParsingException(error_message)
try:
diff --git a/patrole_tempest_plugin/tests/api/compute/rbac_base.py b/patrole_tempest_plugin/tests/api/compute/rbac_base.py
index 0be42e7..0a3a1f5 100644
--- a/patrole_tempest_plugin/tests/api/compute/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/compute/rbac_base.py
@@ -37,7 +37,9 @@
super(BaseV2ComputeRbacTest, cls).setup_clients()
cls.auth_provider = cls.os_primary.auth_provider
cls.rbac_utils = rbac_utils.RbacUtils(cls)
+
cls.hosts_client = cls.os_primary.hosts_client
+ cls.tenant_usages_client = cls.os_primary.tenant_usages_client
@classmethod
def resource_setup(cls):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_attach_interfaces_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_attach_interfaces_rbac.py
deleted file mode 100644
index 1ad8861..0000000
--- a/patrole_tempest_plugin/tests/api/compute/test_attach_interfaces_rbac.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# Copyright 2017 AT&T Corporation.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.common import waiters
-from tempest import config
-from tempest.lib.common.utils import test_utils
-from tempest.lib import decorators
-from tempest import test
-
-from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.compute import rbac_base
-
-CONF = config.CONF
-
-
-class AttachInterfacesRbacTest(rbac_base.BaseV2ComputeRbacTest):
-
- @classmethod
- def skip_checks(cls):
- super(AttachInterfacesRbacTest, cls).skip_checks()
- if not test.is_extension_enabled('os-attach-interfaces', 'compute'):
- msg = ("%s skipped as os-attach-interfaces not "
- "enabled." % cls.__name__)
- raise cls.skipException(msg)
- if not CONF.compute_feature_enabled.interface_attach:
- raise cls.skipException(
- "%s skipped as interface attachment is not available"
- % cls.__name__)
- if not CONF.service_available.neutron:
- raise cls.skipException(
- '%s skipped as Neutron is required' % cls.__name__)
-
- @classmethod
- def setup_credentials(cls):
- cls.prepare_instance_network()
- super(AttachInterfacesRbacTest, cls).setup_credentials()
-
- @classmethod
- def resource_setup(cls):
- super(AttachInterfacesRbacTest, cls).resource_setup()
- cls.server = cls.create_test_server(wait_until='ACTIVE')
-
- def _attach_interface_to_server(self):
- interface = self.interfaces_client.create_interface(
- self.server['id'])['interfaceAttachment']
- waiters.wait_for_interface_status(
- self.interfaces_client, self.server['id'], interface['port_id'],
- 'ACTIVE')
- self.addCleanup(
- test_utils.call_and_ignore_notfound_exc,
- self.os_admin.interfaces_client.delete_interface,
- self.server['id'], interface['port_id'])
- return interface
-
- @decorators.idempotent_id('ddf53cb6-4a0a-4e5a-91e3-6c32aaa3b9b6')
- @rbac_rule_validation.action(
- service="nova",
- rule="os_compute_api:os-attach-interfaces")
- def test_list_interfaces(self):
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.interfaces_client.list_interfaces(
- self.server['id'])['interfaceAttachments']
-
- @decorators.idempotent_id('d2d3a24d-4738-4bce-a287-36d664746cde')
- @rbac_rule_validation.action(
- service="nova",
- rule="os_compute_api:os-attach-interfaces:create")
- def test_create_interface(self):
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self._attach_interface_to_server()
-
- @decorators.idempotent_id('55b05692-ed44-4608-a84c-cd4219c82799')
- @rbac_rule_validation.action(
- service="nova",
- rule="os_compute_api:os-attach-interfaces:delete")
- def test_delete_interface(self):
- interface = self._attach_interface_to_server()
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.interfaces_client.delete_interface(self.server['id'],
- interface['port_id'])
diff --git a/patrole_tempest_plugin/tests/api/compute/test_config_drive_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_config_drive_rbac.py
deleted file mode 100644
index 794e0d2..0000000
--- a/patrole_tempest_plugin/tests/api/compute/test_config_drive_rbac.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright 2017 AT&T Corporation.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.lib import decorators
-from tempest import test
-
-from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.compute import rbac_base
-
-
-class ConfigDriveRbacTest(rbac_base.BaseV2ComputeRbacTest):
-
- @classmethod
- def setup_clients(cls):
- super(ConfigDriveRbacTest, cls).setup_clients()
- cls.client = cls.servers_client
-
- @classmethod
- def skip_checks(cls):
- super(ConfigDriveRbacTest, cls).skip_checks()
- if not test.is_extension_enabled('os-config-drive', 'compute'):
- msg = "%s skipped as os-config-drive extension not enabled." \
- % cls.__name__
- raise cls.skipException(msg)
-
- @decorators.idempotent_id('55c62ef7-b72b-4970-acc6-05b0a4316e5d')
- @rbac_rule_validation.action(
- service="nova",
- rule="os_compute_api:os-config-drive")
- def test_create_test_server_with_config_drive(self):
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- # NOTE(felipemonteiro): This policy action is always enforced,
- # regardless whether the config_drive flag is set to true or false.
- # However, it has been explicitly set to true below, in case that this
- # behavior ever changes in the future.
- self.create_test_server(config_drive=True)
diff --git a/patrole_tempest_plugin/tests/api/compute/test_ips_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_ips_rbac.py
deleted file mode 100644
index c42459a..0000000
--- a/patrole_tempest_plugin/tests/api/compute/test_ips_rbac.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright 2017 AT&T Corporation.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest import config
-from tempest.lib import decorators
-from tempest import test
-
-from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.compute import rbac_base
-
-CONF = config.CONF
-
-
-class IpsRbacTest(rbac_base.BaseV2ComputeRbacTest):
-
- @classmethod
- def skip_checks(cls):
- super(IpsRbacTest, cls).skip_checks()
- if not test.is_extension_enabled('os-ips', 'compute'):
- msg = "%s skipped as os-ips not enabled." % cls.__name__
- raise cls.skipException(msg)
- if not CONF.service_available.neutron:
- raise cls.skipException(
- '%s skipped as Neutron is required' % cls.__name__)
-
- @classmethod
- def setup_credentials(cls):
- cls.prepare_instance_network()
- super(IpsRbacTest, cls).setup_credentials()
-
- @classmethod
- def resource_setup(cls):
- super(IpsRbacTest, cls).resource_setup()
- cls.server = cls.create_test_server(wait_until='ACTIVE')
-
- @decorators.idempotent_id('6886d360-0d86-4760-b1a3-882d81fbebcc')
- @rbac_rule_validation.action(
- service="nova",
- rule="os_compute_api:ips:index")
- def test_list_addresses(self):
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.servers_client.list_addresses(self.server['id'])['addresses']
-
- @decorators.idempotent_id('fa43e7e5-0db9-48eb-9c6b-c11eb766b8e4')
- @rbac_rule_validation.action(
- service="nova",
- rule="os_compute_api:ips:show")
- def test_list_addresses_by_network(self):
- addresses = self.servers_client.list_addresses(
- self.server['id'])['addresses']
- address = next(iter(addresses))
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.servers_client.list_addresses_by_network(
- self.server['id'], address)[address]
diff --git a/patrole_tempest_plugin/tests/api/compute/test_multinic_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_multinic_rbac.py
deleted file mode 100644
index e409269..0000000
--- a/patrole_tempest_plugin/tests/api/compute/test_multinic_rbac.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright 2017 AT&T Corporation.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest 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 MultinicRbacTest(rbac_base.BaseV2ComputeRbacTest):
-
- @classmethod
- def skip_checks(cls):
- super(MultinicRbacTest, cls).skip_checks()
- if not CONF.service_available.neutron:
- raise cls.skipException("Neutron is required")
- if not CONF.compute_feature_enabled.interface_attach:
- raise cls.skipException("Interface attachment is not available.")
-
- @classmethod
- def setup_credentials(cls):
- # This test class requires network and subnet
- cls.set_network_resources(network=True, subnet=True)
- super(MultinicRbacTest, cls).setup_credentials()
-
- @classmethod
- def resource_setup(cls):
- super(MultinicRbacTest, cls).resource_setup()
- cls.server = cls.create_test_server(wait_until='ACTIVE')
-
- @rbac_rule_validation.action(
- service="nova", rule="os_compute_api:os-multinic")
- @decorators.idempotent_id('bd3e2c74-130a-40f0-8085-124d93fe67da')
- def test_add_fixed_ip(self):
- """Add fixed IP to server."""
- interfaces = (self.interfaces_client.list_interfaces(self.server['id'])
- ['interfaceAttachments'])
- if interfaces:
- network_id = interfaces[0]['net_id']
- else:
- network_id = self.interfaces_client.create_interface(
- self.server['id'])['interfaceAttachment']['net_id']
-
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.servers_client.add_fixed_ip(self.server['id'],
- networkId=network_id)
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 ffbc530..b115ab5 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
@@ -155,7 +155,7 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self._test_stop_server()
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@rbac_rule_validation.action(
service="nova",
rule="os_compute_api:servers:start")
@@ -165,7 +165,7 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self._test_start_server()
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@rbac_rule_validation.action(
service="nova",
rule="os_compute_api:servers:resize")
@@ -176,7 +176,7 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self._test_resize_server(self.flavor_ref_alt)
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@rbac_rule_validation.action(
service="nova",
rule="os_compute_api:servers:revert_resize")
@@ -188,7 +188,7 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self._test_revert_resize_server()
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@rbac_rule_validation.action(
service="nova",
rule="os_compute_api:servers:confirm_resize")
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_migrations_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_migrations_rbac.py
index 842d8b1..a7f16a1 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_migrations_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_migrations_rbac.py
@@ -18,7 +18,6 @@
from tempest.common import waiters
from tempest import config
from tempest.lib import decorators
-from tempest import test
from patrole_tempest_plugin import rbac_rule_validation
from patrole_tempest_plugin.tests.api.compute import rbac_base as base
@@ -63,7 +62,7 @@
if host_record['service'] == 'compute'
]
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
'Cold migration not available.')
@rbac_rule_validation.action(
@@ -77,7 +76,7 @@
waiters.wait_for_server_status(self.admin_servers_client,
server['id'], 'VERIFY_RESIZE')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@testtools.skipUnless(CONF.compute_feature_enabled.live_migration,
'Live migration feature is not enabled.')
@rbac_rule_validation.action(
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 d4c856d..c1bf9f7 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
@@ -18,10 +18,12 @@
from tempest.common import waiters
from tempest import config
from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
from tempest import test
+from patrole_tempest_plugin import rbac_exceptions
from patrole_tempest_plugin import rbac_rule_validation
from patrole_tempest_plugin.tests.api.compute import rbac_base
@@ -83,7 +85,7 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.servers_client.inject_network_info(self.server_id)
- @test.attr(type=['slow'])
+ @decorators.attr(type=['slow'])
@test.requires_ext(extension='os-admin-actions', service='compute')
@rbac_rule_validation.action(
service="nova",
@@ -112,6 +114,33 @@
waiters.wait_for_server_status(
self.os_admin.servers_client, self.server_id, 'ACTIVE')
+ @test.requires_ext(extension='os-config-drive', service='compute')
+ @decorators.idempotent_id('2c82e819-382d-4d6f-87f0-a45954cbbc64')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:os-config-drive")
+ def test_list_servers_with_details_config_drive(self):
+ """Test list servers with config_drive property in response body."""
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ body = self.servers_client.list_servers(detail=True)['servers']
+ # If the first server contains "config_drive", then all the others do.
+ if 'config_drive' not in body[0]:
+ raise rbac_exceptions.RbacActionFailed(
+ '"config_drive" attribute not found in response body.')
+
+ @test.requires_ext(extension='os-config-drive', service='compute')
+ @decorators.idempotent_id('55c62ef7-b72b-4970-acc6-05b0a4316e5d')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:os-config-drive")
+ def test_show_server_config_drive(self):
+ """Test show server with config_drive property in response body."""
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ body = self.servers_client.show_server(self.server_id)['server']
+ if 'config_drive' not in body:
+ raise rbac_exceptions.RbacActionFailed(
+ '"config_drive" attribute not found in response body.')
+
@test.requires_ext(extension='os-deferred-delete', service='compute')
@decorators.idempotent_id('189bfed4-1e6d-475c-bb8c-d57e60895391')
@rbac_rule_validation.action(
@@ -210,12 +239,34 @@
def test_show_server_usage(self):
"""Test show server usage, part of os-server-usage.
- TODO(felipemonteiro): Once multiple policy test is supported, this
+ TODO(felipemonteiro): Once multiple policy testing is supported, this
test can be combined with the generic test for showing a server.
"""
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.servers_client.show_server(self.server_id)
+ @test.requires_ext(extension='os-simple-tenant-usage', service='compute')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:os-simple-tenant-usage:list")
+ @decorators.idempotent_id('2aef094f-0452-4df6-a66a-0ec22a92b16e')
+ def test_list_simple_tenant_usages(self):
+ """Test list tenant usages, part of os-simple-tenant-usage."""
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.tenant_usages_client.list_tenant_usages()
+
+ @test.requires_ext(extension='os-simple-tenant-usage', service='compute')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:os-simple-tenant-usage:show")
+ @decorators.idempotent_id('fe7eacda-15c4-4bf7-93ef-1091c4546a9d')
+ def test_show_simple_tenant_usage(self):
+ """Test show tenant usage, part of os-simple-tenant-usage."""
+ tenant_id = self.auth_provider.credentials.tenant_id
+
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.tenant_usages_client.show_tenant_usage(tenant_id=tenant_id)
+
@testtools.skipUnless(CONF.compute_feature_enabled.suspend,
"Suspend compute feature is not available.")
@decorators.idempotent_id('b775930f-237c-431c-83ae-d33ed1b9700b')
@@ -246,3 +297,149 @@
self.servers_client.resume_server(self.server_id)
waiters.wait_for_server_status(
self.os_admin.servers_client, self.server_id, 'ACTIVE')
+
+
+class MiscPolicyActionsNetworkRbacTest(rbac_base.BaseV2ComputeRbacTest):
+ """Test multiple policy actions that require a server to be created.
+
+ Only applies to:
+ * policy "families" that require server creation
+ * small policy "families" -- i.e. containing one to three policies
+ * tests that require network resources
+ """
+
+ @classmethod
+ def skip_checks(cls):
+ super(MiscPolicyActionsNetworkRbacTest, cls).skip_checks()
+ # All tests below require Neutron availability.
+ if not CONF.service_available.neutron:
+ raise cls.skipException(
+ '%s skipped as Neutron is required' % cls.__name__)
+
+ @classmethod
+ def setup_credentials(cls):
+ cls.prepare_instance_network()
+ super(MiscPolicyActionsNetworkRbacTest, cls).setup_credentials()
+
+ @classmethod
+ def resource_setup(cls):
+ super(MiscPolicyActionsNetworkRbacTest, cls).resource_setup()
+ cls.server = cls.create_test_server(wait_until='ACTIVE')
+
+ def _attach_interface_to_server(self):
+ interface = self.interfaces_client.create_interface(
+ self.server['id'])['interfaceAttachment']
+ waiters.wait_for_interface_status(
+ self.os_admin.interfaces_client, self.server['id'],
+ interface['port_id'], 'ACTIVE')
+ self.addCleanup(
+ test_utils.call_and_ignore_notfound_exc,
+ self.interfaces_client.delete_interface,
+ self.server['id'], interface['port_id'])
+ return interface
+
+ @testtools.skipUnless(CONF.compute_feature_enabled.interface_attach,
+ "Interface attachment is not available.")
+ @test.requires_ext(extension='os-attach-interfaces', service='compute')
+ @decorators.idempotent_id('ddf53cb6-4a0a-4e5a-91e3-6c32aaa3b9b6')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:os-attach-interfaces")
+ def test_list_interfaces(self):
+ """Test list interfaces, part of os-attach-interfaces."""
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.interfaces_client.list_interfaces(
+ self.server['id'])['interfaceAttachments']
+
+ @testtools.skipUnless(CONF.compute_feature_enabled.interface_attach,
+ "Interface attachment is not available.")
+ @test.requires_ext(extension='os-attach-interfaces', service='compute')
+ @decorators.idempotent_id('d2d3a24d-4738-4bce-a287-36d664746cde')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:os-attach-interfaces:create")
+ def test_create_interface(self):
+ """Test create interface, part of os-attach-interfaces."""
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self._attach_interface_to_server()
+
+ @testtools.skipUnless(CONF.compute_feature_enabled.interface_attach,
+ "Interface attachment is not available.")
+ @test.requires_ext(extension='os-attach-interfaces', service='compute')
+ @decorators.idempotent_id('55b05692-ed44-4608-a84c-cd4219c82799')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:os-attach-interfaces:delete")
+ def test_delete_interface(self):
+ """Test delete interface, part of os-attach-interfaces."""
+ interface = self._attach_interface_to_server()
+
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.interfaces_client.delete_interface(self.server['id'],
+ interface['port_id'])
+
+ @decorators.idempotent_id('6886d360-0d86-4760-b1a3-882d81fbebcc')
+ @test.requires_ext(extension='os-ips', service='compute')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:ips:index")
+ def test_list_addresses(self):
+ """Test list server addresses, part of ips policy family."""
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.servers_client.list_addresses(self.server['id'])['addresses']
+
+ @decorators.idempotent_id('fa43e7e5-0db9-48eb-9c6b-c11eb766b8e4')
+ @test.requires_ext(extension='os-ips', service='compute')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:ips:show")
+ def test_list_addresses_by_network(self):
+ """Test list server addresses by network, part of ips policy family."""
+ addresses = self.servers_client.list_addresses(self.server['id'])[
+ 'addresses']
+ address = next(iter(addresses))
+
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.servers_client.list_addresses_by_network(
+ self.server['id'], address)[address]
+
+ @testtools.skipUnless(CONF.compute_feature_enabled.interface_attach,
+ "Interface attachment is not available.")
+ @test.requires_ext(extension='os-multinic', service='compute')
+ @rbac_rule_validation.action(
+ service="nova", rule="os_compute_api:os-multinic")
+ @decorators.idempotent_id('bd3e2c74-130a-40f0-8085-124d93fe67da')
+ def test_add_fixed_ip(self):
+ """Test add fixed ip to server network, part of os-multinic."""
+ interfaces = (self.interfaces_client.list_interfaces(self.server['id'])
+ ['interfaceAttachments'])
+ if interfaces:
+ network_id = interfaces[0]['net_id']
+ else:
+ network_id = self.interfaces_client.create_interface(
+ self.server['id'])['interfaceAttachment']['net_id']
+
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.servers_client.add_fixed_ip(self.server['id'],
+ networkId=network_id)
+
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:os-virtual-interfaces")
+ @decorators.idempotent_id('fc719ae3-0f73-4689-8378-1b841f0f2818')
+ def test_list_virtual_interfaces(self):
+ """Test list virtual interfaces, part of os-virtual-interfaces.
+
+ If Neutron is available, then call the API and expect it to fail
+ with a 400 BadRequest (policy enforcement is done before that happens).
+
+ For more information, see:
+ https://developer.openstack.org/api-ref/compute/#servers-virtual-interfaces-servers-os-virtual-interfaces-deprecated
+ """
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ if CONF.service_available.neutron:
+ msg = "Listing virtual interfaces is not supported by this cloud."
+ with self.assertRaisesRegex(lib_exc.BadRequest, msg):
+ self.servers_client.list_virtual_interfaces(self.server['id'])
+ else:
+ self.servers_client.list_virtual_interfaces(self.server['id'])
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
index c6ac1c5..f5d9dc0 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
@@ -219,7 +219,7 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.servers_client.list_security_groups_by_server(self.server['id'])
- @test.attr(type=["slow"])
+ @decorators.attr(type=["slow"])
@rbac_rule_validation.action(
service="nova",
rule="os_compute_api:os-security-groups")
@@ -233,7 +233,7 @@
self.servers_client.remove_security_group,
self.server['id'], name=sg_name)
- @test.attr(type=["slow"])
+ @decorators.attr(type=["slow"])
@rbac_rule_validation.action(
service="nova",
rule="os_compute_api:os-security-groups")
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_virtual_interfaces_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_virtual_interfaces_rbac.py
deleted file mode 100644
index 1480444..0000000
--- a/patrole_tempest_plugin/tests/api/compute/test_server_virtual_interfaces_rbac.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2017 AT&T Corporation.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest import config
-from tempest.lib import decorators
-from tempest.lib import exceptions
-
-from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.compute import rbac_base as base
-
-CONF = config.CONF
-
-
-class ServerVirtualInterfacesRbacTest(base.BaseV2ComputeRbacTest):
-
- @rbac_rule_validation.action(
- service="nova",
- rule="os_compute_api:os-virtual-interfaces")
- @decorators.idempotent_id('fc719ae3-0f73-4689-8378-1b841f0f2818')
- def test_list_virtual_interfaces(self):
- server = self.create_test_server(wait_until='ACTIVE')
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-
- if CONF.service_available.neutron:
- msg = "Listing virtual interfaces is not supported by this cloud."
- with self.assertRaisesRegex(exceptions.BadRequest, msg):
- self.servers_client.list_virtual_interfaces(server['id'])
- else:
- self.servers_client.list_virtual_interfaces(server['id'])
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_volume_attachments_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_volume_attachments_rbac.py
index 98dbee5..943cb69 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_volume_attachments_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_volume_attachments_rbac.py
@@ -17,7 +17,6 @@
from tempest import config
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
-from tempest import test
from patrole_tempest_plugin import rbac_rule_validation
from patrole_tempest_plugin.tests.api.compute import rbac_base
@@ -30,7 +29,7 @@
@classmethod
def setup_clients(cls):
super(ServerVolumeAttachmentRbacTest, cls).setup_clients()
- cls.volumes_client = cls.os_primary.volumes_client
+ cls.volumes_client = cls.os_primary.volumes_client_latest
@classmethod
def skip_checks(cls):
@@ -68,11 +67,12 @@
@decorators.idempotent_id('997df9c2-6e54-47b6-ab74-e4fdb500f385')
def test_show_volume_attachment(self):
attachment = self.attach_volume(self.server, self.volume)
+
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.servers_client.show_volume_attachment(
self.server['id'], attachment['id'])
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@rbac_rule_validation.action(
service="nova",
rule="os_compute_api:os-volumes-attachments:update")
@@ -80,6 +80,7 @@
def test_update_volume_attachment(self):
attachment = self.attach_volume(self.server, self.volume)
alt_volume = self.create_volume()
+
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.servers_client.update_attached_volume(
self.server['id'], attachment['id'], volumeId=alt_volume['id'])
@@ -102,6 +103,7 @@
@decorators.idempotent_id('12b03e90-d087-46af-9c4d-507d021c4984')
def test_delete_volume_attachment(self):
self.attach_volume(self.server, self.volume)
+
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.servers_client.detach_volume(self.server['id'], self.volume['id'])
waiters.wait_for_volume_resource_status(self.volumes_client,
diff --git a/patrole_tempest_plugin/tests/api/compute/test_simple_tenant_usage_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_simple_tenant_usage_rbac.py
deleted file mode 100644
index ae2a0fd..0000000
--- a/patrole_tempest_plugin/tests/api/compute/test_simple_tenant_usage_rbac.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright 2017 AT&T Corporation.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.lib import decorators
-from tempest import test
-
-from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.compute import rbac_base
-
-
-class SimpleTenantUsageRbacTest(rbac_base.BaseV2ComputeRbacTest):
-
- @classmethod
- def setup_clients(cls):
- super(SimpleTenantUsageRbacTest, cls).setup_clients()
- cls.tenant_usages_client = cls.os_primary.tenant_usages_client
-
- @classmethod
- def skip_checks(cls):
- super(SimpleTenantUsageRbacTest, cls).skip_checks()
- if not test.is_extension_enabled('os-simple-tenant-usage', 'compute'):
- msg = ("%s skipped as os-simple-tenant-usage not "
- "enabled." % cls.__name__)
- raise cls.skipException(msg)
-
- @rbac_rule_validation.action(
- service="nova",
- rule="os_compute_api:os-simple-tenant-usage:list")
- @decorators.idempotent_id('2aef094f-0452-4df6-a66a-0ec22a92b16e')
- def test_simple_tenant_usage_list(self):
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.tenant_usages_client.list_tenant_usages()
-
- @rbac_rule_validation.action(
- service="nova",
- rule="os_compute_api:os-simple-tenant-usage:show")
- @decorators.idempotent_id('fe7eacda-15c4-4bf7-93ef-1091c4546a9d')
- def test_simple_tenant_usage_show(self):
- # A server must be created in order for usage activity to exist; else
- # the validation method in the API call throws an error.
- self.create_test_server(wait_until='ACTIVE')['id']
- tenant_id = self.auth_provider.credentials.tenant_id
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.tenant_usages_client.show_tenant_usage(tenant_id=tenant_id)
diff --git a/patrole_tempest_plugin/tests/api/compute/test_volume_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_volume_rbac.py
index b775872..0935c95 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_volume_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_volume_rbac.py
@@ -34,6 +34,11 @@
max_microversion = '2.35'
@classmethod
+ def setup_clients(cls):
+ super(VolumeRbacTest, cls).setup_clients()
+ cls.admin_volumes_client = cls.os_admin.volumes_client_latest
+
+ @classmethod
def resource_setup(cls):
super(VolumeRbacTest, cls).resource_setup()
cls.volume = cls.create_volume()
@@ -56,7 +61,7 @@
size=CONF.volume.volume_size)['volume']
# Use the admin volumes client to wait, because waiting involves
# calling show API action which enforces a different policy.
- waiters.wait_for_volume_resource_status(self.os_admin.volumes_client,
+ waiters.wait_for_volume_resource_status(self.admin_volumes_client,
volume['id'], 'available')
# Use non-deprecated volumes_client for deletion.
self.addCleanup(self.volumes_client.delete_volume, volume['id'])
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_ep_filter_groups_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_ep_filter_groups_rbac.py
index e7b73b6..00c9f55 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_ep_filter_groups_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_ep_filter_groups_rbac.py
@@ -73,7 +73,7 @@
@decorators.idempotent_id('5c16368d-1485-4c28-9803-db3fa3510623')
@rbac_rule_validation.action(service="keystone",
- rule="identity:check_endpoint_group")
+ rule="identity:get_endpoint_group")
def test_check_endpoint_group(self):
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.endpoint_groups_client.check_endpoint_group(
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 c25af25..0532b2d 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
@@ -16,7 +16,6 @@
from tempest import config
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
from patrole_tempest_plugin import rbac_rule_validation
from patrole_tempest_plugin.tests.api.identity import rbac_base
@@ -77,7 +76,7 @@
trustee_user_id=self.trustee_user_id)
@decorators.idempotent_id('bd72d22a-6e11-4840-bd93-17b382e7f0e0')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@rbac_rule_validation.action(
service="keystone",
rule="identity:create_trust",
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 b666a2d..e8e3ef8 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
@@ -33,6 +33,7 @@
def setup_clients(cls):
super(VolumesActionsRbacTest, 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):
@@ -54,7 +55,7 @@
server['id'], volumeId=volume_id,
device='/dev/%s' % CONF.compute.volume_device_name)
waiters.wait_for_volume_resource_status(
- self.os_admin.volumes_client, volume_id, 'in-use')
+ self.admin_volumes_client, volume_id, 'in-use')
self.addCleanup(self._detach_volume, volume_id)
def _detach_volume(self, volume_id=None):
@@ -63,7 +64,7 @@
self.volumes_client.detach_volume(volume_id)
waiters.wait_for_volume_resource_status(
- self.os_admin.volumes_client, volume_id, 'available')
+ self.admin_volumes_client, volume_id, 'available')
@test.services('compute')
@rbac_rule_validation.action(service="cinder", rule="volume:attach")
@@ -73,8 +74,8 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self._attach_volume(server)
- @test.attr(type=["slow"])
@test.services('compute')
+ @decorators.attr(type='slow')
@rbac_rule_validation.action(service="cinder", rule="volume:detach")
@decorators.idempotent_id('5a042f6a-688b-42e6-a02e-fe5c47b89b07')
def test_detach_volume_from_instance(self):
@@ -84,7 +85,7 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self._detach_volume()
- @test.attr(type=["slow"])
+ @decorators.attr(type=["slow"])
@test.services('image')
@rbac_rule_validation.action(
service="cinder",
@@ -106,7 +107,7 @@
image_id)
waiters.wait_for_image_status(self.admin_image_client, image_id,
'active')
- waiters.wait_for_volume_resource_status(self.os_admin.volumes_client,
+ waiters.wait_for_volume_resource_status(self.admin_volumes_client,
self.volume['id'], 'available')
@rbac_rule_validation.action(service="cinder",
@@ -161,7 +162,7 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.volumes_client.retype_volume(volume['id'], new_type=vol_type)
waiters.wait_for_volume_retype(
- self.os_admin.volumes_client, volume['id'], vol_type)
+ self.admin_volumes_client, volume['id'], vol_type)
@rbac_rule_validation.action(
service="cinder",
@@ -204,7 +205,7 @@
self.volumes_client.force_detach_volume(
volume['id'], connector=None,
attachment_id=attachment['attachment_id'])
- waiters.wait_for_volume_resource_status(self.os_admin.volumes_client,
+ waiters.wait_for_volume_resource_status(self.admin_volumes_client,
volume['id'], 'available')
@@ -221,8 +222,9 @@
def setup_clients(cls):
super(VolumesActionsV310RbacTest, cls).setup_clients()
cls.admin_image_client = cls.os_admin.image_client_v2
+ cls.admin_volumes_client = cls.os_admin.volumes_client_latest
- @test.attr(type=["slow"])
+ @decorators.attr(type=["slow"])
@test.services('image')
@rbac_rule_validation.action(
service="cinder",
@@ -243,7 +245,7 @@
image_id)
waiters.wait_for_image_status(self.admin_image_client, image_id,
'active')
- waiters.wait_for_volume_resource_status(self.os_admin.volumes_client,
+ waiters.wait_for_volume_resource_status(self.admin_volumes_client,
volume['id'], 'available')
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 6e9812b..851d468 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
@@ -29,7 +29,7 @@
@classmethod
def setup_clients(cls):
super(VolumeQuotasRbacTest, cls).setup_clients()
- cls.quotas_client = cls.os_primary.volume_quotas_client
+ cls.quotas_client = cls.os_primary.volume_quotas_v2_client
@rbac_rule_validation.action(service="cinder",
rule="volume_extension:quotas:show")
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 212482c..405d02b 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
@@ -23,13 +23,11 @@
class VolumesTransfersRbacTest(rbac_base.BaseVolumeRbacTest):
- credentials = ['primary', 'admin']
-
@classmethod
def setup_clients(cls):
super(VolumesTransfersRbacTest, cls).setup_clients()
cls.transfers_client = cls.os_primary.volume_transfers_v2_client
- cls.adm_volumes_client = cls.os_admin.volumes_v2_client
+ cls.admin_volumes_client = cls.os_admin.volumes_client_latest
@classmethod
def resource_setup(cls):
@@ -43,7 +41,7 @@
test_utils.call_and_ignore_notfound_exc(
self.transfers_client.delete_volume_transfer, transfer['id'])
waiters.wait_for_volume_resource_status(
- self.adm_volumes_client, self.volume['id'], 'available')
+ self.admin_volumes_client, self.volume['id'], 'available')
def _create_transfer(self):
transfer = self.transfers_client.create_volume_transfer(
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 97eaab7..210901c 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
@@ -54,23 +54,26 @@
self.vol_type['id'], self.spec_key)
@decorators.idempotent_id('76c36be2-2b6c-4acf-9aac-c9dc5c17cdbe')
- @rbac_rule_validation.action(service="cinder",
- rule="volume_extension:types_extra_specs")
+ @rbac_rule_validation.action(
+ service="cinder",
+ rule="volume_extension:types_extra_specs:index")
def test_list_volume_types_extra_specs(self):
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.volume_types_client.list_volume_types_extra_specs(
self.vol_type['id'])['extra_specs']
- @rbac_rule_validation.action(service="cinder",
- rule="volume_extension:types_extra_specs")
+ @rbac_rule_validation.action(
+ service="cinder",
+ rule="volume_extension:types_extra_specs:create")
@decorators.idempotent_id('eea40251-990b-49b0-99ae-10e4585b479b')
def test_create_volume_type_extra_specs(self):
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self._create_volume_type_extra_specs(ignore_not_found=True)
@decorators.idempotent_id('e2dcc9c6-2fef-431d-afaf-92b45bc76d1a')
- @rbac_rule_validation.action(service="cinder",
- rule="volume_extension:types_extra_specs")
+ @rbac_rule_validation.action(
+ service="cinder",
+ rule="volume_extension:types_extra_specs:show")
def test_show_volume_type_extra_specs(self):
self._create_volume_type_extra_specs()
@@ -79,8 +82,9 @@
self.vol_type['id'], self.spec_key)
@decorators.idempotent_id('93001912-f938-41c7-8787-62dc7010fd52')
- @rbac_rule_validation.action(service="cinder",
- rule="volume_extension:types_extra_specs")
+ @rbac_rule_validation.action(
+ service="cinder",
+ rule="volume_extension:types_extra_specs:delete")
def test_delete_volume_type_extra_specs(self):
self._create_volume_type_extra_specs(ignore_not_found=True)
@@ -89,8 +93,9 @@
self.vol_type['id'], self.spec_key)
@decorators.idempotent_id('0a444437-7402-4fbe-a18a-93af2ee00618')
- @rbac_rule_validation.action(service="cinder",
- rule="volume_extension:types_extra_specs")
+ @rbac_rule_validation.action(
+ service="cinder",
+ rule="volume_extension:types_extra_specs:update")
def test_update_volume_type_extra_specs(self):
self._create_volume_type_extra_specs()
update_extra_specs = {self.spec_key: "val2"}
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 e6944cc..01c1b08 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
@@ -21,7 +21,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
-from tempest import test
from patrole_tempest_plugin import rbac_rule_validation
from patrole_tempest_plugin.tests.api.volume import rbac_base
@@ -41,6 +40,11 @@
if not CONF.volume_feature_enabled.backup:
raise cls.skipException("Cinder backup feature disabled")
+ @classmethod
+ def setup_clients(cls):
+ super(VolumesBackupsRbacTest, cls).setup_clients()
+ cls.admin_backups_client = cls.os_admin.backups_v2_client
+
def _decode_url(self, backup_url):
return json.loads(base64.decode_as_text(backup_url))
@@ -53,7 +57,7 @@
backup.update(changes)
return self._encode_backup(backup)
- @test.attr(type=["slow"])
+ @decorators.attr(type='slow')
@rbac_rule_validation.action(service="cinder",
rule="backup:create")
@decorators.idempotent_id('6887ec94-0bcf-4ab7-b30f-3808a4b5a2a5')
@@ -61,7 +65,7 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.create_backup(volume_id=self.volume['id'])
- @test.attr(type=["slow"])
+ @decorators.attr(type='slow')
@rbac_rule_validation.action(service="cinder",
rule="backup:get")
@decorators.idempotent_id('abd92bdd-b0fb-4dc4-9cfc-de9e968f8c8a')
@@ -95,21 +99,22 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.backups_client.reset_backup_status(backup_id=backup['id'],
status='error')
- waiters.wait_for_volume_resource_status(self.os_admin.backups_client,
+ waiters.wait_for_volume_resource_status(self.admin_backups_client,
backup['id'], 'error')
- @test.attr(type=["slow"])
+ @decorators.attr(type='slow')
@rbac_rule_validation.action(service="cinder",
rule="backup:restore")
@decorators.idempotent_id('9c794bf9-2446-4f41-8fe0-80b71e757f9d')
def test_restore_backup(self):
backup = self.create_backup(volume_id=self.volume['id'])
+
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
restore = self.backups_client.restore_backup(backup['id'])['restore']
waiters.wait_for_volume_resource_status(
- self.os_admin.backups_client, restore['backup_id'], 'available')
+ self.admin_backups_client, restore['backup_id'], 'available')
- @test.attr(type=["slow"])
+ @decorators.attr(type='slow')
@rbac_rule_validation.action(service="cinder",
rule="backup:delete")
@decorators.idempotent_id('d5d0c6a2-413d-437e-a73f-4bf2b41a20ed')
@@ -121,7 +126,7 @@
volume_id=self.volume['id'])['backup']
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
self.backups_client.delete_backup, backup['id'])
- waiters.wait_for_volume_resource_status(self.os_admin.backups_client,
+ waiters.wait_for_volume_resource_status(self.admin_backups_client,
backup['id'], 'available')
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
@@ -129,7 +134,7 @@
# Wait for deletion so error isn't thrown during clean up.
self.backups_client.wait_for_resource_deletion(backup['id'])
- @test.attr(type=["slow"])
+ @decorators.attr(type='slow')
@rbac_rule_validation.action(service="cinder",
rule="backup:backup-export")
@decorators.idempotent_id('e984ec8d-e8eb-485c-98bc-f1856020303c')
@@ -138,7 +143,7 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.backups_client.export_backup(backup['id'])['backup-record']
- @test.attr(type=["slow"])
+ @decorators.attr(type='slow')
@rbac_rule_validation.action(service="cinder",
rule="backup:backup-import")
@decorators.idempotent_id('1e70f039-4556-44cc-9cc1-edf2b7ed648b')
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volumes_extend_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volumes_extend_rbac.py
index 5709669..971e079 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
@@ -26,6 +26,11 @@
class VolumesExtendRbacTest(rbac_base.BaseVolumeRbacTest):
@classmethod
+ def setup_clients(cls):
+ super(VolumesExtendRbacTest, cls).setup_clients()
+ cls.admin_volumes_client = cls.os_admin.volumes_client_latest
+
+ @classmethod
def resource_setup(cls):
super(VolumesExtendRbacTest, cls).resource_setup()
# Create a test shared volume for tests
@@ -40,7 +45,7 @@
self.volumes_client.extend_volume(self.volume['id'],
new_size=extend_size)
waiters.wait_for_volume_resource_status(
- self.os_admin.volumes_client, self.volume['id'], 'available')
+ self.admin_volumes_client, self.volume['id'], 'available')
class VolumesExtendV3RbacTest(VolumesExtendRbacTest):
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 8e71ee8..bea3a46 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
@@ -42,6 +42,7 @@
def setup_clients(cls):
super(VolumesManageRbacTest, cls).setup_clients()
cls.volume_manage_client = cls.os_primary.volume_manage_v2_client
+ cls.admin_volumes_client = cls.os_admin.volumes_client_latest
def _manage_volume(self, org_volume):
# Manage volume
@@ -59,14 +60,14 @@
new_volume_id = self.volume_manage_client.manage_volume(
**new_volume_ref)['volume']['id']
- waiters.wait_for_volume_resource_status(self.os_admin.volumes_client,
+ waiters.wait_for_volume_resource_status(self.admin_volumes_client,
new_volume_id, 'available')
self.addCleanup(self.delete_volume,
self.volumes_client, new_volume_id)
def _unmanage_volume(self, volume):
self.volumes_client.unmanage_volume(volume['id'])
- self.volumes_client.wait_for_resource_deletion(volume['id'])
+ self.admin_volumes_client.wait_for_resource_deletion(volume['id'])
@rbac_rule_validation.action(
service="cinder",
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 422a3db..13ced96 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
@@ -32,6 +32,11 @@
raise cls.skipException("Cinder volume snapshots are disabled")
@classmethod
+ def setup_clients(cls):
+ super(VolumesSnapshotRbacTest, cls).setup_clients()
+ cls.admin_snapshots_client = cls.os_admin.snapshots_v2_client
+
+ @classmethod
def resource_setup(cls):
super(VolumesSnapshotRbacTest, cls).resource_setup()
# Create a test shared volume for tests
@@ -77,8 +82,7 @@
self.snapshots_client.update_snapshot(
self.snapshot['id'], **params)['snapshot']
waiters.wait_for_volume_resource_status(
- self.os_admin.snapshots_client,
- self.snapshot['id'], 'available')
+ self.admin_snapshots_client, self.snapshot['id'], 'available')
@rbac_rule_validation.action(service="cinder",
rule="volume:get_all_snapshots")
@@ -99,7 +103,7 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
# Delete the snapshot
self.snapshots_client.delete_snapshot(temp_snapshot['id'])
- self.os_admin.snapshots_client.wait_for_resource_deletion(
+ self.admin_snapshots_client.wait_for_resource_deletion(
temp_snapshot['id'])
diff --git a/patrole_tempest_plugin/tests/unit/test_rbac_policy_parser.py b/patrole_tempest_plugin/tests/unit/test_rbac_policy_parser.py
index 09de6bf..e3a4429 100644
--- a/patrole_tempest_plugin/tests/unit/test_rbac_policy_parser.py
+++ b/patrole_tempest_plugin/tests/unit/test_rbac_policy_parser.py
@@ -28,12 +28,21 @@
class RbacPolicyTest(base.TestCase):
+ services = {
+ 'services': [
+ {'name': 'custom_rbac_policy'},
+ {'name': 'admin_rbac_policy'},
+ {'name': 'alt_admin_rbac_policy'},
+ {'name': 'tenant_rbac_policy'},
+ {'name': 'test_service'}
+ ]
+ }
+
def setUp(self):
super(RbacPolicyTest, self).setUp()
- self.mock_admin_mgr = mock.patch.object(
- rbac_policy_parser, 'credentials').start()
- self.mock_path = mock.patch.object(
- rbac_policy_parser, 'os').start()
+ mock_admin_mgr = self.patchobject(rbac_policy_parser, 'credentials')
+ mock_admin_mgr.AdminManager().identity_services_v3_client.\
+ list_services.return_value = self.services
current_directory = os.path.dirname(os.path.realpath(__file__))
self.custom_policy_file = os.path.join(current_directory,
@@ -48,35 +57,13 @@
self.tenant_policy_file = os.path.join(current_directory,
'resources',
'tenant_rbac_policy.json')
- services = {
- 'services': [
- {'name': 'cinder', 'links': 'link', 'enabled': True,
- 'type': 'volume', 'id': 'id',
- 'description': 'description'},
- {'name': 'glance', 'links': 'link', 'enabled': True,
- 'type': 'image', 'id': 'id',
- 'description': 'description'},
- {'name': 'nova', 'links': 'link', 'enabled': True,
- 'type': 'compute', 'id': 'id',
- 'description': 'description'},
- {'name': 'keystone', 'links': 'link', 'enabled': True,
- 'type': 'identity', 'id': 'id',
- 'description': 'description'},
- {'name': 'heat', 'links': 'link', 'enabled': True,
- 'type': 'orchestration', 'id': 'id',
- 'description': 'description'},
- {'name': 'neutron', 'links': 'link', 'enabled': True,
- 'type': 'networking', 'id': 'id',
- 'description': 'description'},
- {'name': 'test_service', 'links': 'link', 'enabled': True,
- 'type': 'unit_test', 'id': 'id',
- 'description': 'description'}
- ]
- }
- self.mock_admin_mgr.AdminManager.return_value.\
- identity_services_v3_client.list_services.return_value = \
- services
+ CONF.set_override(
+ 'custom_policy_files',
+ [os.path.join(current_directory, 'resources', '%s.json')],
+ group='rbac')
+ self.addCleanup(CONF.clear_override, 'custom_policy_files',
+ group='rbac')
def _get_fake_policy_rule(self, name, rule):
fake_rule = mock.Mock(check=rule)
@@ -90,9 +77,8 @@
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
- self.mock_path.path.join.return_value = self.custom_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test_service")
+ test_tenant_id, test_user_id, "custom_rbac_policy")
expected = {
'policy_action_1': ['two', 'four', 'six', 'eight'],
@@ -113,9 +99,8 @@
def test_admin_policy_file_with_admin_role(self):
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
- self.mock_path.path.join.return_value = self.admin_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test_service")
+ test_tenant_id, test_user_id, "admin_rbac_policy")
role = 'admin'
allowed_rules = [
@@ -134,9 +119,8 @@
def test_admin_policy_file_with_member_role(self):
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
- self.mock_path.path.join.return_value = self.admin_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test_service")
+ test_tenant_id, test_user_id, "admin_rbac_policy")
role = 'Member'
allowed_rules = [
@@ -153,12 +137,11 @@
allowed = parser.allowed(rule, role)
self.assertFalse(allowed)
- def test_admin_policy_file_with_context_is_admin(self):
+ def test_alt_admin_policy_file_with_context_is_admin(self):
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
- self.mock_path.path.join.return_value = self.alt_admin_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test_service")
+ test_tenant_id, test_user_id, "alt_admin_rbac_policy")
role = 'fake_admin'
allowed_rules = ['non_admin_rule']
@@ -193,9 +176,8 @@
"""
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
- self.mock_path.path.join.return_value = self.tenant_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test_service")
+ test_tenant_id, test_user_id, "tenant_rbac_policy")
# Check whether Member role can perform expected actions.
allowed_rules = ['rule1', 'rule2', 'rule3', 'rule4']
@@ -254,7 +236,7 @@
service)
m_log.debug.assert_called_once_with(
- '%s is NOT a valid service.', 'invalid_service')
+ '%s is NOT a valid service.', service)
@mock.patch.object(rbac_policy_parser, 'LOG', autospec=True)
def test_service_is_none_raises_exception(self, m_log):
@@ -274,9 +256,8 @@
def test_invalid_policy_rule_throws_rbac_parsing_exception(self, m_log):
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
- self.mock_path.path.join.return_value = self.custom_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test_service")
+ test_tenant_id, test_user_id, "custom_rbac_policy")
fake_rule = 'fake_rule'
expected_message = "Policy action: {0} not found in policy file: {1}."\
@@ -291,10 +272,9 @@
def test_unknown_exception_throws_rbac_parsing_exception(self, m_log):
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
- self.mock_path.path.join.return_value = self.custom_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test_service")
+ test_tenant_id, test_user_id, "custom_rbac_policy")
parser.rules = mock.MagicMock(
**{'__getitem__.return_value.side_effect': Exception(
mock.sentinel.error)})
@@ -327,12 +307,10 @@
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
- self.mock_path.path.join.return_value = self.tenant_policy_file
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test_service")
+ test_tenant_id, test_user_id, "tenant_rbac_policy")
policy_data = parser._get_policy_data('fake_service')
-
self.assertIsInstance(policy_data, str)
actual_policy_data = json.loads(policy_data)
@@ -346,7 +324,6 @@
"rule4": "user_id:%(user_id)s",
"admin_tenant_rule": "role:admin and tenant_id:%(tenant_id)s",
"admin_user_rule": "role:admin and user_id:%(user_id)s"
-
}
self.assertEqual(expected_policy_data, actual_policy_data)
@@ -371,12 +348,10 @@
test_tenant_id = mock.sentinel.tenant_id
test_user_id = mock.sentinel.user_id
- self.mock_path.path.join.return_value = self.tenant_policy_file
+
parser = rbac_policy_parser.RbacPolicyParser(
- test_tenant_id, test_user_id, "test_service")
-
+ test_tenant_id, test_user_id, 'tenant_rbac_policy')
policy_data = parser._get_policy_data('fake_service')
-
self.assertIsInstance(policy_data, str)
actual_policy_data = json.loads(policy_data)
@@ -392,23 +367,18 @@
self.assertEqual(expected_policy_data, actual_policy_data)
- @mock.patch.object(rbac_policy_parser, 'credentials', autospec=True)
@mock.patch.object(rbac_policy_parser, 'stevedore', autospec=True)
- def test_get_policy_data_cannot_find_policy(self, mock_stevedore,
- mock_creds):
+ def test_get_policy_data_cannot_find_policy(self, mock_stevedore):
mock_stevedore.named.NamedExtensionManager.return_value = None
- mock_creds.AdminManager.return_value.identity_services_v3_client.\
- list_services.return_value = {
- 'services': [{'name': 'test_service'}]}
- self.mock_path.path.join.return_value = '/etc/test_service/policy.json'
e = self.assertRaises(rbac_exceptions.RbacParsingException,
rbac_policy_parser.RbacPolicyParser,
None, None, 'test_service')
expected_error = \
'Policy file for {0} service neither found in code '\
- 'nor at {1}.'.format('test_service',
- '/etc/test_service/policy.json')
+ 'nor at {1}.'.format(
+ 'test_service',
+ [CONF.rbac.custom_policy_files[0] % 'test_service'])
self.assertIn(expected_error, str(e))
@@ -416,8 +386,6 @@
@mock.patch.object(rbac_policy_parser, 'stevedore', autospec=True)
def test_get_policy_data_without_valid_policy(self, mock_stevedore,
mock_json):
- self.mock_path.path.isfile.return_value = False
-
test_policy_action = mock.Mock(check='rule:bar')
test_policy_action.configure_mock(name='foo')
@@ -450,13 +418,12 @@
mock_json):
mock_stevedore.named.NamedExtensionManager.return_value = None
mock_json.loads.side_effect = ValueError
- self.mock_path.path.join.return_value = self.tenant_policy_file
e = self.assertRaises(rbac_exceptions.RbacParsingException,
rbac_policy_parser.RbacPolicyParser,
- None, None, 'test_service')
+ None, None, 'tenant_rbac_policy')
- expected_error = 'Policy file for {0} service neither found in code '\
- 'nor at {1}.'.format('test_service',
- self.tenant_policy_file)
-
+ expected_error = (
+ 'Policy file for {0} service neither found in code nor at {1}.'
+ .format('tenant_rbac_policy', [CONF.rbac.custom_policy_files[0]
+ % 'tenant_rbac_policy']))
self.assertIn(expected_error, str(e))
diff --git a/releasenotes/notes/dynamic-policy-file-discovery-104cbfc64b55d605.yaml b/releasenotes/notes/dynamic-policy-file-discovery-104cbfc64b55d605.yaml
new file mode 100644
index 0000000..59019cf
--- /dev/null
+++ b/releasenotes/notes/dynamic-policy-file-discovery-104cbfc64b55d605.yaml
@@ -0,0 +1,22 @@
+---
+features:
+ - |
+ Add new configuration option ``[rbac] custom_policy_files``,
+ allowing users to specify list of the paths to search for custom
+ 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.
+deprecations:
+ - |
+ Deprecate the following configuration options from ``[rbac]`` group:
+
+ * cinder_policy_file
+ * glance_policy_file
+ * keystone_policy_file
+ * neutron_policy_file
+ * nova_policy_file
+
+ It is better to use ``[rbac] custom_policy_files`` which supports
+ any OpenStack service.
diff --git a/requirements.txt b/requirements.txt
index 126a3dc..cd6a577 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,5 +7,5 @@
oslo.log>=3.22.0 # Apache-2.0
oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
oslo.policy>=1.23.0 # Apache-2.0
-tempest>=14.0.0 # Apache-2.0
+tempest>=16.1.0 # Apache-2.0
stevedore>=1.20.0 # Apache-2.0
diff --git a/setup.cfg b/setup.cfg
index 6fd4b8a..212db9e 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -27,6 +27,7 @@
source-dir = doc/source
build-dir = doc/build
all_files = 1
+warning-is-error = 1
[upload_sphinx]
upload-dir = doc/build/html
diff --git a/test-requirements.txt b/test-requirements.txt
index 3e03437..177d0fd 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -13,4 +13,4 @@
oslotest>=1.10.0 # Apache-2.0
oslo.policy>=1.23.0 # Apache-2.0
oslo.log>=3.22.0 # Apache-2.0
-tempest>=14.0.0 # Apache-2.0
+tempest>=16.1.0 # Apache-2.0