Merge "AIM Policy Driver - Part 5 - L3 Policies (implicit)"

This commit is contained in:
Jenkins 2016-09-09 20:34:03 +00:00 committed by Gerrit Code Review
commit 7f41e94f71
12 changed files with 784 additions and 67 deletions

View File

@ -284,35 +284,38 @@ class LocalAPI(object):
except n_exc.NetworkNotFound:
LOG.warning(_LW('Network %s already deleted'), network_id)
def _get_router(self, plugin_context, router_id):
def _get_router(self, plugin_context, router_id, clean_session=True):
return self._get_resource(self._l3_plugin, plugin_context, 'router',
router_id)
router_id, clean_session=clean_session)
def _get_routers(self, plugin_context, filters=None):
def _get_routers(self, plugin_context, filters=None, clean_session=True):
filters = filters or {}
return self._get_resources(self._l3_plugin, plugin_context, 'routers',
filters)
filters, clean_session=clean_session)
def _create_router(self, plugin_context, attrs):
def _create_router(self, plugin_context, attrs, clean_session=True):
return self._create_resource(self._l3_plugin, plugin_context, 'router',
attrs)
attrs, clean_session=clean_session)
def _update_router(self, plugin_context, router_id, attrs):
def _update_router(self, plugin_context, router_id, attrs,
clean_session=True):
return self._update_resource(self._l3_plugin, plugin_context, 'router',
router_id, attrs)
router_id, attrs,
clean_session=clean_session)
def _add_router_interface(self, plugin_context, router_id, interface_info):
self._l3_plugin.add_router_interface(plugin_context,
router_id, interface_info)
def _remove_router_interface(self, plugin_context, router_id,
interface_info):
interface_info, clean_session=True):
# To detach Router interface either port ID or Subnet ID is mandatory
key = 'port_id' if 'port_id' in interface_info else 'subnet_id'
fixed_ips_filter = {key: [interface_info.get(key)]}
filters = {'device_id': [router_id],
'fixed_ips': fixed_ips_filter}
ports = self._get_ports(plugin_context, filters=filters)
ports = self._get_ports(plugin_context, filters=filters,
clean_session=clean_session)
try:
self._l3_plugin.remove_router_interface(plugin_context, router_id,
@ -330,21 +333,22 @@ class LocalAPI(object):
{'port': ports[0]},
'port' + '.delete.end')
def _add_router_gw_interface(self, plugin_context, router_id, gw_info):
def _add_router_gw_interface(self, plugin_context, router_id, gw_info,
clean_session=True):
return self._l3_plugin.update_router(
plugin_context, router_id,
{'router': {'external_gateway_info': gw_info}})
def _remove_router_gw_interface(self, plugin_context, router_id,
interface_info):
interface_info, clean_session=True):
self._l3_plugin.update_router(
plugin_context, router_id,
{'router': {'external_gateway_info': None}})
def _delete_router(self, plugin_context, router_id):
def _delete_router(self, plugin_context, router_id, clean_session=True):
try:
self._delete_resource(self._l3_plugin, plugin_context, 'router',
router_id)
router_id, clean_session=clean_session)
except l3.RouterNotFound:
LOG.warning(_LW('Router %s already deleted'), router_id)
@ -436,6 +440,75 @@ class LocalAPI(object):
except l3.FloatingIPNotFound:
LOG.warning(_LW('Floating IP %s Already deleted'), fip_id)
def _get_address_scope(self, plugin_context, address_scope_id,
clean_session=True):
return self._get_resource(self._core_plugin, plugin_context,
'address_scope', address_scope_id,
clean_session=clean_session)
def _get_address_scopes(self, plugin_context, filters=None,
clean_session=True):
filters = filters or {}
return self._get_resources(self._core_plugin, plugin_context,
'address_scopes', filters,
clean_session=clean_session)
def _create_address_scope(self, plugin_context, attrs,
clean_session=True):
return self._create_resource(self._core_plugin, plugin_context,
'address_scope', attrs,
clean_session=clean_session)
def _update_address_scope(self, plugin_context, address_scope_id, attrs,
clean_session=True):
return self._update_resource(self._core_plugin, plugin_context,
'address_scope', address_scope_id, attrs,
clean_session=clean_session)
def _delete_address_scope(self, plugin_context, address_scope_id,
clean_session=True):
try:
self._delete_resource(self._core_plugin, plugin_context,
'address_scope', address_scope_id,
clean_session=clean_session)
except n_exc.AddressScopeNotFound:
LOG.warning(_LW('Address Scope %s already deleted'),
address_scope_id)
def _get_subnetpool(self, plugin_context, subnetpool_id,
clean_session=True):
return self._get_resource(self._core_plugin, plugin_context,
'subnetpool', subnetpool_id,
clean_session=clean_session)
def _get_subnetpools(self, plugin_context, filters=None,
clean_session=True):
filters = filters or {}
return self._get_resources(self._core_plugin, plugin_context,
'subnetpools', filters,
clean_session=clean_session)
def _create_subnetpool(self, plugin_context, attrs,
clean_session=True):
return self._create_resource(self._core_plugin, plugin_context,
'subnetpool', attrs,
clean_session=clean_session)
def _update_subnetpool(self, plugin_context, subnetpool_id, attrs,
clean_session=True):
return self._update_resource(self._core_plugin, plugin_context,
'subnetpool', subnetpool_id, attrs,
clean_session=clean_session)
def _delete_subnetpool(self, plugin_context, subnetpool_id,
clean_session=True):
try:
self._delete_resource(self._core_plugin, plugin_context,
'subnetpool', subnetpool_id,
clean_session=clean_session)
except n_exc.SubnetpoolNotFound:
LOG.warning(_LW('Subnetpool %s already deleted'), subnetpool_id)
def _get_l2_policy(self, plugin_context, l2p_id, clean_session=True):
return self._get_resource(self._group_policy_plugin, plugin_context,
'l2_policy', l2p_id,

View File

@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from neutron.common import exceptions as nexc
from neutron.db import model_base
from oslo_log import helpers as log
from oslo_log import log as logging
@ -25,6 +26,10 @@ from gbpservice.neutron.services.grouppolicy.common import exceptions
LOG = logging.getLogger(__name__)
class AddressScopeUpdateForL3PNotSupported(nexc.BadRequest):
message = _("Address Scope update for L3 Policy is not supported.")
class PolicyTargetMapping(gpdb.PolicyTarget):
"""Mapping of PolicyTarget to Neutron Port."""
__table_args__ = {'extend_existing': True}
@ -71,12 +76,40 @@ class L3PolicyRouterAssociation(model_base.BASEV2):
primary_key=True)
class L3PolicySubnetpoolV4Association(model_base.BASEV2):
"""Models one to many relation between a L3Policy and v4 Subnetpools."""
__tablename__ = 'gp_l3_policy_subnetpool_v4_associations'
l3_policy_id = sa.Column(sa.String(36), sa.ForeignKey('gp_l3_policies.id'),
primary_key=True)
subnetpool_id = sa.Column(
sa.String(36), sa.ForeignKey('subnetpools.id'), primary_key=True)
class L3PolicySubnetpoolV6Association(model_base.BASEV2):
"""Models one to many relation between a L3Policy and v6 Subnetpools."""
__tablename__ = 'gp_l3_policy_subnetpool_v6_associations'
l3_policy_id = sa.Column(sa.String(36), sa.ForeignKey('gp_l3_policies.id'),
primary_key=True)
subnetpool_id = sa.Column(
sa.String(36), sa.ForeignKey('subnetpools.id'), primary_key=True)
class L3PolicyMapping(gpdb.L3Policy):
"""Mapping of L3Policy to set of Neutron Routers."""
"""Mapping of L3Policy to set of Neutron Resources."""
__table_args__ = {'extend_existing': True}
__mapper_args__ = {'polymorphic_identity': 'mapping'}
routers = orm.relationship(L3PolicyRouterAssociation,
cascade='all', lazy="joined")
address_scope_v4_id = sa.Column(
sa.String(36), sa.ForeignKey('address_scopes.id'),
nullable=True, unique=True)
address_scope_v6_id = sa.Column(
sa.String(36), sa.ForeignKey('address_scopes.id'),
nullable=True, unique=True)
subnetpools_v4 = orm.relationship(L3PolicySubnetpoolV4Association,
cascade='all', lazy="joined")
subnetpools_v6 = orm.relationship(L3PolicySubnetpoolV6Association,
cascade='all', lazy="joined")
class ExternalSegmentMapping(gpdb.ExternalSegment):
@ -122,6 +155,10 @@ class GroupPolicyMappingDbPlugin(gpdb.GroupPolicyDbPlugin):
res = super(GroupPolicyMappingDbPlugin,
self)._make_l3_policy_dict(l3p)
res['routers'] = [router.router_id for router in l3p.routers]
res['address_scope_v4_id'] = l3p.address_scope_v4_id
res['address_scope_v6_id'] = l3p.address_scope_v6_id
res['subnetpools_v4'] = [sp.subnetpool_id for sp in l3p.subnetpools_v4]
res['subnetpools_v6'] = [sp.subnetpool_id for sp in l3p.subnetpools_v6]
return self._fields(res, fields)
def _make_external_segment_dict(self, es, fields=None):
@ -171,6 +208,98 @@ class GroupPolicyMappingDbPlugin(gpdb.GroupPolicyDbPlugin):
l2p_db = self._get_l2_policy(context, l2p_id)
l2p_db.network_id = network_id
def _set_address_scope_for_l3_policy(self, context, l3p_id,
address_scope_id, ip_version=4):
if not address_scope_id:
return
# TODO(Sumit): address_scope_id validation
with context.session.begin(subtransactions=True):
l3p_db = self._get_l3_policy(context, l3p_id)
if ip_version == 4:
l3p_db.address_scope_v4_id = address_scope_id
else:
l3p_db.address_scope_v6_id = address_scope_id
def _add_subnetpool_to_l3_policy(self, context, l3p_id,
subnetpool_id, ip_version=4):
# TODO(Sumit): subnetpool_id validation
with context.session.begin(subtransactions=True):
l3p_db = self._get_l3_policy(context, l3p_id)
if ip_version == 4:
assoc = L3PolicySubnetpoolV4Association(
l3_policy_id=l3p_id, subnetpool_id=subnetpool_id)
l3p_db.subnetpools_v4.append(assoc)
else:
assoc = L3PolicySubnetpoolV6Association(
l3_policy_id=l3p_id, subnetpool_id=subnetpool_id)
l3p_db.subnetpools_v6.append(assoc)
return {4: [sp.subnetpool_id for sp in l3p_db.subnetpools_v4],
6: [sp.subnetpool_id for sp in l3p_db.subnetpools_v6]}
def _add_subnetpools_to_l3_policy(self, context, l3p_id,
subnetpools, ip_version=4):
for sp in subnetpools or []:
self._add_subnetpool_to_l3_policy(
context, l3p_id, sp, ip_version=ip_version)
def _remove_subnetpool_from_l3_policy(self, context, l3p_id,
subnetpool_id, ip_version=4):
with context.session.begin(subtransactions=True):
l3p_db = self._get_l3_policy(context, l3p_id)
if ip_version == 4:
assoc = (context.session.query(
L3PolicySubnetpoolV4Association).filter_by(
l3_policy_id=l3p_id,
subnetpool_id=subnetpool_id).one())
l3p_db.subnetpools_v4.remove(assoc)
else:
assoc = (context.session.query(
L3PolicySubnetpoolV6Association).filter_by(
l3_policy_id=l3p_id,
subnetpool_id=subnetpool_id).one())
l3p_db.subnetpools_v6.remove(assoc)
context.session.delete(assoc)
return {4: [sp.subnetpool_id for sp in l3p_db.subnetpools_v4],
6: [sp.subnetpool_id for sp in l3p_db.subnetpools_v6]}
def _update_subnetpools_for_l3_policy(self, context, l3p_id,
subnetpools, ip_version=4):
# Add/remove associations for changes in subnetpools
# TODO(Sumit): Before disassociating a subnetpool, check that
# there is no PT present on a subnet which belongs to that subnetpool
if not subnetpools:
return
with context.session.begin(subtransactions=True):
l3p_db = self._get_l3_policy(context, l3p_id)
new_subnetpools = set(subnetpools)
if ip_version == 4:
old_subnetpools = set(sp.subnetpool_id
for sp in l3p_db.subnetpools_v4)
else:
old_subnetpools = set(sp.subnetpool_id
for sp in l3p_db.subnetpools_v6)
for sp in new_subnetpools - old_subnetpools:
if ip_version == 4:
assoc = L3PolicySubnetpoolV4Association(
l3_policy_id=l3p_id, subnetpool_id=sp)
l3p_db.subnetpools_v4.append(assoc)
else:
assoc = L3PolicySubnetpoolV6Association(
l3_policy_id=l3p_id, subnetpool_id=sp)
l3p_db.subnetpools_v6.append(assoc)
for sp in old_subnetpools - new_subnetpools:
if ip_version == 4:
assoc = (context.session.query(
L3PolicySubnetpoolV4Association).filter_by(
l3_policy_id=l3p_id, subnetpool_id=sp).one())
l3p_db.subnetpools_v4.remove(assoc)
else:
assoc = (context.session.query(
L3PolicySubnetpoolV6Association).filter_by(
l3_policy_id=l3p_id, subnetpool_id=sp).one())
l3p_db.subnetpools_v6.remove(assoc)
context.session.delete(assoc)
def _add_router_to_l3_policy(self, context, l3p_id, router_id):
with context.session.begin(subtransactions=True):
l3p_db = self._get_l3_policy(context, l3p_id)
@ -424,6 +553,18 @@ class GroupPolicyMappingDbPlugin(gpdb.GroupPolicyDbPlugin):
l3p['subnet_prefix_length'],
description=l3p['description'],
shared=l3p.get('shared', False))
self._set_address_scope_for_l3_policy(
context, l3p_db['id'], l3p.get('address_scope_v4_id'),
ip_version=4)
self._set_address_scope_for_l3_policy(
context, l3p_db['id'], l3p.get('address_scope_v6_id'),
ip_version=6)
self._add_subnetpools_to_l3_policy(
context, l3p_db['id'], l3p.get('subnetpools_v4'), ip_version=4)
self._add_subnetpools_to_l3_policy(
context, l3p_db['id'], l3p.get('subnetpools_v6'), ip_version=6)
if 'routers' in l3p:
for router in l3p['routers']:
assoc = L3PolicyRouterAssociation(
@ -440,12 +581,27 @@ class GroupPolicyMappingDbPlugin(gpdb.GroupPolicyDbPlugin):
@log.log_method_call
def update_l3_policy(self, context, l3_policy_id, l3_policy):
l3p = l3_policy['l3_policy']
if 'address_scope_v4_id' in l3p or 'address_scope_v6_id' in l3p:
raise AddressScopeUpdateForL3PNotSupported()
with context.session.begin(subtransactions=True):
l3p_db = self._get_l3_policy(context, l3_policy_id)
self._update_subnetpools_for_l3_policy(context, l3_policy_id,
l3p.get('subnetpools_v4'),
ip_version=4)
l3p.pop('subnetpools_v4', None)
self._update_subnetpools_for_l3_policy(context, l3_policy_id,
l3p.get('subnetpools_v6'),
ip_version=4)
l3p.pop('subnetpools_v6', None)
if 'subnet_prefix_length' in l3p:
self.validate_subnet_prefix_length(l3p_db.ip_version,
l3p['subnet_prefix_length'],
l3p_db.ip_pool)
if 'routers' in l3p:
# Add/remove associations for changes in routers.
new_routers = set(l3p['routers'])

View File

@ -1 +1 @@
7afacef00d31
d4bb487a81b8

View File

@ -0,0 +1,96 @@
# 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.
"""address_scope and subnetpool mapping for l3_policies
Revision ID: d4bb487a81b8
Revises: c1aab79622fe
Create Date: 2016-08-28 11:35:32.724952
"""
# revision identifiers, used by Alembic.
revision = 'd4bb487a81b8'
down_revision = '7afacef00d31'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.create_table(
'gp_l3_policy_subnetpool_v4_associations',
sa.Column('l3_policy_id', sa.String(length=36), nullable=False),
sa.Column('subnetpool_id', sa.String(length=36), nullable=False),
sa.ForeignKeyConstraint(['l3_policy_id'], ['gp_l3_policies.id'],
name='gpm_l3p_subnetpool_v4_assoc_fk_l3pid'),
sa.ForeignKeyConstraint(['subnetpool_id'], ['subnetpools.id'],
name='gpm_l3p_subnetpool_v4_assoc_fk_spid'),
sa.PrimaryKeyConstraint('l3_policy_id', 'subnetpool_id')
)
op.create_table(
'gp_l3_policy_subnetpool_v6_associations',
sa.Column('l3_policy_id', sa.String(length=36), nullable=False),
sa.Column('subnetpool_id', sa.String(length=36), nullable=False),
sa.ForeignKeyConstraint(['l3_policy_id'], ['gp_l3_policies.id'],
name='gpm_l3p_subnetpool_v6_assoc_fk_l3pid'),
sa.ForeignKeyConstraint(['subnetpool_id'], ['subnetpools.id'],
name='gpm_l3p_subnetpool_v6_assoc_fk_spid'),
sa.PrimaryKeyConstraint('l3_policy_id', 'subnetpool_id')
)
op.add_column(
'gp_l3_policies',
sa.Column('address_scope_v4_id', sa.String(length=36), nullable=True),
)
op.add_column(
'gp_l3_policies',
sa.Column('address_scope_v6_id', sa.String(length=36), nullable=True),
)
op.create_unique_constraint('gpm_l3p_addr_scope_v4_uq',
'gp_l3_policies', ['address_scope_v4_id'])
op.create_unique_constraint('gpm_l3p_addr_scope_v6_uq',
'gp_l3_policies', ['address_scope_v6_id'])
op.create_foreign_key('gpm_l3p_addr_scope_v4_fk',
source='gp_l3_policies', referent='address_scopes',
local_cols=['address_scope_v4_id'],
remote_cols=['id'])
op.create_foreign_key('gpm_l3p_addr_scope_v6_fk',
source='gp_l3_policies', referent='address_scopes',
local_cols=['address_scope_v6_id'],
remote_cols=['id'])
op.create_table(
'gpm_owned_address_scopes',
sa.Column('address_scope_id', sa.String(length=36), nullable=False),
sa.ForeignKeyConstraint(
['address_scope_id'], ['address_scopes.id'],
name='rmd_addr_scope_owned_fk', ondelete='CASCADE'),
sa.PrimaryKeyConstraint('address_scope_id')
)
op.create_table(
'gpm_owned_subnetpools',
sa.Column('subnetpool_id', sa.String(length=36), nullable=False),
sa.ForeignKeyConstraint(['subnetpool_id'], ['subnetpools.id'],
name='rmd_subnetpool_owned_fk',
ondelete='CASCADE'),
sa.PrimaryKeyConstraint('subnetpool_id')
)
def downgrade():
pass

View File

@ -41,6 +41,20 @@ EXTENDED_ATTRIBUTES_2_0 = {
'is_visible': True, 'default': None},
},
gp.L3_POLICIES: {
'address_scope_v4_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:uuid_or_none': None},
'is_visible': True, 'default': None},
'address_scope_v6_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:uuid_or_none': None},
'is_visible': True, 'default': None},
'subnetpools_v4': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'is_visible': True, 'default': None},
'subnetpools_v6': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'is_visible': True, 'default': None},
'routers': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,

