Merge "Do not skip nova API extensions policy test for rocky"
diff --git a/.zuul.yaml b/.zuul.yaml
index 270300c..8b8a94b 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -108,15 +108,6 @@
         TEMPEST_PLUGINS: /opt/stack/patrole
 
 - job:
-    name: patrole-member-queens
-    nodeset: openstack-single-node-xenial
-    parent: patrole-member
-    override-checkout: stable/queens
-    vars:
-      devstack_localrc:
-        TEMPEST_PLUGINS: /opt/stack/patrole
-
-- job:
     name: patrole-multinode-admin
     parent: patrole-base-multinode
     voting: false
@@ -210,7 +201,6 @@
         - patrole-reader
         - patrole-member-stein
         - patrole-member-rocky
-        - patrole-member-queens
         - patrole-multinode-admin
         - patrole-multinode-member
         - patrole-extension-admin
@@ -223,4 +213,3 @@
       jobs:
         - patrole-member-stein
         - patrole-member-rocky
-        - patrole-member-queens
diff --git a/devstack/plugin.sh b/devstack/plugin.sh
index d6dc019..af71066 100644
--- a/devstack/plugin.sh
+++ b/devstack/plugin.sh
@@ -43,6 +43,10 @@
        # The Keystone Trust API is enforced differently depending on passed
        # arguments
        iniset $TEMPEST_CONFIG policy-feature-enabled keystone_policy_enforcement_train False
+
+       # TODO(rb560u): Remove this once stable/pike becomes EOL.
+       # These policies were removed in Ussuri but are available in Pike.
+       iniset $TEMPEST_CONFIG policy-feature-enabled changed_nova_policies_ussuri False
     fi
 
     if [[ ${DEVSTACK_SERIES} == 'queens' ]]; then
@@ -66,6 +70,10 @@
        # The Keystone Trust API is enforced differently depending on passed
        # arguments
        iniset $TEMPEST_CONFIG policy-feature-enabled keystone_policy_enforcement_train False
+
+       # TODO(rb560u): Remove this once stable/queens becomes EOL.
+       # These policies were removed in Ussuri but are available in Queens.
+       iniset $TEMPEST_CONFIG policy-feature-enabled changed_nova_policies_ussuri False
     fi
 
     if [[ ${DEVSTACK_SERIES} == 'rocky' ]]; then
@@ -79,6 +87,10 @@
        # The Keystone Trust API is enforced differently depending on passed
        # arguments
        iniset $TEMPEST_CONFIG policy-feature-enabled keystone_policy_enforcement_train False
+
+       # TODO(rb560u): Remove this once stable/rocky becomes EOL.
+       # These policies were removed in Ussuri but are available in Rocky.
+       iniset $TEMPEST_CONFIG policy-feature-enabled changed_nova_policies_ussuri False
     fi
 
     if [[ ${DEVSTACK_SERIES} == 'stein' ]]; then
@@ -87,6 +99,10 @@
        # The Keystone Trust API is enforced differently depending on passed
        # arguments
        iniset $TEMPEST_CONFIG policy-feature-enabled keystone_policy_enforcement_train False
+
+       # TODO(rb560u): Remove this once stable/stein becomes EOL.
+       # These policies were removed in Ussuri but are available in Stein.
+       iniset $TEMPEST_CONFIG policy-feature-enabled changed_nova_policies_ussuri 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 5eab0e3..b087148 100644
--- a/patrole_tempest_plugin/config.py
+++ b/patrole_tempest_plugin/config.py
@@ -189,7 +189,12 @@
                 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""")
+arguments"""),
+    cfg.BoolOpt('changed_nova_policies_ussuri',
+                default=True,
+                help="""Are the Nova API policies available in the
+cloud (e.g. os_compute_api:os-services)? These policies were
+changed in Ussuri.""")
 ]
 
 
diff --git a/patrole_tempest_plugin/tests/api/compute/test_services_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_services_rbac.py
index dd7a4c3..da4923d 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_services_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_services_rbac.py
@@ -14,11 +14,19 @@
 #    under the License.
 
 from tempest.common import utils
+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
+
+if CONF.policy_feature_enabled.changed_nova_policies_ussuri:
+    _OS_COMPUTE_API_OS_SERVICES = "os_compute_api:os-services:list"
+else:
+    _OS_COMPUTE_API_OS_SERVICES = "os_compute_api:os-services"
+
 
 class ServicesRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
