Merge "pbr: Remove unused translation sections from setup.cfg"
diff --git a/HACKING.rst b/HACKING.rst
index 28a977d..87e3b1f 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -33,12 +33,15 @@
The following are Patrole's specific Commandments:
- [P100] The ``rbac_rule_validation.action`` decorator must be applied to
- an RBAC test
+ all RBAC tests
- [P101] RBAC test filenames must end with "_rbac.py"; for example,
test_servers_rbac.py, not test_servers.py
- [P102] RBAC test class names must end in 'RbacTest'
- [P103] ``self.client`` must not be used as a client alias; this allows for
code that is more maintainable and easier to read
+- [P104] RBAC `plugin test class`_ names must end in 'PluginRbacTest'
+
+.. _plugin test class: https://github.com/openstack/patrole/tree/master/patrole_tempest_plugin/tests/api/network#neutron-plugin-tests
Role Overriding
---------------
diff --git a/devstack/plugin.sh b/devstack/plugin.sh
index a6259f4..502b68c 100644
--- a/devstack/plugin.sh
+++ b/devstack/plugin.sh
@@ -27,10 +27,10 @@
iniset $TEMPEST_CONFIG policy-feature-enabled volume_extension_volume_actions_reserve_policy False
iniset $TEMPEST_CONFIG policy-feature-enabled volume_extension_volume_actions_unreserve_policy False
+ # TODO(cl566n): Remove these once stable/pike becomes EOL.
# These policies were removed in Stein but are available in Pike.
iniset $TEMPEST_CONFIG policy-feature-enabled removed_nova_policies_stein False
-
- # TODO(cl566n): Policies used by Patrole testing. Remove these once stable/pike becomes EOL.
+ iniset $TEMPEST_CONFIG policy-feature-enabled removed_keystone_policies_stein False
iniset $TEMPEST_CONFIG policy-feature-enabled added_cinder_policies_stein False
fi
@@ -39,16 +39,17 @@
RBAC_TEST_ROLE="Member"
fi
+ # TODO(cl566n): Remove these once stable/queens becomes EOL.
# These policies were removed in Stein but are available in Queens.
iniset $TEMPEST_CONFIG policy-feature-enabled removed_nova_policies_stein False
-
- # TODO(cl566n): Policies used by Patrole testing. Remove these once stable/queens becomes EOL.
+ iniset $TEMPEST_CONFIG policy-feature-enabled removed_keystone_policies_stein False
iniset $TEMPEST_CONFIG policy-feature-enabled added_cinder_policies_stein False
fi
if [[ ${DEVSTACK_SERIES} == 'rocky' ]]; then
# TODO(cl566n): Policies used by Patrole testing. Remove these once stable/rocky becomes EOL.
iniset $TEMPEST_CONFIG policy-feature-enabled added_cinder_policies_stein False
+ iniset $TEMPEST_CONFIG policy-feature-enabled removed_keystone_policies_stein False
fi
iniset $TEMPEST_CONFIG patrole rbac_test_role $RBAC_TEST_ROLE
diff --git a/patrole_tempest_plugin/config.py b/patrole_tempest_plugin/config.py
index 56a786b..dc0ed25 100644
--- a/patrole_tempest_plugin/config.py
+++ b/patrole_tempest_plugin/config.py
@@ -162,11 +162,16 @@
help="""Are the Nova API extension policies available in the
cloud (e.g. os_compute_api:os-extended-availability-zone)? These policies were
removed in Stein because Nova API extension concept was removed in Pike."""),
+ cfg.BoolOpt('removed_keystone_policies_stein',
+ default=True,
+ help="""Are the obsolete Keystone policies available in the
+cloud (e.g. identity:[create|update|get|delete]_credential)? These policies
+were removed in Stein."""),
cfg.BoolOpt('added_cinder_policies_stein',
default=True,
- help="""Are the Cinder API extension policies available in the
-cloud (e.g. [create|update|get|delete]_encryption_policy)? These policies are
-added in Stein.""")
+ help="""Are the Cinder Stein policies available in the cloud
+(e.g. [create|update|get|delete]_encryption_policy)? These policies are added
+in Stein.""")
]
diff --git a/patrole_tempest_plugin/hacking/checks.py b/patrole_tempest_plugin/hacking/checks.py
index d106da8..1f06258 100644
--- a/patrole_tempest_plugin/hacking/checks.py
+++ b/patrole_tempest_plugin/hacking/checks.py
@@ -36,6 +36,8 @@
RULE_VALIDATION_DECORATOR = re.compile(
r'\s*@rbac_rule_validation.action\(.*')
IDEMPOTENT_ID_DECORATOR = re.compile(r'\s*@decorators\.idempotent_id\((.*)\)')
+PLUGIN_RBAC_TEST = re.compile(
+ r"class .+\(.+PluginRbacTest\)|class .+PluginRbacTest\(.+\)")
have_rbac_decorator = False
@@ -211,6 +213,44 @@
return 0, "Do not use 'self.client' as a service client alias"
+def no_plugin_rbac_test_suffix_in_plugin_test_class_name(physical_line,
+ filename):
+ """Check that Plugin RBAC class names end with "PluginRbacTest"
+
+ P104
+ """
+ suffix = "PluginRbacTest"
+ if "patrole_tempest_plugin/tests/api" in filename:
+ if PLUGIN_RBAC_TEST.match(physical_line):
+ subclass, superclass = physical_line.split('(')
+ subclass = subclass.split('class')[1].strip()
+ superclass = superclass.split(')')[0].strip()
+ if "." in superclass:
+ superclass = superclass.split(".")[1]
+
+ both_have = all(
+ clazz.endswith(suffix) for clazz in [subclass, superclass])
+ none_have = not any(
+ clazz.endswith(suffix) for clazz in [subclass, superclass])
+
+ if not (both_have or none_have):
+ if (subclass.startswith("Base") and
+ superclass.startswith("Base")):
+ return
+
+ # Case 1: Subclass of "BasePluginRbacTest" must end in `suffix`
+ # Case 2: Subclass that ends in `suffix` must inherit from base
+ # class ending in `suffix`.
+ if not subclass.endswith(suffix):
+ error = ("Plugin RBAC test subclasses must end in "
+ "'PluginRbacTest'")
+ return len(subclass) - 1, error
+ elif not superclass.endswith(suffix):
+ error = ("Plugin RBAC test subclasses must inherit from a "
+ "'PluginRbacTest' base class")
+ return len(superclass) - 1, error
+
+
def factory(register):
register(import_no_clients_in_api_tests)
register(no_setup_teardown_class_for_tests)
@@ -223,3 +263,4 @@
register(no_rbac_rule_validation_decorator)
register(no_rbac_suffix_in_test_filename)
register(no_rbac_test_suffix_in_test_class_name)
+ register(no_plugin_rbac_test_suffix_in_plugin_test_class_name)
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_credentials_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_credentials_rbac.py
index 977a830..26e34da 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_credentials_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_credentials_rbac.py
@@ -13,13 +13,21 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+
+from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from patrole_tempest_plugin import rbac_rule_validation
from patrole_tempest_plugin.tests.api.identity import rbac_base
+CONF = config.CONF
+
+@testtools.skipIf(
+ CONF.policy_feature_enabled.removed_keystone_policies_stein,
+ "This policy is unavailable in Stein so cannot be tested.")
class IdentityCredentialsV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
def _create_user_project_and_credential(self):
diff --git a/patrole_tempest_plugin/tests/api/network/test_address_scope_rbac.py b/patrole_tempest_plugin/tests/api/network/test_address_scope_rbac.py
index db28b82..893942e 100644
--- a/patrole_tempest_plugin/tests/api/network/test_address_scope_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_address_scope_rbac.py
@@ -23,18 +23,18 @@
from patrole_tempest_plugin.tests.api.network import rbac_base as base
-class AddressScopeRbacTest(base.BaseNetworkPluginRbacTest):
+class AddressScopePluginRbacTest(base.BaseNetworkPluginRbacTest):
@classmethod
def skip_checks(cls):
- super(AddressScopeRbacTest, cls).skip_checks()
+ super(AddressScopePluginRbacTest, cls).skip_checks()
if not utils.is_extension_enabled('address-scope', 'network'):
msg = "address-scope extension not enabled."
raise cls.skipException(msg)
@classmethod
def resource_setup(cls):
- super(AddressScopeRbacTest, cls).resource_setup()
+ super(AddressScopePluginRbacTest, cls).resource_setup()
cls.network = cls.create_network()
def _create_address_scope(self, name=None, **kwargs):
diff --git a/patrole_tempest_plugin/tests/api/network/test_qos_rbac.py b/patrole_tempest_plugin/tests/api/network/test_qos_rbac.py
index 20f9e61..aae326c 100644
--- a/patrole_tempest_plugin/tests/api/network/test_qos_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_qos_rbac.py
@@ -22,18 +22,18 @@
from patrole_tempest_plugin.tests.api.network import rbac_base as base
-class QosRbacTest(base.BaseNetworkPluginRbacTest):
+class QosPluginRbacTest(base.BaseNetworkPluginRbacTest):
@classmethod
def skip_checks(cls):
- super(QosRbacTest, cls).skip_checks()
+ super(QosPluginRbacTest, cls).skip_checks()
if not utils.is_extension_enabled('qos', 'network'):
msg = "qos extension not enabled."
raise cls.skipException(msg)
@classmethod
def resource_setup(cls):
- super(QosRbacTest, cls).resource_setup()
+ super(QosPluginRbacTest, cls).resource_setup()
cls.network = cls.create_network()
def create_policy(self, name=None):
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 a18a370..b7e45f9 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
@@ -54,12 +54,17 @@
def test_create_volume_transfer(self):
with self.rbac_utils.override_role(self):
self._create_transfer()
+ waiters.wait_for_volume_resource_status(
+ self.volumes_client, self.volume['id'], 'awaiting-transfer')
@rbac_rule_validation.action(service="cinder",
rules=["volume:get_transfer"])
@decorators.idempotent_id('7a0925d3-ed97-4c25-8299-e5cdabe2eb55')
def test_get_volume_transfer(self):
transfer = self._create_transfer()
+ waiters.wait_for_volume_resource_status(
+ self.volumes_client, self.volume['id'], 'awaiting-transfer')
+
with self.rbac_utils.override_role(self):
self.transfers_client.show_volume_transfer(transfer['id'])
@@ -82,15 +87,23 @@
@decorators.idempotent_id('987f2a11-d657-4984-a6c9-28f06c1cd014')
def test_accept_volume_transfer(self):
transfer = self._create_transfer()
+ waiters.wait_for_volume_resource_status(
+ self.volumes_client, self.volume['id'], 'awaiting-transfer')
+
with self.rbac_utils.override_role(self):
self.transfers_client.accept_volume_transfer(
transfer['id'], auth_key=transfer['auth_key'])
+ waiters.wait_for_volume_resource_status(self.volumes_client,
+ self.volume['id'], 'available')
@rbac_rule_validation.action(service="cinder",
rules=["volume:delete_transfer"])
@decorators.idempotent_id('4672187e-7fff-454b-832a-5c8865dda868')
def test_delete_volume_transfer(self):
transfer = self._create_transfer()
+ waiters.wait_for_volume_resource_status(
+ self.volumes_client, self.volume['id'], 'awaiting-transfer')
+
with self.rbac_utils.override_role(self):
self.transfers_client.delete_volume_transfer(transfer['id'])
waiters.wait_for_volume_resource_status(
diff --git a/patrole_tempest_plugin/tests/unit/test_hacking.py b/patrole_tempest_plugin/tests/unit/test_hacking.py
index 6096c24..d35b816 100644
--- a/patrole_tempest_plugin/tests/unit/test_hacking.py
+++ b/patrole_tempest_plugin/tests/unit/test_hacking.py
@@ -256,3 +256,53 @@
self.assertTrue(checks.no_client_alias_in_test_cases(
" cls.client",
"./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+
+ def test_no_plugin_rbac_test_suffix_in_plugin_test_class_name(self):
+ check = checks.no_plugin_rbac_test_suffix_in_plugin_test_class_name
+
+ # Passing cases: these do not inherit from "PluginRbacTest" base class.
+ self.assertFalse(check(
+ "class FakeRbacTest(BaseFakeRbacTest)",
+ "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+ self.assertFalse(check(
+ "class FakeRbacTest(base.BaseFakeRbacTest)",
+ "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+
+ # Passing cases: these **do** end in correct test class suffix.
+ self.assertFalse(check(
+ "class FakePluginRbacTest(BaseFakePluginRbacTest)",
+ "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+ self.assertFalse(check(
+ "class FakePluginRbacTest(base.BaseFakePluginRbacTest)",
+ "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+
+ # Passing cases: plugin base class inherits from another base class.
+ self.assertFalse(check(
+ "class BaseFakePluginRbacTest(base.BaseFakeRbacTest)",
+ "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+ self.assertFalse(check(
+ "class BaseFakePluginRbacTest(BaseFakeRbacTest)",
+ "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+
+ # Failing cases: these **do not** end in correct test class suffix.
+ # Case 1: RbacTest subclass doesn't end in PluginRbacTest.
+ self.assertTrue(check(
+ "class FakeRbacTest(base.BaseFakePluginRbacTest)",
+ "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+ self.assertTrue(check(
+ "class FakeRbacTest(BaseFakePluginRbacTest)",
+ "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+ self.assertTrue(check(
+ "class FakeRbacTest(BaseFakeNetworkPluginRbacTest)",
+ "./patrole_tempest_plugin/tests/api/network/fake_test_rbac.py"))
+ # Case 2: PluginRbacTest subclass doesn't inherit from
+ # BasePluginRbacTest.
+ self.assertTrue(check(
+ "class FakePluginRbacTest(base.BaseFakeRbacTest)",
+ "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+ self.assertTrue(check(
+ "class FakePluginRbacTest(BaseFakeRbacTest)",
+ "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+ self.assertTrue(check(
+ "class FakeNeutronPluginRbacTest(BaseFakeNeutronRbacTest)",
+ "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
diff --git a/releasenotes/notes/removed-keystone-policies-stein-feature-flag-6cfebbf64ed525d7.yaml b/releasenotes/notes/removed-keystone-policies-stein-feature-flag-6cfebbf64ed525d7.yaml
new file mode 100644
index 0000000..3bed287
--- /dev/null
+++ b/releasenotes/notes/removed-keystone-policies-stein-feature-flag-6cfebbf64ed525d7.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ Added new feature flag called ``removed_keystone_policies_stein`` under
+ the configuration group ``[policy-feature-enabled]`` for skipping Keystone
+ tests whose policies were removed in Stein. This feature flag is currently
+ applied to credentials-related policies, e.g.:
+ identity:[create|update|get|delete]_credential
diff --git a/tox.ini b/tox.ini
index ea9abf1..bc829d2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-minversion = 1.6
+minversion = 2.0
envlist = pep8,py35,py27
skipsdist = True