Merge "Increase unit test coverage for rbac_utils."
diff --git a/contrib/pre_test_hook.sh b/contrib/pre_test_hook.sh
new file mode 100755
index 0000000..65d1801
--- /dev/null
+++ b/contrib/pre_test_hook.sh
@@ -0,0 +1,39 @@
+#!/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'.
+
+sudo chown -R jenkins:stack $BASE/new/tempest
+sudo chown -R jenkins:stack $BASE/data/tempest
+
+# Import devstack function 'iniset'
+source $BASE/new/devstack/functions
+
+export TEMPEST_CONFIG=${TEMPEST_CONFIG:-$BASE/new/tempest/etc/tempest.conf}
+
+# 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
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_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/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/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