@@ -31,7 +39,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rules=["os_compute_api:os-services"])
+        rules=[_OS_COMPUTE_API_OS_SERVICES])
     @decorators.idempotent_id('7472261b-9c6d-453a-bcb3-aecaa29ad281')
     def test_list_services(self):
         with self.override_role():
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 185dc8c..aa77aa3 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
@@ -45,6 +45,9 @@
         with self.override_role():
             self.setup_test_credential(user=user)
 
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an immutable user '
+                      'source and solely provides read-only access to users.')
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:update_credential"])
     @decorators.idempotent_id('cfb05ce3-bffb-496e-a3c2-9515d730da63')
@@ -61,6 +64,9 @@
                 secret_key=new_keys[1],
                 project_id=credential['project_id'])
 
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an immutable user '
+                      'source and solely provides read-only access to users.')
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:delete_credential"])
     @decorators.idempotent_id('87ab42af-8d41-401b-90df-21e72919fcde')
@@ -70,6 +76,9 @@
         with self.override_role():
             self.creds_client.delete_credential(credential['id'])
 
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an immutable user '
+                      'source and solely provides read-only access to users.')
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:get_credential"])
     @decorators.idempotent_id('1b6eeae6-f1e8-4cdf-8903-1c002b1fc271')
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_groups_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_groups_rbac.py
index 7fc4a43..1e79501 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_groups_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_groups_rbac.py
@@ -13,12 +13,17 @@
 #    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
+
 
 class IdentityGroupsV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
 
@@ -82,6 +87,9 @@
         with self.override_role():
             self.groups_client.add_group_user(group['id'], user['id'])
 
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an immutable user '
+                      'source and solely provides read-only access to users.')
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:remove_user_from_group"])
     @decorators.idempotent_id('8a60d11c-7d2b-47e5-a0f3-9ea900ca66fe')
@@ -100,6 +108,9 @@
         with self.override_role():
             self.groups_client.list_group_users(group['id'])
 
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      'Skipped because environment has an immutable user '
+                      'source and solely provides read-only access to users.')
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:check_user_in_group"])
     @decorators.idempotent_id('d3603241-fd87-4a2d-94f9-f32469d1aaba')
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_roles_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_roles_rbac.py
index 4f447c0..c93afa1 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_roles_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_roles_rbac.py
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+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
@@ -20,6 +21,8 @@
 from patrole_tempest_plugin import rbac_rule_validation
 from patrole_tempest_plugin.tests.api.identity import rbac_base
 
+CONF = config.CONF
+
 
 class IdentityRolesV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
 
@@ -31,7 +34,6 @@
         cls.group = cls.setup_test_group()
         cls.role = cls.setup_test_role()
         cls.implies_role = cls.setup_test_role()