View File

@ -28,6 +28,7 @@ from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import model
from gbpservice.neutron.services.grouppolicy.common import (
constants as gp_const)
from gbpservice.neutron.services.grouppolicy.common import constants as g_const
from gbpservice.neutron.services.grouppolicy.common import exceptions as exc
from gbpservice.neutron.services.grouppolicy.drivers import (
neutron_resources as nrd)
from gbpservice.neutron.services.grouppolicy.drivers.cisco.apic import (
@ -41,6 +42,7 @@ REVERSE = 'Reverse'
FILTER_DIRECTIONS = {FORWARD: False, REVERSE: True}
FORWARD_FILTER_ENTRIES = 'Forward-FilterEntries'
REVERSE_FILTER_ENTRIES = 'Reverse-FilterEntries'
ADDR_SCOPE_KEYS = ['address_scope_v4_id', 'address_scope_v6_id']
# Definitions duplicated from apicapi lib
APIC_OWNED = 'apic_owned_'
@ -82,6 +84,76 @@ class AIMMappingDriver(nrd.CommonNeutronBase):
def aim_display_name(self, name):
return name
@log.log_method_call
def create_l3_policy_precommit(self, context):
l3p = context.current
l3p_db = context._plugin._get_l3_policy(
context._plugin_context, l3p['id'])
ascp = 'address_scope_v4_id' if l3p['ip_version'] == 4 else (
'address_scope_v6_id')
if not l3p[ascp]:
# REVISIT: For dual stack.
# This logic assumes either 4 or 6 but not both
self._use_implicit_address_scope(context, clean_session=False)
l3p_db[ascp] = l3p[ascp]
subpool = 'subnetpools_v4' if l3p['ip_version'] == 4 else (
'subnetpools_v6')
if not l3p[subpool]:
# REVISIT: For dual stack.
# This logic assumes either 4 or 6 but not both
self._use_implicit_subnetpool(
context, address_scope_id=l3p_db[ascp],
ip_version=l3p['ip_version'], clean_session=False)
# REVISIT: Check if the following constraint still holds
if len(l3p['routers']) > 1:
raise exc.L3PolicyMultipleRoutersNotSupported()
# REVISIT: Validate non overlapping IPs in the same tenant.
# Currently this validation is not required for the
# AIM driver, and since the AIM driver is the only
# driver inheriting from this driver, we are okay
# without the check.
self._reject_invalid_router_access(context, clean_session=False)
if not l3p['routers']:
self._use_implicit_router(context, clean_session=False)
@log.log_method_call
def update_l3_policy_precommit(self, context):
if context.current['routers'] != context.original['routers']:
raise exc.L3PolicyRoutersUpdateNotSupported()
# Currently there is no support for router update in l3p update.
# Added this check just in case it is supported in future.
self._reject_invalid_router_access(context, clean_session=False)
# TODO(Sumit): For extra safety add validation for address_scope change
@log.log_method_call
def delete_l3_policy_precommit(self, context):
l3p_db = context._plugin._get_l3_policy(
context._plugin_context, context.current['id'])
v4v6subpools = {4: l3p_db.subnetpools_v4, 6: l3p_db.subnetpools_v6}
for k, v in v4v6subpools.iteritems():
subpools = [sp.subnetpool_id for sp in v]
for sp_id in subpools:
self._db_plugin(
context._plugin)._remove_subnetpool_from_l3_policy(
context._plugin_context, l3p_db['id'], sp_id,
ip_version=k)
self._cleanup_subnetpool(context._plugin_context, sp_id,
clean_session=False)
for ascp in ADDR_SCOPE_KEYS:
if l3p_db[ascp]:
ascp_id = l3p_db[ascp]
l3p_db.update({ascp: None})
self._cleanup_address_scope(context._plugin_context, ascp_id,
clean_session=False)
routers = [router.router_id for router in l3p_db.routers]
for router_id in routers:
self._db_plugin(context._plugin)._remove_router_from_l3_policy(
context._plugin_context, l3p_db['id'], router_id)
self._cleanup_router(context._plugin_context, router_id,
clean_session=False)
# TODO(Sumit): Implement get_l3_policy_status()
@log.log_method_call
def create_l2_policy_precommit(self, context):
super(AIMMappingDriver, self).create_l2_policy_precommit(context)
@ -117,6 +189,8 @@ class AIMMappingDriver(nrd.CommonNeutronBase):
context, context.current, default_epg_dn)
super(AIMMappingDriver, self).delete_l2_policy_precommit(context)
# TODO(Sumit): Implement get_l2_policy_status()
@log.log_method_call
def create_policy_target_group_precommit(self, context):
if context.current['subnets']:
@ -208,6 +282,7 @@ class AIMMappingDriver(nrd.CommonNeutronBase):
for subnet_id in subnet_ids:
if not context._plugin._get_ptgs_for_subnet(
context._plugin_context, subnet_id):
# TODO(Sumit): pass router_id of default router
self._cleanup_subnet(plugin_context, subnet_id,
clean_session=False)
@ -258,11 +333,6 @@ class AIMMappingDriver(nrd.CommonNeutronBase):
if pt_db['port_id']:
self._cleanup_port(context._plugin_context, pt_db['port_id'])
@log.log_method_call
def delete_l3_policy_precommit(self, context):
# TODO(Sumit): Implement
pass
@log.log_method_call
def update_policy_classifier_precommit(self, context):
o_dir = context.original['direction']
@ -694,12 +764,14 @@ class AIMMappingDriver(nrd.CommonNeutronBase):
context._plugin_context, l2p_id)
name = APIC_OWNED + l2p['name']
added = super(
AIMMappingDriver, self)._use_implicit_subnet(
AIMMappingDriver,
self)._use_implicit_subnet_from_subnetpool(
context, subnet_specifics={'name': name},
is_proxy=False, clean_session=clean_session)
clean_session=clean_session)
context.add_subnets(subs - set(context.current['subnets']))
for subnet in added:
self._sync_ptg_subnets(context, l2p)
# TODO(Sumit): This subnet needs to added to the default router
def _create_implicit_contracts_and_configure_default_epg(
self, context, l2p, epg_dn):

