Allow sharing of address scopes via RBAC mechanism
Neutron-lib api ref: https://review.opendev.org/#/c/707407/ Client: https://review.opendev.org/#/c/709124/ Tempest tests: https://review.opendev.org/#/c/711610/ Change-Id: I74bedae4de4eb25e5427ecb129543885a020a0a8 Depends-On: https://review.opendev.org/712633 Partial-Bug: #1862968 Closes-Bug: #1697925
This commit is contained in:
parent
c90011ee49
commit
eb6104c0ac
@ -18,6 +18,7 @@ is supported by:
|
||||
* Binding QoS policies permissions to networks or ports (since Mitaka).
|
||||
* Attaching router gateways to networks (since Mitaka).
|
||||
* Binding security groups to ports (since Stein).
|
||||
* Assigning address scopes to subnet pools (since Ussuri).
|
||||
|
||||
|
||||
Sharing an object with specific projects
|
||||
@ -281,12 +282,87 @@ This process can be repeated any number of times to share a security-group
|
||||
with an arbitrary number of projects.
|
||||
|
||||
|
||||
Sharing an address scope with specific projects
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Create an address scope to share:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack address scope create my_address_scope
|
||||
+-------------------+--------------------------------------+
|
||||
| Field | Value |
|
||||
+-------------------+--------------------------------------+
|
||||
| id | c19cb654-3489-4160-9c82-8a3015483643 |
|
||||
| ip_version | 4 |
|
||||
| location | ... |
|
||||
| name | my_address_scope |
|
||||
| project_id | 34304bc4f233470fa4a2448d153b6324 |
|
||||
| shared | False |
|
||||
+-------------------+--------------------------------------+
|
||||
|
||||
|
||||
Create the RBAC policy entry using the :command:`openstack network rbac create`
|
||||
command (in this example, the ID of the project we want to share with is
|
||||
``32016615de5d43bb88de99e7f2e26a1e``):
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack network rbac create --target-project \
|
||||
32016615de5d43bb88de99e7f2e26a1e --action access_as_shared \
|
||||
--type address_scope c19cb654-3489-4160-9c82-8a3015483643
|
||||
+-------------------+--------------------------------------+
|
||||
| Field | Value |
|
||||
+-------------------+--------------------------------------+
|
||||
| action | access_as_shared |
|
||||
| id | d54b1482-98c4-44aa-9115-ede80387ffe0 |
|
||||
| location | ... |
|
||||
| name | None |
|
||||
| object_id | c19cb654-3489-4160-9c82-8a3015483643 |
|
||||
| object_type | address_scope |
|
||||
| project_id | 34304bc4f233470fa4a2448d153b6324 |
|
||||
| target_project_id | 32016615de5d43bb88de99e7f2e26a1e |
|
||||
+-------------------+--------------------------------------+
|
||||
|
||||
|
||||
The ``target-project`` parameter specifies the project that requires
|
||||
access to the address scope. The ``action`` parameter specifies what
|
||||
the project is allowed to do. The ``type`` parameter says
|
||||
that the target object is an address scope. The final parameter is the ID of
|
||||
the address scope we are granting access to.
|
||||
|
||||
Project ``32016615de5d43bb88de99e7f2e26a1e`` will now be able to see
|
||||
the address scope when running :command:`openstack address scope list` and
|
||||
:command:`openstack address scope show` and will also be able to assign
|
||||
it to its subnet pools. No other users (other than admins and the owner)
|
||||
will be able to see the address scope.
|
||||
|
||||
To remove access for that project, delete the RBAC policy that allows
|
||||
it using the :command:`openstack network rbac delete` command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack network rbac delete d54b1482-98c4-44aa-9115-ede80387ffe0
|
||||
|
||||
If that project has subnet pools with the address scope applied to them,
|
||||
the server will not delete the RBAC policy until
|
||||
the address scope is no longer in use:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack network rbac delete d54b1482-98c4-44aa-9115-ede80387ffe0
|
||||
RBAC policy on object c19cb654-3489-4160-9c82-8a3015483643
|
||||
cannot be removed because other objects depend on it.
|
||||
|
||||
This process can be repeated any number of times to share an address scope
|
||||
with an arbitrary number of projects.
|
||||
|
||||
How the 'shared' flag relates to these entries
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As introduced in other guide entries, neutron provides a means of
|
||||
making an object (``network``, ``qos-policy``, ``security-group``) available
|
||||
to every project.
|
||||
making an object (``address-scope``, ``network``, ``qos-policy``,
|
||||
``security-group``) available to every project.
|
||||
This is accomplished using the ``shared`` flag on the supported object:
|
||||
|
||||
.. code-block:: console
|
||||
|
@ -36,11 +36,7 @@ class AddressScopeDbMixin(ext_address_scope.AddressScopePluginBase):
|
||||
|
||||
@staticmethod
|
||||
def _make_address_scope_dict(address_scope, fields=None):
|
||||
res = {'id': address_scope['id'],
|
||||
'name': address_scope['name'],
|
||||
'tenant_id': address_scope['tenant_id'],
|
||||
'shared': address_scope['shared'],
|
||||
'ip_version': address_scope['ip_version']}
|
||||
res = address_scope.to_dict()
|
||||
return db_utils.resource_fields(res, fields)
|
||||
|
||||
def _get_address_scope(self, context, id):
|
||||
|
@ -59,6 +59,7 @@ from neutron import ipam
|
||||
from neutron.ipam import exceptions as ipam_exc
|
||||
from neutron.ipam import subnet_alloc
|
||||
from neutron import neutron_plugin_base_v2
|
||||
from neutron.objects import address_scope as address_scope_obj
|
||||
from neutron.objects import base as base_obj
|
||||
from neutron.objects import network as network_obj
|
||||
from neutron.objects import ports as port_obj
|
||||
@ -1112,7 +1113,7 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
|
||||
Subnetpool can associate with an address scope if
|
||||
- the tenant user is the owner of both the subnetpool and
|
||||
address scope
|
||||
- the admin is associating the subnetpool with the shared
|
||||
- the user is associating the subnetpool with a shared
|
||||
address scope
|
||||
- there is no prefix conflict with the existing subnetpools
|
||||
associated with the address scope.
|
||||
@ -1122,8 +1123,14 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
|
||||
if not validators.is_attr_set(address_scope_id):
|
||||
return
|
||||
|
||||
if not self.is_address_scope_owned_by_tenant(context,
|
||||
address_scope_id):
|
||||
address_scope = self._get_address_scope(context, address_scope_id)
|
||||
is_accessible = (
|
||||
address_scope_obj.AddressScope.is_accessible(
|
||||
context, address_scope
|
||||
)
|
||||
)
|
||||
|
||||
if not is_accessible:
|
||||
raise exc.IllegalSubnetPoolAssociationToAddressScope(
|
||||
subnetpool_id=subnetpool_id, address_scope_id=address_scope_id)
|
||||
|
||||
|
@ -1 +1 @@
|
||||
18a7e90ae768
|
||||
e4e236b0e1ff
|
||||
|
@ -0,0 +1,82 @@
|
||||
# Copyright 2020 OpenStack Foundation
|
||||
#
|
||||
# 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 alembic import op
|
||||
from oslo_utils import uuidutils
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import sql
|
||||
|
||||
|
||||
"""add_rbac_support_for_address_scope
|
||||
|
||||
Revision ID: e4e236b0e1ff
|
||||
Revises: 18a7e90ae768
|
||||
Create Date: 2020-03-12 11:24:07.435031
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e4e236b0e1ff'
|
||||
down_revision = '18a7e90ae768'
|
||||
depends_on = ('7d9d8eeec6ad',)
|
||||
|
||||
|
||||
def upgrade():
|
||||
address_scope_rbacs = op.create_table(
|
||||
'addressscoperbacs', sa.MetaData(),
|
||||
sa.Column('project_id', sa.String(length=255), nullable=True),
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('target_tenant', sa.String(length=255), nullable=False),
|
||||
sa.Column('action', sa.String(length=255), nullable=False),
|
||||
sa.Column('object_id', sa.String(length=36), nullable=False),
|
||||
sa.ForeignKeyConstraint(['object_id'], ['address_scopes.id'],
|
||||
ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('target_tenant', 'object_id', 'action',
|
||||
name='uniq_address_scopes_rbacs0'
|
||||
'target_tenant0object_id0action')
|
||||
)
|
||||
|
||||
op.alter_column('address_scopes', 'shared', server_default=sql.false())
|
||||
|
||||
op.bulk_insert(address_scope_rbacs,
|
||||
get_rbac_policies_for_shared_address_scopes())
|
||||
|
||||
op.create_index(op.f('ix_addressscoperbacs_project_id'),
|
||||
'addressscoperbacs', ['project_id'], unique=False)
|
||||
|
||||
|
||||
def get_rbac_policies_for_shared_address_scopes():
|
||||
# A simple model of the address_scopes table with only the fields needed
|
||||
# for the migration.
|
||||
address_scope = sa.Table(
|
||||
'address_scopes', sa.MetaData(),
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('project_id', sa.String(length=255)),
|
||||
sa.Column('shared', sa.Boolean(), nullable=False)
|
||||
)
|
||||
|
||||
session = sa.orm.Session(bind=op.get_bind())
|
||||
shared_address_scopes = session.query(address_scope).filter(
|
||||
address_scope.c.shared).all()
|
||||
values = []
|
||||
|
||||
for row in shared_address_scopes:
|
||||
values.append({'id': uuidutils.generate_uuid(), 'object_id': row[0],
|
||||
'project_id': row[1], 'target_tenant': '*',
|
||||
'action': 'access_as_shared'})
|
||||
# this commit appears to be necessary to allow further operations
|
||||
session.commit()
|
||||
return values
|
@ -13,6 +13,9 @@
|
||||
from neutron_lib.db import constants as db_const
|
||||
from neutron_lib.db import model_base
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import sql
|
||||
|
||||
from neutron.db import rbac_db_models
|
||||
|
||||
|
||||
class AddressScope(model_base.BASEV2, model_base.HasId, model_base.HasProject):
|
||||
@ -21,5 +24,18 @@ class AddressScope(model_base.BASEV2, model_base.HasId, model_base.HasProject):
|
||||
__tablename__ = "address_scopes"
|
||||
|
||||
name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE), nullable=False)
|
||||
shared = sa.Column(sa.Boolean, nullable=False)
|
||||
|
||||
# TODO(imalinovskiy): drop this field when contract migrations will be
|
||||
# allowed again
|
||||
# NOTE(imalinovskiy): this field cannot be removed from model due to
|
||||
# functional test test_models_sync, trailing underscore is required to
|
||||
# prevent conflicts with RBAC code
|
||||
shared_ = sa.Column("shared", sa.Boolean, nullable=False,
|
||||
server_default=sql.false())
|
||||
|
||||
ip_version = sa.Column(sa.Integer(), nullable=False)
|
||||
|
||||
rbac_entries = sa.orm.relationship(rbac_db_models.AddressScopeRBAC,
|
||||
backref='address_scopes',
|
||||
lazy='subquery',
|
||||
cascade='all, delete, delete-orphan')
|
||||
|
@ -126,3 +126,14 @@ class SecurityGroupRBAC(RBACColumns, model_base.BASEV2):
|
||||
@staticmethod
|
||||
def get_valid_actions():
|
||||
return (ACCESS_SHARED,)
|
||||
|
||||
|
||||
class AddressScopeRBAC(RBACColumns, model_base.BASEV2):
|
||||
"""RBAC table for address_scope."""
|
||||
|
||||
object_id = _object_id_column('address_scopes.id')
|
||||
object_type = 'address_scope'
|
||||
|
||||
@staticmethod
|
||||
def get_valid_actions():
|
||||
return (ACCESS_SHARED,)
|
||||
|
22
neutron/extensions/rbac_address_scope.py
Normal file
22
neutron/extensions/rbac_address_scope.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright (c) 2020 Cloudification GmbH. 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 neutron_lib.api.definitions import rbac_address_scope
|
||||
from neutron_lib.api import extensions
|
||||
|
||||
|
||||
class Rbac_address_scope(extensions.APIExtensionDescriptor):
|
||||
"""Extension class supporting address scope RBAC."""
|
||||
|
||||
api_definition = rbac_address_scope
|
@ -14,17 +14,50 @@
|
||||
|
||||
from neutron_lib.objects import common_types
|
||||
from oslo_versionedobjects import fields as obj_fields
|
||||
import sqlalchemy as sa
|
||||
|
||||
from neutron.db.models import address_scope as models
|
||||
from neutron.db import models_v2
|
||||
from neutron.db import rbac_db_models
|
||||
from neutron.objects import base
|
||||
from neutron.objects import rbac
|
||||
from neutron.objects import rbac_db
|
||||
from neutron.objects import subnetpool
|
||||
|
||||
|
||||
@base.NeutronObjectRegistry.register
|
||||
class AddressScope(base.NeutronDbObject):
|
||||
class AddressScopeRBAC(rbac.RBACBaseObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
db_model = rbac_db_models.AddressScopeRBAC
|
||||
|
||||
@classmethod
|
||||
def get_projects(cls, context, object_id=None, action=None,
|
||||
target_tenant=None):
|
||||
clauses = []
|
||||
|
||||
if object_id:
|
||||
clauses.append(cls.db_model.object_id == object_id)
|
||||
if action:
|
||||
clauses.append(cls.db_model.action == action)
|
||||
if target_tenant:
|
||||
clauses.append(cls.db_model.target_tenant == target_tenant)
|
||||
query = context.session.query(cls.db_model.target_tenant)
|
||||
if clauses:
|
||||
query = query.filter(sa.and_(*clauses))
|
||||
return [data[0] for data in query]
|
||||
|
||||
|
||||
@base.NeutronObjectRegistry.register
|
||||
class AddressScope(rbac_db.NeutronRbacObject):
|
||||
# Version 1.0: Initial version
|
||||
# Version 1.1: Add RBAC support
|
||||
VERSION = '1.1'
|
||||
|
||||
# required by RbacNeutronMetaclass
|
||||
rbac_db_cls = AddressScopeRBAC
|
||||
|
||||
db_model = models.AddressScope
|
||||
|
||||
fields = {
|
||||
@ -51,3 +84,10 @@ class AddressScope(base.NeutronDbObject):
|
||||
return cls._load_object(context, scope_model_obj)
|
||||
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def get_bound_tenant_ids(cls, context, obj_id):
|
||||
snp_objs = subnetpool.SubnetPool.get_objects(
|
||||
context, address_scope_id=obj_id
|
||||
)
|
||||
return {snp.project_id for snp in snp_objs}
|
||||
|
@ -236,6 +236,37 @@ class RbacNeutronDbObjectMixin(rbac_db_mixin.RbacPluginMixin,
|
||||
self._validate_rbac_policy_delete(self.obj_context, obj_id, '*')
|
||||
return self.obj_context.session.delete(shared_prev)
|
||||
|
||||
def from_db_object(self, db_obj):
|
||||
self._load_shared(db_obj)
|
||||
super(RbacNeutronDbObjectMixin, self).from_db_object(db_obj)
|
||||
|
||||
def obj_load_attr(self, attrname):
|
||||
if attrname == 'shared':
|
||||
return self._load_shared()
|
||||
super(RbacNeutronDbObjectMixin, self).obj_load_attr(attrname)
|
||||
|
||||
def _load_shared(self, db_obj=None):
|
||||
# Do not override 'shared' attribute on create() or update()
|
||||
if 'shared' in self.obj_get_changes():
|
||||
return
|
||||
|
||||
if db_obj:
|
||||
# NOTE(korzen) db_obj is passed when object is loaded from DB
|
||||
rbac_entries = db_obj.get('rbac_entries') or {}
|
||||
shared = self.is_network_shared(self.obj_context, rbac_entries)
|
||||
else:
|
||||
# NOTE(korzen) this case is used when object was
|
||||
# instantiated and without DB interaction (get_object(s), update,
|
||||
# create), it should be rare case to load 'shared' by that method
|
||||
shared = self.get_shared_with_tenant(
|
||||
self.obj_context.elevated(),
|
||||
self.rbac_db_cls,
|
||||
self.id,
|
||||
self.project_id
|
||||
)
|
||||
setattr(self, 'shared', shared)
|
||||
self.obj_reset_changes(['shared'])
|
||||
|
||||
|
||||
def _update_post(self, obj_changes):
|
||||
if "shared" in obj_changes:
|
||||
@ -249,6 +280,7 @@ def _update_hook(self, update_orig):
|
||||
obj_changes = self.obj_get_changes()
|
||||
update_orig(self)
|
||||
_update_post(self, obj_changes)
|
||||
self._load_shared(db_obj=self.db_obj)
|
||||
|
||||
|
||||
def _create_post(self):
|
||||
@ -260,6 +292,7 @@ def _create_hook(self, orig_create):
|
||||
with self.db_context_writer(self.obj_context):
|
||||
orig_create(self)
|
||||
_create_post(self)
|
||||
self._load_shared(db_obj=self.db_obj)
|
||||
|
||||
|
||||
def _to_dict_hook(self, to_dict_orig):
|
||||
|
@ -43,6 +43,7 @@ from neutron_lib.api.definitions import port_security as psec
|
||||
from neutron_lib.api.definitions import portbindings
|
||||
from neutron_lib.api.definitions import portbindings_extended as pbe_ext
|
||||
from neutron_lib.api.definitions import provider_net
|
||||
from neutron_lib.api.definitions import rbac_address_scope
|
||||
from neutron_lib.api.definitions import rbac_security_groups as rbac_sg_apidef
|
||||
from neutron_lib.api.definitions import security_groups_port_filtering
|
||||
from neutron_lib.api.definitions import stateful_security_group
|
||||
@ -181,6 +182,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
_supported_extension_aliases = [provider_net.ALIAS,
|
||||
external_net.ALIAS, portbindings.ALIAS,
|
||||
"quotas", "security-group",
|
||||
rbac_address_scope.ALIAS,
|
||||
rbac_sg_apidef.ALIAS,
|
||||
agent_apidef.ALIAS,
|
||||
dhcpagentscheduler.ALIAS,
|
||||
|
@ -45,6 +45,7 @@ NETWORK_API_EXTENSIONS+=",qos-gateway-ip"
|
||||
NETWORK_API_EXTENSIONS+=",quotas"
|
||||
NETWORK_API_EXTENSIONS+=",quota_details"
|
||||
NETWORK_API_EXTENSIONS+=",rbac-policies"
|
||||
NETWORK_API_EXTENSIONS+=",rbac-address-scope""
|
||||
NETWORK_API_EXTENSIONS+=",rbac-security-groups""
|
||||
NETWORK_API_EXTENSIONS+=",router"
|
||||
NETWORK_API_EXTENSIONS+=",router-admin-state-down-before-update"
|
||||
|
@ -98,9 +98,8 @@ class AddressScopeTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
|
||||
expected=None, tenant_id=None):
|
||||
update_req = self.new_update_request(
|
||||
'address-scopes', data, addr_scope_id)
|
||||
if not admin:
|
||||
neutron_context = context.Context('', tenant_id or self._tenant_id)
|
||||
update_req.environ['neutron.context'] = neutron_context
|
||||
update_req.environ['neutron.context'] = context.Context(
|
||||
'', tenant_id or self._tenant_id, is_admin=admin)
|
||||
|
||||
update_res = update_req.get_response(self.ext_api)
|
||||
if expected:
|
||||
|
@ -12,17 +12,48 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron_lib import constants as lib_constants
|
||||
|
||||
from neutron.objects import address_scope
|
||||
from neutron.tests.unit.objects import test_base as obj_test_base
|
||||
from neutron.tests.unit.objects import test_base
|
||||
from neutron.tests.unit.objects import test_rbac
|
||||
from neutron.tests.unit import testlib_api
|
||||
|
||||
|
||||
class AddressScopeIfaceObjectTestCase(obj_test_base.BaseObjectIfaceTestCase):
|
||||
class AddressScopeIfaceObjectTestCase(test_base.BaseObjectIfaceTestCase):
|
||||
|
||||
_test_class = address_scope.AddressScope
|
||||
|
||||
|
||||
class AddressScopeDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
|
||||
class AddressScopeDbObjectTestCase(test_base.BaseDbObjectTestCase,
|
||||
testlib_api.SqlTestCase):
|
||||
|
||||
_test_class = address_scope.AddressScope
|
||||
|
||||
|
||||
class AddressScopeRBACDbObjectTestCase(test_rbac.TestRBACObjectMixin,
|
||||
test_base.BaseDbObjectTestCase,
|
||||
testlib_api.SqlTestCase):
|
||||
|
||||
_test_class = address_scope.AddressScopeRBAC
|
||||
|
||||
def setUp(self):
|
||||
super(AddressScopeRBACDbObjectTestCase, self).setUp()
|
||||
for obj in self.db_objs:
|
||||
as_obj = address_scope.AddressScope(
|
||||
self.context,
|
||||
id=obj['object_id'],
|
||||
name="test_as_%s_%s" % (obj['object_id'], obj['project_id']),
|
||||
project_id=obj['project_id'],
|
||||
ip_version=lib_constants.IP_ALLOWED_VERSIONS[0],
|
||||
)
|
||||
as_obj.create()
|
||||
|
||||
def _create_test_address_scope_rbac(self):
|
||||
self.objs[0].create()
|
||||
return self.objs[0]
|
||||
|
||||
|
||||
class AddressScopeRBACIfaceObjectTestCase(test_rbac.TestRBACObjectMixin,
|
||||
test_base.BaseObjectIfaceTestCase):
|
||||
_test_class = address_scope.AddressScopeRBAC
|
||||
|
@ -746,7 +746,7 @@ class BaseObjectIfaceTestCase(_BaseObjectTestCase, test_base.BaseTestCase):
|
||||
'is_shared_with_tenant', return_value=False).start()
|
||||
mock.patch.object(
|
||||
rbac_db.RbacNeutronDbObjectMixin,
|
||||
'get_shared_with_tenant').start()
|
||||
'get_shared_with_tenant', return_value=False).start()
|
||||
|
||||
def fake_get_object(self, context, model, **kwargs):
|
||||
objs = self.model_map[model]
|
||||
|
@ -10,8 +10,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import random
|
||||
|
||||
import mock
|
||||
|
||||
from neutron.db import rbac_db_models
|
||||
@ -24,18 +22,7 @@ from neutron.tests.unit.objects import test_rbac
|
||||
from neutron.tests.unit import testlib_api
|
||||
|
||||
|
||||
class _NetworkRBACBase(object):
|
||||
|
||||
def get_random_object_fields(self, obj_cls=None):
|
||||
fields = (super(_NetworkRBACBase, self).
|
||||
get_random_object_fields(obj_cls))
|
||||
rnd_actions = self._test_class.db_model.get_valid_actions()
|
||||
idx = random.randint(0, len(rnd_actions) - 1)
|
||||
fields['action'] = rnd_actions[idx]
|
||||
return fields
|
||||
|
||||
|
||||
class NetworkRBACDbObjectTestCase(_NetworkRBACBase,
|
||||
class NetworkRBACDbObjectTestCase(test_rbac.TestRBACObjectMixin,
|
||||
obj_test_base.BaseDbObjectTestCase,
|
||||
testlib_api.SqlTestCase):
|
||||
|
||||
@ -64,7 +51,7 @@ class NetworkRBACDbObjectTestCase(_NetworkRBACBase,
|
||||
self.assertNotIn('id', network_rbac_obj['versioned_object.data'])
|
||||
|
||||
|
||||
class NetworkRBACIfaceOjectTestCase(_NetworkRBACBase,
|
||||
class NetworkRBACIfaceOjectTestCase(test_rbac.TestRBACObjectMixin,
|
||||
obj_test_base.BaseObjectIfaceTestCase):
|
||||
|
||||
_test_class = network.NetworkRBAC
|
||||
|
@ -26,7 +26,8 @@ from neutron.tests import base as test_base
|
||||
# corresponding version bump in the affected objects. Please keep the list in
|
||||
# alphabetic order.
|
||||
object_data = {
|
||||
'AddressScope': '1.0-dd0dfdb67775892d3adc090e28e43bd8',
|
||||
'AddressScope': '1.1-dd0dfdb67775892d3adc090e28e43bd8',
|
||||
'AddressScopeRBAC': '1.0-192845c5ed0718e1c54fac36936fcd7d',
|
||||
'Agent': '1.1-64b670752d57b3c7602cb136e0338507',
|
||||
'AllowedAddressPair': '1.0-9f9186b6f952fbf31d257b0458b852c0',
|
||||
'AutoAllocatedTopology': '1.0-74642e58c53bf3610dc224c59f81b242',
|
||||
|
@ -9,9 +9,11 @@
|
||||
# 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 random
|
||||
|
||||
import mock
|
||||
|
||||
from neutron.objects import address_scope
|
||||
from neutron.objects import network
|
||||
from neutron.objects.qos import policy
|
||||
from neutron.objects import rbac
|
||||
@ -20,10 +22,22 @@ from neutron.tests import base as neutron_test_base
|
||||
from neutron.tests.unit.objects import test_base
|
||||
|
||||
|
||||
class TestRBACObjectMixin(object):
|
||||
|
||||
def get_random_object_fields(self, obj_cls=None):
|
||||
fields = (super(TestRBACObjectMixin, self).
|
||||
get_random_object_fields(obj_cls))
|
||||
rnd_actions = self._test_class.db_model.get_valid_actions()
|
||||
idx = random.randint(0, len(rnd_actions) - 1)
|
||||
fields['action'] = rnd_actions[idx]
|
||||
return fields
|
||||
|
||||
|
||||
class RBACBaseObjectTestCase(neutron_test_base.BaseTestCase):
|
||||
|
||||
def test_get_type_class_map(self):
|
||||
class_map = {'qos_policy': policy.QosPolicyRBAC,
|
||||
class_map = {'address_scope': address_scope.AddressScopeRBAC,
|
||||
'qos_policy': policy.QosPolicyRBAC,
|
||||
'network': network.NetworkRBAC,
|
||||
'security_group': securitygroup.SecurityGroupRBAC}
|
||||
self.assertEqual(class_map, rbac.RBACBaseObject.get_type_class_map())
|
||||
|
@ -12,7 +12,6 @@
|
||||
|
||||
import collections
|
||||
import itertools
|
||||
import random
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
@ -22,18 +21,7 @@ from neutron.tests.unit.objects import test_rbac
|
||||
from neutron.tests.unit import testlib_api
|
||||
|
||||
|
||||
class _SecurityGroupRBACBase(object):
|
||||
|
||||
def get_random_object_fields(self, obj_cls=None):
|
||||
fields = (super(_SecurityGroupRBACBase, self).
|
||||
get_random_object_fields(obj_cls))
|
||||
rnd_actions = self._test_class.db_model.get_valid_actions()
|
||||
idx = random.randint(0, len(rnd_actions) - 1)
|
||||
fields['action'] = rnd_actions[idx]
|
||||
return fields
|
||||
|
||||
|
||||
class SecurityGroupRBACDbObjectTestCase(_SecurityGroupRBACBase,
|
||||
class SecurityGroupRBACDbObjectTestCase(test_rbac.TestRBACObjectMixin,
|
||||
test_base.BaseDbObjectTestCase,
|
||||
testlib_api.SqlTestCase):
|
||||
|
||||
@ -59,7 +47,7 @@ class SecurityGroupRBACDbObjectTestCase(_SecurityGroupRBACBase,
|
||||
security_group_rbac_dict['versioned_object.data'])
|
||||
|
||||
|
||||
class SecurityGroupRBACIfaceObjectTestCase(_SecurityGroupRBACBase,
|
||||
class SecurityGroupRBACIfaceObjectTestCase(test_rbac.TestRBACObjectMixin,
|
||||
test_base.BaseObjectIfaceTestCase):
|
||||
_test_class = securitygroup.SecurityGroupRBAC
|
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Address scope is now supported via the network RBAC mechanism.
|
||||
Please refer to the admin guide for further details.
|
Loading…
Reference in New Issue
Block a user