Scope common App Profile with the system ID

For compatibility on multi OS instances per APIC

Change-Id: Iaf93f24466889a7f2a1e84605ca59b6e9ad5ec56
This commit is contained in:
Ivar Lazzaro 2017-10-24 18:32:18 -07:00
parent 599f9c70e2
commit f8b41855ac
No known key found for this signature in database
GPG Key ID: ACEEC8CB558DC3CF
8 changed files with 240 additions and 13 deletions

View File

@ -1 +1 @@
d978a7a73785
facc1ababba1

View File

@ -0,0 +1,46 @@
# 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.
#
"""change common ap name
Revision ID: facc1ababba1
Revises: d978a7a73785
Create Date: 2017-05-15 00:00:00.000000
"""
# revision identifiers, used by Alembic.
revision = 'facc1ababba1'
down_revision = 'd978a7a73785'
from alembic import op
import sqlalchemy as sa
def upgrade():
# See if AIM is being used, and if so, migrate data.
bind = op.get_bind()
insp = sa.engine.reflection.Inspector.from_engine(bind)
if 'aim_tenants' in insp.get_table_names():
# Note - this cannot be imported unless we know the
# apic_aim mechanism driver is deployed, since the AIM
# library may not be installed.
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import (
data_migrations)
session = sa.orm.Session(bind=bind, autocommit=True)
data_migrations.do_ap_name_change(session)
def downgrade():
pass

View File

@ -13,6 +13,13 @@
from neutron.db.migration.cli import * # noqa
global_opts = [
cfg.StrOpt('apic_system_id',
help="Prefix for APIC domain/names/profiles created"),
]
CONF.register_cli_opts(global_opts)
def main():
config = alembic_config.Config(
os.path.join(os.path.dirname(__file__), 'alembic.ini'))

View File

@ -19,10 +19,13 @@ from aim.api import resource as aim_resource
from aim import context as aim_context
from alembic import util as alembic_util
from neutron.db.models import address_scope as as_db
from neutron.db.migration.cli import * # noqa
from neutron.db import models_v2
from neutron.db import segments_db # noqa
from neutron_lib.db import model_base
import sqlalchemy as sa
from gbpservice.neutron.extensions import cisco_apic as ext
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import apic_mapper
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import db
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import extension_db
@ -149,3 +152,81 @@ def do_apic_aim_persist_migration(session):
alembic_util.msg(
"Finished data migration for apic_aim mechanism driver persistence.")
def do_ap_name_change(session, conf=None):
alembic_util.msg("Starting data migration for apic_aim ap name change.")
cfg = conf or CONF
aim = aim_manager.AimManager()
aim_ctx = aim_context.AimContext(session)
system_id = cfg.apic_system_id
alembic_util.msg("APIC System ID: %s" % system_id)
ext_mixin = extension_db.ExtensionDbMixin()
db_mixin = db.DbMixin()
with session.begin(subtransactions=True):
net_dbs = session.query(models_v2.Network).all()
for net_db in net_dbs:
ext_db = ext_mixin.get_network_extn_db(session, net_db.id)
if ext_db and ext_db[ext.EXTERNAL_NETWORK]:
alembic_util.msg("Migrating external network: %s" % net_db)
# Its a managed external network.
ext_net = aim_resource.ExternalNetwork.from_dn(
ext_db[ext.EXTERNAL_NETWORK])
ext_net = aim.get(aim_ctx, ext_net)
l3out = aim_resource.L3Outside(tenant_name=ext_net.tenant_name,
name=ext_net.l3out_name)
if ext_db[ext.NAT_TYPE] == '':
ns_cls = nat_strategy.NoNatStrategy
elif ext_db[ext.NAT_TYPE] == 'edge':
ns_cls = nat_strategy.EdgeNatStrategy
else:
ns_cls = nat_strategy.DistributedNatStrategy
clone_ext_nets = {}
ns = ns_cls(aim)
ns.app_profile_name = 'OpenStack'
ns.common_scope = None
# Start Cleanup
if not isinstance(ns, nat_strategy.NoNatStrategy):
l3out_clones = ns.db.get_clones(aim_ctx, l3out)
# Retrieve External Networks
for l3out_clone in l3out_clones:
for extc in aim.find(
aim_ctx, aim_resource.ExternalNetwork,
tenant_name=l3out_clone[0],
l3out_name=l3out_clone[1]):
clone_ext_nets[(l3out.tenant_name,
l3out.name,
extc.name)] = extc
vrfs = ns.read_vrfs(aim_ctx, ext_net)
session.query(db.NetworkMapping).filter(
db.NetworkMapping.network_id == net_db.id).delete()
for vrf in vrfs:
ns.disconnect_vrf(aim_ctx, ext_net, vrf)
ns.delete_external_network(aim_ctx, ext_net)
ns.delete_l3outside(aim_ctx, l3out)
# Recreate
ns.common_scope = system_id
ns.create_l3outside(aim_ctx, l3out)
ns.create_external_network(aim_ctx, ext_net)
ns.update_external_cidrs(aim_ctx, ext_net,
ext_db[ext.EXTERNAL_CIDRS])
for subnet in net_db.subnets:
aim_subnet = aim_resource.Subnet.to_gw_ip_mask(
subnet.gateway_ip, int(subnet.cidr.split('/')[1]))
ns.create_subnet(aim_ctx, l3out, aim_subnet)
for resource in ns.get_l3outside_resources(aim_ctx, l3out):
if isinstance(resource, aim_resource.BridgeDomain):
bd = resource
elif isinstance(resource, aim_resource.EndpointGroup):
epg = resource
elif isinstance(resource, aim_resource.VRF):
vrf = resource
db_mixin._add_network_mapping(session, net_db.id, bd, epg, vrf)
eid = (ext_net.tenant_name, ext_net.l3out_name, ext_net.name)
for vrf in vrfs:
if eid in clone_ext_nets:
ext_net.provided_contract_names = clone_ext_nets[
eid].provided_contract_names
ext_net.consumed_contract_names = clone_ext_nets[
eid].consumed_contract_names
ns.connect_vrf(aim_ctx, ext_net, vrf)

