Merge "Add PDF building"
diff --git a/.zuul.yaml b/.zuul.yaml
index f9d972e..270300c 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -117,15 +117,6 @@
         TEMPEST_PLUGINS: /opt/stack/patrole
 
 - job:
-    name: patrole-member-pike
-    nodeset: openstack-single-node-xenial
-    parent: patrole-member
-    override-checkout: stable/pike
-    vars:
-      devstack_localrc:
-        TEMPEST_PLUGINS: /opt/stack/patrole
-
-- job:
     name: patrole-multinode-admin
     parent: patrole-base-multinode
     voting: false
@@ -220,7 +211,6 @@
         - patrole-member-stein
         - patrole-member-rocky
         - patrole-member-queens
-        - patrole-member-pike
         - patrole-multinode-admin
         - patrole-multinode-member
         - patrole-extension-admin
@@ -234,4 +224,3 @@
         - patrole-member-stein
         - patrole-member-rocky
         - patrole-member-queens
-        - patrole-member-pike
diff --git a/devstack/plugin.sh b/devstack/plugin.sh
index 6b95182..f60f0f4 100644
--- a/devstack/plugin.sh
+++ b/devstack/plugin.sh
@@ -37,6 +37,12 @@
         iniset $TEMPEST_CONFIG policy-feature-enabled removed_nova_policies_stein False
         iniset $TEMPEST_CONFIG policy-feature-enabled removed_keystone_policies_stein False
         iniset $TEMPEST_CONFIG policy-feature-enabled added_cinder_policies_stein False
+
+       # TODO(rb560u): Remove this once stable/pike becomes EOL.
+       # Make the 'test_list_trusts' test backwards compatible.
+       # The Keystone Trust API is enforced differently depending on passed
+       # arguments
+       iniset $TEMPEST_CONFIG policy-feature-enabled keystone_policy_enforcement_train False
     fi
 
     if [[ ${DEVSTACK_SERIES} == 'queens' ]]; then
@@ -54,12 +60,32 @@
         iniset $TEMPEST_CONFIG policy-feature-enabled removed_nova_policies_stein False
         iniset $TEMPEST_CONFIG policy-feature-enabled removed_keystone_policies_stein False
         iniset $TEMPEST_CONFIG policy-feature-enabled added_cinder_policies_stein False
+
+       # TODO(rb560u): Remove this once stable/queens becomes EOL.
+       # Make the 'test_list_trusts' test backwards compatible.
+       # The Keystone Trust API is enforced differently depending on passed
+       # arguments
+       iniset $TEMPEST_CONFIG policy-feature-enabled keystone_policy_enforcement_train 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
+
+       # TODO(rb560u): Remove this once stable/rocky becomes EOL.
+       # Make the 'test_list_trusts' test backwards compatible.
+       # The Keystone Trust API is enforced differently depending on passed
+       # arguments
+       iniset $TEMPEST_CONFIG policy-feature-enabled keystone_policy_enforcement_train False
+    fi
+
+    if [[ ${DEVSTACK_SERIES} == 'stein' ]]; then
+       # TODO(rb560u): Remove this once stable/stein becomes EOL.
+       # Make the 'test_list_trusts' test backwards compatible.
+       # The Keystone Trust API is enforced differently depending on passed
+       # arguments
+       iniset $TEMPEST_CONFIG policy-feature-enabled keystone_policy_enforcement_train False
     fi
 
     iniset $TEMPEST_CONFIG patrole rbac_test_roles $RBAC_TEST_ROLES