View File

@ -44,8 +44,13 @@ class CommonNeutronBase(ipd.ImplicitPolicyBase, rmd.OwnedResourcesOperations,
if not context.current['l3_policy_id']:
self._create_implicit_l3_policy(context, clean_session=False)
l2p_db['l3_policy_id'] = context.current['l3_policy_id']
l3p_db = context._plugin._get_l3_policy(
context._plugin_context, l2p_db['l3_policy_id'])
if not context.current['network_id']:
self._use_implicit_network(context, clean_session=False)
self._use_implicit_network(
context, address_scope_v4=l3p_db['address_scope_v4_id'],
address_scope_v6=l3p_db['address_scope_v6_id'],
clean_session=False)
l2p_db['network_id'] = context.current['network_id']
@log.log_method_call

View File

@ -85,6 +85,26 @@ class OwnedNetwork(model_base.BASEV2):
nullable=False, primary_key=True)
class OwnedAddressScope(model_base.BASEV2):
"""An Address Scope owned by the resource_mapping driver."""
__tablename__ = 'gpm_owned_address_scopes'
address_scope_id = sa.Column(sa.String(36),
sa.ForeignKey('address_scopes.id',
ondelete='CASCADE'),
nullable=False, primary_key=True)
class OwnedSubnetpool(model_base.BASEV2):
"""A Subnetpool owned by the resource_mapping driver."""
__tablename__ = 'gpm_owned_subnetpools'
subnetpool_id = sa.Column(sa.String(36),
sa.ForeignKey('subnetpools.id',
ondelete='CASCADE'),
nullable=False, primary_key=True)
class OwnedRouter(model_base.BASEV2):
"""A Router owned by the resource_mapping driver."""
@ -115,6 +135,10 @@ class CidrInUse(exc.GroupPolicyInternalError):
class OwnedResourcesOperations(object):
# TODO(Sumit): All the following operations can be condensed into
# a single _mark_resource_owned() and _resource_is_owned() method,
# by creating a resource to DB class name mapping.
def _mark_port_owned(self, session, port_id):
with session.begin(subtransactions=True):
owned = OwnedPort(port_id=port_id)
@ -159,9 +183,89 @@ class OwnedResourcesOperations(object):
filter_by(router_id=router_id).
first() is not None)
def _mark_address_scope_owned(self, session, address_scope_id):
with session.begin(subtransactions=True):
owned = OwnedAddressScope(address_scope_id=address_scope_id)
session.add(owned)
def _address_scope_is_owned(self, session, address_scope_id):
with session.begin(subtransactions=True):
return (session.query(OwnedAddressScope).
filter_by(address_scope_id=address_scope_id).
first() is not None)
def _mark_subnetpool_owned(self, session, subnetpool_id):
with session.begin(subtransactions=True):
owned = OwnedSubnetpool(subnetpool_id=subnetpool_id)
session.add(owned)
def _subnetpool_is_owned(self, session, subnetpool_id):
with session.begin(subtransactions=True):
return (session.query(OwnedSubnetpool).
filter_by(subnetpool_id=subnetpool_id).
first() is not None)
class ImplicitResourceOperations(local_api.LocalAPI):
def _create_implicit_address_scope(self, context, clean_session=True,
**kwargs):
attrs = {'tenant_id': context.current['tenant_id'],
'name': context.current['name'], 'ip_version':
context.current['ip_version'],
'shared': context.current.get('shared', False)}
attrs.update(**kwargs)
address_scope = self._create_address_scope(
context._plugin_context, attrs, clean_session)
as_id = address_scope['id']
self._mark_address_scope_owned(context._plugin_context.session, as_id)
return address_scope
def _use_implicit_address_scope(self, context, clean_session=True):
address_scope = self._create_implicit_address_scope(
context, clean_session, name='l3p_' + context.current['name'])
context.set_address_scope_id(address_scope['id'])
def _cleanup_address_scope(self, plugin_context, address_scope_id,
clean_session=True):
if self._address_scope_is_owned(plugin_context.session,
address_scope_id):
self._delete_address_scope(plugin_context, address_scope_id,
clean_session)
def _create_implicit_subnetpool(self, context, clean_session=True,
**kwargs):
attrs = {'tenant_id': context.current['tenant_id'],
'name': context.current['name'], 'ip_version':
context.current['ip_version'],
'default_prefixlen': context.current['subnet_prefix_length'],
'prefixes': [context.current['ip_pool']],
'shared': context.current.get('shared', False),
# Per current understanding, is_default is used for
# auto_allocation and is a per-tenant setting.
'is_default': False}
attrs.update(**kwargs)
subnetpool = self._create_subnetpool(
context._plugin_context, attrs, clean_session)
sp_id = subnetpool['id']
self._mark_subnetpool_owned(context._plugin_context.session, sp_id)
return subnetpool
def _use_implicit_subnetpool(self, context, address_scope_id, ip_version,
clean_session=True):
subnetpool = self._create_implicit_subnetpool(
context, clean_session, name='l3p_' + context.current['name'],
address_scope_id=address_scope_id)
context.add_subnetpool(subnetpool_id=subnetpool['id'],
ip_version=ip_version)
def _cleanup_subnetpool(self, plugin_context, subnetpool_id,
clean_session=True):
if self._subnetpool_is_owned(plugin_context.session,
subnetpool_id):
self._delete_subnetpool(plugin_context, subnetpool_id,
clean_session)
def _create_implicit_network(self, context, clean_session=True, **kwargs):
attrs = {'tenant_id': context.current['tenant_id'],
'name': context.current['name'], 'admin_state_up': True,
@ -173,9 +277,12 @@ class ImplicitResourceOperations(local_api.LocalAPI):
self._mark_network_owned(context._plugin_context.session, network_id)
return network
def _use_implicit_network(self, context, clean_session=True):
def _use_implicit_network(self, context, address_scope_v4=None,
address_scope_v6=None, clean_session=True):
network = self._create_implicit_network(
context, clean_session, name='l2p_' + context.current['name'])
context, clean_session, name='l2p_' + context.current['name'],
ipv4_address_scope=address_scope_v4,
ipv6_address_scope=address_scope_v6)
context.set_network_id(network['id'])
def _cleanup_network(self, plugin_context, network_id, clean_session=True):
@ -363,6 +470,53 @@ class ImplicitResourceOperations(local_api.LocalAPI):
context, is_proxy, prefix_len, subnet_specifics, l2p, l3p,
clean_session=clean_session)
def _use_implicit_subnet_from_subnetpool(
self, context, subnet_specifics=None, clean_session=True):
# If a subnet needs to be created with a prefix_length other than
# the subnet_prefix_length set for the l3_policy, a 'prefixlen' can be
# passed explicitly in the subnet_specifics dict.
# If a subnet with a specific CIDR needs to be created, the 'cidr' can
# be passed explicitly in the subnet_specifics dict.
# Note that either 'prefixlen' or 'cidr' can be requested, not both.
# If a 'subnetpool_id' other than the one considered default is to be
# used, it can be passed explicitly in the subnet_specifics dict.
subnet_specifics = subnet_specifics or {}
l2p_id = context.current['l2_policy_id']
l2p = context._plugin.get_l2_policy(context._plugin_context, l2p_id)
l3p_id = l2p['l3_policy_id']
l3p_db = context._plugin.get_l3_policy(context._plugin_context, l3p_id)
# REVISIT: For dual stack
# Current assumption is that either v4 or v6 subnet needs to be
# allocated, but not both
if l3p_db['address_scope_v4_id']:
# Since this is implicit assignment, the first subnetpool
# is considered as the default and always used (here and in
# the v6 case below)
subnetpool_id = l3p_db['subnetpools_v4'][0]
ip_version = 4
else:
subnetpool_id = l3p_db['subnetpools_v6'][0]
ip_version = 6
attrs = {'tenant_id': context.current['tenant_id'],
'name': 'ptg_' + context.current['name'],
'network_id': l2p['network_id'],
'ip_version': ip_version,
'subnetpool_id': subnetpool_id,
'cidr': attributes.ATTR_NOT_SPECIFIED,
'prefixlen': attributes.ATTR_NOT_SPECIFIED,
'enable_dhcp': True,
'gateway_ip': attributes.ATTR_NOT_SPECIFIED,
'allocation_pools': attributes.ATTR_NOT_SPECIFIED,
'dns_nameservers': (
cfg.CONF.resource_mapping.dns_nameservers or
attributes.ATTR_NOT_SPECIFIED),
'host_routes': attributes.ATTR_NOT_SPECIFIED}
attrs.update(subnet_specifics)
subnet = self._create_subnet(context._plugin_context, attrs,
clean_session=clean_session)
self._mark_subnet_owned(context._plugin_context.session, subnet['id'])
return [subnet]
def _cleanup_subnet(self, plugin_context, subnet_id, router_id=None,
clean_session=True):
interface_info = {'subnet_id': subnet_id}
@ -431,6 +585,48 @@ class ImplicitResourceOperations(local_api.LocalAPI):
except n_exc.PortNotFound:
LOG.warning(_LW("Port %s is missing") % port_id)
def _reject_invalid_router_access(self, context, clean_session=True):
# Validate if the explicit router(s) belong to the tenant.
# Are routers shared across tenants ??
# How to check if admin and if admin can access all routers ??
for router_id in context.current['routers']:
router = None
try:
router = self._get_router(context._plugin_context, router_id,
clean_session=clean_session)
except n_exc.NotFound:
raise exc.InvalidRouterAccess(
msg="Can't access other tenants router",
router_id=router_id,
tenant_id=context.current['tenant_id'])
if router:
tenant_id_of_explicit_router = router['tenant_id']
curr_tenant_id = context.current['tenant_id']
if tenant_id_of_explicit_router != curr_tenant_id:
raise exc.InvalidRouterAccess(
msg="Can't access other tenants router",
router_id=router_id,
tenant_id=context.current['tenant_id'])
def _use_implicit_router(self, context, router_name=None,
clean_session=True):
attrs = {'tenant_id': context.current['tenant_id'],
'name': router_name or ('l3p_' + context.current['name']),
'external_gateway_info': None,
'admin_state_up': True}
router = self._create_router(context._plugin_context, attrs,
clean_session=clean_session)
router_id = router['id']
self._mark_router_owned(context._plugin_context.session, router_id)
context.add_router(router_id)
return router_id
def _cleanup_router(self, plugin_context, router_id, clean_session=True):
if self._router_is_owned(plugin_context.session, router_id):
self._delete_router(plugin_context, router_id,
clean_session=clean_session)
class ResourceMappingDriver(api.PolicyDriver, ImplicitResourceOperations,
nsp_manager.NetworkServicePolicyMappingMixin,
@ -531,29 +727,6 @@ class ResourceMappingDriver(api.PolicyDriver, ImplicitResourceOperations,
network_id=context.current['network_id'],
tenant_id=context.current['tenant_id'])
def _reject_invalid_router_access(self, context):
# Validate if the explicit router(s) belong to the tenant.
# Are routers shared across tenants ??
# How to check if admin and if admin can access all routers ??
for router_id in context.current['routers']:
router = None
try:
router = self._get_router(context._plugin_context, router_id)
except n_exc.NotFound:
raise exc.InvalidRouterAccess(
msg="Can't access other tenants router",
router_id=router_id,
tenant_id=context.current['tenant_id'])
if router:
tenant_id_of_explicit_router = router['tenant_id']
curr_tenant_id = context.current['tenant_id']
if tenant_id_of_explicit_router != curr_tenant_id:
raise exc.InvalidRouterAccess(
msg="Can't access other tenants router",
router_id=router_id,
tenant_id=context.current['tenant_id'])
@log.log_method_call
def create_policy_target_precommit(self, context):
self._check_create_policy_target(context)
@ -1794,21 +1967,6 @@ class ResourceMappingDriver(api.PolicyDriver, ImplicitResourceOperations,
self._delete_subnet(context._plugin_context, subnet_id)
raise exc.GroupPolicyInternalError()
def _use_implicit_router(self, context, router_name=None):
attrs = {'tenant_id': context.current['tenant_id'],
'name': router_name or ('l3p_' + context.current['name']),
'external_gateway_info': None,
'admin_state_up': True}
router = self._create_router(context._plugin_context, attrs)
router_id = router['id']
self._mark_router_owned(context._plugin_context.session, router_id)
context.add_router(router_id)
return router_id
def _cleanup_router(self, plugin_context, router_id):
if self._router_is_owned(plugin_context.session, router_id):
self._delete_router(plugin_context, router_id)
def _create_policy_rule_set_sg(self, context, sg_name_prefix):
return self._create_gbp_sg(
context._plugin_context, context.current['tenant_id'],

View File

@ -130,6 +130,29 @@ class L3PolicyContext(GroupPolicyContext, api.L3PolicyContext):
def original(self):
return self._original_l3_policy
def set_address_scope_id(self, address_scope_id, version=4):
self._plugin._set_address_scope_for_l3_policy(
self._plugin_context, self._l3_policy['id'], address_scope_id,
ip_version=version)
if version == 4:
self._l3_policy['address_scope_v4_id'] = address_scope_id
else:
self._l3_policy['address_scope_v6_id'] = address_scope_id
def add_subnetpool(self, subnetpool_id, ip_version=4):
subnetpools = self._plugin._add_subnetpool_to_l3_policy(
self._plugin_context, self._l3_policy['id'], subnetpool_id,
ip_version=ip_version)
self._l3_policy['subnetpools_v4'] = subnetpools[4]
self._l3_policy['subnetpools_v6'] = subnetpools[6]
def remove_subnetpool(self, subnetpool_id, ip_version=4):
subnetpools = self._plugin._remove_subnetpool_to_l3_policy(
self._plugin_context, self._l3_policy['id'], subnetpool_id,
ip_version=ip_version)
self._l3_policy['subnetpools_v4'] = subnetpools[4]
self._l3_policy['subnetpools_v6'] = subnetpools[6]
def add_router(self, router_id):
routers = self._plugin._add_router_to_l3_policy(
self._plugin_context, self._l3_policy['id'], router_id)

View File

@ -346,6 +346,7 @@ class GroupPolicyDbTestCase(GroupPolicyDBTestBase,
plugins = manager.NeutronManager.get_service_plugins()
self._gbp_plugin = plugins.get(constants.GROUP_POLICY)
self._sc_plugin = plugins.get(constants.SERVICECHAIN)
self._l3_plugin = plugins.get(constants.L3_ROUTER_NAT)
class TestGroupResources(GroupPolicyDbTestCase):

View File

@ -220,6 +220,99 @@ class TestAIMStatus(AIMBaseTestCase):
self.aim_mgr.get_status = orig_get_status
class TestL3Policy(AIMBaseTestCase):
def test_create_l3_policy_lifecycle_implicit_address_scope(self):
# Create L3 policy with implicit router.
l3p = self.create_l3_policy(name="l3p1")['l3_policy']
l3p_id = l3p['id']
self.assertIsNone(l3p['address_scope_v6_id'])
ascp_id = l3p['address_scope_v4_id']
self.assertEqual(len(l3p['subnetpools_v4']), 1)
sp_id = l3p['subnetpools_v4'][0]
self.assertIsNotNone(ascp_id)
routers = l3p['routers']
self.assertIsNotNone(routers)
self.assertEqual(len(routers), 1)
router_id = routers[0]
"""
# TODO(Sumit): Address-scope retrieval is creating issues, requires
# some fixing in the UT setup
req = self.new_show_request('address-scopes', ascp_id, fmt=self.fmt)
res = self.deserialize(self.fmt, req.get_response(self.api))
ascope = res['address_scope']
"""
req = self.new_show_request('subnetpools', sp_id, fmt=self.fmt)
res = self.deserialize(self.fmt, req.get_response(self.api))
subpool = res['subnetpool']
self.assertEqual(l3p['ip_pool'], subpool['prefixes'][0])
self.assertEqual(l3p['subnet_prefix_length'],
int(subpool['default_prefixlen']))
self.assertEqual(l3p['ip_version'],
subpool['ip_version'])
router = self._get_object('routers', router_id, self.ext_api)['router']
self.assertEqual('l3p_l3p1', router['name'])
# TODO(Sumit): Test update of relevant attributes
req = self.new_delete_request('l3_policies', l3p_id)
res = req.get_response(self.ext_api)
self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int)
req = self.new_show_request('subnetpools', sp_id, fmt=self.fmt)
res = req.get_response(self.api)
self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int)
req = self.new_show_request('address_scopes', ascp_id, fmt=self.fmt)
res = req.get_response(self.api)
self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int)
req = self.new_show_request('routers', router_id, fmt=self.fmt)
res = req.get_response(self.ext_api)
self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int)
class TestL3PolicyRollback(AIMBaseTestCase):
def test_l3_policy_create_fail(self):
orig_func = self.dummy.create_l3_policy_precommit
self.dummy.create_l3_policy_precommit = mock.Mock(
side_effect=Exception)
self.create_l3_policy(name="l3p1", expected_res_status=500)
self.assertEqual([], self._plugin.get_address_scopes(self._context))
self.assertEqual([], self._plugin.get_subnetpools(self._context))
self.assertEqual([], self._l3_plugin.get_routers(self._context))
self.assertEqual([], self._gbp_plugin.get_l3_policies(self._context))
# restore mock
self.dummy.create_l3_policy_precommit = orig_func
def test_l3_policy_update_fail(self):
orig_func = self.dummy.update_l3_policy_precommit
self.dummy.update_l3_policy_precommit = mock.Mock(
side_effect=Exception)
l3p = self.create_l3_policy(name="l3p1")['l3_policy']
l3p_id = l3p['id']
self.update_l3_policy(l3p_id, expected_res_status=500,
name="new name")
new_l3p = self.show_l3_policy(l3p_id, expected_res_status=200)
self.assertEqual(l3p['name'],
new_l3p['l3_policy']['name'])
# restore mock
self.dummy.update_l3_policy_precommit = orig_func
def test_l3_policy_delete_fail(self):
orig_func = self.dummy.delete_l3_policy_precommit
self.dummy.delete_l3_policy_precommit = mock.Mock(
side_effect=Exception)
l3p = self.create_l3_policy(name="l3p1")['l3_policy']
l3p_id = l3p['id']
self.delete_l3_policy(l3p_id, expected_res_status=500)
self.show_l3_policy(l3p_id, expected_res_status=200)
self.assertEqual(
1, len(self._plugin.get_address_scopes(self._context)))
self.assertEqual(1, len(self._plugin.get_subnetpools(self._context)))
self.assertEqual(1, len(self._l3_plugin.get_routers(self._context)))
# restore mock
self.dummy.delete_l3_policy_precommit = orig_func
class TestL2PolicyBase(test_nr_base.TestL2Policy, AIMBaseTestCase):
def _validate_implicit_contracts_exist(self, l2p):
@ -366,7 +459,7 @@ class TestL2Policy(TestL2PolicyBase):
class TestL2PolicyRollback(TestL2PolicyBase):
def test_l2_policy_create_fail(self):
orig_func = self.dummy.create_policy_target_group_precommit
orig_func = self.dummy.create_l2_policy_precommit
self.dummy.create_l2_policy_precommit = mock.Mock(
side_effect=Exception)
self.create_l2_policy(name="l2p1", expected_res_status=500)
@ -474,9 +567,14 @@ class TestPolicyTargetGroup(AIMBaseTestCase):
l2p = self.show_l2_policy(ptg['l2_policy_id'],
expected_res_status=200)['l2_policy']
l3p = self.show_l3_policy(l2p['l3_policy_id'],
expected_res_status=200)['l3_policy']
req = self.new_show_request('subnets', ptg['subnets'][0], fmt=self.fmt)
res = self.deserialize(self.fmt, req.get_response(self.api))
self.assertIsNotNone(res['subnet']['id'])
subnet = self.deserialize(self.fmt,
req.get_response(self.api))['subnet']
self.assertIsNotNone(subnet['id'])
self.assertEqual(l3p['subnetpools_v4'][0],
subnet['subnetpool_id'])
ptg_name = ptg['name']
aim_epg_name = str(self.name_mapper.policy_target_group(
self._neutron_context.session, ptg_id, ptg_name))
@ -602,6 +700,17 @@ class TestPolicyTargetGroup(AIMBaseTestCase):
self.assertIsNotNone(res['subnet']['id'])
# TODO(Sumit): Add tests here which tests different scenarios for subnet
# allocation for PTGs
# 1. Multiple PTGs share the subnets associated with the l2_policy
# 2. Associated subnets are correctly used for IP address allocation
# 3. New subnets are created when the last available is exhausted
# 4. If multiple subnets are present, all are deleted at the time of
# l2_policy deletion
# 5. 'prefixlen', 'cidr', and 'subnetpool_id' overrides as a part of
# the subnet_specifics dictionary
class TestPolicyTargetGroupRollback(AIMBaseTestCase):
def test_policy_target_group_create_fail(self):

