Merge "Correct reno list formatting"
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 178e538..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
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/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/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/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/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/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