diff --git a/patrole_tempest_plugin/config.py b/patrole_tempest_plugin/config.py
index 63f0a8a..5eab0e3 100644
--- a/patrole_tempest_plugin/config.py
+++ b/patrole_tempest_plugin/config.py
@@ -184,7 +184,12 @@
                 default=True,
                 help="""Are the Cinder Stein policies available in the cloud
 (e.g. [create|update|get|delete]_encryption_policy)? These policies are added
-in Stein.""")
+in Stein."""),
+    cfg.BoolOpt('keystone_policy_enforcement_train',
+                default=True,
+                help="""Is the cloud running the Train release or newer? If
+so, the Keystone Trust API is enforced differently depending on passed
+arguments""")
 ]
 
 
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 d1e896b..94fd921 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
@@ -611,8 +611,7 @@
         cls.server = cls.create_test_server(wait_until='ACTIVE')
 
         # Create network the interface will be attached to
-        network_name = \
-            data_utils.rand_name(cls.__class__.__name__ + '-network')
+        network_name = data_utils.rand_name(cls.__name__ + '-network')
         post_body = {'name': network_name}
         post_body['router:external'] = False
         post_body['shared'] = True
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 c8db04e..bd34f9e 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
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
@@ -94,7 +96,10 @@
     @decorators.idempotent_id('d9a6fd06-08f6-462c-a86c-ce009adf1230')
     @rbac_rule_validation.action(
         service="keystone",
-        rules=["identity:delete_trust"])
+        rules=["identity:delete_trust"],
+        extra_target_data={
+            "target.trust.trustor_user_id": "os_primary.credentials.user_id"
+        })
     def test_delete_trust(self):
         trust = self.setup_test_trust(trustor_user_id=self.trustor_user_id,
                                       trustee_user_id=self.trustee_user_id)
@@ -107,14 +112,54 @@
         service="keystone",
         rules=["identity:list_trusts"])
     def test_list_trusts(self):
+        # Depending on the passed arguments to the list trusts API, different
+        # policy actions are enforced.
+        feature_flag = \
+            CONF.policy_feature_enabled.keystone_policy_enforcement_train
+        with self.override_role():
+            if feature_flag:
+                self.trusts_client.list_trusts()
+            else:
+                self.trusts_client.list_trusts(
+                    trustor_user_id=self.trustor_user_id)
+
+    @testtools.skipUnless(
+        CONF.policy_feature_enabled.keystone_policy_enforcement_train,
+        'This test tests Keystone policy actions introduced in Train')
+    @decorators.idempotent_id('6273ab11-32ad-450e-be4e-deaa856d7051')
+    @rbac_rule_validation.action(
+        service="keystone",
+        rules=["identity:list_trusts_for_trustor"],
+        extra_target_data={
+            "target.trust.trustor_user_id": "os_primary.credentials.user_id"
+        })
+    def test_list_trusts_for_trustor(self):
         with self.override_role():
             self.trusts_client.list_trusts(
                 trustor_user_id=self.trustor_user_id)
 
+    @testtools.skipUnless(
+        CONF.policy_feature_enabled.keystone_policy_enforcement_train,
+        'This test tests Keystone policy actions introduced in Train')
+    @decorators.idempotent_id('90bbbd77-c1df-43f9-99dc-088d52b95eff')
+    @rbac_rule_validation.action(
+        service="keystone",
+        rules=["identity:list_trusts_for_trustee"],
+        extra_target_data={
+            "target.trust.trustee_user_id": "trustee_user_id"
+        })
+    def test_list_trusts_for_trustee(self):
+        with self.override_role():
+            self.trusts_client.list_trusts(
+                trustee_user_id=self.trustee_user_id)
+
     @decorators.idempotent_id('3c9ff92f-a73e-4f9b-8865-e017f38c70f5')
     @rbac_rule_validation.action(
         service="keystone",
-        rules=["identity:list_roles_for_trust"])
+        rules=["identity:list_roles_for_trust"],
+        extra_target_data={
+            "target.trust.trustor_user_id": "os_primary.credentials.user_id"
+        })
     def test_list_roles_for_trust(self):
         with self.override_role():
             self.trusts_client.list_trust_roles(self.trust['id'])
@@ -122,7 +167,10 @@
     @decorators.idempotent_id('3bb4f97b-cecd-4c7d-ad10-b88ee6c5d573')
     @rbac_rule_validation.action(
         service="keystone",
-        rules=["identity:get_role_for_trust"])
+        rules=["identity:get_role_for_trust"],
+        extra_target_data={
+            "target.trust.trustor_user_id": "os_primary.credentials.user_id"
+        })
     def test_show_trust_role(self):
         with self.override_role():
             self.trusts_client.show_trust_role(
@@ -131,7 +179,10 @@
     @decorators.idempotent_id('0184e0fb-641e-4b52-ab73-81c1ce6ca5c1')
     @rbac_rule_validation.action(
         service="keystone",
-        rules=["identity:get_trust"])
+        rules=["identity:get_trust"],
+        extra_target_data={
+            "target.trust.trustor_user_id": "os_primary.credentials.user_id"
+        })
     def test_show_trust(self):
         with self.override_role():
             self.trusts_client.show_trust(self.trust['id'])
diff --git a/patrole_tempest_plugin/tests/api/network/test_agents_rbac.py b/patrole_tempest_plugin/tests/api/network/test_agents_rbac.py
index 99210a4..3a34128 100644
--- a/patrole_tempest_plugin/tests/api/network/test_agents_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_agents_rbac.py
@@ -266,6 +266,10 @@
         super(L3AgentsExtRbacTest, cls).resource_setup()
         name = data_utils.rand_name(cls.__name__ + '-Router')
         cls.router = cls.ntp_client.create_router(name)['router']
+        cls.addClassResourceCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            cls.ntp_client.delete_router,
+            cls.router['id'])
 
     @decorators.idempotent_id('5d2bbdbc-40a5-43d2-828a-84dc93bcd321')
     @rbac_rule_validation.action(service="neutron",
diff --git a/patrole_tempest_plugin/tests/api/network/test_dscp_marking_rule_rbac.py b/patrole_tempest_plugin/tests/api/network/test_dscp_marking_rule_rbac.py
index e67981f..444dcd2 100644
--- a/patrole_tempest_plugin/tests/api/network/test_dscp_marking_rule_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_dscp_marking_rule_rbac.py
@@ -34,7 +34,7 @@
     @classmethod
     def resource_setup(cls):
         super(DscpMarkingRuleExtRbacTest, cls).resource_setup()
-        name = data_utils.rand_name(cls.__class__.__name__ + '-qos')
+        name = data_utils.rand_name(cls.__name__ + '-qos')
         cls.policy_id = cls.ntp_client.create_qos_policy(
             name=name)["policy"]["id"]
         cls.addClassResourceCleanup(
diff --git a/patrole_tempest_plugin/tests/api/network/test_flavor_service_profile_rbac.py b/patrole_tempest_plugin/tests/api/network/test_flavor_service_profile_rbac.py
index 0ada6ac..bcc7670 100644
--- a/patrole_tempest_plugin/tests/api/network/test_flavor_service_profile_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_flavor_service_profile_rbac.py
@@ -61,6 +61,7 @@
             self.create_flavor_service_profile(self.flavor_id,
                                                self.service_profile_id)
 
+    @decorators.skip_because(bug='1843290', bug_type='launchpad')
     @decorators.idempotent_id('3b680d9e-946a-4670-ab7f-0e4576675833')
     @rbac_rule_validation.action(service="neutron",
                                  rules=["delete_flavor_service_profile"])
diff --git a/patrole_tempest_plugin/tests/api/network/test_policy_bandwidth_limit_rule_rbac.py b/patrole_tempest_plugin/tests/api/network/test_policy_bandwidth_limit_rule_rbac.py
index 209b011..cd750a0 100644
--- a/patrole_tempest_plugin/tests/api/network/test_policy_bandwidth_limit_rule_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_policy_bandwidth_limit_rule_rbac.py
@@ -34,7 +34,7 @@
     @classmethod
     def resource_setup(cls):
         super(PolicyBandwidthLimitRuleExtRbacTest, cls).resource_setup()
-        name = data_utils.rand_name(cls.__class__.__name__ + '-qos-policy')
+        name = data_utils.rand_name(cls.__name__ + '-qos-policy')
         cls.policy_id = cls.ntp_client.create_qos_policy(
             name=name)["policy"]["id"]
         cls.addClassResourceCleanup(cls.ntp_client.delete_qos_policy,
diff --git a/patrole_tempest_plugin/tests/api/network/test_policy_minimum_bandwidth_rule_rbac.py b/patrole_tempest_plugin/tests/api/network/test_policy_minimum_bandwidth_rule_rbac.py
index 954be71..990a357 100644
--- a/patrole_tempest_plugin/tests/api/network/test_policy_minimum_bandwidth_rule_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_policy_minimum_bandwidth_rule_rbac.py
@@ -34,7 +34,7 @@
     @classmethod
     def resource_setup(cls):
         super(PolicyMinimumBandwidthRuleExtRbacTest, cls).resource_setup()
-        name = data_utils.rand_name(cls.__class__.__name__ + '-qos')
+        name = data_utils.rand_name(cls.__name__ + '-qos')
         cls.policy_id = cls.ntp_client.create_qos_policy(
             name=name)["policy"]["id"]
         cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
diff --git a/releasenotes/notes/keystone_policy_enforcement_rocky-b52fb471ac31189b.yaml b/releasenotes/notes/keystone_policy_enforcement_rocky-b52fb471ac31189b.yaml
new file mode 100644
index 0000000..1520cab
--- /dev/null
+++ b/releasenotes/notes/keystone_policy_enforcement_rocky-b52fb471ac31189b.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Added new feature flag called ``keystone_policy_enforcement_train`` under
+    the configuration group ``[policy-feature-enabled]`` to make ``test_list_trusts``
+    test backwards compatible, test the current release, and test the correct policy
+    action. The Keystone Trust API is enforced differently depending on passed arguments