View File

@ -2248,16 +2248,6 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
mapping = self._get_network_mapping(session, network['id'])
return mapping and self._get_network_vrf(mapping)
# DB Configuration callbacks
def _set_enable_metadata_opt(self, new_conf):
self.enable_metadata_opt = new_conf['value']
def _set_enable_dhcp_opt(self, new_conf):
self.enable_dhcp_opt = new_conf['value']
def _set_ap_name(self, new_conf):
self.ap_name = new_conf['value']
def get_aim_domains(self, aim_ctx):
vmms = [x.name for x in self.aim.find(aim_ctx, aim_resource.VMMDomain)
if x.type == utils.OPENSTACK_VMM_TYPE]
@ -2276,6 +2266,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
ns_cls = nat_strategy.EdgeNatStrategy
ns = ns_cls(self.aim)
ns.app_profile_name = self.ap_name
ns.common_scope = self.apic_system_id
return ns
def _get_aim_nat_strategy(self, network):

View File

@ -1985,6 +1985,8 @@ class AIMMappingDriver(nrd.CommonNeutronBase, aim_rpc.AIMMappingRPCMixin):
ext_segment_name = dn.replace('/', ':')
ipms.append({'external_segment_name': ext_segment_name,
'nat_epg_name': ext_net_epg.name,
'nat_epg_app_profile': (
ext_net_epg.app_profile_name),
'nat_epg_tenant': ext_net_epg.tenant_name})
# TODO(amitbose) Set next_hop_ep_tenant for per-tenant NAT EPG
if host:
@ -1996,6 +1998,7 @@ class AIMMappingDriver(nrd.CommonNeutronBase, aim_rpc.AIMMappingRPCMixin):
else:
for f in fips_in_ext_net:
f['nat_epg_name'] = ext_net_epg.name
f['nat_epg_app_profile'] = ext_net_epg.app_profile_name
f['nat_epg_tenant'] = ext_net_epg.tenant_name
return fips, ipms, host_snat_ips

View File