-        cls.user = cls.setup_test_user()
 
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:create_role"])
@@ -76,21 +78,6 @@
 
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:create_grant"])
-    @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d909')
-    def test_create_user_role_on_project(self):
-        with self.override_role():
-            self.roles_client.create_user_role_on_project(
-                self.project['id'],
-                self.user['id'],
-                self.role['id'])
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.roles_client.delete_role_from_user_on_project,
-                        self.project['id'],
-                        self.user['id'],
-                        self.role['id'])
-
-    @rbac_rule_validation.action(service="keystone",
-                                 rules=["identity:create_grant"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d90c')
     def test_create_group_role_on_project(self):
         with self.override_role():
@@ -106,21 +93,6 @@
 
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:create_grant"])
-    @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d90f')
-    def test_create_user_role_on_domain(self):
-        with self.override_role():
-            self.roles_client.create_user_role_on_domain(
-                self.domain['id'],
-                self.user['id'],
-                self.role['id'])
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.roles_client.delete_role_from_user_on_domain,
-                        self.domain['id'],
-                        self.user['id'],
-                        self.role['id'])
-
-    @rbac_rule_validation.action(service="keystone",
-                                 rules=["identity:create_grant"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d912')
     def test_create_group_role_on_domain(self):
         with self.override_role():
@@ -134,46 +106,6 @@
                         self.group['id'],
                         self.role['id'])
 
-    @rbac_rule_validation.action(service="keystone",
-                                 rules=["identity:check_grant"])
-    @decorators.idempotent_id('22921b1e-1a33-4026-bff9-f236d6dd149c')
-    def test_check_user_role_existence_on_project(self):
-        self.roles_client.create_user_role_on_project(
-            self.project['id'],
-            self.user['id'],
-            self.role['id'])
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.roles_client.delete_role_from_user_on_project,
-                        self.project['id'],
-                        self.user['id'],
-                        self.role['id'])
-
-        with self.override_role():
-            self.roles_client.check_user_role_existence_on_project(
-                self.project['id'],
-                self.user['id'],
-                self.role['id'])
-
-    @decorators.idempotent_id('92f8e67d-85bf-407d-9814-edd5664abc47')
-    @rbac_rule_validation.action(service="keystone",
-                                 rules=["identity:check_grant"])
-    def test_check_user_role_existence_on_domain(self):
-        self.roles_client.create_user_role_on_domain(
-            self.domain['id'],
-            self.user['id'],
-            self.role['id'])
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.roles_client.delete_role_from_user_on_domain,
-                        self.domain['id'],
-                        self.user['id'],
-                        self.role['id'])
-
-        with self.override_role():
-            self.roles_client.check_user_role_existence_on_domain(
-                self.domain['id'],
-                self.user['id'],
-                self.role['id'])
-
     @decorators.idempotent_id('8738d3d2-8c84-4423-b36c-7c59eaa08b73')
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:check_grant"])
@@ -216,26 +148,6 @@
 
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:revoke_grant"])
-    @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d90a')
-    def test_delete_role_from_user_on_project(self):
-        self.roles_client.create_user_role_on_project(
-            self.project['id'],
-            self.user['id'],
-            self.role['id'])
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.roles_client.delete_role_from_user_on_project,
-                        self.project['id'],
-                        self.user['id'],
-                        self.role['id'])
-
-        with self.override_role():
-            self.roles_client.delete_role_from_user_on_project(
-                self.project['id'],
-                self.user['id'],
-                self.role['id'])
-
-    @rbac_rule_validation.action(service="keystone",
-                                 rules=["identity:revoke_grant"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d90d')
     def test_delete_role_from_group_on_project(self):
         self.roles_client.create_group_role_on_project(
@@ -256,26 +168,6 @@
 
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:revoke_grant"])
-    @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d910')
-    def test_delete_role_from_user_on_domain(self):
-        self.roles_client.create_user_role_on_domain(
-            self.domain['id'],
-            self.user['id'],
-            self.role['id'])
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.roles_client.delete_role_from_user_on_domain,
-                        self.domain['id'],
-                        self.user['id'],
-                        self.role['id'])
-
-        with self.override_role():
-            self.roles_client.delete_role_from_user_on_domain(
-                self.domain['id'],
-                self.user['id'],
-                self.role['id'])
-
-    @rbac_rule_validation.action(service="keystone",
-                                 rules=["identity:revoke_grant"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d913')
     def test_delete_role_from_group_on_domain(self):
         self.roles_client.create_group_role_on_domain(
@@ -296,15 +188,6 @@
 
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:list_grants"])
-    @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d90b')
-    def test_list_user_roles_on_project(self):
-        with self.override_role():
-            self.roles_client.list_user_roles_on_project(
-                self.project['id'],
-                self.user['id'])
-
-    @rbac_rule_validation.action(service="keystone",
-                                 rules=["identity:list_grants"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d90e')
     def test_list_group_roles_on_project(self):
         with self.override_role():
@@ -314,15 +197,6 @@
 
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:list_grants"])
-    @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d911')
-    def test_list_user_roles_on_domain(self):
-        with self.override_role():
-            self.roles_client.list_user_roles_on_domain(
-                self.domain['id'],
-                self.user['id'])
-
-    @rbac_rule_validation.action(service="keystone",
-                                 rules=["identity:list_grants"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d914')
     def test_list_group_roles_on_domain(self):
         with self.override_role():
@@ -393,3 +267,156 @@
     def test_list_all_role_inference_rules(self):
         with self.override_role():
             self.roles_client.list_all_role_inference_rules()
+
+
+class IdentityRolesUserCreateV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
+    """Tests identity roles v3 API endpoints that require user creation.
+    This is in a separate class to better manage immutable user source feature
+    flag.
+    """
+
+    @classmethod
+    def skip_checks(cls):
+        super(IdentityRolesUserCreateV3RbacTest, cls).skip_checks()
+        if CONF.identity_feature_enabled.immutable_user_source:
+            raise cls.skipException('Skipped because environment has an '
+                                    'immutable user source and solely '
+                                    'provides read-only access to users.')
+
+    @classmethod
+    def resource_setup(cls):
+        super(IdentityRolesUserCreateV3RbacTest, cls).resource_setup()
+        cls.domain = cls.setup_test_domain()
+        cls.project = cls.setup_test_project()
+        cls.group = cls.setup_test_group()
+        cls.role = cls.setup_test_role()
+        cls.implies_role = cls.setup_test_role()
+        cls.user = cls.setup_test_user()
+
+    @rbac_rule_validation.action(service="keystone",
+                                 rules=["identity:create_grant"])
+    @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d909')
+    def test_create_user_role_on_project(self):
+        with self.override_role():
+            self.roles_client.create_user_role_on_project(
+                self.project['id'],
+                self.user['id'],
+                self.role['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.roles_client.delete_role_from_user_on_project,
+                        self.project['id'],
+                        self.user['id'],
+                        self.role['id'])
+
+    @rbac_rule_validation.action(service="keystone",
+                                 rules=["identity:create_grant"])
+    @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d90f')
+    def test_create_user_role_on_domain(self):
+        with self.override_role():
+            self.roles_client.create_user_role_on_domain(
+                self.domain['id'],
+                self.user['id'],
+                self.role['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.roles_client.delete_role_from_user_on_domain,
+                        self.domain['id'],
+                        self.user['id'],
+                        self.role['id'])
+
+    @rbac_rule_validation.action(service="keystone",
+                                 rules=["identity:check_grant"])
+    @decorators.idempotent_id('22921b1e-1a33-4026-bff9-f236d6dd149c')
+    def test_check_user_role_existence_on_project(self):
+        self.roles_client.create_user_role_on_project(
+            self.project['id'],
+            self.user['id'],
+            self.role['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.roles_client.delete_role_from_user_on_project,
+                        self.project['id'],
+                        self.user['id'],
+                        self.role['id'])
+
+        with self.override_role():
+            self.roles_client.check_user_role_existence_on_project(
+                self.project['id'],
+                self.user['id'],
+                self.role['id'])
+
+    @decorators.idempotent_id('92f8e67d-85bf-407d-9814-edd5664abc47')
+    @rbac_rule_validation.action(service="keystone",
+                                 rules=["identity:check_grant"])
+    def test_check_user_role_existence_on_domain(self):
+        self.roles_client.create_user_role_on_domain(
+            self.domain['id'],
+            self.user['id'],
+            self.role['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.roles_client.delete_role_from_user_on_domain,
+                        self.domain['id'],
+                        self.user['id'],
+                        self.role['id'])
+
+        with self.override_role():
+            self.roles_client.check_user_role_existence_on_domain(
+                self.domain['id'],
+                self.user['id'],
+                self.role['id'])
+
+    @rbac_rule_validation.action(service="keystone",
+                                 rules=["identity:revoke_grant"])
+    @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d90a')
+    def test_delete_role_from_user_on_project(self):
+        self.roles_client.create_user_role_on_project(
+            self.project['id'],
+            self.user['id'],
+            self.role['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.roles_client.delete_role_from_user_on_project,
+                        self.project['id'],
+                        self.user['id'],
+                        self.role['id'])
+
+        with self.override_role():
+            self.roles_client.delete_role_from_user_on_project(
+                self.project['id'],
+                self.user['id'],
+                self.role['id'])
+
+    @rbac_rule_validation.action(service="keystone",
+                                 rules=["identity:revoke_grant"])
+    @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d910')
+    def test_delete_role_from_user_on_domain(self):
+        self.roles_client.create_user_role_on_domain(
+            self.domain['id'],
+            self.user['id'],
+            self.role['id'])
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.roles_client.delete_role_from_user_on_domain,
+                        self.domain['id'],
+                        self.user['id'],
+                        self.role['id'])
+
+        with self.override_role():
+            self.roles_client.delete_role_from_user_on_domain(
+                self.domain['id'],
+                self.user['id'],
+                self.role['id'])
+
+    @rbac_rule_validation.action(service="keystone",
+                                 rules=["identity:list_grants"])
+    @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d90b')
+    def test_list_user_roles_on_project(self):
+        with self.override_role():
+            self.roles_client.list_user_roles_on_project(
+                self.project['id'],
+                self.user['id'])
+
+    @rbac_rule_validation.action(service="keystone",
+                                 rules=["identity:list_grants"])
+    @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d911')
+    def test_list_user_roles_on_domain(self):
+        with self.override_role():
+            self.roles_client.list_user_roles_on_domain(
+                self.domain['id'],
+                self.user['id'])
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 bd34f9e..23c75c6 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
@@ -35,6 +35,10 @@
         if not CONF.identity_feature_enabled.trust:
             raise cls.skipException(
                 "%s skipped as trust feature isn't enabled" % cls.__name__)
+        if CONF.identity_feature_enabled.immutable_user_source:
+            raise cls.skipException('Skipped because environment has an '
+                                    'immutable user source and solely '
+                                    'provides read-only access to users.')
 
     @classmethod
     def resource_setup(cls):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_users_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_users_rbac.py
index 9f6f028..0d2a04d 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_users_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_users_rbac.py
@@ -13,12 +13,17 @@
 #    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
+
 
 class IdentityUserV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
 
@@ -27,6 +32,8 @@
         super(IdentityUserV3RbacTest, cls).resource_setup()
         cls.default_user_id = cls.os_primary.credentials.user_id
 
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      "Configured to use an immutable user source")
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:create_user"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d904')
@@ -34,6 +41,8 @@
         with self.override_role():
             self.setup_test_user()
 
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      "Configured to use an immutable user source")
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:update_user"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d905')
@@ -47,6 +56,8 @@
                                           name=user['name'],
                                           email=new_email)
 
+    @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
+                      "Configured to use an immutable user source")
     @rbac_rule_validation.action(service="keystone",
                                  rules=["identity:delete_user"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d906')
diff --git a/releasenotes/notes/changed_nova_policies_ussuri-177582b3ded63411.yaml b/releasenotes/notes/changed_nova_policies_ussuri-177582b3ded63411.yaml
new file mode 100644
index 0000000..5362f04
--- /dev/null
+++ b/releasenotes/notes/changed_nova_policies_ussuri-177582b3ded63411.yaml
@@ -0,0 +1,13 @@
+---
+features:
+  - |
+    A new policy feature flag called
+    ``[policy_feature_flag].changed_nova_policies_ussuri`` has been added to
+    Patrole's config to handle Nova policies changed in Ussuri. The policy
+    feature flag is applied to tests that use policies changed in Ussuri,
+    including the following:
+
+      - os_compute_api:os-services
+
+    Note that not all changed policies are included above because test coverage
+    is missing for them.
diff --git a/releasenotes/notes/intermediate-ussuri-release-8f7bb2140bca827c.yaml b/releasenotes/notes/intermediate-ussuri-release-8f7bb2140bca827c.yaml
new file mode 100644
index 0000000..8a89b65
--- /dev/null
+++ b/releasenotes/notes/intermediate-ussuri-release-8f7bb2140bca827c.yaml
@@ -0,0 +1,14 @@
+---
+prelude: >
+    This is an intermediate release during the Ussuri development cycle to
+    mark the end of support for EM Queens in Patrole.
+    After this release, Patrole will support below OpenStack Releases:
+
+      * Train
+      * Stein
+      * Rocky
+
+    Current development of Patrole is for OpenStack Ussuri development
+    cycle.
+
+    This is the last release of Patrole to officially support python2.7.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index 543561f..32f74ec 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   v0.8.0
    v0.7.0
    v0.6.0
    v0.5.0
diff --git a/releasenotes/source/v0.8.0.rst b/releasenotes/source/v0.8.0.rst
new file mode 100644
index 0000000..154bcac
--- /dev/null
+++ b/releasenotes/source/v0.8.0.rst
@@ -0,0 +1,6 @@
+=====================
+v0.8.0 Release Notes
+=====================
+
+.. release-notes:: 0.8.0 Release Notes
+   :version: 0.8.0
diff --git a/tox.ini b/tox.ini
index b7e02e6..ff035e0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,7 +5,7 @@
 
 [testenv]
 usedevelop = True
-install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
+install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} {opts} {packages}
 setenv =
    VIRTUAL_ENV={envdir}
    OS_TEST_PATH=./patrole_tempest_plugin/tests/unit
@@ -56,7 +56,7 @@
 [testenv:docs]
 basepython = python3
 deps =
-  -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+  -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
   -r{toxinidir}/requirements.txt
   -r{toxinidir}/doc/requirements.txt
 commands =
@@ -76,7 +76,7 @@
 [testenv:releasenotes]
 basepython = python3
 deps =
-  -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
+  -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
   -r{toxinidir}/requirements.txt
   -r{toxinidir}/doc/requirements.txt
 commands =