View File

@ -103,16 +103,26 @@ class GroupPolicyMappingExtTestCase(tgp.GroupPolicyExtensionTestCase):
def get_create_l3_policy_default_attrs(self):
attrs = cm.get_create_l3_policy_default_attrs()
attrs.update({'address_scope_v4_id': None})
attrs.update({'address_scope_v6_id': None})
attrs.update({'subnetpools_v4': []})
attrs.update({'subnetpools_v6': []})
attrs.update({'routers': []})
return attrs
def get_create_l3_policy_attrs(self):
attrs = cm.get_create_l3_policy_attrs()
attrs.update({'address_scope_v4_id': tgp._uuid()})
attrs.update({'address_scope_v6_id': tgp._uuid()})
attrs.update({'subnetpools_v4': [tgp._uuid(), tgp._uuid()]})
attrs.update({'subnetpools_v6': [tgp._uuid(), tgp._uuid()]})
attrs.update({'routers': [tgp._uuid(), tgp._uuid()]})
return attrs
def get_update_l3_policy_attrs(self):
attrs = cm.get_update_l3_policy_attrs()
attrs.update({'subnetpools_v4': [tgp._uuid(), tgp._uuid()]})
attrs.update({'subnetpools_v6': [tgp._uuid(), tgp._uuid()]})
attrs.update({'routers': [tgp._uuid(), tgp._uuid()]})
return attrs