@ -3282,6 +3282,102 @@ class TestMigrations(ApicAimTestCase, db.DbMixin):
epg = self._find_by_dn(net1_epg, aim_resource.EndpointGroup)
self.assertIsNone(epg)
def test_ap_name_change(self):
self.tenant_1 = 't1'
net = self._make_ext_network(
'net2', dn='uni/tn-common/out-l1/instP-n1')
self._make_subnet(self.fmt, {'network': net}, '10.0.1.1',
'10.0.1.0/24')
addr_scope = self._make_address_scope(
self.fmt, 4, name='as1', tenant_id=self.tenant_1)['address_scope']
subnetpool = self._make_subnetpool(
self.fmt, ['10.0.0.0/8'], name='spool1', tenant_id=self.tenant_1,
address_scope_id=addr_scope['id'])['subnetpool']
net_int = self._make_network(self.fmt, 'pvt-net', True,
tenant_id=self.tenant_1)['network']
sp_id = subnetpool['id']
sub = self._make_subnet(
self.fmt, {'network': net_int}, '10.10.1.1', '10.10.1.0/24',
subnetpool_id=sp_id)['subnet']
router = self._make_router(
self.fmt, self.tenant_1, 'router',
arg_list=self.extension_attributes,
external_gateway_info={'network_id': net['id']})['router']
self._router_interface_action('add', router['id'], sub['id'], None)
aim = self.aim_mgr
aim_ctx = aim_context.AimContext(self.db_session)
ns = self.driver._nat_type_to_strategy(None)
ext_net = aim_resource.ExternalNetwork.from_dn(
net[DN]['ExternalNetwork'])
l3out = aim_resource.L3Outside(tenant_name=ext_net.tenant_name,
name=ext_net.l3out_name)
right_res = ns.get_l3outside_resources(aim_ctx, l3out)
for res in copy.deepcopy(right_res):
if isinstance(res, aim_resource.ApplicationProfile):
aim.delete(aim_ctx, res)
res.name = self.driver.ap_name
wrong_ap = aim.create(aim_ctx, res)
if isinstance(res, aim_resource.EndpointGroup):
aim.delete(aim_ctx, res)
res.app_profile_name = self.driver.ap_name
res.bd_name = 'EXT-%s' % l3out.name
wrong_epg = aim.create(aim_ctx, res)
if isinstance(res, aim_resource.BridgeDomain):
aim.delete(aim_ctx, res)
res.name = 'EXT-%s' % l3out.name
wrong_bd = aim.create(aim_ctx, res)
if isinstance(res, aim_resource.Contract):
aim.delete(aim_ctx, res)
res.name = 'EXT-%s' % l3out.name
wrong_ctr = aim.create(aim_ctx, res)
if isinstance(res, aim_resource.Filter):
aim.delete(aim_ctx, res)
res.name = 'EXT-%s' % l3out.name
wrong_flt = aim.create(aim_ctx, res)
if isinstance(res, aim_resource.FilterEntry):
aim.delete(aim_ctx, res)
res.filter_name = 'EXT-%s' % l3out.name
wrong_entry = aim.create(aim_ctx, res)
if isinstance(res, aim_resource.ContractSubject):
aim.delete(aim_ctx, res)
res.contract_name = 'EXT-%s' % l3out.name
wrong_sbj = aim.create(aim_ctx, res)
ns.common_scope = None
wrong_res = ns.get_l3outside_resources(aim_ctx, l3out)
self.assertEqual(len(right_res), len(wrong_res))
self.assertNotEqual(sorted(right_res), sorted(wrong_res))
data_migrations.do_ap_name_change(self.db_session, cfg.CONF)
# Verify new model
ns = self.driver._nat_type_to_strategy(None)
final_res = ns.get_l3outside_resources(aim_ctx, l3out)
self.assertEqual(sorted(right_res), sorted(final_res))
self.assertIsNone(aim.get(aim_ctx, wrong_ap))
self.assertIsNone(aim.get(aim_ctx, wrong_epg))
self.assertIsNone(aim.get(aim_ctx, wrong_bd))
self.assertIsNone(aim.get(aim_ctx, wrong_ctr))
self.assertIsNone(aim.get(aim_ctx, wrong_flt))
self.assertIsNone(aim.get(aim_ctx, wrong_entry))
self.assertIsNone(aim.get(aim_ctx, wrong_sbj))
self.assertIsNotNone(ns.get_subnet(aim_ctx, l3out, '10.0.1.1/24'))
# Top level objects are scoped
for r in final_res:
if any(isinstance(r, x) for x in [aim_resource.ApplicationProfile,
aim_resource.BridgeDomain,
aim_resource.Contract,
aim_resource.Filter]):
self.assertTrue(r.name.startswith(self.driver.apic_system_id),
'%s name: %s' % (type(r), r.name))
self._update('routers', router['id'],
{'router': {'external_gateway_info': {}}})
self._delete('networks', net['id'])
for r in final_res:
self.assertIsNone(aim.get(aim_ctx, r),
'Resource: %s still in AIM' % r)
class TestPortBinding(ApicAimTestCase):
def test_bind_opflex_agent(self):

View File

@ -2886,17 +2886,20 @@ class TestPolicyTarget(AIMBaseTestCase):
return ext_seg, subnet
def _verify_fip_details(self, mapping, fip, ext_epg_tenant,
ext_epg_name):
ext_epg_name, ext_epg_app_profile='OpenStack'):
self.assertEqual(1, len(mapping['floating_ip']))
fip = copy.deepcopy(fip)
fip['nat_epg_name'] = ext_epg_name
fip['nat_epg_tenant'] = ext_epg_tenant
fip['nat_epg_app_profile'] = ext_epg_app_profile
self.assertEqual(fip, mapping['floating_ip'][0])
def _verify_ip_mapping_details(self, mapping, ext_segment_name,
ext_epg_tenant, ext_epg_name):
ext_epg_tenant, ext_epg_name,
ext_epg_app_profile='OpenStack'):
self.assertTrue({'external_segment_name': ext_segment_name,
'nat_epg_name': ext_epg_name,
'nat_epg_app_profile': ext_epg_app_profile,
'nat_epg_tenant': ext_epg_tenant}
in mapping['ip_mapping'])