Merge "Adds tests for compute snapshots API"
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 595730a..243a9c0 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -1,14 +1,14 @@
If you would like to contribute to the development of OpenStack, you must
follow the steps in this page:
- http://docs.openstack.org/infra/manual/developers.html
+ https://docs.openstack.org/infra/manual/developers.html
If you already have a good understanding of how the system works and your
OpenStack accounts are set up, you can skip to the development workflow
section of this documentation to learn how changes to OpenStack should be
submitted for review via the Gerrit tool:
- http://docs.openstack.org/infra/manual/developers.html#development-workflow
+ https://docs.openstack.org/infra/manual/developers.html#development-workflow
Pull requests submitted through GitHub will be ignored.
diff --git a/HACKING.rst b/HACKING.rst
index b1d730d..5281b53 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -1,7 +1,7 @@
Patrole Style Commandments
==========================
-- Step 1: Read the OpenStack Style Commandments: `<http://docs.openstack.org/developer/hacking/>`__
+- Step 1: Read the OpenStack Style Commandments: `<https://docs.openstack.org/developer/hacking/>`__
- Step 2: Review Tempest's Style Commandments: `<https://docs.openstack.org/developer/tempest/HACKING.html>`__
- Step 3: Read on
@@ -37,6 +37,8 @@
- [P101] RBAC test filenames must end with "_rbac.py"; for example,
test_servers_rbac.py, not test_servers.py
- [P102] RBAC test class names must end in 'RbacTest'
+- [P103] ``self.client`` must not be used as a client alias; this allows for
+ code that is more maintainable and easier to read
Role Switching
--------------
diff --git a/README.rst b/README.rst
index ad8add0..51940d7 100644
--- a/README.rst
+++ b/README.rst
@@ -11,9 +11,9 @@
custom roles.
* Free software: Apache license
-* Documentation: http://docs.openstack.org/developer/patrole
-* Source: http://git.openstack.org/cgit/openstack/patrole
-* Bugs: http://bugs.launchpad.net/patrole
+* Documentation: https://docs.openstack.org/developer/patrole
+* Source: https://git.openstack.org/cgit/openstack/patrole
+* Bugs: https://bugs.launchpad.net/patrole
Features
========
diff --git a/contrib/post_test_hook.sh b/contrib/post_test_hook.sh
deleted file mode 100644
index a0353ca..0000000
--- a/contrib/post_test_hook.sh
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/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'.
-
-# 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="(?!.*\[.*\bslow\b.*\])(^patrole_tempest_plugin\.tests\.api)"
-DEVSTACK_MULTINODE_GATE_TEMPEST_REGEX="(?=.*\[.*\bslow\b.*\])(^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
-
-# Second argument is expected to contain value indicating whether the
-# environment is "multinode" or not (empty string).
-TYPE=$2
-
-function set_uuid_tokens() {
- # Use uuid tokens for faster test runs
- KEYSTONE_CONF=/etc/keystone/keystone.conf
- iniset $KEYSTONE_CONF token provider uuid
- sudo service apache2 restart
-}
-
-function setup_config() {
- # Set enable_rbac=True under [rbac] section in tempest.conf
- iniset $TEMPEST_CONFIG rbac enable_rbac True
- # Set rbac_test_role=$RBAC_ROLE under [rbac] section in tempest.conf
- iniset $TEMPEST_CONFIG rbac rbac_test_role $RBAC_ROLE
- # Set strict_policy_check=False under [rbac] section in tempest.conf
- iniset $TEMPEST_CONFIG rbac strict_policy_check False
- # Set additional, necessary CONF values
- iniset $TEMPEST_CONFIG auth tempest_roles Member
-}
-
-function run_tests() {
- # 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
-
- if [[ "$TYPE" == "multinode" ]]; then
- $TEMPEST_COMMAND -eall-plugin -- $DEVSTACK_MULTINODE_GATE_TEMPEST_REGEX --concurrency=$TEMPEST_CONCURRENCY
- else
- $TEMPEST_COMMAND -eall-plugin -- $DEVSTACK_GATE_TEMPEST_REGEX --concurrency=$TEMPEST_CONCURRENCY
- fi
-
- sudo -H -u tempest .tox/all-plugin/bin/tempest list-plugins
-}
-
-set_uuid_tokens
-setup_config
-run_tests
diff --git a/contrib/pre_test_hook.sh b/contrib/pre_test_hook.sh
deleted file mode 100755
index bbe05c7..0000000
--- a/contrib/pre_test_hook.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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/devstack/plugin.sh b/devstack/plugin.sh
new file mode 100644
index 0000000..1066136
--- /dev/null
+++ b/devstack/plugin.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+# Plugin file for Patrole Tempest plugin
+# --------------------------------------
+
+# Dependencies:
+# ``functions`` file
+# ``DEST`` must be defined
+
+# Save trace setting
+XTRACE=$(set +o | grep xtrace)
+set -o xtrace
+
+function install_patrole_tempest_plugin() {
+ if is_service_enabled tempest; then
+ setup_package $PATROLE_DIR -e
+
+ if [[ "$RBAC_TEST_ROLE" == "member" ]]; then
+ RBAC_TEST_ROLE="Member"
+ fi
+
+ iniset $TEMPEST_CONFIG rbac enable_rbac True
+ iniset $TEMPEST_CONFIG rbac rbac_test_role $RBAC_TEST_ROLE
+ iniset $TEMPEST_CONFIG rbac strict_policy_check False
+ fi
+}
+
+if is_service_enabled tempest; then
+ if [[ "$1" == "stack" && "$2" == "test-config" ]]; then
+ echo_summary "Installing Patrole Tempest plugin"
+ install_patrole_tempest_plugin
+ fi
+fi
+
+# Restore xtrace
+$XTRACE
diff --git a/devstack/settings b/devstack/settings
new file mode 100644
index 0000000..670e878
--- /dev/null
+++ b/devstack/settings
@@ -0,0 +1,8 @@
+# Settings needed for the Patrole Tempest plugin
+# ----------------------------------------------
+
+PATROLE_DIR=$DEST/patrole
+TEMPEST_DIR=$DEST/tempest
+TEMPEST_CONFIG_DIR=${TEMPEST_CONFIG_DIR:-$TEMPEST_DIR/etc}
+TEMPEST_CONFIG=$TEMPEST_CONFIG_DIR/tempest.conf
+RBAC_TEST_ROLE=${RBAC_TEST_ROLE:-admin}
diff --git a/doc/source/installation.rst b/doc/source/installation.rst
index e342dd8..31f94f4 100644
--- a/doc/source/installation.rst
+++ b/doc/source/installation.rst
@@ -7,6 +7,7 @@
At the command line::
+ $ git clone http://git.openstack.org/openstack/patrole
$ sudo pip install patrole
Or, if you have virtualenvwrapper installed::
@@ -19,6 +20,17 @@
$ navigate to patrole directory
$ sudo pip install -e .
+DevStack Installation
+=====================
+
+Patrole can be installed like any other DevStack plugin by including the
+``install_plugin`` directive inside local.conf::
+
+ [[local|localrc]]
+ ...
+
+ enable_plugin patrole git://git.openstack.org/openstack/patrole
+
Configuration Information
=========================
diff --git a/doc/source/usage.rst b/doc/source/usage.rst
index c2fc6d3..d2570bc 100644
--- a/doc/source/usage.rst
+++ b/doc/source/usage.rst
@@ -4,17 +4,28 @@
Usage
========
-Running Patrole Tests in Tempest
-================================
+RBAC (API) Tests
+================
-If Patrole is installed correctly, then the API tests can be executed
-from inside the tempest root directory as follows: ::
+If Patrole is installed correctly, then the RBAC tests can be executed
+from inside the tempest root directory as follows::
- tox -eall-plugin -- patrole_tempest_plugin.tests.api
+ $ tox -eall-plugin -- patrole_tempest_plugin.tests.api
-To execute patrole tests for a specific module, run: ::
+To execute patrole tests for a specific module, run::
- tox -eall-plugin -- patrole_tempest_plugin.tests.api.compute
+ $ tox -eall-plugin -- patrole_tempest_plugin.tests.api.compute
+
+.. note::
+
+ It is possible to run Patrole via ``tox -eall`` in order to run Patrole
+ isolated from other plugins. This can be accomplished by including the
+ installation of services that currently use policy in code -- for example,
+ Nova and Keystone. For example::
+
+ $ tox -evenv-tempest -- pip install /opt/stack/patrole /opt/stack/keystone /opt/stack/nova
+ $ tox -eall -- patrole_tempest_plugin.tests.api
+..
To change the role that the patrole tests are being run as, edit
``rbac_test_role`` in the ``rbac`` section of tempest.conf: ::
@@ -34,3 +45,17 @@
For more information about the Member role,
please see: `<https://ask.openstack.org/en/question/4759/member-vs-_member_/>`__.
+
+Unit Tests
+==========
+
+Patrole includes unit tests for its RBAC framework. They can be run by
+executing::
+
+ $ tox -e py27
+
+or::
+
+ $ tox -e py35
+
+against the Python 3.5 interpreter.
diff --git a/patrole_tempest_plugin/hacking/checks.py b/patrole_tempest_plugin/hacking/checks.py
index e7e5cb0..a3ef01f 100644
--- a/patrole_tempest_plugin/hacking/checks.py
+++ b/patrole_tempest_plugin/hacking/checks.py
@@ -202,6 +202,16 @@
return 0, "RBAC test class names must end in 'RbacTest'"
+def no_client_alias_in_test_cases(filename, logical_line):
+ """Check that test cases don't use "self.client" to define a client.
+
+ P103
+ """
+ if "patrole_tempest_plugin/tests/api" in filename:
+ if "self.client" in logical_line or "cls.client" in logical_line:
+ return 0, "Do not use 'self.client' as a service client alias"
+
+
def factory(register):
register(import_no_clients_in_api_tests)
register(no_setup_teardown_class_for_tests)
diff --git a/patrole_tempest_plugin/rbac_rule_validation.py b/patrole_tempest_plugin/rbac_rule_validation.py
index c63ef90..ba04a30 100644
--- a/patrole_tempest_plugin/rbac_rule_validation.py
+++ b/patrole_tempest_plugin/rbac_rule_validation.py
@@ -81,7 +81,7 @@
LOG.info("As admin_only is True, only admin role should be "
"allowed to perform the API. Skipping oslo.policy "
"check for policy action {0}.".format(rule))
- allowed = CONF.rbac.rbac_test_role == CONF.identity.admin_role
+ allowed = test_obj.rbac_utils.is_admin
else:
allowed = _is_authorized(test_obj, service, rule,
extra_target_data)
diff --git a/patrole_tempest_plugin/rbac_utils.py b/patrole_tempest_plugin/rbac_utils.py
index fe2d99f..3bb2cbd 100644
--- a/patrole_tempest_plugin/rbac_utils.py
+++ b/patrole_tempest_plugin/rbac_utils.py
@@ -162,3 +162,11 @@
self.admin_role_id = admin_role_id
self.rbac_role_id = rbac_role_id
+
+ @property
+ def is_admin(self):
+ """Verifies whether the current test role equals the admin role.
+
+ :returns: True if ``rbac_test_role`` is the admin role.
+ """
+ return CONF.rbac.rbac_test_role == CONF.identity.admin_role
diff --git a/patrole_tempest_plugin/tests/api/compute/test_assisted_volume_snapshot_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_assisted_volume_snapshot_rbac.py
deleted file mode 100644
index 37e8573..0000000
--- a/patrole_tempest_plugin/tests/api/compute/test_assisted_volume_snapshot_rbac.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# 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 patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.compute import rbac_base
-
-from tempest.lib.common.utils import data_utils
-from tempest.lib import decorators
-
-
-class AssistedVolumeSnapshotRbacTest(rbac_base.BaseV2ComputeRbacTest):
- """Assisted volume snapshot tests.
-
- Test class for create and delete
- """
-
- @classmethod
- def setup_clients(cls):
- """Setup clients."""
- super(AssistedVolumeSnapshotRbacTest, cls).setup_clients()
- cls.client = cls.servers_client
-
- def _create_and_attach(self):
- self.server = self.create_test_server(wait_until='ACTIVE')
- self.volume = self.create_volume()
- self.attachment = self.attach_volume(
- self.server, self.volume)
-
- @decorators.skip_because(bug="1668407")
- @decorators.idempotent_id('74f64957-912d-4537-983b-cea4a31c5c9f')
- @rbac_rule_validation.action(
- service="nova",
- rule="os_compute_api:os-assisted-volume-snapshots:create")
- def test_assisted_volume_snapshot_create(self):
- """Create Role Test.
-
- RBAC test for assisted volume snapshot role-create
- """
- self._create_and_attach()
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.assisted_volume_snapshot_client.\
- create_volume_attachments(self.volume['id'],
- data_utils.rand_uuid())
-
- @decorators.skip_because(bug="1668407")
- @decorators.idempotent_id('01323040-c5df-4e15-8b1a-3df98fa7d998')
- @rbac_rule_validation.action(
- service="nova",
- rule="os_compute_api:os-assisted-volume-snapshots:delete")
- def test_assisted_volume_snapshot_delete(self):
- """Delete Role Test.
-
- RBAC test for assisted volume snapshot role-delete
- """
- self._create_and_attach()
- snapshot_id = data_utils.rand_uuid()
- self.assisted_volume_snapshot_client.\
- create_volume_attachments(self.volume['id'], snapshot_id)
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.assisted_volume_snapshot_client.\
- delete_volume_attachments(snapshot_id, self.volume['id'])
diff --git a/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py
index 3d66f2e..cdabf6f 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py
@@ -178,3 +178,33 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.compute_images_client.delete_image_metadata_item(self.image['id'],
key='foo')
+
+
+class ImageSizeRbacTest(rbac_base.BaseV2ComputeRbacTest):
+ """Tests the ``image_size`` compute policies.
+
+ NOTE(felipemonteiro): If Patrole is enhanced to test multiple policies
+ simultaneously, these policy actions can be combined with the related
+ tests from ``ImagesRbacTest`` above.
+ """
+
+ # These tests will fail with a 404 starting from microversion 2.36.
+ # See the following link for details:
+ # https://developer.openstack.org/api-ref/compute/#images-deprecated
+ max_microversion = '2.35'
+
+ @decorators.idempotent_id('fe34d2a6-5743-45bf-8f92-a1d703d7c7ab')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:image-size")
+ def test_list_images(self):
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.compute_images_client.list_images()
+
+ @decorators.idempotent_id('08342c7d-297d-42ee-b398-90fce2443792')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:image-size")
+ def test_list_images_with_details(self):
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.compute_images_client.list_images(detail=True)
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 a3197b3..516a143 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
@@ -19,7 +19,6 @@
from tempest.common import waiters
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
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -36,11 +35,9 @@
@classmethod
def resource_setup(cls):
- cls.set_validation_resources()
super(ServerActionsRbacTest, cls).resource_setup()
# Create test server
- cls.server_id = cls.create_test_server(wait_until='ACTIVE',
- validatable=True)['id']
+ cls.server_id = cls.create_test_server(wait_until='ACTIVE')['id']
cls.flavor_ref = CONF.compute.flavor_ref
cls.flavor_ref_alt = CONF.compute.flavor_ref_alt
cls.image_ref = CONF.compute.image_ref
@@ -66,16 +63,13 @@
waiters.wait_for_server_status(self.servers_client,
self.server_id, 'ACTIVE')
except lib_exc.NotFound:
- # if the server was found to be deleted by a previous test,
+ # 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')
+ server = self.create_test_server(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)
+ self.__class__.server_id = self.rebuild_server(self.server_id)
@classmethod
def resource_cleanup(cls):
@@ -95,15 +89,12 @@
waiters.wait_for_volume_resource_status(
cls.snapshots_extensions_client, snapshot['id'],
'available')
- test_utils.call_and_ignore_notfound_exc(
- cls.snapshots_extensions_client.delete_snapshot,
+ cls.snapshots_extensions_client.delete_snapshot(
snapshot['id'])
for snapshot in volume_snapshots:
if snapshot['volumeId'] == cls.volume_id:
- test_utils.call_and_ignore_notfound_exc(
- (cls.snapshots_extensions_client.
- wait_for_resource_deletion),
+ cls.snapshots_extensions_client.wait_for_resource_deletion(
snapshot['id'])
super(ServerActionsRbacTest, cls).resource_cleanup()
@@ -273,6 +264,7 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.servers_client.show_server(self.server_id)
+ @test.services('image')
@rbac_rule_validation.action(
service="nova",
rule="os_compute_api:servers:create_image")
@@ -284,6 +276,7 @@
self.create_image_from_server(self.server_id,
wait_until='ACTIVE')
+ @test.services('image', 'volume')
@rbac_rule_validation.action(
service="nova",
rule="os_compute_api:servers:create_image:allow_volume_backed")
@@ -297,6 +290,32 @@
wait_until='ACTIVE')
+class ServerActionsV214RbacTest(rbac_base.BaseV2ComputeRbacTest):
+
+ min_microversion = '2.14'
+ max_microversion = 'latest'
+
+ @classmethod
+ def resource_setup(cls):
+ super(ServerActionsV214RbacTest, cls).resource_setup()
+ cls.server_id = cls.create_test_server(wait_until='ACTIVE')['id']
+
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:os-evacuate")
+ @decorators.idempotent_id('78ecef3c-faff-412a-83be-47651963eb21')
+ def test_evacuate_server(self):
+ fake_host_name = data_utils.rand_name(
+ self.__class__.__name__ + '-FakeHost')
+
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.assertRaisesRegex(lib_exc.NotFound,
+ "Compute host %s not found." % fake_host_name,
+ self.servers_client.evacuate_server,
+ self.server_id,
+ host=fake_host_name)
+
+
class ServerActionsV216RbacTest(rbac_base.BaseV2ComputeRbacTest):
# This class has test case(s) that requires at least microversion 2.16.
@@ -306,33 +325,9 @@
max_microversion = 'latest'
@classmethod
- def setup_clients(cls):
- super(ServerActionsV216RbacTest, cls).setup_clients()
- cls.client = cls.servers_client
-
- @classmethod
def resource_setup(cls):
- cls.set_validation_resources()
super(ServerActionsV216RbacTest, cls).resource_setup()
- cls.server_id = cls.create_test_server(wait_until='ACTIVE',
- validatable=True)['id']
-
- def setUp(self):
- super(ServerActionsV216RbacTest, self).setUp()
- try:
- waiters.wait_for_server_status(self.servers_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)
+ cls.server_id = cls.create_test_server(wait_until='ACTIVE')['id']
@rbac_rule_validation.action(
service="nova",
@@ -346,32 +341,3 @@
LOG.info("host_status attribute not returned when role doesn't "
"have permission to access it.")
raise rbac_exceptions.RbacActionFailed
-
-
-class ServerActionsV214RbacTest(rbac_base.BaseV2ComputeRbacTest):
-
- min_microversion = '2.14'
- max_microversion = 'latest'
-
- @classmethod
- def setup_clients(cls):
- super(ServerActionsV214RbacTest, cls).setup_clients()
- cls.client = cls.servers_client
-
- @classmethod
- def resource_setup(cls):
- cls.set_validation_resources()
- super(ServerActionsV214RbacTest, cls).resource_setup()
- cls.server_id = cls.create_test_server(wait_until='ACTIVE')['id']
-
- @rbac_rule_validation.action(
- service="nova",
- rule="os_compute_api:os-evacuate")
- @decorators.idempotent_id('78ecef3c-faff-412a-83be-47651963eb21')
- def test_evacuate_server(self):
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.assertRaisesRegex(lib_exc.NotFound,
- "Compute host fake-host not found.",
- self.servers_client.evacuate_server,
- self.server_id,
- host='fake-host')
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_metadata_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_metadata_rbac.py
new file mode 100644
index 0000000..ac2dcb0
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_metadata_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 import decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.compute import rbac_base
+
+
+class ServerMetadataRbacTest(rbac_base.BaseV2ComputeRbacTest):
+
+ @classmethod
+ def resource_setup(cls):
+ super(ServerMetadataRbacTest, cls).resource_setup()
+ cls.server = cls.create_test_server(metadata={}, wait_until='ACTIVE')
+ cls.meta = {'default_key': 'value1', 'delete_key': 'value2'}
+
+ def setUp(self):
+ super(ServerMetadataRbacTest, self).setUp()
+ self.servers_client.set_server_metadata(self.server['id'], self.meta)[
+ 'metadata']
+
+ @decorators.idempotent_id('b07bbc27-58e2-4581-869d-ad228cec5d9a')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:server-metadata:index")
+ def test_list_server_metadata(self):
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.servers_client.list_server_metadata(self.server['id'])['metadata']
+
+ @decorators.idempotent_id('6e76748b-2417-4fa2-b41a-c0cc4bff356b')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:server-metadata:update_all")
+ def test_set_server_metadata(self):
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.servers_client.set_server_metadata(self.server['id'], {})[
+ 'metadata']
+
+ @decorators.idempotent_id('1060bac4-fe16-4a77-be64-d8e482a06eab')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:server-metadata:create")
+ def test_update_server_metadata(self):
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.servers_client.update_server_metadata(self.server['id'], {})[
+ 'metadata']
+
+ @decorators.idempotent_id('93dd8323-d3fa-48d1-8bd6-91c1b62fc341')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:server-metadata:show")
+ def test_show_server_metadata_item(self):
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.servers_client.show_server_metadata_item(
+ self.server['id'], 'default_key')['meta']
+
+ @decorators.idempotent_id('79511293-4bd7-447d-ba7e-634d0f4da70c')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:server-metadata:update")
+ def test_set_server_metadata_item(self):
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.servers_client.set_server_metadata_item(
+ self.server['id'], 'default_key', {'default_key': 'value2'})[
+ 'meta']
+
+ @decorators.idempotent_id('feec5064-678d-40bc-a88f-c856e18d1e31')
+ @rbac_rule_validation.action(
+ service="nova",
+ rule="os_compute_api:server-metadata:delete")
+ def test_delete_server_metadata_item(self):
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.servers_client.delete_server_metadata_item(
+ self.server['id'], 'delete_key')
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
index 3613a25..3c8ef69 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
@@ -127,8 +127,9 @@
# We just need any host out of the hosts list to build the
# availability_zone attribute. So, picking the first one is fine.
# The first key of the dictionary specifies the host name.
- host = hosts[0].keys()[0]
+ host = list(hosts[0].keys())[0]
availability_zone = 'nova:' + host
+
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.create_test_server(wait_until='ACTIVE',
availability_zone=availability_zone)
diff --git a/patrole_tempest_plugin/tests/api/identity/v2/test_projects_rbac.py b/patrole_tempest_plugin/tests/api/identity/v2/test_projects_rbac.py
index 9a4363d..784045a 100644
--- a/patrole_tempest_plugin/tests/api/identity/v2/test_projects_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v2/test_projects_rbac.py
@@ -112,8 +112,7 @@
admin-scoped tenants, raise ``RbacActionFailed`` exception otherwise.
"""
tenants_client = self.os_admin.tenants_client if \
- CONF.identity.admin_role == CONF.rbac.rbac_test_role else \
- self.os_primary.tenants_client
+ self.rbac_utils.is_admin else self.os_primary.tenants_client
admin_tenant_id = self.os_admin.auth_provider.credentials.project_id
non_admin_tenant_id = self.auth_provider.credentials.project_id
diff --git a/patrole_tempest_plugin/tests/api/image/v1/test_images_rbac.py b/patrole_tempest_plugin/tests/api/image/v1/test_images_rbac.py
index 7010baa..a90790b 100644
--- a/patrole_tempest_plugin/tests/api/image/v1/test_images_rbac.py
+++ b/patrole_tempest_plugin/tests/api/image/v1/test_images_rbac.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from six import moves
+import six
from tempest import config
from tempest.lib.common.utils import data_utils
@@ -77,7 +77,7 @@
properties=properties)
image_id = body['id']
# Now try uploading an image file
- image_file = moves.cStringIO(data_utils.random_bytes())
+ image_file = six.BytesIO(data_utils.random_bytes())
self.client.update_image(image_id, data=image_file)
# Toggle role and get created image
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
@@ -99,7 +99,7 @@
properties=properties)
image_id = body['id']
# Now try uploading an image file
- image_file = moves.cStringIO(data_utils.random_bytes())
+ image_file = six.BytesIO(data_utils.random_bytes())
self.client.update_image(image_id, data=image_file)
# Toggle role and get created image
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
diff --git a/patrole_tempest_plugin/tests/api/image/v2/test_images_rbac.py b/patrole_tempest_plugin/tests/api/image/v2/test_images_rbac.py
index 5b7c823..78ba9e5 100644
--- a/patrole_tempest_plugin/tests/api/image/v2/test_images_rbac.py
+++ b/patrole_tempest_plugin/tests/api/image/v2/test_images_rbac.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from six import moves
+import six
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
@@ -38,7 +38,7 @@
return image
def _upload_image(self, image_id):
- image_file = moves.cStringIO(data_utils.random_bytes())
+ image_file = six.BytesIO(data_utils.random_bytes())
return self.client.store_image_file(image_id, image_file)
@rbac_rule_validation.action(service="glance",
@@ -184,6 +184,18 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self._create_image(visibility='public')
+ @decorators.idempotent_id('0f2d8427-134a-4d3c-a102-5fcdf5443d09')
+ @rbac_rule_validation.action(service="glance",
+ rule="communitize_image")
+ def test_communitize_image(self):
+
+ """Communitize Image Test
+
+ RBAC test for the glance communitize_image policy
+ """
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self._create_image(visibility='community')
+
@rbac_rule_validation.action(service="glance",
rule="deactivate")
@decorators.idempotent_id('b488458c-65df-11e6-9947-080027824017')
diff --git a/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py b/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py
index 8208fde..f72fe75 100644
--- a/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py
@@ -17,8 +17,8 @@
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
+from tempest import test
from patrole_tempest_plugin import rbac_exceptions
from patrole_tempest_plugin import rbac_rule_validation
@@ -27,68 +27,37 @@
CONF = config.CONF
-class RbacNetworksTest(base.BaseNetworkRbacTest):
+class NetworksRbacTest(base.BaseNetworkRbacTest):
@classmethod
def resource_setup(cls):
- super(RbacNetworksTest, cls).resource_setup()
-
- network_name = data_utils.rand_name(
- cls.__name__ + '-rbac-admin-network-')
-
- post_body = {'name': network_name}
- body = cls.networks_client.create_network(**post_body)
- cls.admin_network = body['network']
- cls.networks.append(cls.admin_network)
-
- # Create a subnet by admin user
+ super(NetworksRbacTest, cls).resource_setup()
+ cls.network = cls.create_network()
cls.cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
-
- cls.admin_subnet = cls.create_subnet(cls.admin_network,
- cidr=cls.cidr,
- mask_bits=24,
- enable_dhcp=False)
-
- def _delete_network(self, network):
- # Deleting network also deletes its subnets if exists
- self.networks_client.delete_network(network['id'])
- if network in self.networks:
- self.networks.remove(network)
- for subnet in self.subnets:
- if subnet['network_id'] == network['id']:
- self.subnets.remove(subnet)
+ cls.subnet = cls.create_subnet(
+ cls.network, cidr=cls.cidr, mask_bits=24, enable_dhcp=False)
def _create_network(self,
- shared=None,
router_external=None,
router_private=None,
provider_network_type=None,
provider_physical_network=None,
- provider_segmentation_id=None):
+ provider_segmentation_id=None,
+ **kwargs):
+ if router_external is not None:
+ kwargs['router:external'] = router_external
+ if router_private is not None:
+ kwargs['router:private'] = router_private
+ if provider_network_type is not None:
+ kwargs['provider:network_type'] = provider_network_type
+ if provider_physical_network is not None:
+ kwargs['provider:physical_network'] = provider_physical_network
+ if provider_segmentation_id is not None:
+ kwargs['provider:segmentation_id'] = provider_segmentation_id
network_name = data_utils.rand_name(
- self.__class__.__name__ + '-test-network-')
- post_body = {'name': network_name}
-
- if shared is not None:
- post_body['shared'] = shared
- if router_external is not None:
- post_body['router:external'] = router_external
- if router_private is not None:
- post_body['router:private'] = router_private
- if provider_network_type is not None:
- post_body['provider:network_type'] = provider_network_type
- if provider_physical_network is not None:
- post_body['provider:physical_network'] = provider_physical_network
- if provider_segmentation_id is not None:
- post_body['provider:segmentation_id'] = provider_segmentation_id
-
- body = self.networks_client.create_network(**post_body)
- network = body['network']
-
- self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- self._delete_network, network)
- self.assertEqual('ACTIVE', network['status'])
+ self.__class__.__name__ + '-Network')
+ network = self.create_network(network_name=network_name, **kwargs)
return network
def _update_network(self,
@@ -97,30 +66,24 @@
shared_network=None,
router_external=None,
router_private=None,
- segments=None):
-
- # update a network that has been created during class setup
+ segments=None,
+ **kwargs):
if not net_id:
- net_id = self.admin_network['id']
-
- post_body = {}
- updated_network = None
+ net_id = self.network['id']
if admin is not None:
- post_body['admin_state_up'] = admin
+ kwargs['admin_state_up'] = admin
elif shared_network is not None:
- post_body['shared'] = shared_network
+ kwargs['shared'] = shared_network
elif router_external is not None:
- post_body['router:external'] = router_external
+ kwargs['router:external'] = router_external
elif router_private is not None:
- post_body['router:private'] = router_private
+ kwargs['router:private'] = router_private
elif segments is not None:
- post_body['segments'] = segments
- else:
- return updated_network
+ kwargs['segments'] = segments
- body = self.networks_client.update_network(net_id, **post_body)
- updated_network = body['network']
+ updated_network = self.networks_client.update_network(
+ net_id, **kwargs)['network']
return updated_network
@rbac_rule_validation.action(service="neutron",
@@ -194,13 +157,11 @@
RBAC test for the neutron update_network policy
"""
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- updated_network = self._update_network(admin=False)
- self.assertEqual(updated_network['admin_state_up'], False)
+ updated_name = data_utils.rand_name(
+ self.__class__.__name__ + '-Network')
- # Revert back to True
- updated_network = self._update_network(admin=True)
- self.assertEqual(updated_network['admin_state_up'], True)
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self._update_network(name=updated_name)
@rbac_rule_validation.action(service="neutron",
rule="update_network:shared")
@@ -212,12 +173,8 @@
RBAC test for the neutron update_network:shared policy
"""
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- updated_network = self._update_network(shared_network=True)
- self.assertEqual(updated_network['shared'], True)
-
- # Revert back to False
- updated_network = self._update_network(shared_network=False)
- self.assertEqual(updated_network['shared'], False)
+ self._update_network(shared_network=True)
+ self.addCleanup(self._update_network, shared_network=False)
@rbac_rule_validation.action(service="neutron",
rule="update_network:router:external")
@@ -242,8 +199,7 @@
RBAC test for the neutron get_network policy
"""
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- # show a network that has been created during class setup
- self.networks_client.show_network(self.admin_network['id'])
+ self.networks_client.show_network(self.network['id'])
@rbac_rule_validation.action(service="neutron",
rule="get_network:router:external")
@@ -254,11 +210,11 @@
RBAC test for the neutron get_network:router:external policy
"""
- post_body = {'fields': 'router:external'}
+ kwargs = {'fields': 'router:external'}
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.networks_client.show_network(self.admin_network['id'],
- **post_body)
+ self.networks_client.show_network(self.network['id'],
+ **kwargs)
@rbac_rule_validation.action(service="neutron",
rule="get_network:provider:network_type")
@@ -269,14 +225,13 @@
RBAC test for the neutron get_network:provider:network_type policy
"""
- post_body = {'fields': 'provider:network_type'}
+ kwargs = {'fields': 'provider:network_type'}
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- body = self.networks_client.show_network(self.admin_network['id'],
- **post_body)
- showed_net = body['network']
+ retrieved_network = self.networks_client.show_network(
+ self.network['id'], **kwargs)['network']
- if len(showed_net) == 0:
+ if len(retrieved_network) == 0:
raise rbac_exceptions.RbacActionFailed
@rbac_rule_validation.action(service="neutron",
@@ -288,14 +243,13 @@
RBAC test for the neutron get_network:provider:physical_network policy
"""
- post_body = {'fields': 'provider:physical_network'}
+ kwargs = {'fields': 'provider:physical_network'}
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- body = self.networks_client.show_network(self.admin_network['id'],
- **post_body)
- showed_net = body['network']
+ retrieved_network = self.networks_client.show_network(
+ self.network['id'], **kwargs)['network']
- if len(showed_net) == 0:
+ if len(retrieved_network) == 0:
raise rbac_exceptions.RbacActionFailed
@rbac_rule_validation.action(service="neutron",
@@ -307,18 +261,17 @@
RBAC test for the neutron get_network:provider:segmentation_id policy
"""
- post_body = {'fields': 'provider:segmentation_id'}
+ kwargs = {'fields': 'provider:segmentation_id'}
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- body = self.networks_client.show_network(self.admin_network['id'],
- **post_body)
- showed_net = body['network']
+ retrieved_network = self.networks_client.show_network(
+ self.network['id'], **kwargs)['network']
- if len(showed_net) == 0:
+ if len(retrieved_network) == 0:
raise rbac_exceptions.RbacActionFailed
- key = showed_net.get('provider:segmentation_id', "NotFound")
- self.assertIsNot(key, "NotFound")
+ key = retrieved_network.get('provider:segmentation_id', "NotFound")
+ self.assertNotEqual(key, "NotFound")
@rbac_rule_validation.action(service="neutron",
rule="delete_network")
@@ -343,10 +296,8 @@
RBAC test for the neutron create_subnet policy
"""
network = self._create_network()
- self.assertEqual('ACTIVE', network['status'])
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- # Create a subnet
self.create_subnet(network, enable_dhcp=False)
@rbac_rule_validation.action(service="neutron",
@@ -359,7 +310,7 @@
RBAC test for the neutron get_subnet policy
"""
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.subnets_client.show_subnet(self.admin_subnet['id'])
+ self.subnets_client.show_subnet(self.subnet['id'])
@rbac_rule_validation.action(service="neutron",
rule="update_subnet")
@@ -370,9 +321,12 @@
RBAC test for the neutron update_subnet policy
"""
+ updated_name = data_utils.rand_name(
+ self.__class__.__name__ + '-Network')
+
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.subnets_client.update_subnet(self.admin_subnet['id'],
- name="New_subnet")
+ self.subnets_client.update_subnet(self.subnet['id'],
+ name=updated_name)
@rbac_rule_validation.action(service="neutron",
rule="delete_subnet")
@@ -383,13 +337,22 @@
RBAC test for the neutron delete_subnet policy
"""
- # Create a network using admin privilege
network = self._create_network()
- self.assertEqual('ACTIVE', network['status'])
-
- # Create a subnet using admin privilege
subnet = self.create_subnet(network, enable_dhcp=False)
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- # Delete the subnet
self.subnets_client.delete_subnet(subnet['id'])
+
+ @test.requires_ext(extension='dhcp_agent_scheduler', service='network')
+ @decorators.idempotent_id('b524f19f-fbb4-4d11-a85d-03bfae17bf0e')
+ @rbac_rule_validation.action(service="neutron",
+ rule="get_dhcp-agents")
+ def test_list_dhcp_agents_on_hosting_network(self):
+
+ """List DHCP Agents on Hosting Network Test
+
+ RBAC test for the neutron "get_dhcp-agents" policy
+ """
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.networks_client.list_dhcp_agents_on_hosting_network(
+ self.network['id'])
diff --git a/patrole_tempest_plugin/tests/api/network/test_ports_rbac.py b/patrole_tempest_plugin/tests/api/network/test_ports_rbac.py
index 33e0d2e..cec860c 100644
--- a/patrole_tempest_plugin/tests/api/network/test_ports_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_ports_rbac.py
@@ -16,20 +16,17 @@
import netaddr
-from oslo_log import log
-
from tempest.common.utils import net_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
+from tempest import test
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
CONF = config.CONF
-LOG = log.getLogger(__name__)
class PortsRbacTest(base.BaseNetworkRbacTest):
@@ -37,36 +34,28 @@
@classmethod
def resource_setup(cls):
super(PortsRbacTest, cls).resource_setup()
+ # Create a network and subnet.
cls.network = cls.create_network()
-
- # Create a subnet by admin user
cls.cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
-
cls.subnet = cls.create_subnet(cls.network, cidr=cls.cidr,
mask_bits=24)
cls.ip_range = netaddr.IPRange(
cls.subnet['allocation_pools'][0]['start'],
cls.subnet['allocation_pools'][0]['end'])
- # Create a port by admin user
- body = cls.ports_client.create_port(network_id=cls.network['id'])
- cls.port = body['port']
- cls.ports.append(cls.port)
+ cls.port = cls.create_port(cls.network)
ipaddr = cls.port['fixed_ips'][0]['ip_address']
cls.port_ip_address = ipaddr
cls.port_mac_address = cls.port['mac_address']
- def _create_port(self, **post_body):
-
- body = self.ports_client.create_port(**post_body)
- port = body['port']
-
- # Schedule port deletion with verification upon test completion
- self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- self.ports_client.delete_port,
- port['id'])
-
- return port
+ def _get_unused_ip_address(self):
+ # Pick an unused ip address.
+ ip_list = net_utils.get_unused_ip_addresses(self.ports_client,
+ self.subnets_client,
+ self.network['id'],
+ self.subnet['id'],
+ 1)
+ return ip_list
@rbac_rule_validation.action(service="neutron",
rule="create_port")
@@ -74,51 +63,35 @@
def test_create_port(self):
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- post_body = {'network_id': self.network['id']}
- self._create_port(**post_body)
+ self.create_port(self.network)
+ @decorators.idempotent_id('045ee797-4962-4913-b96a-5d7ea04099e7')
+ @rbac_rule_validation.action(service="neutron",
+ rule="create_port:device_owner")
+ def test_create_port_device_owner(self):
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.create_port(self.network, device_owner='network:router_interface')
+
+ @decorators.idempotent_id('c4fa8844-f5ef-4daa-bfa2-b89897dfaedf')
+ @rbac_rule_validation.action(service="neutron",
+ rule="create_port:port_security_enabled")
+ def test_create_port_security_enabled(self):
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.create_port(self.network, port_security_enabled=True)
+
+ @test.requires_ext(extension='binding', service='network')
@rbac_rule_validation.action(service="neutron",
rule="create_port:binding:host_id")
@decorators.idempotent_id('a54bd6b8-a7eb-4101-bfe8-093930b0d660')
def test_create_port_binding_host_id(self):
- post_body = {'network_id': self.network['id'],
+ post_body = {'network': self.network,
'binding:host_id': "rbac_test_host"}
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self._create_port(**post_body)
+ self.create_port(**post_body)
- @rbac_rule_validation.action(service="neutron",
- rule="create_port:fixed_ips")
- @decorators.idempotent_id('2551e10d-006a-413c-925a-8c6f834c09ac')
- def test_create_port_fixed_ips(self):
-
- # Pick an unused ip address.
- ip_list = net_utils.get_unused_ip_addresses(self.ports_client,
- self.subnets_client,
- self.network['id'],
- self.subnet['id'],
- 1)
- fixed_ips = [{'ip_address': ip_list[0]},
- {'subnet_id': self.subnet['id']}]
-
- post_body = {'network_id': self.network['id'],
- 'fixed_ips': fixed_ips}
-
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self._create_port(**post_body)
-
- @rbac_rule_validation.action(service="neutron",
- rule="create_port:mac_address")
- @decorators.idempotent_id('aee6d0be-a7f3-452f-aefc-796b4eb9c9a8')
- def test_create_port_mac_address(self):
-
- post_body = {'network_id': self.network['id'],
- 'mac_address': data_utils.rand_mac_address()}
-
- self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self._create_port(**post_body)
-
+ @test.requires_ext(extension='binding', service='network')
@rbac_rule_validation.action(service="neutron",
rule="create_port:binding:profile")
@decorators.idempotent_id('98fa38ab-c2ed-46a0-99f0-59f18cbd257a')
@@ -126,11 +99,37 @@
binding_profile = {"foo": "1"}
- post_body = {'network_id': self.network['id'],
+ post_body = {'network': self.network,
'binding:profile': binding_profile}
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self._create_port(**post_body)
+ self.create_port(**post_body)
+
+ @rbac_rule_validation.action(service="neutron",
+ rule="create_port:fixed_ips")
+ @decorators.idempotent_id('2551e10d-006a-413c-925a-8c6f834c09ac')
+ def test_create_port_fixed_ips(self):
+
+ ip_list = self._get_unused_ip_address()
+ fixed_ips = [{'ip_address': ip_list[0]},
+ {'subnet_id': self.subnet['id']}]
+
+ post_body = {'network': self.network,
+ 'fixed_ips': fixed_ips}
+
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.create_port(**post_body)
+
+ @rbac_rule_validation.action(service="neutron",
+ rule="create_port:mac_address")
+ @decorators.idempotent_id('aee6d0be-a7f3-452f-aefc-796b4eb9c9a8')
+ def test_create_port_mac_address(self):
+
+ post_body = {'network': self.network,
+ 'mac_address': data_utils.rand_mac_address()}
+
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.create_port(**post_body)
@rbac_rule_validation.action(service="neutron",
rule="create_port:allowed_address_pairs")
@@ -141,11 +140,11 @@
allowed_address_pairs = [{'ip_address': self.port_ip_address,
'mac_address': self.port_mac_address}]
- post_body = {'network_id': self.network['id'],
+ post_body = {'network': self.network,
'allowed_address_pairs': allowed_address_pairs}
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self._create_port(**post_body)
+ self.create_port(**post_body)
@rbac_rule_validation.action(service="neutron",
rule="get_port",
@@ -155,6 +154,7 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.ports_client.show_port(self.port['id'])
+ @test.requires_ext(extension='binding', service='network')
@rbac_rule_validation.action(service="neutron",
rule="get_port:binding:vif_type")
@decorators.idempotent_id('125aff0b-8fed-4f8e-8410-338616594b06')
@@ -172,6 +172,7 @@
if fields[0] not in retrieved_port:
raise rbac_exceptions.RbacActionFailed
+ @test.requires_ext(extension='binding', service='network')
@rbac_rule_validation.action(service="neutron",
rule="get_port:binding:vif_details")
@decorators.idempotent_id('e42bfd77-fcce-45ee-9728-3424300f0d6f')
@@ -189,6 +190,7 @@
if fields[0] not in retrieved_port:
raise rbac_exceptions.RbacActionFailed
+ @test.requires_ext(extension='binding', service='network')
@rbac_rule_validation.action(service="neutron",
rule="get_port:binding:host_id")
@decorators.idempotent_id('8e61bcdc-6f81-443c-833e-44410266551e')
@@ -196,12 +198,11 @@
# Verify specific fields of a port
fields = ['binding:host_id']
- post_body = {'network_id': self.network['id'],
+ post_body = {'network': self.network,
'binding:host_id': data_utils.rand_name('host-id')}
- port = self._create_port(**post_body)
+ port = self.create_port(**post_body)
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-
retrieved_port = self.ports_client.show_port(
port['id'], fields=fields)['port']
@@ -209,6 +210,7 @@
if fields[0] not in retrieved_port:
raise rbac_exceptions.RbacActionFailed
+ @test.requires_ext(extension='binding', service='network')
@rbac_rule_validation.action(service="neutron",
rule="get_port:binding:profile")
@decorators.idempotent_id('d497cea9-c4ad-42e0-acc9-8d257d6b01fc')
@@ -217,12 +219,11 @@
# Verify specific fields of a port
fields = ['binding:profile']
binding_profile = {"foo": "1"}
- post_body = {'network_id': self.network['id'],
+ post_body = {'network': self.network,
'binding:profile': binding_profile}
- port = self._create_port(**post_body)
+ port = self.create_port(**post_body)
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-
retrieved_port = self.ports_client.show_port(
port['id'], fields=fields)['port']
@@ -234,21 +235,34 @@
rule="update_port")
@decorators.idempotent_id('afa80981-3c59-42fd-9531-3bcb2cd03711')
def test_update_port(self):
-
- port = self.create_port(self.network)
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.ports_client.update_port(port['id'], admin_state_up=False)
+ self.ports_client.update_port(self.port['id'], admin_state_up=False)
+ self.addCleanup(self.ports_client.update_port, self.port['id'],
+ admin_state_up=True)
+
+ @decorators.idempotent_id('08d70f59-67cb-4fb1-bd6c-a5e59dd5db2b')
+ @rbac_rule_validation.action(service="neutron",
+ rule="update_port:device_owner")
+ def test_update_port_device_owner(self):
+ original_device_owner = self.port['device_owner']
+
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.ports_client.update_port(
+ self.port['id'], device_owner='network:router_interface')
+ self.addCleanup(self.ports_client.update_port, self.port['id'],
+ device_owner=original_device_owner)
@rbac_rule_validation.action(service="neutron",
rule="update_port:mac_address")
@decorators.idempotent_id('507140c8-7b14-4d63-b627-2103691d887e')
def test_update_port_mac_address(self):
+ original_mac_address = self.port['mac_address']
- port = self.create_port(self.network)
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.ports_client.update_port(
- port['id'],
- mac_address=data_utils.rand_mac_address())
+ self.port['id'], mac_address=data_utils.rand_mac_address())
+ self.addCleanup(self.ports_client.update_port, self.port['id'],
+ mac_address=original_mac_address)
@rbac_rule_validation.action(service="neutron",
rule="update_port:fixed_ips")
@@ -256,39 +270,31 @@
def test_update_port_fixed_ips(self):
# Pick an ip address within the allocation_pools range.
- post_body = {'network_id': self.network['id']}
- port = self._create_port(**post_body)
+ post_body = {'network': self.network}
+ port = self.create_port(**post_body)
- # Pick an unused ip address.
- ip_list = net_utils.get_unused_ip_addresses(self.ports_client,
- self.subnets_client,
- self.network['id'],
- self.subnet['id'],
- 1)
+ ip_list = self._get_unused_ip_address()
fixed_ips = [{'ip_address': ip_list[0]}]
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.ports_client.update_port(port['id'],
- fixed_ips=fixed_ips)
+ self.ports_client.update_port(port['id'], fixed_ips=fixed_ips)
@rbac_rule_validation.action(service="neutron",
rule="update_port:port_security_enabled")
@decorators.idempotent_id('795541af-6652-4e35-9581-fd58224f7545')
def test_update_port_security_enabled(self):
-
- port = self.create_port(self.network)
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
- self.ports_client.update_port(port['id'],
- security_groups=[])
+ self.ports_client.update_port(self.port['id'], security_groups=[])
+ @test.requires_ext(extension='binding', service='network')
@rbac_rule_validation.action(service="neutron",
rule="update_port:binding:host_id")
@decorators.idempotent_id('24206a72-0d90-4712-918c-5c9a1ebef64d')
def test_update_port_binding_host_id(self):
- post_body = {'network_id': self.network['id'],
+ post_body = {'network': self.network,
'binding:host_id': 'rbac_test_host'}
- port = self._create_port(**post_body)
+ port = self.create_port(**post_body)
updated_body = {'port_id': port['id'],
'binding:host_id': 'rbac_test_host_updated'}
@@ -296,16 +302,17 @@
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.ports_client.update_port(**updated_body)
+ @test.requires_ext(extension='binding', service='network')
@rbac_rule_validation.action(service="neutron",
rule="update_port:binding:profile")
@decorators.idempotent_id('990ea8d1-9257-4f71-a3bf-d6d0914625c5')
def test_update_port_binding_profile(self):
binding_profile = {"foo": "1"}
- post_body = {'network_id': self.network['id'],
+ post_body = {'network': self.network,
'binding:profile': binding_profile}
- port = self._create_port(**post_body)
+ port = self.create_port(**post_body)
new_binding_profile = {"foo": "2"}
updated_body = {'port_id': port['id'],
@@ -319,17 +326,12 @@
@decorators.idempotent_id('729c2151-bb49-4f4f-9d58-3ed8819b7582')
def test_update_port_allowed_address_pairs(self):
- # Pick an unused ip address.
- ip_list = net_utils.get_unused_ip_addresses(self.ports_client,
- self.subnets_client,
- self.network['id'],
- self.subnet['id'],
- 1)
+ ip_list = self._get_unused_ip_address()
# Update allowed address pair attribute of port
address_pairs = [{'ip_address': ip_list[0],
'mac_address': data_utils.rand_mac_address()}]
- post_body = {'network_id': self.network['id']}
- port = self._create_port(**post_body)
+ post_body = {'network': self.network}
+ port = self.create_port(**post_body)
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.ports_client.update_port(port['id'],
@@ -341,6 +343,6 @@
@decorators.idempotent_id('1cf8e582-bc09-46cb-b32a-82bf991ad56f')
def test_delete_port(self):
- port = self._create_port(network_id=self.network['id'])
+ port = self.create_port(self.network)
self.rbac_utils.switch_role(self, toggle_rbac_role=True)
self.ports_client.delete_port(port['id'])
diff --git a/patrole_tempest_plugin/tests/api/network/test_subnetpools_rbac.py b/patrole_tempest_plugin/tests/api/network/test_subnetpools_rbac.py
index e7682df..8c799b6 100644
--- a/patrole_tempest_plugin/tests/api/network/test_subnetpools_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_subnetpools_rbac.py
@@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from oslo_log import log
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
@@ -24,7 +23,6 @@
from patrole_tempest_plugin.tests.api.network import rbac_base as base
CONF = config.CONF
-LOG = log.getLogger(__name__)
class SubnetPoolsRbacTest(base.BaseNetworkRbacTest):
@@ -36,14 +34,14 @@
msg = "subnet_allocation extension not enabled."
raise cls.skipException(msg)
- def _create_subnetpool(self, shared=None):
+ def _create_subnetpool(self, **kwargs):
post_body = {'name': data_utils.rand_name(self.__class__.__name__),
'min_prefixlen': 24,
'max_prefixlen': 32,
'prefixes': [CONF.network.project_network_cidr]}
- if shared is not None:
- post_body['shared'] = shared
+ if kwargs:
+ post_body.update(kwargs)
body = self.subnetpools_client.create_subnetpool(**post_body)
subnetpool = body['subnetpool']
@@ -102,6 +100,28 @@
self.subnetpools_client.update_subnetpool(subnetpool['id'],
min_prefixlen=24)
+ @decorators.idempotent_id('a16f4e5c-0675-415f-b636-00af00638693')
+ @rbac_rule_validation.action(service="neutron",
+ rule="update_subnetpool:is_default",
+ expected_error_code=404)
+ def test_update_subnetpool_is_default(self):
+ """Update default subnetpool.
+
+ RBAC test for the neutron update_subnetpool:is_default policy
+ """
+ subnetpools = self.subnetpools_client.list_subnetpools()['subnetpools']
+ default_pool = list(
+ filter(lambda p: p['is_default'] is True, subnetpools))
+ if default_pool:
+ default_pool = default_pool[0]
+ else:
+ default_pool = self._create_subnetpool(is_default=True)
+ original_desc = default_pool['description']
+
+ self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+ self.subnetpools_client.update_subnetpool(
+ default_pool['id'], description=original_desc, is_default=True)
+
@rbac_rule_validation.action(service="neutron",
rule="delete_subnetpool",
expected_error_code=404)
diff --git a/patrole_tempest_plugin/version.py b/patrole_tempest_plugin/version.py
new file mode 100644
index 0000000..5f8aaf6
--- /dev/null
+++ b/patrole_tempest_plugin/version.py
@@ -0,0 +1,18 @@
+# Copyright (c) 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.
+
+
+import pbr.version
+
+version_info = pbr.version.VersionInfo('patrole')
diff --git a/releasenotes/notes/add-extra-hypervisor-tests-9374e5fcdb0266e2.yaml b/releasenotes/notes/add-extra-hypervisor-tests-9374e5fcdb0266e2.yaml
index 0c46435..4cb7b4a 100644
--- a/releasenotes/notes/add-extra-hypervisor-tests-9374e5fcdb0266e2.yaml
+++ b/releasenotes/notes/add-extra-hypervisor-tests-9374e5fcdb0266e2.yaml
@@ -4,8 +4,9 @@
Add additional compute hypervisor RBAC tests, so that the previously
missing hypervisor endpoints are covered. Tests for the following
endpoints were written:
- * show_hypervisor
- * list_servers_on_hypervisor
- * show_hypervisor_statistics
- * show_hypervisor_uptime
- * search_hypervisor
+
+ * show_hypervisor
+ * list_servers_on_hypervisor
+ * show_hypervisor_statistics
+ * show_hypervisor_uptime
+ * search_hypervisor
diff --git a/releasenotes/notes/additional-network-ports-rbac-tests-3f48ce1b6bda7694.yaml b/releasenotes/notes/additional-network-ports-rbac-tests-3f48ce1b6bda7694.yaml
new file mode 100644
index 0000000..a75db95
--- /dev/null
+++ b/releasenotes/notes/additional-network-ports-rbac-tests-3f48ce1b6bda7694.yaml
@@ -0,0 +1,13 @@
+---
+features:
+ - |
+ Add additional port-related RBAC tests to ``test_ports_rbac`` in the
+ network module, providing coverage for the following policy actions:
+ * create_port:device_owner
+ * create_port:port_security_enabled
+ * create_port:binding:profile
+ * update_port:device_owner
+fixes:
+ - |
+ Add ``test.requires_ext`` above tests that require the ``binding``
+ extension.
diff --git a/releasenotes/notes/communitize-image-rbac-test-bdf1109e58a6c2e0.yaml b/releasenotes/notes/communitize-image-rbac-test-bdf1109e58a6c2e0.yaml
new file mode 100644
index 0000000..200cc58
--- /dev/null
+++ b/releasenotes/notes/communitize-image-rbac-test-bdf1109e58a6c2e0.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Add RBAC test for communitizing image, providing coverage for the policy
+ action "communitize_image".
diff --git a/releasenotes/notes/dhcp-agent-scheduler-test-842fc1df45799def.yaml b/releasenotes/notes/dhcp-agent-scheduler-test-842fc1df45799def.yaml
new file mode 100644
index 0000000..0a422e6
--- /dev/null
+++ b/releasenotes/notes/dhcp-agent-scheduler-test-842fc1df45799def.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Added RBAC network test for listing dhcp agents on a hosting
+ network, providing coverage for the "get_dhcp-agents" policy.
diff --git a/releasenotes/notes/image-size-rbac-tests-545e5ace0d88ab34.yaml b/releasenotes/notes/image-size-rbac-tests-545e5ace0d88ab34.yaml
new file mode 100644
index 0000000..c0340cf
--- /dev/null
+++ b/releasenotes/notes/image-size-rbac-tests-545e5ace0d88ab34.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Add RBAC tests related to the ``image_size`` compute policy action:
+ "os_compute_api:image-size".
diff --git a/releasenotes/notes/patrole-devstack-plugin-551c9af3325723c9.yaml b/releasenotes/notes/patrole-devstack-plugin-551c9af3325723c9.yaml
new file mode 100644
index 0000000..a0950bd
--- /dev/null
+++ b/releasenotes/notes/patrole-devstack-plugin-551c9af3325723c9.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ Add Patrole DevStack plugin, allowing Patrole to be installed using
+ DevStack by adding "enable_plugin patrole" to "local" section of
+ local.conf.
diff --git a/releasenotes/notes/remove-assisted-volume-snapshot-tests-c204bc72779cb53a.yaml b/releasenotes/notes/remove-assisted-volume-snapshot-tests-c204bc72779cb53a.yaml
new file mode 100644
index 0000000..1988f74
--- /dev/null
+++ b/releasenotes/notes/remove-assisted-volume-snapshot-tests-c204bc72779cb53a.yaml
@@ -0,0 +1,5 @@
+---
+deprecations:
+ - |
+ Remove assisted volume snapshot RBAC tests, because the Tempest client
+ does not yet exist.
diff --git a/releasenotes/notes/server-metadata-rbac-tests-2404b5d13c492b62.yaml b/releasenotes/notes/server-metadata-rbac-tests-2404b5d13c492b62.yaml
new file mode 100644
index 0000000..1bb63c9
--- /dev/null
+++ b/releasenotes/notes/server-metadata-rbac-tests-2404b5d13c492b62.yaml
@@ -0,0 +1,12 @@
+---
+features:
+ - |
+ Add RBAC tests for the compute server metadata API, providing
+ coverage for the following policy actions:
+
+ * os_compute_api:server-metadata:index
+ * os_compute_api:server-metadata:update_all
+ * os_compute_api:server-metadata:create
+ * os_compute_api:server-metadata:show
+ * os_compute_api:server-metadata:update
+ * os_compute_api:server-metadata:delete
diff --git a/releasenotes/notes/subnetpools_update_is_default_test-d3540a87469b6dc8.yaml b/releasenotes/notes/subnetpools_update_is_default_test-d3540a87469b6dc8.yaml
new file mode 100644
index 0000000..25aa2d1
--- /dev/null
+++ b/releasenotes/notes/subnetpools_update_is_default_test-d3540a87469b6dc8.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Add RBAC test for updating the default subnetpool, providing coverage
+ for the policy action: "update_subnetpool:is_default".
\ No newline at end of file
diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py
index d86d91c..c8439f6 100644
--- a/releasenotes/source/conf.py
+++ b/releasenotes/source/conf.py
@@ -55,18 +55,18 @@
master_doc = 'index'
# General information about the project.
-project = u'patrole Release Notes'
+project = u'Patrole Release Notes'
copyright = u'2017, Patrole Developers'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
-# The short X.Y version.
+from patrole_tempest_plugin.version import version_info as patrole_version
# The full version, including alpha/beta/rc tags.
-release = ''
+release = patrole_version.version_string_with_vcs()
# The short X.Y version.
-version = ''
+version = patrole_version.canonical_version_string()
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -256,7 +256,7 @@
texinfo_documents = [
('index', 'PatroleReleaseNotes', u'Patrole Release Notes Documentation',
u'Patrole Developers', 'PatroleReleaseNotes',
- 'One line description of project.',
+ 'A Tempest plugin for performing RBAC testing.',
'Miscellaneous'),
]
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index b2e9377..11e1ac4 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -1,8 +1,9 @@
-============================================
- patrole Release Notes
-============================================
+======================
+ Patrole Release Notes
+======================
.. toctree::
:maxdepth: 1
unreleased
+ v0.1.0
diff --git a/releasenotes/source/v0.1.0.rst b/releasenotes/source/v0.1.0.rst
new file mode 100644
index 0000000..5db583f
--- /dev/null
+++ b/releasenotes/source/v0.1.0.rst
@@ -0,0 +1,6 @@
+=====================
+ v0.1.0 Release Notes
+=====================
+
+.. release-notes:: 0.1.0 Release Notes
+ :version: 0.1.0
diff --git a/setup.cfg b/setup.cfg
index d8c9505..f76d172 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,7 +5,7 @@
README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
-home-page = http://docs.openstack.org/developer/patrole/
+home-page = https://docs.openstack.org/developer/patrole/
classifier =
Environment :: OpenStack
Intended Audience :: Information Technology