Merge "Add multi-provider networks test cases for RBAC."
diff --git a/contrib/post_test_hook.sh b/contrib/post_test_hook.sh
new file mode 100644
index 0000000..ea4b5e3
--- /dev/null
+++ b/contrib/post_test_hook.sh
@@ -0,0 +1,60 @@
+#!/bin/bash -xe
+#
+# 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.
+
+# This script is executed inside post_test_hook function in devstack gate.
+# First argument ($1) expects 'rbac-role' as value for setting appropriate
+# tempest rbac option 'rbac_test_role'.
+
+# Install pip manually.
+PATROLE_DIR=$BASE/new/patrole
+sudo pip install -e $PATROLE_DIR
+
+# Allow tempest.conf to be modified by Jenkins.
+sudo chown -R jenkins:stack $BASE/new/tempest
+sudo chown -R jenkins:stack $BASE/data/tempest
+
+TEMPEST_CONFIG=$BASE/new/tempest/etc/tempest.conf
+TEMPEST_COMMAND="sudo -H -u tempest tox"
+DEVSTACK_GATE_TEMPEST_REGEX="(^patrole_tempest_plugin\.tests\.api)"
+
+# Import devstack function 'iniset'.
+source $BASE/new/devstack/functions
+
+# First argument is expected to contain value equal either to 'admin' or
+# 'member' (both lower-case).
+RBAC_ROLE=$1
+
+if [[ "$RBAC_ROLE" == "member" ]]; then
+    RBAC_ROLE="Member"
+fi
+
+# Set rbac_flag=True under [rbac] section in tempest.conf
+iniset $TEMPEST_CONFIG rbac rbac_flag True
+# Set rbac_test_role=$RBAC_ROLE under [rbac] section in tempest.conf
+iniset $TEMPEST_CONFIG rbac rbac_test_role $RBAC_ROLE
+# Set additional, necessary CONF values
+iniset $TEMPEST_CONFIG auth use_dynamic_credentials True
+iniset $TEMPEST_CONFIG auth tempest_roles Member
+
+# Give permissions back to Tempest.
+sudo chown -R tempest:stack $BASE/new/tempest
+sudo chown -R tempest:stack $BASE/data/tempest
+
+set -o errexit
+
+# cd into Tempest directory before executing tox.
+cd $BASE/new/tempest
+
+$TEMPEST_COMMAND -eall-plugin -- $DEVSTACK_GATE_TEMPEST_REGEX --concurrency=$TEMPEST_CONCURRENCY
+sudo -H -u tempest .tox/all-plugin/bin/tempest list-plugins
diff --git a/contrib/pre_test_hook.sh b/contrib/pre_test_hook.sh
new file mode 100755
index 0000000..ffa4c20
--- /dev/null
+++ b/contrib/pre_test_hook.sh
@@ -0,0 +1,19 @@
+#!/bin/bash -xe
+#
+# 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.
+
+# This script is executed inside pre_test_hook function in devstack gate.
+# Installs patrole tempest plugin manually.
+
+PATROLE_DIR=$BASE/new/patrole
+sudo pip install -e $PATROLE_DIR
diff --git a/patrole_tempest_plugin/config.py b/patrole_tempest_plugin/config.py
index 94e6aef..1edf877 100644
--- a/patrole_tempest_plugin/config.py
+++ b/patrole_tempest_plugin/config.py
@@ -24,6 +24,6 @@
                help="The current RBAC role against which to run"
                     " Patrole tests."),
     cfg.BoolOpt('rbac_flag',
-                default=False,
+                default=True,
                 help="Enables RBAC tests.")
 ]
diff --git a/patrole_tempest_plugin/rbac_utils.py b/patrole_tempest_plugin/rbac_utils.py
index d5f1c2b..69c6ccd 100644
--- a/patrole_tempest_plugin/rbac_utils.py
+++ b/patrole_tempest_plugin/rbac_utils.py
@@ -55,32 +55,25 @@
                 cls.rbac_role_id = item['id']
             if item['name'] == 'admin':
                 cls.admin_role_id = item['id']
-        # Check if admin and rbac role exits
-        if not cls.admin_role_id or not cls.rbac_role_id:
-            msg = ("defined 'rbac_role' or 'admin' role does not exist"
-                   " in the system.")
-            raise rbac_exceptions.RbacResourceSetupFailed(msg)
-
-    def clear_user_roles(cls, user_id, tenant_id):
-        roles = cls.creds_client.roles_client.list_user_roles_on_project(
-            tenant_id, user_id)['roles']
-
-        for role in roles:
-            cls.creds_client.roles_client.delete_role_from_user_on_project(
-                tenant_id, user_id, role['id'])
 
     def switch_role(cls, test_obj, switchToRbacRole=None):
         LOG.debug('Switching role to: %s', switchToRbacRole)
+        # Check if admin and rbac roles exist.
+        if not cls.admin_role_id or not cls.rbac_role_id:
+            msg = ("Defined 'rbac_role' or 'admin' role does not exist"
+                   " in the system.")
+            raise rbac_exceptions.RbacResourceSetupFailed(msg)
+
         if not isinstance(switchToRbacRole, bool):
             msg = ("Wrong value for parameter 'switchToRbacRole' is passed."
                    " It should be either 'True' or 'False'.")
-            raise rbac_exceptions.RbacActionFailed(msg)
+            raise rbac_exceptions.RbacResourceSetupFailed(msg)
 
         try:
             user_id = test_obj.auth_provider.credentials.user_id
             project_id = test_obj.auth_provider.credentials.tenant_id
 
