[apic_aim] Map neutron resources to AIM, part 3
Implements an L3 service plugin, apic_aim_l3, that, in conjunction with the apic_aim mechanism driver, maps each Neutron router to an AIM Contract and ContractSubject whose DNs and status are exposed via extended attributes similar to those on the core Neutron resources. An "any" Filter and FilterEntry are created per-tenant, and referenced in this contract, allowing all traffic from EPGs providing and consuming this contract to be routed. The add_router_interface and remove_router_interface methods are stubs that will be implemented in the next patch set. They will manage the mapping of router interfaces to AIM Subnets, along with having the default EPGs associated with those interfaces provide and consume the router's Contract. The corresponding GBP policy driver's extension is renamed apic_aim_gbp for consistency with the apic_aim and apic_aim_l3 extensions at the Neutron level, and all extensions are now in the gbpservice.neutron.extensions module. The GBP policy driver's unit tests are updated to account for the Filter and FilterEntry resources created by the mechanism driver. The apic_aim unit tests wipe the AIM DB in tearDown, and use the aci_integration_manager branch of the apicapi repo. The GBP devstack plugin, when ENABLE_APIC_AIM=True, configures neutron to use the apic_aim_l3 service plugin, and installs the aci_integration_manager branch of the apicapi repo. Change-Id: I1b7f0c80e66d55d58c27fe9e4cb461f62aec3c42
This commit is contained in:
parent
ceebaff57d
commit
e382e7611f
@ -4,6 +4,7 @@ function install_apic_aim {
|
||||
install_apic_ml2
|
||||
install_aim
|
||||
install_opflex
|
||||
install_apicapi
|
||||
}
|
||||
|
||||
function configure_apic_aim {
|
||||
|
@ -28,6 +28,8 @@ APICML2_REPO=http://github.com/noironetworks/apic-ml2-driver.git
|
||||
APICML2_DIR=$DEST/apic_ml2
|
||||
OPFLEX_REPO=http://github.com/noironetworks/python-opflex-agent.git
|
||||
OPFLEX_DIR=$DEST/opflexagent
|
||||
APICAPI_REPO=http://github.com/noironetworks/apicapi.git
|
||||
APICAPI_DIR=$DEST/apicapi
|
||||
|
||||
# Save trace setting
|
||||
XTRACE=$(set +o | grep xtrace)
|
||||
@ -90,6 +92,14 @@ function install_apic_ml2 {
|
||||
mv $APICML2_DIR/_test-requirements.txt $APICML2_DIR/test-requirements.txt
|
||||
}
|
||||
|
||||
function install_apicapi {
|
||||
git_clone $APICAPI_REPO $APICAPI_DIR $APICAPI_BRANCH
|
||||
mv $APICAPI_DIR/test-requirements.txt $APICAPI_DIR/_test-requirements.txt
|
||||
touch $APICAPI_DIR/setup.cfg
|
||||
setup_develop $APICAPI_DIR
|
||||
mv $APICAPI_DIR/_test-requirements.txt $APICAPI_DIR/test-requirements.txt
|
||||
}
|
||||
|
||||
|
||||
# Restore xtrace
|
||||
$XTRACE
|
||||
|
@ -9,5 +9,6 @@ if [[ $ENABLE_APIC_AIM = True ]]; then
|
||||
Q_ML2_TENANT_NETWORK_TYPE=${Q_ML2_TENANT_NETWORK_TYPE:-opflex}
|
||||
Q_ML2_PLUGIN_TYPE_DRIVERS=${Q_ML2_PLUGIN_TYPE_DRIVERS:-local,vlan,opflex}
|
||||
Q_ML2_PLUGIN_MECHANISM_DRIVERS=${Q_ML2_PLUGIN_MECHANISM_DRIVERS:-apic_aim}
|
||||
Q_ML2_PLUGIN_EXT_DRIVERS=${Q_ML2_PLUGIN_EXT_DRIVERS-apic_aim,port_security}
|
||||
Q_ML2_PLUGIN_EXT_DRIVERS=${Q_ML2_PLUGIN_EXT_DRIVERS:-apic_aim,port_security}
|
||||
ML2_L3_PLUGIN=${ML2_L3_PLUGIN:-apic_aim_l3}
|
||||
fi
|
||||
|
@ -9,8 +9,9 @@ ENABLE_NFP=${ENABLE_NFP:-False}
|
||||
# VM locations
|
||||
ConfiguratorQcow2Image=${ConfiguratorQcow2Image:-build}
|
||||
|
||||
# Enable necessary Neutron plugins, including group_policy and ncp
|
||||
Q_SERVICE_PLUGIN_CLASSES=neutron.services.l3_router.l3_router_plugin.L3RouterPlugin,group_policy,ncp
|
||||
# Enable necessary Neutron plugins, including group_policy and ncp (L3
|
||||
# is set via ML2_L3_PLUGIN, so isn't listed here).
|
||||
Q_SERVICE_PLUGIN_CLASSES=group_policy,ncp
|
||||
|
||||
# Preferred git mirror
|
||||
GIT_BASE=${GIT_BASE:-https://git.openstack.org}
|
||||
@ -27,6 +28,7 @@ GBPHEAT_BRANCH=${GBPHEAT_BRANCH:-master}
|
||||
AIM_BRANCH=${AIM_BRANCH:-master}
|
||||
APICML2_BRANCH=${APICML2_BRANCH:-master}
|
||||
OPFLEX_BRANCH=${OPFLEX_BRANCH:-master}
|
||||
APICAPI_BRANCH=${APICAPI_BRANCH:-aci_integration_manager}
|
||||
|
||||
# Enable necessary services, including group-policy (and disable others)
|
||||
disable_service n-net
|
||||
|
@ -23,7 +23,6 @@ DIST_NAMES = 'apic:distinguished_names'
|
||||
SYNC_STATE = 'apic:synchronization_state'
|
||||
|
||||
BD = 'BridgeDomain'
|
||||
CTX = 'Context'
|
||||
EPG = 'EndpointGroup'
|
||||
SUBNET = 'Subnet'
|
||||
VRF = 'VRF'
|
@ -12,11 +12,11 @@
|
||||
|
||||
from neutron.api import extensions
|
||||
|
||||
from gbpservice.neutron.extensions import cisco_apic
|
||||
from gbpservice.neutron.extensions import group_policy as gp
|
||||
|
||||
ALIAS = 'cisco-apic-gbp'
|
||||
|
||||
AIM_DRIVER_EXT = 'aim-driver-extensions'
|
||||
DIST_NAMES = 'apic:distinguished_names'
|
||||
FORWARD_FILTER_ENTRIES = 'Forward-FilterEntries'
|
||||
REVERSE_FILTER_ENTRIES = 'Reverse-FilterEntries'
|
||||
CONTRACT = 'Contract'
|
||||
@ -24,38 +24,34 @@ CONTRACT_SUBJECT = 'ContractSubject'
|
||||
|
||||
EXTENDED_ATTRIBUTES_2_0 = {
|
||||
gp.POLICY_TARGET_GROUPS: {
|
||||
DIST_NAMES: {
|
||||
cisco_apic.DIST_NAMES: {
|
||||
'allow_post': False, 'allow_put': False, 'is_visible': True},
|
||||
},
|
||||
gp.POLICY_RULES: {
|
||||
DIST_NAMES: {
|
||||
cisco_apic.DIST_NAMES: {
|
||||
'allow_post': False, 'allow_put': False, 'is_visible': True},
|
||||
},
|
||||
gp.POLICY_RULE_SETS: {
|
||||
DIST_NAMES: {
|
||||
cisco_apic.DIST_NAMES: {
|
||||
'allow_post': False, 'allow_put': False, 'is_visible': True},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class Aim_driver_ext(extensions.ExtensionDescriptor):
|
||||
class Cisco_apic_gbp(extensions.ExtensionDescriptor):
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "Extensions for AIM driver"
|
||||
return "Cisco APIC GBP"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return AIM_DRIVER_EXT
|
||||
return ALIAS
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return _("Adds AIM driver specific attributes to GBP resources.")
|
||||
|
||||
@classmethod
|
||||
def get_namespace(cls):
|
||||
return ("http://docs.openstack.org/ext/neutron/grouppolicy/"
|
||||
"aim_driver_ext/api/v1.0")
|
||||
return _("Extension exposing mapping of GBP resources to Cisco "
|
||||
"APIC constructs")
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
54
gbpservice/neutron/extensions/cisco_apic_l3.py
Normal file
54
gbpservice/neutron/extensions/cisco_apic_l3.py
Normal file
@ -0,0 +1,54 @@
|
||||
# Copyright (c) 2016 Cisco Systems Inc.
|
||||
# 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.api import extensions
|
||||
from neutron.extensions import l3
|
||||
|
||||
from gbpservice.neutron.extensions import cisco_apic
|
||||
|
||||
ALIAS = 'cisco-apic-l3'
|
||||
|
||||
CONTRACT = 'Contract'
|
||||
CONTRACT_SUBJECT = 'ContractSubject'
|
||||
|
||||
EXTENDED_ATTRIBUTES_2_0 = {
|
||||
l3.ROUTERS: cisco_apic.APIC_ATTRIBUTES
|
||||
}
|
||||
|
||||
|
||||
class Cisco_apic_l3(extensions.ExtensionDescriptor):
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "Cisco APIC L3"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return ALIAS
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return ("Extension exposing mapping of Neutron L3 resources to Cisco "
|
||||
"APIC constructs")
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2016-09-06T12:00:00-00:00"
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version == "2.0":
|
||||
return EXTENDED_ATTRIBUTES_2_0
|
||||
else:
|
||||
return {}
|
@ -23,6 +23,7 @@ LOG = None
|
||||
NAME_TYPE_TENANT = 'tenant'
|
||||
NAME_TYPE_NETWORK = 'network'
|
||||
NAME_TYPE_ADDRESS_SCOPE = 'address_scope'
|
||||
NAME_TYPE_ROUTER = 'router'
|
||||
NAME_TYPE_POLICY_TARGET_GROUP = 'policy_target_group'
|
||||
NAME_TYPE_L3_POLICY = 'l3_policy'
|
||||
NAME_TYPE_L2_POLICY = 'l2_policy'
|
||||
@ -151,6 +152,10 @@ class APICNameMapper(object):
|
||||
address_scope_name=None):
|
||||
return address_scope_name
|
||||
|
||||
@mapper(NAME_TYPE_ROUTER)
|
||||
def router(self, session, router_id, router_name=None):
|
||||
return router_name
|
||||
|
||||
@mapper(NAME_TYPE_POLICY_TARGET_GROUP)
|
||||
def policy_target_group(self, session, policy_target_group_id,
|
||||
policy_target_group_name=None):
|
||||
|
@ -18,9 +18,8 @@ from neutron.api import extensions
|
||||
from neutron import manager as n_manager
|
||||
from oslo_log import log
|
||||
|
||||
from gbpservice.neutron import extensions as extensions_pkg
|
||||
from gbpservice.neutron.plugins.ml2plus import driver_api as api_plus
|
||||
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import (
|
||||
extensions as extensions_pkg)
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
@ -31,17 +31,20 @@ from opflexagent import constants as ofcst
|
||||
from opflexagent import rpc as o_rpc
|
||||
from oslo_log import log
|
||||
|
||||
from gbpservice.neutron.extensions import cisco_apic
|
||||
from gbpservice.neutron.extensions import cisco_apic_l3
|
||||
from gbpservice.neutron.plugins.ml2plus import driver_api as api_plus
|
||||
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import apic_mapper
|
||||
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import cache
|
||||
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim.extensions import (
|
||||
cisco_apic)
|
||||
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import model
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
AP_NAME = 'NeutronAP'
|
||||
ANY_FILTER_NAME = 'AnyFilter'
|
||||
ANY_FILTER_ENTRY_NAME = 'AnyFilterEntry'
|
||||
UNROUTED_VRF_NAME = 'UnroutedVRF'
|
||||
COMMON_TENANT_NAME = 'common'
|
||||
ROUTER_SUBJECT_NAME = 'route'
|
||||
AGENT_TYPE_DVS = 'DVS agent'
|
||||
VIF_TYPE_DVS = 'dvs'
|
||||
PROMISCUOUS_TYPES = [n_constants.DEVICE_OWNER_DHCP,
|
||||
@ -79,9 +82,9 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
|
||||
|
||||
self.project_name_cache.ensure_project(tenant_id)
|
||||
|
||||
# TODO(rkukura): Move the following to precommit methods so
|
||||
# AIM tenants and application profiles are created whenever
|
||||
# needed.
|
||||
# TODO(rkukura): Move the following to calls made from
|
||||
# precommit methods so AIM Tenants, ApplicationProfiles, and
|
||||
# Filters are [re]created whenever needed.
|
||||
session = plugin_context.session
|
||||
with session.begin(subtransactions=True):
|
||||
project_name = self.project_name_cache.get_project_name(tenant_id)
|
||||
@ -103,6 +106,19 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
|
||||
if not self.aim.get(aim_ctx, ap):
|
||||
self.aim.create(aim_ctx, ap)
|
||||
|
||||
filter = aim_resource.Filter(tenant_name=tenant_aname,
|
||||
name=ANY_FILTER_NAME,
|
||||
display_name='Any Filter')
|
||||
if not self.aim.get(aim_ctx, filter):
|
||||
self.aim.create(aim_ctx, filter)
|
||||
|
||||
entry = aim_resource.FilterEntry(tenant_name=tenant_aname,
|
||||
filter_name=ANY_FILTER_NAME,
|
||||
name=ANY_FILTER_ENTRY_NAME,
|
||||
display_name='Any FilterEntry')
|
||||
if not self.aim.get(aim_ctx, entry):
|
||||
self.aim.create(aim_ctx, entry)
|
||||
|
||||
def create_network_precommit(self, context):
|
||||
LOG.debug("APIC AIM MD creating network: %s", context.current)
|
||||
|
||||
@ -124,22 +140,20 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
|
||||
|
||||
vrf = self._get_unrouted_vrf(aim_ctx)
|
||||
|
||||
bd = aim_resource.BridgeDomain(
|
||||
tenant_name=tenant_aname,
|
||||
name=aname,
|
||||
display_name=dname,
|
||||
vrf_name=vrf.name,
|
||||
enable_arp_flood=True,
|
||||
enable_routing=False,
|
||||
limit_ip_learn_to_subnets=True)
|
||||
bd = aim_resource.BridgeDomain(tenant_name=tenant_aname,
|
||||
name=aname,
|
||||
display_name=dname,
|
||||
vrf_name=vrf.name,
|
||||
enable_arp_flood=True,
|
||||
enable_routing=False,
|
||||
limit_ip_learn_to_subnets=True)
|
||||
self.aim.create(aim_ctx, bd)
|
||||
|
||||
epg = aim_resource.EndpointGroup(
|
||||
tenant_name=tenant_aname,
|
||||
app_profile_name=AP_NAME,
|
||||
name=aname,
|
||||
display_name=dname,
|
||||
bd_name=aname)
|
||||
epg = aim_resource.EndpointGroup(tenant_name=tenant_aname,
|
||||
app_profile_name=AP_NAME,
|
||||
name=aname,
|
||||
display_name=dname,
|
||||
bd_name=aname)
|
||||
self.aim.create(aim_ctx, epg)
|
||||
|
||||
def update_network_precommit(self, context):
|
||||
@ -268,13 +282,13 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
|
||||
LOG.debug("Mapped address_scope_id %(id)s with name %(name)s to "
|
||||
"%(aname)s",
|
||||
{'id': id, 'name': name, 'aname': aname})
|
||||
dname = aim_utils.sanitize_display_name(name)
|
||||
|
||||
aim_ctx = aim_context.AimContext(session)
|
||||
|
||||
vrf = aim_resource.VRF(
|
||||
tenant_name=tenant_aname,
|
||||
name=aname,
|
||||
display_name=aim_utils.sanitize_display_name(name))
|
||||
vrf = aim_resource.VRF(tenant_name=tenant_aname,
|
||||
name=aname,
|
||||
display_name=dname)
|
||||
self.aim.create(aim_ctx, vrf)
|
||||
|
||||
# ML2Plus does not extend address scope dict after precommit.
|
||||
@ -358,6 +372,149 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
|
||||
result[cisco_apic.DIST_NAMES] = {cisco_apic.VRF: vrf.dn}
|
||||
result[cisco_apic.SYNC_STATE] = sync_state
|
||||
|
||||
def create_router(self, context, current):
|
||||
LOG.debug("APIC AIM MD creating router: %s", current)
|
||||
|
||||
session = context.session
|
||||
|
||||
tenant_id = current['tenant_id']
|
||||
tenant_aname = self.name_mapper.tenant(session, tenant_id)
|
||||
LOG.debug("Mapped tenant_id %(id)s to %(aname)s",
|
||||
{'id': tenant_id, 'aname': tenant_aname})
|
||||
|
||||
id = current['id']
|
||||
name = current['name']
|
||||
aname = self.name_mapper.router(session, id, name)
|
||||
LOG.debug("Mapped router_id %(id)s with name %(name)s to "
|
||||
"%(aname)s",
|
||||
{'id': id, 'name': name, 'aname': aname})
|
||||
dname = aim_utils.sanitize_display_name(name)
|
||||
|
||||
aim_ctx = aim_context.AimContext(session)
|
||||
|
||||
contract = aim_resource.Contract(tenant_name=tenant_aname,
|
||||
name=aname,
|
||||
display_name=dname)
|
||||
self.aim.create(aim_ctx, contract)
|
||||
|
||||
subject = aim_resource.ContractSubject(tenant_name=tenant_aname,
|
||||
contract_name=aname,
|
||||
name=ROUTER_SUBJECT_NAME,
|
||||
display_name=dname,
|
||||
bi_filters=[ANY_FILTER_NAME])
|
||||
self.aim.create(aim_ctx, subject)
|
||||
|
||||
# REVISIT(rkukura): Consider having L3 plugin extend router
|
||||
# dict again after calling this function.
|
||||
sync_state = cisco_apic.SYNC_SYNCED
|
||||
sync_state = self._merge_status(aim_ctx, sync_state, contract)
|
||||
sync_state = self._merge_status(aim_ctx, sync_state, subject)
|
||||
current[cisco_apic.DIST_NAMES] = {cisco_apic_l3.CONTRACT: contract.dn,
|
||||
cisco_apic_l3.CONTRACT_SUBJECT:
|
||||
subject.dn}
|
||||
current[cisco_apic.SYNC_STATE] = sync_state
|
||||
|
||||
def update_router(self, context, current, original):
|
||||
LOG.debug("APIC AIM MD updating router: %s", current)
|
||||
|
||||
if current['name'] != original['name']:
|
||||
session = context.session
|
||||
|
||||
tenant_id = current['tenant_id']
|
||||
tenant_aname = self.name_mapper.tenant(session, tenant_id)
|
||||
LOG.debug("Mapped tenant_id %(id)s to %(aname)s",
|
||||
{'id': tenant_id, 'aname': tenant_aname})
|
||||
|
||||
id = current['id']
|
||||
name = current['name']
|
||||
aname = self.name_mapper.router(session, id, name)
|
||||
LOG.debug("Mapped router_id %(id)s with name %(name)s to "
|
||||
"%(aname)s",
|
||||
{'id': id, 'name': name, 'aname': aname})
|
||||
dname = aim_utils.sanitize_display_name(name)
|
||||
|
||||
aim_ctx = aim_context.AimContext(session)
|
||||
|
||||
contract = aim_resource.Contract(tenant_name=tenant_aname,
|
||||
name=aname)
|
||||
contract = self.aim.update(aim_ctx, contract, display_name=dname)
|
||||
|
||||
subject = aim_resource.ContractSubject(tenant_name=tenant_aname,
|
||||
contract_name=aname,
|
||||
name=ROUTER_SUBJECT_NAME)
|
||||
subject = self.aim.update(aim_ctx, subject, display_name=dname)
|
||||
|
||||
# REVISIT(rkukura): Update extension attributes?
|
||||
|
||||
def delete_router(self, context, current):
|
||||
LOG.debug("APIC AIM MD deleting router: %s", current)
|
||||
|
||||
session = context.session
|
||||
|
||||
tenant_id = current['tenant_id']
|
||||
tenant_aname = self.name_mapper.tenant(session, tenant_id)
|
||||
LOG.debug("Mapped tenant_id %(id)s to %(aname)s",
|
||||
{'id': tenant_id, 'aname': tenant_aname})
|
||||
|
||||
id = current['id']
|
||||
name = current['name']
|
||||
aname = self.name_mapper.router(session, id, name)
|
||||
LOG.debug("Mapped router_id %(id)s with name %(name)s to "
|
||||
"%(aname)s",
|
||||
{'id': id, 'name': name, 'aname': aname})
|
||||
|
||||
aim_ctx = aim_context.AimContext(session)
|
||||
|
||||
subject = aim_resource.ContractSubject(tenant_name=tenant_aname,
|
||||
contract_name=aname,
|
||||
name=ROUTER_SUBJECT_NAME)
|
||||
self.aim.delete(aim_ctx, subject)
|
||||
|
||||
contract = aim_resource.Contract(tenant_name=tenant_aname,
|
||||
name=aname)
|
||||
self.aim.delete(aim_ctx, contract)
|
||||
|
||||
self.name_mapper.delete_apic_name(session, id)
|
||||
|
||||
def extend_router_dict(self, session, base_model, result):
|
||||
LOG.debug("APIC AIM MD extending dict for router: %s", result)
|
||||
|
||||
tenant_id = result['tenant_id']
|
||||
tenant_aname = self.name_mapper.tenant(session, tenant_id)
|
||||
LOG.debug("Mapped tenant_id %(id)s to %(aname)s",
|
||||
{'id': tenant_id, 'aname': tenant_aname})
|
||||
|
||||
id = result['id']
|
||||
name = result['name']
|
||||
aname = self.name_mapper.router(session, id, name)
|
||||
LOG.debug("Mapped router_id %(id)s with name %(name)s to "
|
||||
"%(aname)s",
|
||||
{'id': id, 'name': name, 'aname': aname})
|
||||
|
||||
contract = aim_resource.Contract(tenant_name=tenant_aname,
|
||||
name=aname)
|
||||
|
||||
subject = aim_resource.ContractSubject(tenant_name=tenant_aname,
|
||||
contract_name=aname,
|
||||
name=ROUTER_SUBJECT_NAME)
|
||||
|
||||
aim_ctx = aim_context.AimContext(session)
|
||||
sync_state = cisco_apic.SYNC_SYNCED
|
||||
sync_state = self._merge_status(aim_ctx, sync_state, contract)
|
||||
sync_state = self._merge_status(aim_ctx, sync_state, subject)
|
||||
result[cisco_apic.DIST_NAMES] = {cisco_apic_l3.CONTRACT: contract.dn,
|
||||
cisco_apic_l3.CONTRACT_SUBJECT:
|
||||
subject.dn}
|
||||
result[cisco_apic.SYNC_STATE] = sync_state
|
||||
|
||||
def add_router_interface(self, context, info):
|
||||
LOG.debug("APIC AIM MD adding router interface: %s", info)
|
||||
# TODO(rkukura): Implement.
|
||||
|
||||
def remove_router_interface(self, context, info):
|
||||
LOG.debug("APIC AIM MD removing router interface: %s", info)
|
||||
# TODO(rkukura): Implement.
|
||||
|
||||
def bind_port(self, context):
|
||||
LOG.debug("Attempting to bind port %(port)s on network %(net)s",
|
||||
{'port': context.current['id'],
|
||||
|
148
gbpservice/neutron/services/apic_aim/l3_plugin.py
Normal file
148
gbpservice/neutron/services/apic_aim/l3_plugin.py
Normal file
@ -0,0 +1,148 @@
|
||||
# Copyright (c) 2016 Cisco Systems Inc.
|
||||
# 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._i18n import _LI
|
||||
from neutron.api import extensions
|
||||
from neutron.db import common_db_mixin
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.db import extraroute_db
|
||||
from neutron.db import l3_gwmode_db
|
||||
from neutron.extensions import l3
|
||||
from neutron.plugins.common import constants
|
||||
from oslo_log import log as logging
|
||||
from sqlalchemy import inspect
|
||||
|
||||
from gbpservice.neutron import extensions as extensions_pkg
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ApicL3Plugin(common_db_mixin.CommonDbMixin,
|
||||
extraroute_db.ExtraRoute_db_mixin,
|
||||
l3_gwmode_db.L3_NAT_db_mixin):
|
||||
|
||||
supported_extension_aliases = ["router", "ext-gw-mode", "extraroute",
|
||||
"cisco-apic-l3"]
|
||||
|
||||
@staticmethod
|
||||
def get_plugin_type():
|
||||
return constants.L3_ROUTER_NAT
|
||||
|
||||
@staticmethod
|
||||
def get_plugin_description():
|
||||
return _("L3 Router Service Plugin using the APIC via AIM")
|
||||
|
||||
def __init__(self):
|
||||
LOG.info(_LI("APIC AIM L3 Plugin __init__"))
|
||||
extensions.append_api_extensions_path(extensions_pkg.__path__)
|
||||
self._mechanism_driver = None
|
||||
super(ApicL3Plugin, self).__init__()
|
||||
|
||||
@property
|
||||
def _md(self):
|
||||
if not self._mechanism_driver:
|
||||
# REVISIT(rkukura): It might be safer to search the MDs by
|
||||
# class rather than index by name, or to use a class
|
||||
# variable to find the instance.
|
||||
mech_mgr = self._core_plugin.mechanism_manager
|
||||
self._mechanism_driver = mech_mgr.mech_drivers['apic_aim'].obj
|
||||
return self._mechanism_driver
|
||||
|
||||
def _extend_router_dict_apic(self, router_res, router_db):
|
||||
LOG.debug("APIC AIM L3 Plugin extending router dict: %s", router_res)
|
||||
session = inspect(router_db).session
|
||||
self._md.extend_router_dict(session, router_db, router_res)
|
||||
|
||||
db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
|
||||
l3.ROUTERS, ['_extend_router_dict_apic'])
|
||||
|
||||
def create_router(self, context, router):
|
||||
LOG.debug("APIC AIM L3 Plugin creating router: %s", router)
|
||||
self._md.ensure_tenant(context, router['router']['tenant_id'])
|
||||
with context.session.begin(subtransactions=True):
|
||||
# REVISIT(rkukura): The base operation may create a port,
|
||||
# which should generally not be done inside a
|
||||
# transaction. But we need to ensure atomicity, and are
|
||||
# generally not concerned with mechanism driver postcommit
|
||||
# processing. Consider overriding create_router_db()
|
||||
# instead, and/or reimplementing the base funtionality to
|
||||
# be completely transaction safe.
|
||||
result = super(ApicL3Plugin, self).create_router(context, router)
|
||||
self._md.create_router(context, result)
|
||||
return result
|
||||
|
||||
def update_router(self, context, id, router):
|
||||
LOG.debug("APIC AIM L3 Plugin updating router %(id)s with: %(router)s",
|
||||
{'id': id, 'router': router})
|
||||
with context.session.begin(subtransactions=True):
|
||||
# REVISIT(rkukura): The base operation sends notification
|
||||
# RPCs, which should generally not be done inside a
|
||||
# transaction. But we need to ensure atomicity, and are
|
||||
# not using an L3 agent. Consider overriding
|
||||
# create_router_db() instead, and/or reimplementing the
|
||||
# base funtionality to be completely transaction safe.
|
||||
original = self.get_router(context, id)
|
||||
result = super(ApicL3Plugin, self).update_router(context, id,
|
||||
router)
|
||||
self._md.update_router(context, result, original)
|
||||
return result
|
||||
|
||||
def delete_router(self, context, id):
|
||||
LOG.debug("APIC AIM L3 Plugin deleting router: %s", id)
|
||||
with context.session.begin(subtransactions=True):
|
||||
# REVISIT(rkukura): The base operation may delete ports
|
||||
# and sends notification RPCs, which should generally not
|
||||
# be done inside a transaction. But we need to ensure
|
||||
# atomicity, are not using an L3 agent, and are generally
|
||||
# not concerned with mechanism driver postcommit
|
||||
# processing. Consider reimplementing the base
|
||||
# funtionality to be completely transaction safe.
|
||||
router = self.get_router(context, id)
|
||||
super(ApicL3Plugin, self).delete_router(context, id)
|
||||
self._md.delete_router(context, router)
|
||||
|
||||
def add_router_interface(self, context, router_id, interface_info):
|
||||
LOG.debug("APIC AIM L3 Plugin adding interface %(interface)s "
|
||||
"to router %(router)s",
|
||||
{'interface': interface_info, 'router': router_id})
|
||||
with context.session.begin(subtransactions=True):
|
||||
# REVISIT(rkukura): The base operation may create or
|
||||
# update a port and sends notification RPCs, which should
|
||||
# generally not be done inside a transaction. But we need
|
||||
# to ensure atomicity, are not using an L3 agent, and are
|
||||
# generally not concerned with mechanism driver postcommit
|
||||
# processing. Consider reimplementing the base
|
||||
# funtionality to be completely transaction safe.
|
||||
info = super(ApicL3Plugin, self).add_router_interface(
|
||||
context, router_id, interface_info)
|
||||
self._md.add_router_interface(context, info)
|
||||
return info
|
||||
|
||||
def remove_router_interface(self, context, router_id, interface_info):
|
||||
LOG.debug("APIC AIM L3 Plugin removing interface %(interface)s "
|
||||
"from router %(router)s",
|
||||
{'interface': interface_info, 'router': router_id})
|
||||
with context.session.begin(subtransactions=True):
|
||||
# REVISIT(rkukura): The base operation may delete or
|
||||
# update a port and sends notification RPCs, which should
|
||||
# generally not be done inside a transaction. But we need
|
||||
# to ensure atomicity, are not using an L3 agent, and are
|
||||
# generally not concerned with mechanism driver postcommit
|
||||
# processing. Consider reimplementing the base
|
||||
# funtionality to be completely transaction safe.
|
||||
info = super(ApicL3Plugin, self).remove_router_interface(
|
||||
context, router_id, interface_info)
|
||||
self._md.remove_router_interface(context, info)
|
||||
return info
|
@ -18,12 +18,11 @@ from oslo_concurrency import lockutils
|
||||
from oslo_log import helpers as log
|
||||
from oslo_log import log as logging
|
||||
|
||||
from gbpservice.neutron.extensions import aim_driver_ext as aim_ext
|
||||
from gbpservice.neutron.extensions import cisco_apic
|
||||
from gbpservice.neutron.extensions import cisco_apic_gbp as aim_ext
|
||||
from gbpservice.neutron.extensions import group_policy as gpolicy
|
||||
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import (
|
||||
mechanism_driver as aim_md)
|
||||
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim.extensions import (
|
||||
cisco_apic)
|
||||
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import model
|
||||
from gbpservice.neutron.services.grouppolicy.common import (
|
||||
constants as gp_const)
|
||||
|
@ -14,7 +14,7 @@ from neutron._i18n import _LI
|
||||
from neutron import manager as n_manager
|
||||
from oslo_log import log as logging
|
||||
|
||||
from gbpservice.neutron.extensions import aim_driver_ext
|
||||
from gbpservice.neutron.extensions import cisco_apic_gbp
|
||||
from gbpservice.neutron.services.grouppolicy import (
|
||||
group_policy_driver_api as api)
|
||||
|
||||
@ -22,8 +22,8 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AIMExtensionDriver(api.ExtensionDriver):
|
||||
_supported_extension_alias = aim_driver_ext.AIM_DRIVER_EXT
|
||||
_extension_dict = aim_driver_ext.EXTENDED_ATTRIBUTES_2_0
|
||||
_supported_extension_alias = cisco_apic_gbp.ALIAS
|
||||
_extension_dict = cisco_apic_gbp.EXTENDED_ATTRIBUTES_2_0
|
||||
|
||||
def __init__(self):
|
||||
LOG.info(_LI("AIM Extension __init__"))
|
||||
|
@ -22,10 +22,12 @@ from neutron.api import extensions
|
||||
from neutron import context
|
||||
from neutron.db import api as db_api
|
||||
from neutron import manager
|
||||
from neutron.plugins.common import constants as service_constants
|
||||
from neutron.plugins.ml2 import config
|
||||
from neutron.tests.unit.api import test_extensions
|
||||
from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin
|
||||
from neutron.tests.unit.extensions import test_address_scope
|
||||
from neutron.tests.unit.extensions import test_l3
|
||||
from opflexagent import constants as ofcst
|
||||
|
||||
PLUGIN_NAME = 'gbpservice.neutron.plugins.ml2plus.plugin.Ml2PlusPlugin'
|
||||
@ -66,7 +68,8 @@ class FakeKeystoneClient(object):
|
||||
self.projects = FakeProjectManager()
|
||||
|
||||
|
||||
class ApicAimTestCase(test_address_scope.AddressScopeTestCase):
|
||||
class ApicAimTestCase(test_address_scope.AddressScopeTestCase,
|
||||
test_l3.L3NatTestCaseMixin):
|
||||
def setUp(self):
|
||||
# Enable the test mechanism driver to ensure that
|
||||
# we can successfully call through to all mechanism
|
||||
@ -87,7 +90,12 @@ class ApicAimTestCase(test_address_scope.AddressScopeTestCase):
|
||||
['physnet1:1000:1099'],
|
||||
group='ml2_type_vlan')
|
||||
|
||||
super(ApicAimTestCase, self).setUp(PLUGIN_NAME)
|
||||
service_plugins = {
|
||||
'L3_ROUTER_NAT':
|
||||
'gbpservice.neutron.services.apic_aim.l3_plugin.ApicL3Plugin'}
|
||||
|
||||
super(ApicAimTestCase, self).setUp(PLUGIN_NAME,
|
||||
service_plugins=service_plugins)
|
||||
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
|
||||
self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr)
|
||||
self.port_create_status = 'DOWN'
|
||||
@ -102,6 +110,8 @@ class ApicAimTestCase(test_address_scope.AddressScopeTestCase):
|
||||
self.plugin.start_rpc_listeners()
|
||||
self.driver = self.plugin.mechanism_manager.mech_drivers[
|
||||
'apic_aim'].obj
|
||||
self.l3_plugin = manager.NeutronManager.get_service_plugins()[
|
||||
service_constants.L3_ROUTER_NAT]
|
||||
self.aim_mgr = aim_manager.AimManager()
|
||||
self._app_profile_name = 'NeutronAP'
|
||||
self._tenant_name = self._map_name({'id': 'test-tenant',
|
||||
@ -109,6 +119,11 @@ class ApicAimTestCase(test_address_scope.AddressScopeTestCase):
|
||||
self._unrouted_vrf_name = 'UnroutedVRF'
|
||||
|
||||
def tearDown(self):
|
||||
engine = db_api.get_engine()
|
||||
with engine.begin() as conn:
|
||||
for table in reversed(
|
||||
aim_model_base.Base.metadata.sorted_tables):
|
||||
conn.execute(table.delete())
|
||||
ksc_client.Client = self.saved_keystone_client
|
||||
super(ApicAimTestCase, self).tearDown()
|
||||
|
||||
@ -167,6 +182,58 @@ class TestAimMapping(ApicAimTestCase):
|
||||
self.assertIsNone(epg)
|
||||
return epg
|
||||
|
||||
def _get_contract(self, contract_name, tenant_name, should_exist=True):
|
||||
session = db_api.get_session()
|
||||
aim_ctx = aim_context.AimContext(session)
|
||||
contract = aim_resource.Contract(tenant_name=tenant_name,
|
||||
name=contract_name)
|
||||
contract = self.aim_mgr.get(aim_ctx, contract)
|
||||
if should_exist:
|
||||
self.assertIsNotNone(contract)
|
||||
else:
|
||||
self.assertIsNone(contract)
|
||||
return contract
|
||||
|
||||
def _get_subject(self, subject_name, contract_name, tenant_name,
|
||||
should_exist=True):
|
||||
session = db_api.get_session()
|
||||
aim_ctx = aim_context.AimContext(session)
|
||||
subject = aim_resource.ContractSubject(tenant_name=tenant_name,
|
||||
contract_name=contract_name,
|
||||
name=subject_name)
|
||||
subject = self.aim_mgr.get(aim_ctx, subject)
|
||||
if should_exist:
|
||||
self.assertIsNotNone(subject)
|
||||
else:
|
||||
self.assertIsNone(subject)
|
||||
return subject
|
||||
|
||||
def _get_filter(self, filter_name, tenant_name, should_exist=True):
|
||||
session = db_api.get_session()
|
||||
aim_ctx = aim_context.AimContext(session)
|
||||
filter = aim_resource.Filter(tenant_name=tenant_name,
|
||||
name=filter_name)
|
||||
filter = self.aim_mgr.get(aim_ctx, filter)
|
||||
if should_exist:
|
||||
self.assertIsNotNone(filter)
|
||||
else:
|
||||
self.assertIsNone(filter)
|
||||
return filter
|
||||
|
||||
def _get_filter_entry(self, entry_name, filter_name, tenant_name,
|
||||
should_exist=True):
|
||||
session = db_api.get_session()
|
||||
aim_ctx = aim_context.AimContext(session)
|
||||
entry = aim_resource.FilterEntry(tenant_name=tenant_name,
|
||||
filter_name=filter_name,
|
||||
name=entry_name)
|
||||
entry = self.aim_mgr.get(aim_ctx, entry)
|
||||
if should_exist:
|
||||
self.assertIsNotNone(entry)
|
||||
else:
|
||||
self.assertIsNone(entry)
|
||||
return entry
|
||||
|
||||
def _check_dn(self, resource, aim_resource, key):
|
||||
dist_names = resource.get('apic:distinguished_names')
|
||||
self.assertIsInstance(dist_names, dict)
|
||||
@ -274,6 +341,69 @@ class TestAimMapping(ApicAimTestCase):
|
||||
self._tenant_name,
|
||||
should_exist=False)
|
||||
|
||||
def _check_router(self, router, orig_router=None):
|
||||
orig_router = orig_router or router
|
||||
|
||||
# REVISIT(rkukura): Check AIM Tenant here?
|
||||
self.assertEqual('test-tenant', router['tenant_id'])
|
||||
|
||||
aname = self._map_name(orig_router)
|
||||
|
||||
aim_contract = self._get_contract(aname, self._tenant_name)
|
||||
self.assertEqual(self._tenant_name, aim_contract.tenant_name)
|
||||
self.assertEqual(aname, aim_contract.name)
|
||||
self.assertEqual(router['name'], aim_contract.display_name)
|
||||
self.assertEqual('context', aim_contract.scope) # REVISIT(rkukura)
|
||||
self._check_dn(router, aim_contract, 'Contract')
|
||||
|
||||
aim_subject = self._get_subject('route', aname, self._tenant_name)
|
||||
self.assertEqual(self._tenant_name, aim_subject.tenant_name)
|
||||
self.assertEqual(aname, aim_subject.contract_name)
|
||||
self.assertEqual('route', aim_subject.name)
|
||||
self.assertEqual(router['name'], aim_subject.display_name)
|
||||
self.assertEqual([], aim_subject.in_filters)
|
||||
self.assertEqual([], aim_subject.out_filters)
|
||||
self.assertEqual(['AnyFilter'], aim_subject.bi_filters)
|
||||
self._check_dn(router, aim_subject, 'ContractSubject')
|
||||
|
||||
self._check_any_filter()
|
||||
# REVISIT(rkukura): Anything else to check?
|
||||
|
||||
def _check_router_deleted(self, router):
|
||||
aname = self._map_name(router)
|
||||
|
||||
self._get_contract(aname, self._tenant_name, should_exist=False)
|
||||
|
||||
self._get_subject('route', aname, self._tenant_name,
|
||||
should_exist=False)
|
||||
|
||||
# REVISIT(rkukura): Anything else to check?
|
||||
|
||||
def _check_any_filter(self):
|
||||
aim_filter = self._get_filter('AnyFilter', self._tenant_name)
|
||||
self.assertEqual(self._tenant_name, aim_filter.tenant_name)
|
||||
self.assertEqual('AnyFilter', aim_filter.name)
|
||||
self.assertEqual('Any Filter', aim_filter.display_name)
|
||||
|
||||
aim_entry = self._get_filter_entry('AnyFilterEntry', 'AnyFilter',
|
||||
self._tenant_name)
|
||||
self.assertEqual(self._tenant_name, aim_entry.tenant_name)
|
||||
self.assertEqual('AnyFilter', aim_entry.filter_name)
|
||||
self.assertEqual('AnyFilterEntry', aim_entry.name)
|
||||
self.assertEqual('Any FilterEntry', aim_entry.display_name)
|
||||
self.assertEqual('unspecified', aim_entry.arp_opcode)
|
||||
self.assertEqual('unspecified', aim_entry.ether_type)
|
||||
self.assertEqual('unspecified', aim_entry.ip_protocol)
|
||||
self.assertEqual('unspecified', aim_entry.icmpv4_type)
|
||||
self.assertEqual('unspecified', aim_entry.icmpv6_type)
|
||||
self.assertEqual('unspecified', aim_entry.source_from_port)
|
||||
self.assertEqual('unspecified', aim_entry.source_to_port)
|
||||
self.assertEqual('unspecified', aim_entry.dest_from_port)
|
||||
self.assertEqual('unspecified', aim_entry.dest_to_port)
|
||||
self.assertEqual('unspecified', aim_entry.tcp_flags)
|
||||
self.assertFalse(aim_entry.stateful)
|
||||
self.assertFalse(aim_entry.fragment_only)
|
||||
|
||||
def test_network_lifecycle(self):
|
||||
# Test create.
|
||||
orig_net = self._make_network(self.fmt, 'net1', True)['network']
|
||||
@ -336,6 +466,58 @@ class TestAimMapping(ApicAimTestCase):
|
||||
self._delete('address-scopes', a_s_id)
|
||||
self._check_address_scope_deleted(orig_a_s)
|
||||
|
||||
def test_router_lifecycle(self):
|
||||
# Test create.
|
||||
orig_router = self._make_router(
|
||||
self.fmt, 'test-tenant', 'router1')['router']
|
||||
router_id = orig_router['id']
|
||||
self._check_router(orig_router)
|
||||
|
||||
# Test show.
|
||||
router = self._show('routers', router_id)['router']
|
||||
self._check_router(router)
|
||||
|
||||
# Test update.
|
||||
data = {'router': {'name': 'newnameforrouter'}}
|
||||
router = self._update('routers', router_id, data)['router']
|
||||
self._check_router(router, orig_router)
|
||||
|
||||
# Test delete.
|
||||
self._delete('routers', router_id)
|
||||
self._check_router_deleted(orig_router)
|
||||
|
||||
def test_router_interface(self):
|
||||
# Create router.
|
||||
router = self._make_router(
|
||||
self.fmt, 'test-tenant', 'router1')['router']
|
||||
router_id = router['id']
|
||||
self._check_router(router)
|
||||
|
||||
# Create network.
|
||||
net_resp = self._make_network(self.fmt, 'net1', True)
|
||||
self._check_unrouted_network(net_resp['network'])
|
||||
|
||||
# Create subnet.
|
||||
subnet = self._make_subnet(self.fmt, net_resp, '10.0.0.1',
|
||||
'10.0.0.0/24')['subnet']
|
||||
subnet_id = subnet['id']
|
||||
self._check_unrouted_subnet(subnet)
|
||||
|
||||
# Add subnet to router.
|
||||
info = self.l3_plugin.add_router_interface(
|
||||
context.get_admin_context(), router_id, {'subnet_id': subnet_id})
|
||||
self.assertIn(subnet_id, info['subnet_ids'])
|
||||
self._check_router(router)
|
||||
|
||||
# TODO(rkukura): Check router and subnet extension attributes,
|
||||
# BD, EPG, etc..
|
||||
|
||||
# Remove subnet from router.
|
||||
info = self.l3_plugin.remove_router_interface(
|
||||
context.get_admin_context(), router_id, {'subnet_id': subnet_id})
|
||||
self.assertIn(subnet_id, info['subnet_ids'])
|
||||
self._check_router(router)
|
||||
|
||||
# def test_create_subnet_with_address_scope(self):
|
||||
# net = self._make_network(self.fmt, 'net1', True)
|
||||
# name = self._map_name(net['network'])
|
||||
|
@ -353,11 +353,11 @@ class TestL2PolicyBase(test_nr_base.TestL2Policy, AIMBaseTestCase):
|
||||
aim_filters = self.aim_mgr.find(
|
||||
self._aim_context, aim_resource.Filter,
|
||||
tenant_name=aim_tenant_name)
|
||||
self.assertEqual(9, len(aim_filters))
|
||||
self.assertEqual(10, len(aim_filters)) # 1 belongs to MD
|
||||
aim_filter_entries = self.aim_mgr.find(
|
||||
self._aim_context, aim_resource.FilterEntry,
|
||||
tenant_name=aim_tenant_name)
|
||||
self.assertEqual(9, len(aim_filter_entries))
|
||||
self.assertEqual(10, len(aim_filter_entries)) # 1 belongs to MD
|
||||
entries_attrs = alib.get_service_contract_filter_entries().values()
|
||||
entries_attrs.extend(alib.get_arp_filter_entry().values())
|
||||
expected_entries_attrs = []
|
||||
@ -369,9 +369,11 @@ class TestL2PolicyBase(test_nr_base.TestL2Policy, AIMBaseTestCase):
|
||||
entries_attrs = [x.__dict__ for x in aim_filter_entries]
|
||||
observed_entries_attrs = []
|
||||
for entry in entries_attrs:
|
||||
observed_entries_attrs.append(
|
||||
{k: unicode(entry[k]) for k in entry if k not in [
|
||||
'name', 'display_name', 'filter_name', 'tenant_name']})
|
||||
# Ignore entry belonging to MD's filter.
|
||||
if entry['filter_name'] != 'AnyFilter':
|
||||
observed_entries_attrs.append(
|
||||
{k: unicode(entry[k]) for k in entry if k not in [
|
||||
'name', 'display_name', 'filter_name', 'tenant_name']})
|
||||
self.assertItemsEqual(expected_entries_attrs, observed_entries_attrs)
|
||||
|
||||
|
||||
@ -398,11 +400,11 @@ class TestL2Policy(TestL2PolicyBase):
|
||||
aim_filters = self.aim_mgr.find(
|
||||
self._aim_context, aim_resource.Filter,
|
||||
tenant_name=aim_tenant_name)
|
||||
self.assertEqual(0, len(aim_filters))
|
||||
self.assertEqual(1, len(aim_filters)) # belongs to MD
|
||||
aim_filter_entries = self.aim_mgr.find(
|
||||
self._aim_context, aim_resource.FilterEntry,
|
||||
tenant_name=aim_tenant_name)
|
||||
self.assertEqual(0, len(aim_filter_entries))
|
||||
self.assertEqual(1, len(aim_filter_entries)) # belongs to MD
|
||||
|
||||
def test_l2_policy_lifecycle(self):
|
||||
self.assertEqual(0, len(self.aim_mgr.find(
|
||||
@ -482,11 +484,11 @@ class TestL2PolicyRollback(TestL2PolicyBase):
|
||||
aim_filters = self.aim_mgr.find(
|
||||
self._aim_context, aim_resource.Filter,
|
||||
tenant_name=aim_tenant_name)
|
||||
self.assertEqual(0, len(aim_filters))
|
||||
self.assertEqual(1, len(aim_filters)) # belongs to MD
|
||||
aim_filter_entries = self.aim_mgr.find(
|
||||
self._aim_context, aim_resource.FilterEntry,
|
||||
tenant_name=aim_tenant_name)
|
||||
self.assertEqual(0, len(aim_filter_entries))
|
||||
self.assertEqual(1, len(aim_filter_entries)) # belongs to MD
|
||||
# restore mock
|
||||
self.dummy.create_l2_policy_precommit = orig_func
|
||||
|
||||
@ -952,10 +954,10 @@ class TestPolicyRuleRollback(TestPolicyRuleBase):
|
||||
self._gbp_plugin.get_policy_rules(self._context))
|
||||
aim_filters = self.aim_mgr.find(
|
||||
self._aim_context, aim_resource.Filter)
|
||||
self.assertEqual(0, len(aim_filters))
|
||||
self.assertEqual(1, len(aim_filters)) # belongs to MD
|
||||
aim_filter_entries = self.aim_mgr.find(
|
||||
self._aim_context, aim_resource.FilterEntry)
|
||||
self.assertEqual(0, len(aim_filter_entries))
|
||||
self.assertEqual(1, len(aim_filter_entries)) # belongs to MD
|
||||
# restore mock
|
||||
self.dummy.create_policy_rule_precommit = orig_func
|
||||
|
||||
|
@ -54,6 +54,7 @@ neutron.service_plugins =
|
||||
ncp = gbpservice.neutron.services.servicechain.plugins.ncp.plugin:NodeCompositionPlugin
|
||||
apic_gbp_l3 = gbpservice.neutron.services.l3_router.l3_apic:ApicGBPL3ServicePlugin
|
||||
nfp_fwaas = gbpservice.contrib.nfp.service_plugins.firewall.nfp_fwaas_plugin.NFPFirewallPlugin
|
||||
apic_aim_l3 = gbpservice.neutron.services.apic_aim.l3_plugin:ApicL3Plugin
|
||||
gbpservice.neutron.group_policy.extension_drivers =
|
||||
test = gbpservice.neutron.tests.unit.services.grouppolicy.test_extension_driver_api:TestExtensionDriver
|
||||
proxy_group = gbpservice.neutron.services.grouppolicy.drivers.extensions.proxy_group_driver:ProxyGroupDriver
|
||||
|
@ -4,7 +4,8 @@
|
||||
|
||||
setuptools>=19.2
|
||||
-e git+https://git.openstack.org/openstack/neutron.git@stable/mitaka#egg=neutron
|
||||
-e git+https://github.com/noironetworks/apicapi.git@master#egg=apicapi
|
||||
# TODO(rkukura): Switch apicapi back to master branch eventually.
|
||||
-e git+https://github.com/noironetworks/apicapi.git@aci_integration_manager#egg=apicapi
|
||||
-e git+https://github.com/noironetworks/python-opflex-agent.git@master#egg=python-opflexagent-agent
|
||||
-e git+https://github.com/noironetworks/apic-ml2-driver.git@master#egg=apic_ml2
|
||||
-e git+https://git.openstack.org/openstack/python-group-based-policy-client@master#egg=gbpclient
|
||||
|
Loading…
Reference in New Issue
Block a user