-            cls.clear_user_roles(user_id, project_id)
+            cls._clear_user_roles(user_id, project_id)
 
             if switchToRbacRole:
                 cls.creds_client.roles_client.create_user_role_on_project(
@@ -100,4 +93,12 @@
             time.sleep(1)
             test_obj.auth_provider.set_auth()
 
+    def _clear_user_roles(cls, user_id, tenant_id):
+        roles = cls.creds_client.roles_client.list_user_roles_on_project(
+            tenant_id, user_id)['roles']
+
+        for role in roles:
+            cls.creds_client.roles_client.delete_role_from_user_on_project(
+                tenant_id, user_id, role['id'])
+
 rbac_utils = RbacUtils
diff --git a/patrole_tempest_plugin/tests/api/compute/test_keypairs_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_keypairs_rbac.py
new file mode 100644
index 0000000..d4d9306
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/compute/test_keypairs_rbac.py
@@ -0,0 +1,75 @@
+#    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.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.compute import rbac_base
+
+
+class KeypairsRbacTest(rbac_base.BaseV2ComputeRbacTest):
+
+    @classmethod
+    def setup_clients(cls):
+        super(KeypairsRbacTest, cls).setup_clients()
+        cls.client = cls.keypairs_client
+
+    def tearDown(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=False)
+        super(KeypairsRbacTest, self).tearDown()
+
+    def _create_keypair(self):
+        key_name = data_utils.rand_name('key')
+        keypair = self.client.create_keypair(name=key_name)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.client.delete_keypair,
+                        key_name)
+        return keypair
+
+    @decorators.idempotent_id('16e0ae81-e05f-48cd-b253-cf31ab0732f0')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-keypairs:create")
+    def test_create_keypair(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self._create_keypair()
+
+    @decorators.idempotent_id('85a5eb99-40ec-4e77-9358-bee2cdf9d7df')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-keypairs:show")
+    def test_show_keypair(self):
+        kp_name = self._create_keypair()['keypair']['name']
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.client.show_keypair(kp_name)
+
+    @decorators.idempotent_id('6bff9f1c-b809-43c1-8d63-61fbd19d49d3')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-keypairs:delete")
+    def test_delete_keypair(self):
+        kp_name = self._create_keypair()['keypair']['name']
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.client.delete_keypair(kp_name)
+
+    @decorators.idempotent_id('6bb31346-ff7f-4b10-978e-170ac5fcfa3e')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-keypairs:index")
+    def test_index_keypair(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.client.list_keypairs()
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 c4b44e7..4b86210 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
@@ -13,9 +13,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 
 from patrole_tempest_plugin import rbac_rule_validation
 from patrole_tempest_plugin.tests.api.compute import rbac_base
@@ -25,10 +28,6 @@
 
 class ServerActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
-    def tearDown(self):
-        self.rbac_utils.switch_role(self, switchToRbacRole=False)
-        super(ServerActionsRbacTest, self).tearDown()
-
     @classmethod
     def setup_clients(cls):
         super(ServerActionsRbacTest, cls).setup_clients()
@@ -37,13 +36,6 @@
     @classmethod
     def skip_checks(cls):
         super(ServerActionsRbacTest, cls).skip_checks()
-        if not CONF.compute_feature_enabled.api_extensions:
-            raise cls.skipException(
-                '%s skipped as no compute extensions enabled' % cls.__name__)
-        if not CONF.compute_feature_enabled.interface_attach:
-            raise cls.skipException(
-                '%s skipped as interface attachment is not available'
-                % cls.__name__)
 
     @classmethod
     def resource_setup(cls):
@@ -51,6 +43,30 @@
         super(ServerActionsRbacTest, cls).resource_setup()
         cls.server_id = cls.create_test_server(wait_until='ACTIVE',
                                                validatable=True)['id']
+        cls.flavor_ref = CONF.compute.flavor_ref
+        cls.flavor_ref_alt = CONF.compute.flavor_ref_alt
+        cls.image_ref = CONF.compute.image_ref
+
+    def setUp(self):
+        super(ServerActionsRbacTest, self).setUp()
+        try:
+            waiters.wait_for_server_status(self.client,
+                                           self.server_id, 'ACTIVE')
+        except lib_exc.NotFound:
+            # if the server was found to be deleted by a previous test,
+            # a new one is built
+            server = self.create_test_server(
+                validatable=True,
+                wait_until='ACTIVE')
+            self.__class__.server_id = server['id']
+        except Exception:
+            # Rebuilding the server in case something happened during a test
+            self.__class__.server_id = self.rebuild_server(
+                self.server_id, validatable=True)
+
+    def tearDown(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=False)
+        super(ServerActionsRbacTest, self).tearDown()
 
     def _test_start_server(self):
         self.client.start_server(self.server_id)
@@ -62,6 +78,21 @@
         waiters.wait_for_server_status(self.client, self.server_id,
                                        'SHUTOFF')
 
+    def _test_resize_server(self, flavor):
+        self.client.resize_server(self.server_id, flavor)
+        waiters.wait_for_server_status(self.client, self.server_id,
+                                       'VERIFY_RESIZE')
+
+    def _test_revert_resize_server(self):
+        self.client.revert_resize_server(self.server_id)
+        waiters.wait_for_server_status(self.client, self.server_id,
+                                       'ACTIVE')
+
+    def _test_confirm_resize_server(self):
+        self.client.confirm_resize_server(self.server_id)
+        waiters.wait_for_server_status(self.client, self.server_id,
+                                       'ACTIVE')
+
     @rbac_rule_validation.action(
         service="nova",
         rule="os_compute_api:servers:stop")
@@ -78,3 +109,59 @@
         self._test_stop_server()
         self.rbac_utils.switch_role(self, switchToRbacRole=True)
         self._test_start_server()
+
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:servers:resize")
+    @decorators.idempotent_id('0546fbdd-2d8f-4ce8-ac00-f1e2129d0765')
+    @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+                          'Resize is not available.')
+    def test_resize_server(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self._test_resize_server(self.flavor_ref_alt)
+
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:servers:revert_resize")
+    @decorators.idempotent_id('d41b64b8-a72d-414a-a4c5-94e1eb5e5a96')
+    @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+                          'Resize is not available.')
+    def test_revert_resize_server(self):
+        self._test_resize_server(self.flavor_ref_alt)
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self._test_revert_resize_server()
+
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:servers:confirm_resize")
+    @decorators.idempotent_id('f51620cb-dfcb-4e5d-b421-2e0edaa1316e')
+    @testtools.skipUnless(CONF.compute_feature_enabled.resize,
+                          'Resize is not available.')
+    def test_confirm_resize_server(self):
+        self._test_resize_server(self.flavor_ref_alt)
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.addCleanup(
+            lambda: (self._test_resize_server(self.flavor_ref),
+                     self._test_confirm_resize_server())
+        )
+        self._test_confirm_resize_server()
+
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:servers:rebuild")
+    @decorators.idempotent_id('54b1a30b-c96c-472c-9c83-ccaf6ec7e20b')
+    def test_rebuild_server(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.client.rebuild_server(self.server_id, self.image_ref)
+        waiters.wait_for_server_status(self.client, self.server_id,
+                                       'ACTIVE')
+
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:servers:reboot")
+    @decorators.idempotent_id('19f27856-56e1-44f8-8615-7257f6b85cbb')
+    def test_reboot_server(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.client.reboot_server(self.server_id, type='HARD')
+        waiters.wait_for_server_status(self.client, self.server_id,
+                                       'ACTIVE')
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_tags_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_tags_rbac.py
new file mode 100644
index 0000000..14f0638
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_tags_rbac.py
@@ -0,0 +1,105 @@
+#    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.common.utils import data_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
+
+
+class ServerTagsRbacTest(rbac_base.BaseV2ComputeRbacTest):
+
+    min_microversion = '2.26'
+    max_microversion = 'latest'
+
+    @classmethod
+    def skip_checks(cls):
+        super(ServerTagsRbacTest, cls).skip_checks()
+        if not test.is_extension_enabled('os-server-tags', 'compute'):
+            msg = "os-server-tags extension is not enabled."
+            raise cls.skipException(msg)
+
+    @classmethod
+    def setup_clients(cls):
+        super(ServerTagsRbacTest, cls).setup_clients()
+        cls.client = cls.servers_client
+
+    @classmethod
+    def resource_setup(cls):
+        super(ServerTagsRbacTest, cls).resource_setup()
+        cls.server = cls.create_test_server(wait_until='ACTIVE')
+
+    def tearDown(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=False)
+        super(ServerTagsRbacTest, self).tearDown()
+
+    def _add_tag_to_server(self):
+        tag_name = data_utils.rand_name('tag')
+        self.client.update_tag(self.server['id'], tag_name)
+        self.addCleanup(self.client.delete_all_tags, self.server['id'])
+        return tag_name
+
+    @decorators.idempotent_id('99e73dd3-adec-4044-b46c-84bdded35d09')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-server-tags:index")
+    def test_list_tags(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.client.list_tags(self.server['id'])['tags']
+
+    @decorators.idempotent_id('9297c99e-94eb-429f-93cf-9b1838e33622')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-server-tags:show")
+    def test_check_tag_existence(self):
+        tag_name = self._add_tag_to_server()
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.client.check_tag_existence(self.server['id'], tag_name)
+
+    @decorators.idempotent_id('0d84ee94-d3ca-4635-8edf-b7f67ab8e4a3')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-server-tags:update")
+    def test_update_tag(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self._add_tag_to_server()
+
+    @decorators.idempotent_id('115c2694-00aa-41ee-99f6-9eab4040c182')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-server-tags:delete")
+    def test_delete_tag(self):
+        tag_name = self._add_tag_to_server()
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.client.delete_tag(self.server['id'], tag_name)
+
+    @decorators.idempotent_id('a8e19b87-6580-4bc8-9933-e62561ff667d')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-server-tags:update_all")
+    def test_update_all_tags(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        new_tag_name = data_utils.rand_name('tag')
+        self.client.update_all_tags(self.server['id'], [new_tag_name])['tags']
+
+    @decorators.idempotent_id('89d51936-e333-42f9-a045-132a4865ba1a')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-server-tags:delete_all")
+    def test_delete_all_tags(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.client.delete_all_tags(self.server['id'])
diff --git a/patrole_tempest_plugin/tests/api/identity/v2/test_users_rbac.py b/patrole_tempest_plugin/tests/api/identity/v2/test_users_rbac.py
new file mode 100644
index 0000000..568733b
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/identity/v2/test_users_rbac.py
@@ -0,0 +1,87 @@
+# 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.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.v2 import rbac_base
+
+
+class IdentityUserV2AdminRbacTest(rbac_base.BaseIdentityV2AdminRbacTest):
+
+    def tearDown(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=False)
+        super(IdentityUserV2AdminRbacTest, self).tearDown()
+
+    @rbac_rule_validation.action(service="keystone",
+                                 rule="identity:create_user")
+    @decorators.idempotent_id('0f148510-63bf-11e6-1342-080044d0d904')
+    def test_create_user(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self._create_user()
+
+    @rbac_rule_validation.action(service="keystone",
+                                 rule="identity:update_user")
+    @decorators.idempotent_id('0f148510-63bf-11e6-1342-080044d0d905')
+    def test_update_user(self):
+        user = self._create_user()
+
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.users_client.update_user(user['id'], email="changedUser@xyz.com")
+
+    @rbac_rule_validation.action(service="keystone",
+                                 rule="identity:set_user_enabled")
+    @decorators.idempotent_id('0f148510-63bf-11e6-1342-080044d0d9a1')
+    def test_update_user_enabled(self):
+        user = self._create_user()
+
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.users_client.update_user_enabled(user['id'], enabled=True)
+
+    @rbac_rule_validation.action(service="keystone",
+                                 rule="identity:delete_user")
+    @decorators.idempotent_id('0f148510-63bf-11e6-1342-080044d0d906')
+    def test_delete_user(self):
+        user = self._create_user()
+
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.users_client.delete_user(user['id'])
+
+    @rbac_rule_validation.action(service="keystone",
+                                 rule="identity:get_users")
+    @decorators.idempotent_id('0f148510-63bf-11e6-1342-080044d0d907')
+    def test_list_users(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.users_client.list_users()
+
+    @rbac_rule_validation.action(service="keystone",
+                                 rule="identity:get_user")
+    @decorators.idempotent_id('0f148510-63bf-11e6-1342-080044d0d908')
+    def test_show_user(self):
+        user = self._create_user()
+
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.users_client.show_user(user['id'])
+
+    @rbac_rule_validation.action(service="keystone",
+                                 rule="identity:change_password")
+    @decorators.idempotent_id('0f148510-63bf-11e6-1342-080044d0d909')
+    def test_update_user_password(self):
+        user = self._create_user()
+
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.users_client.update_user_password(
+            user['id'], password=data_utils.rand_password())
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 d51fecb..a563784 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,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_endpoints_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_endpoints_rbac.py
index 11b0064..dbdf8dc 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_endpoints_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_endpoints_rbac.py
@@ -13,8 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common.utils import data_utils
 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
 
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 a9c998a..16e2dce 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,8 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common.utils import data_utils
 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
 
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_projects_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_projects_rbac.py
index 9d6467c..a00569d 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_projects_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_projects_rbac.py
@@ -13,8 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common.utils import data_utils
 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
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_services_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_services_rbac.py
index e431216..42d0028 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_services_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_services_rbac.py
@@ -13,8 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.common.utils import data_utils
 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
diff --git a/patrole_tempest_plugin/tests/api/network/test_security_groups_rbac.py b/patrole_tempest_plugin/tests/api/network/test_security_groups_rbac.py
new file mode 100644
index 0000000..25f1acf
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/network/test_security_groups_rbac.py
@@ -0,0 +1,191 @@
+# 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 oslo_log import log
+
+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
+
+from patrole_tempest_plugin import rbac_exceptions
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.network import rbac_base as base
+
+LOG = log.getLogger(__name__)
+
+
+class SecGroupRbacTest(base.BaseNetworkRbacTest):
+
+    def tearDown(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=False)
+        super(SecGroupRbacTest, self).tearDown()
+
+    @classmethod
+    def resource_setup(cls):
+        super(SecGroupRbacTest, cls).resource_setup()
+        secgroup_name = data_utils.rand_name('secgroup')
+        cls.secgroup = cls.security_groups_client.create_security_group(
+            name=secgroup_name)['security_group']
+
+    @classmethod
+    def resource_cleanup(cls):
+        # Clean up security group
+        test_utils.call_and_ignore_notfound_exc(
+            cls.security_groups_client.delete_security_group,
+            cls.secgroup['id'])
+        super(SecGroupRbacTest, cls).resource_cleanup()
+
+    def _create_security_group(self):
+        # Create a security group
+        name = data_utils.rand_name('secgroup')
+        security_group =\
+            self.security_groups_client.create_security_group(
+                name=name)['security_group']
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.security_groups_client.delete_security_group,
+            security_group['id'])
+        return security_group
+
+    def _create_security_group_rule(self):
+        # Create a security group rule
+        sec_group_rule = \
+            self.security_group_rules_client.create_security_group_rule(
+                security_group_id=self.secgroup['id'],
+                direction='ingress',
+                protocol='tcp',
+                port_range_min=99,
+                port_range_max=99)['security_group_rule']
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.security_group_rules_client.delete_security_group_rule,
+            sec_group_rule['id'])
+        return sec_group_rule
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rule="create_security_group")
+    @decorators.idempotent_id('db7003ce-5717-4e5b-afc7-befa35e8c67f')
+    def test_create_security_group(self):
+
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self._create_security_group()
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rule="get_security_group")
+    @decorators.idempotent_id('56335e77-aef2-4b54-86c7-7f772034b585')
+    def test_show_security_groups(self):
+
+        try:
+            self.rbac_utils.switch_role(self, switchToRbacRole=True)
+            self.security_groups_client.show_security_group(
+                self.secgroup['id'])
+        except exceptions.NotFound as e:
+            LOG.info("NotFound exception caught. Exception is thrown when "
+                     "role doesn't have access to the endpoint."
+                     "This is irregular and should be fixed.")
+            raise rbac_exceptions.RbacActionFailed(e)
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rule="delete_security_group")
+    @decorators.idempotent_id('0b1330fd-dd28-40f3-ad73-966052e4b3de')
+    def test_delete_security_group(self):
+
+        # Create a security group
+        secgroup_id = self._create_security_group()['id']
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        try:
+            self.security_groups_client.delete_security_group(secgroup_id)
+        except exceptions.NotFound as e:
+            LOG.info("NotFound exception caught. Exception is thrown when "
+                     "role doesn't have access to the endpoint."
+                     "This is irregular and should be fixed.")
+            raise rbac_exceptions.RbacActionFailed(e)
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rule="update_security_group")
+    @decorators.idempotent_id('56c5e4dc-f8aa-11e6-bc64-92361f002671')
+    def test_update_security_group(self):
+
+        # Create a security group
+        secgroup_id = self._create_security_group()['id']
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        try:
+            self.security_groups_client.update_security_group(
+                secgroup_id,
+                description="test description")
+        except exceptions.NotFound as e:
+            LOG.info("NotFound exception caught. Exception is thrown when "
+                     "role doesn't have access to the endpoint."
+                     "This is irregular and should be fixed.")
+            raise rbac_exceptions.RbacActionFailed(e)
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rule="get_security_groups")
+    @decorators.idempotent_id('fbaf8d96-ed3e-49af-b24c-5fb44f05bbb7')
+    def test_list_security_groups(self):
+
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.security_groups_client.list_security_groups()
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rule="create_security_group_rule")
+    @decorators.idempotent_id('953d78df-00cd-416f-9cbd-b7cb4ea65772')
+    def test_create_security_group_rule(self):
+
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self._create_security_group_rule()
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rule="delete_security_group_rule")
+    @decorators.idempotent_id('2262539e-b7d9-438c-acf9-a5ce0613be28')
+    def test_delete_security_group_rule(self):
+
+        sec_group_rule = self._create_security_group_rule()
+
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        try:
+            self.security_group_rules_client.delete_security_group_rule(
+                sec_group_rule['id'])
+        except exceptions.NotFound as e:
+            LOG.info("NotFound exception caught. Exception is thrown when "
+                     "role doesn't have access to the endpoint."
+                     "This is irregular and should be fixed.")
+            raise rbac_exceptions.RbacActionFailed(e)
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rule="get_security_group_rule")
+    @decorators.idempotent_id('84b4038c-261e-4a94-90d5-c885739ab0d5')
+    def test_show_security_group_rule(self):
+
+        sec_group_rule = self._create_security_group_rule()
+
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        try:
+            self.security_group_rules_client.show_security_group_rule(
+                sec_group_rule['id'])
+        except exceptions.NotFound as e:
+            LOG.info("NotFound exception caught. Exception is thrown when "
+                     "role doesn't have access to the endpoint."
+                     "This is irregular and should be fixed.")
+            raise rbac_exceptions.RbacActionFailed(e)
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rule="get_security_group_rules")
+    @decorators.idempotent_id('05739ab6-fa35-11e6-bc64-92361f002671')
+    def test_list_security_group_rules(self):
+
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.security_group_rules_client.list_security_group_rules()
diff --git a/patrole_tempest_plugin/tests/api/orchestration/__init__.py b/patrole_tempest_plugin/tests/api/orchestration/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/orchestration/__init__.py
diff --git a/patrole_tempest_plugin/tests/api/orchestration/rbac_base.py b/patrole_tempest_plugin/tests/api/orchestration/rbac_base.py
new file mode 100644
index 0000000..7a6ab13
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/orchestration/rbac_base.py
@@ -0,0 +1,47 @@
+# Copyright 2017 AT&T Corporation.
+#    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.api.orchestration import base as heat_base
+from tempest import config
+
+from patrole_tempest_plugin.rbac_utils import rbac_utils
+
+CONF = config.CONF
+
+
+class BaseOrchestrationRbacTest(heat_base.BaseOrchestrationTest):
+
+    credentials = ['admin']
+
+    @classmethod
+    def skip_checks(cls):
+        super(BaseOrchestrationRbacTest, cls).skip_checks()
+        if not CONF.rbac.rbac_flag:
+            raise cls.skipException(
+                "%s skipped as RBAC Flag not enabled" % cls.__name__)
+
+    @classmethod
+    def setup_credentials(cls):
+        super(BaseOrchestrationRbacTest, cls).setup_credentials()
+        cls.os = cls.os_adm
+
+    @classmethod
+    def setup_clients(cls):
+        super(BaseOrchestrationRbacTest, cls).setup_clients()
+        cls.auth_provider = cls.os.auth_provider
+        cls.admin_client = cls.os_adm.orchestration_client
+        cls.rbac_utils = rbac_utils()
+
+    def tearDown(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=False)
+        super(BaseOrchestrationRbacTest, self).tearDown()
diff --git a/patrole_tempest_plugin/tests/api/orchestration/test_soft_config_rbac.py b/patrole_tempest_plugin/tests/api/orchestration/test_soft_config_rbac.py
new file mode 100644
index 0000000..fa3840c
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/orchestration/test_soft_config_rbac.py
@@ -0,0 +1,138 @@
+# 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.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.orchestration import rbac_base
+
+
+class TestRbacSoftwareConfig(rbac_base.BaseOrchestrationRbacTest):
+
+    def setUp(self):
+        super(TestRbacSoftwareConfig, self).setUp()
+        self.config = self._config_create('a')
+        self._deployment_create(self.config['id'])
+
+    @rbac_rule_validation.action(service="heat",
+                                 rule="software_configs:show")
+    @decorators.idempotent_id('b2e7c98c-e17b-4f37-82f3-5d21eff86e79')
+    def test_get_software_config(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.client.show_software_config(self.config['id'])
+
+    @rbac_rule_validation.action(service="heat",
+                                 rule="software_deployments:metadata")
+    @decorators.idempotent_id('defa34ab-9d1f-4b14-8613-34e964c0c478')
+    def test_get_deployment_metadata(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.client.show_software_deployment_metadata(self.server_id)
+
+    @rbac_rule_validation.action(service="heat",
+                                 rule="software_deployments:index")
+    @decorators.idempotent_id('2a4dcb91-1803-4749-9cb7-5b69ba668b18')
+    def test_get_deployment_list(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.client.list_software_deployments()
+
+    @rbac_rule_validation.action(service="heat",
+                                 rule="software_deployments:show")
+    @decorators.idempotent_id('d4e627bc-88a3-4189-8092-151f22ed989d')
+    def test_software_show_deployment(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.client.show_software_deployment(self.deployment_id)
+
+    @rbac_rule_validation.action(service="heat",
+                                 rule="software_deployments:update")
+    @decorators.idempotent_id('90e8958c-6fa7-4515-b6d7-6d6952979f8c')
+    def test_software_deployment_update(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        new_action = data_utils.rand_name('ACTION')
+        new_status = data_utils.rand_name('STATUS')
+        new_reason = data_utils.rand_name('REASON')
+        self.client.update_software_deploy(self.deployment_id,
+                                           self.server_id,
+                                           self.config['id'],
+                                           new_action, new_status,
+                                           self.input_values,
+                                           self.output_values,
+                                           new_reason,
+                                           self.signal_transport)
+
+    @rbac_rule_validation.action(service="heat",
+                                 rule="software_deployments:create")
+    @decorators.idempotent_id('9175fe7b-4210-4c1d-acbb-954998a9fd77')
+    def test_software_deployment_create(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self._deployment_create(self.config['id'])
+
+    @rbac_rule_validation.action(service="heat",
+                                 rule="software_deployments:delete")
+    @decorators.idempotent_id('20f4683d-7316-4d88-a6ea-1ee6013da908')
+    def test_software_deployment_delete(self):
+        deploy_id = self._deployment_create(self.config['id'])
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.client.delete_software_deploy(deploy_id)
+
+    @rbac_rule_validation.action(service="heat",
+                                 rule="software_configs:create")
+    @decorators.idempotent_id('c8fb1c73-fcb6-46c2-9510-8ef0083c9620')
+    def test_config_create(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self._config_create('e')
+
+    @rbac_rule_validation.action(service="heat",
+                                 rule="software_configs:delete")
+    @decorators.idempotent_id('f4f784ea-9878-4306-bc5f-041ba5307ce5')
+    def test_config_delete(self):
+        configuration = self._config_create('d')
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.client.delete_software_config(configuration['id'])
+
+    def _config_create(self, suffix):
+        configuration = {'group': 'script',
+                         'inputs': [],
+                         'outputs': [],
+                         'options': {}}
+        configuration['name'] = 'heat_soft_config_%s' % suffix
+        configuration['config'] = '#!/bin/bash echo init-%s' % suffix
+        api_config = self.client.create_software_config(**configuration)
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.client.delete_software_config,
+            api_config['software_config']['id'])
+        configuration['id'] = api_config['software_config']['id']
+        return configuration
+
+    def _deployment_create(self, config_id):
+        self.server_id = data_utils.rand_name('dummy-server')
+        self.action = 'ACTION_0'
+        self.status = 'STATUS_0'
+        self.input_values = {}
+        self.output_values = []
+        self.status_reason = 'REASON_0'
+        self.signal_transport = 'NO_SIGNAL'
+        self.deployment = self.client.create_software_deploy(
+            self.server_id, config_id, self.action, self.status,
+            self.input_values, self.output_values, self.status_reason,
+            self.signal_transport)
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.client.delete_software_deploy,
+            self.deployment['software_deployment']['id'])
+        self.deployment_id = self.deployment['software_deployment']['id']
+        return self.deployment_id
diff --git a/patrole_tempest_plugin/tests/api/volume/rbac_base.py b/patrole_tempest_plugin/tests/api/volume/rbac_base.py
index 60ec77e..1cb128e 100644
--- a/patrole_tempest_plugin/tests/api/volume/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/volume/rbac_base.py
@@ -65,3 +65,9 @@
         cls.auth_provider = cls.os.auth_provider
         cls.admin_client = cls.os_adm.volumes_client
         cls.rbac_utils = rbac_utils()
+        version_checker = {
+            1: [cls.os.volume_hosts_client, cls.os.volume_types_client],
+            2: [cls.os.volume_hosts_v2_client, cls.os.volume_types_v2_client]
+        }
+        cls.volume_hosts_client, cls.volume_types_client = \
+            version_checker[cls._api_version]
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_hosts_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_hosts_rbac.py
new file mode 100644
index 0000000..45720e6
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_hosts_rbac.py
@@ -0,0 +1,33 @@
+# 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 patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.volume import rbac_base
+
+
+class VolumeHostsAdminRbacTest(rbac_base.BaseVolumeAdminRbacTest):
+
+    def tearDown(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=False)
+        super(VolumeHostsAdminRbacTest, self).tearDown()
+
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="volume_extension:hosts")
+    @decorators.idempotent_id('64e837f5-5452-4e26-b934-c721ea7a8644')
+    def test_list_hosts(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.volume_hosts_client.list_hosts()
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
new file mode 100644
index 0000000..0e9d2c3
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_types_extra_specs_rbac.py
@@ -0,0 +1,40 @@
+# 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.volume import rbac_base
+
+CONF = config.CONF
+
+
+class VolumeTypesExtraSpecsAdminRbacTest(rbac_base.BaseVolumeAdminRbacTest):
+
+    def tearDown(self):
+        self.rbac_utils.switch_role(self, switchToRbacRole=False)
+        super(VolumeTypesExtraSpecsAdminRbacTest, self).tearDown()
+
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="volume_extension:types_extra_specs")
+    @decorators.idempotent_id('eea40251-990b-49b0-99ae-10e4585b479b')
+    def test_volume_type_extra_specs_list(self):
+        vol_type = self.create_volume_type()
+        # List Volume types extra specs.
+        extra_specs = {"spec1": "val1"}
+        self.rbac_utils.switch_role(self, switchToRbacRole=True)
+        self.volume_types_client.create_volume_type_extra_specs(
+            vol_type['id'], extra_specs)
diff --git a/patrole_tempest_plugin/tests/unit/test_rbac_utils.py b/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
index 9dd5985..add1770 100644
--- a/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
+++ b/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
@@ -15,45 +15,141 @@
 
 import mock
 
+from tempest import config
 from tempest.lib import exceptions as lib_exc
 from tempest.tests import base
 
 from patrole_tempest_plugin import rbac_exceptions
 from patrole_tempest_plugin import rbac_utils
 
+CONF = config.CONF
+
 
 class RBACUtilsTest(base.TestCase):
-    def setUp(self):
+
+    @mock.patch.object(rbac_utils, 'time', autospec=True)
+    def setUp(self, _):
         super(RBACUtilsTest, self).setUp()
-        mock_creds_provider = mock.patch.object(
-            rbac_utils, 'credentials_factory').start()
-        mock_creds_provider.get_credentials_provider.return_value.\
-            creds_client.roles_client.list_roles.return_value.\
-            __getitem__.return_value = [
+        self.mock_creds_provider = mock.patch.object(
+            rbac_utils, 'credentials_factory', autospec=True).start()
+
+        available_roles = {
+            'roles': [
                 {'name': 'admin', 'id': 'admin_id'},
                 {'name': 'Member', 'id': 'member_id'}
             ]
+        }
+        self.mock_creds_provider.get_credentials_provider.return_value.\
+            creds_client.roles_client.list_roles.return_value = \
+            available_roles
+        self.addCleanup(mock.patch.stopall)
+
+        CONF.set_override('rbac_test_role', 'Member', group='rbac',
+                          enforce_type=True)
+        self.addCleanup(CONF.clear_override, 'rbac_test_role', group='rbac')
+
+        # Because rbac_utils is a singleton, reset all of its role-related
+        # parameters to the correct values for each test run.
         self.rbac_utils = rbac_utils.rbac_utils()
+        self.rbac_utils.available_roles = available_roles
+        self.rbac_utils.admin_role_id = 'admin_id'
+        self.rbac_utils.rbac_role_id = 'member_id'
 
-    def test_rbac_utils_switch_roles_none(self):
-        self.assertRaises(rbac_exceptions.RbacActionFailed,
-                          self.rbac_utils.switch_role, None)
+    def test_initialization_with_missing_admin_role(self):
+        self.rbac_utils.admin_role_id = None
+        e = self.assertRaises(rbac_exceptions.RbacResourceSetupFailed,
+                              self.rbac_utils.switch_role, None)
+        self.assertIn("Defined 'rbac_role' or 'admin' role does not exist"
+                      " in the system.", e.__str__())
 
-    def test_rbac_utils_switch_roles_false(self):
-        self.auth_provider = mock.Mock()
-        self.auth_provider.credentials.user_id = "user_id"
-        self.auth_provider.credentials.tenant_id = "tenant_id"
-        self.admin_client = mock.Mock()
-        self.admin_client.token = "admin_token"
-        self.assertIsNone(self.rbac_utils.switch_role(self, False))
+    def test_initialization_with_missing_rbac_role(self):
+        self.rbac_utils.rbac_role_id = None
+        e = self.assertRaises(rbac_exceptions.RbacResourceSetupFailed,
+                              self.rbac_utils.switch_role, None)
+        self.assertIn("Defined 'rbac_role' or 'admin' role does not exist"
+                      " in the system.", e.__str__())
 
-    def test_rbac_utils_switch_roles_get_roles_fails(self):
-        self.auth_provider = mock.Mock()
-        self.auth_provider.credentials.user_id = "user_id"
-        self.auth_provider.credentials.tenant_id = "tenant_id"
-        self.admin_client = mock.Mock()
-        self.admin_client.token = "admin_token"
-        self.rbac_utils.creds_client.roles_client.create_user_role_on_project.\
-            side_effect = lib_exc.NotFound
-        self.assertRaises(lib_exc.NotFound, self.rbac_utils.switch_role, self,
-                          False)
+    def test_clear_user_roles(self):
+        self.rbac_utils.creds_client = mock.Mock()
+        creds_client = self.rbac_utils.creds_client
+        creds_client.roles_client.list_user_roles_on_project.return_value = {
+            'roles': [{'id': 'admin_id'}, {'id': 'member_id'}]
+        }
+
+        self.rbac_utils._clear_user_roles(mock.sentinel.user_id,
+                                          mock.sentinel.project_id)
+
+        creds_client.roles_client.list_user_roles_on_project.\
+            assert_called_once_with(mock.sentinel.project_id,
+                                    mock.sentinel.user_id)
+        creds_client.roles_client.delete_role_from_user_on_project.\
+            assert_has_calls([
+                mock.call(mock.sentinel.project_id, mock.sentinel.user_id,
+                          'admin_id'),
+                mock.call(mock.sentinel.project_id, mock.sentinel.user_id,
+                          'member_id'),
+            ])
+
+    @mock.patch.object(rbac_utils.rbac_utils, '_clear_user_roles',
+                       autospec=True)
+    def test_rbac_utils_switch_role_to_admin(self, mock_clear_user_roles):
+        mock_test_object = mock.Mock()
+        mock_test_object.auth_provider.credentials.user_id = \
+            mock.sentinel.user_id
+        mock_test_object.auth_provider.credentials.tenant_id = \
+            mock.sentinel.project_id
+
+        self.rbac_utils.creds_client = mock.Mock()
+        creds_client = self.rbac_utils.creds_client
+
+        self.rbac_utils.switch_role(mock_test_object, False)
+
+        creds_client.roles_client.create_user_role_on_project.\
+            assert_called_once_with(mock.sentinel.project_id,
+                                    mock.sentinel.user_id,
+                                    'admin_id')
+        mock_clear_user_roles.assert_called_once_with(
+            self.rbac_utils, mock.sentinel.user_id, mock.sentinel.project_id)
+        mock_test_object.auth_provider.clear_auth.assert_called_once_with()
+        mock_test_object.auth_provider.set_auth.assert_called_once_with()
+
+    @mock.patch.object(rbac_utils.rbac_utils, '_clear_user_roles',
+                       autospec=True)
+    def test_rbac_utils_switch_role_to_rbac_role(self, mock_clear_user_roles):
+        mock_test_object = mock.Mock()
+        mock_test_object.auth_provider.credentials.user_id = \
+            mock.sentinel.user_id
+        mock_test_object.auth_provider.credentials.tenant_id = \
+            mock.sentinel.project_id
+
+        self.rbac_utils.creds_client = mock.Mock()
+        creds_client = self.rbac_utils.creds_client
+
+        self.rbac_utils.switch_role(mock_test_object, True)
+
+        creds_client.roles_client.create_user_role_on_project.\
+            assert_called_once_with(mock.sentinel.project_id,
+                                    mock.sentinel.user_id,
+                                    'member_id')
+        mock_clear_user_roles.assert_called_once_with(
+            self.rbac_utils, mock.sentinel.user_id, mock.sentinel.project_id)
+        mock_test_object.auth_provider.clear_auth.assert_called_once_with()
+        mock_test_object.auth_provider.set_auth.assert_called_once_with()
+
+    def test_rbac_utils_switch_roles_with_invalid_value(self):
+        e = self.assertRaises(rbac_exceptions.RbacResourceSetupFailed,
+                              self.rbac_utils.switch_role, None)
+        self.assertIn("Wrong value for parameter 'switchToRbacRole' is passed."
+                      " It should be either 'True' or 'False'.", e.__str__())
+
+    @mock.patch.object(rbac_utils.rbac_utils, '_clear_user_roles',
+                       autospec=True)
+    def test_rbac_utils_switch_role_except_exception(self,
+                                                     mock_clear_user_roles):
+        self.rbac_utils.creds_client = mock.Mock()
+        creds_client = self.rbac_utils.creds_client
+        creds_client.roles_client.create_user_role_on_project.side_effect =\
+            lib_exc.NotFound
+
+        self.assertRaises(lib_exc.NotFound, self.rbac_utils.switch_role,
+                          mock.Mock(), True)
diff --git a/test-requirements.txt b/test-requirements.txt
index dddb31f..7c97fa7 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -2,12 +2,14 @@
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
 hacking>=0.12.0,!=0.13.0,<0.14  # Apache-2.0
-# needed for doc build
+
 sphinx>=1.2.1,!=1.3b1,<1.4  # BSD
 oslosphinx>=4.7.0 # Apache-2.0
 reno>=1.8.0 # Apache-2.0
 mock>=2.0 # BSD
 coverage>=4.0 # Apache-2.0
+nose # LGPL
+nosexcover # BSD
 oslotest>=1.10.0 # Apache-2.0
 oslo.policy>=1.17.0  # Apache-2.0
 oslo.log>=3.11.0 # Apache-2.0
diff --git a/tox.ini b/tox.ini
index ba00222..b4953e7 100644
--- a/tox.ini
+++ b/tox.ini
@@ -28,7 +28,15 @@
 commands = {posargs}
 
 [testenv:cover]
-commands = python setup.py test --coverage --testr-args='{posargs}'
+setenv = VIRTUAL_ENV={envdir}
+         NOSE_WITH_COVERAGE=1
+         NOSE_COVER_BRANCHES=1
+         NOSE_COVER_PACKAGE=patrole_tempest_plugin
+         NOSE_COVER_HTML=1
+         NOSE_COVER_HTML_DIR={toxinidir}/cover
+         NOSE_WHERE=patrole_tempest_plugin/tests/unit
+whitelist_externals = nosetests
+commands = nosetests {posargs}
 
 [testenv:docs]
 commands = python setup.py build_sphinx