Merge "SVI network support for APIC."
This commit is contained in:
commit
70731da6f2
|
@ -0,0 +1,44 @@
|
|||
# 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.
|
||||
#
|
||||
|
||||
"""svi-apic
|
||||
|
||||
Revision ID: 5fe2285071fd
|
||||
Revises: 886c376885a3
|
||||
Create Date: 2018-01-05 00:00:00.000000
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '5fe2285071fd'
|
||||
down_revision = '886c376885a3'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('apic_aim_network_extensions',
|
||||
sa.Column('svi', sa.Boolean))
|
||||
op.add_column('apic_aim_network_mappings',
|
||||
sa.Column('l3out_name', sa.String(64), nullable=True))
|
||||
op.add_column('apic_aim_network_mappings',
|
||||
sa.Column('l3out_ext_net_name', sa.String(64),
|
||||
nullable=True))
|
||||
op.add_column('apic_aim_network_mappings',
|
||||
sa.Column('l3out_tenant_name', sa.String(64),
|
||||
nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
|
@ -1 +1 @@
|
|||
886c376885a3
|
||||
5fe2285071fd
|
||||
|
|
|
@ -26,6 +26,7 @@ SYNC_STATE = 'apic:synchronization_state'
|
|||
NAT_TYPE = 'apic:nat_type'
|
||||
SNAT_HOST_POOL = 'apic:snat_host_pool'
|
||||
EXTERNAL_CIDRS = 'apic:external_cidrs'
|
||||
SVI = 'apic:svi'
|
||||
|
||||
BD = 'BridgeDomain'
|
||||
EPG = 'EndpointGroup'
|
||||
|
@ -44,6 +45,14 @@ APIC_ATTRIBUTES = {
|
|||
SYNC_STATE: {'allow_post': False, 'allow_put': False, 'is_visible': True}
|
||||
}
|
||||
|
||||
NET_ATTRIBUTES = {
|
||||
SVI: {
|
||||
'allow_post': True, 'allow_put': False,
|
||||
'is_visible': True, 'default': False,
|
||||
'convert_to': conv.convert_to_boolean,
|
||||
},
|
||||
}
|
||||
|
||||
EXT_NET_ATTRIBUTES = {
|
||||
DIST_NAMES: {
|
||||
# DN of corresponding APIC L3Out external network; can be
|
||||
|
@ -105,7 +114,8 @@ ADDRESS_SCOPE_ATTRIBUTES = {
|
|||
|
||||
EXTENDED_ATTRIBUTES_2_0 = {
|
||||
net_def.COLLECTION_NAME: dict(
|
||||
APIC_ATTRIBUTES.items() + EXT_NET_ATTRIBUTES.items()),
|
||||
APIC_ATTRIBUTES.items() + EXT_NET_ATTRIBUTES.items() +
|
||||
NET_ATTRIBUTES.items()),
|
||||
subnet_def.COLLECTION_NAME: dict(
|
||||
APIC_ATTRIBUTES.items() + EXT_SUBNET_ATTRIBUTES.items()),
|
||||
as_def.COLLECTION_NAME: dict(
|
||||
|
|
|
@ -40,6 +40,11 @@ apic_opts = [
|
|||
default=False,
|
||||
help=("This will enable the iptables firewall implementation "
|
||||
"on those compute nodes.")),
|
||||
# TODO(kentwu): Need to define the external routed domain
|
||||
# AIM object instead.
|
||||
cfg.StrOpt('l3_domain_dn', default='',
|
||||
help=("The DN of the APIC external routed domain used by the "
|
||||
"auto l3out created for the SVI networks.")),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -172,7 +172,7 @@ def do_ap_name_change(session, conf=None):
|
|||
net_dbs = session.query(models_v2.Network).options(lazyload('*')).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]:
|
||||
if ext_db and ext_db.get(ext.EXTERNAL_NETWORK):
|
||||
alembic_util.msg("Migrating external network: %s" % net_db)
|
||||
# Its a managed external network.
|
||||
ext_net = aim_resource.ExternalNetwork.from_dn(
|
||||
|
|
|
@ -64,6 +64,10 @@ class NetworkMapping(model_base.BASEV2):
|
|||
epg_app_profile_name = sa.Column(sa.String(64))
|
||||
epg_tenant_name = sa.Column(sa.String(64))
|
||||
|
||||
l3out_name = sa.Column(sa.String(64))
|
||||
l3out_ext_net_name = sa.Column(sa.String(64))
|
||||
l3out_tenant_name = sa.Column(sa.String(64))
|
||||
|
||||
vrf_name = sa.Column(sa.String(64))
|
||||
vrf_tenant_name = sa.Column(sa.String(64))
|
||||
|
||||
|
@ -95,16 +99,26 @@ class DbMixin(object):
|
|||
tenant_name=mapping.vrf_tenant_name,
|
||||
name=mapping.vrf_name)
|
||||
|
||||
def _add_network_mapping(self, session, network_id, bd, epg, vrf):
|
||||
mapping = NetworkMapping(
|
||||
network_id=network_id,
|
||||
bd_name=bd.name,
|
||||
bd_tenant_name=bd.tenant_name,
|
||||
epg_name=epg.name,
|
||||
epg_app_profile_name=epg.app_profile_name,
|
||||
epg_tenant_name=epg.tenant_name,
|
||||
vrf_name=vrf.name,
|
||||
vrf_tenant_name=vrf.tenant_name)
|
||||
def _add_network_mapping(self, session, network_id, bd, epg, vrf,
|
||||
ext_net=None):
|
||||
if not ext_net:
|
||||
mapping = NetworkMapping(
|
||||
network_id=network_id,
|
||||
bd_name=bd.name,
|
||||
bd_tenant_name=bd.tenant_name,
|
||||
epg_name=epg.name,
|
||||
epg_app_profile_name=epg.app_profile_name,
|
||||
epg_tenant_name=epg.tenant_name,
|
||||
vrf_name=vrf.name,
|
||||
vrf_tenant_name=vrf.tenant_name)
|
||||
else:
|
||||
mapping = NetworkMapping(
|
||||
network_id=network_id,
|
||||
l3out_name=ext_net.l3out_name,
|
||||
l3out_ext_net_name=ext_net.name,
|
||||
l3out_tenant_name=ext_net.tenant_name,
|
||||
vrf_name=vrf.name,
|
||||
vrf_tenant_name=vrf.tenant_name)
|
||||
session.add(mapping)
|
||||
return mapping
|
||||
|
||||
|
@ -142,6 +156,18 @@ class DbMixin(object):
|
|||
app_profile_name=mapping.epg_app_profile_name,
|
||||
name=mapping.epg_name)
|
||||
|
||||
def _get_network_l3out(self, mapping):
|
||||
if not mapping:
|
||||
return None
|
||||
return aim_resource.L3Outside(
|
||||
tenant_name=mapping.l3out_tenant_name,
|
||||
name=mapping.l3out_name)
|
||||
|
||||
def _get_network_l3out_ext_net(self, mapping):
|
||||
return aim_resource.ExternalNetwork(
|
||||
tenant_name=mapping.l3out_tenant_name,
|
||||
l3out_name=mapping.l3out_name, name=mapping.l3out_ext_net_name)
|
||||
|
||||
def _get_network_vrf(self, mapping):
|
||||
return aim_resource.VRF(
|
||||
tenant_name=mapping.vrf_tenant_name,
|
||||
|
@ -156,6 +182,10 @@ class DbMixin(object):
|
|||
mapping.epg_app_profile_name = epg.app_profile_name
|
||||
mapping.epg_name = epg.name
|
||||
|
||||
def _set_network_l3out(self, mapping, l3out):
|
||||
mapping.l3out_tenant_name = l3out.tenant_name
|
||||
mapping.l3out_name = l3out.name
|
||||
|
||||
def _set_network_vrf(self, mapping, vrf):
|
||||
mapping.vrf_tenant_name = vrf.tenant_name
|
||||
mapping.vrf_name = vrf.name
|
||||
|
|
|
@ -53,3 +53,12 @@ class SnatPortsInUse(exceptions.SubnetInUse):
|
|||
|
||||
class SnatPoolCannotBeUsedForFloatingIp(exceptions.InvalidInput):
|
||||
message = _("Floating IP cannot be allocated in SNAT host pool subnet.")
|
||||
|
||||
|
||||
class PreExistingSVICannotBeConnectedToRouter(exceptions.BadRequest):
|
||||
message = _("A SVI network with pre-existing l3out is not allowed to "
|
||||
"be connected to a router.")
|
||||
|
||||
|
||||
class OnlyOneSubnetInSVINetwork(exceptions.BadRequest):
|
||||
message = _("Only one subnet is allowed in SVI network.")
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.db import models_v2
|
||||
from neutron_lib.db import model_base
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
|
||||
from gbpservice.neutron.extensions import cisco_apic
|
||||
from gbpservice.neutron.extensions import cisco_apic_l3
|
||||
|
@ -29,6 +31,12 @@ class NetworkExtensionDb(model_base.BASEV2):
|
|||
primary_key=True)
|
||||
external_network_dn = sa.Column(sa.String(1024))
|
||||
nat_type = sa.Column(sa.Enum('distributed', 'edge', ''))
|
||||
svi = sa.Column(sa.Boolean)
|
||||
|
||||
network = orm.relationship(models_v2.Network,
|
||||
backref=orm.backref(
|
||||
'aim_extension_mapping', lazy='joined',
|
||||
uselist=False, cascade='delete'))
|
||||
|
||||
|
||||
class NetworkExtensionCidrDb(model_base.BASEV2):
|
||||
|
@ -79,8 +87,10 @@ class ExtensionDbMixin(object):
|
|||
db_obj['external_network_dn'])
|
||||
self._set_if_not_none(result, cisco_apic.NAT_TYPE,
|
||||
db_obj['nat_type'])
|
||||
self._set_if_not_none(result, cisco_apic.SVI, db_obj['svi'])
|
||||
if result.get(cisco_apic.EXTERNAL_NETWORK):
|
||||
result[cisco_apic.EXTERNAL_CIDRS] = [c['cidr'] for c in db_cidrs]
|
||||
|
||||
return result
|
||||
|
||||
def set_network_extn_db(self, session, network_id, res_dict):
|
||||
|
@ -93,7 +103,10 @@ class ExtensionDbMixin(object):
|
|||
res_dict[cisco_apic.EXTERNAL_NETWORK])
|
||||
if cisco_apic.NAT_TYPE in res_dict:
|
||||
db_obj['nat_type'] = res_dict[cisco_apic.NAT_TYPE]
|
||||
if cisco_apic.SVI in res_dict:
|
||||
db_obj['svi'] = res_dict[cisco_apic.SVI]
|
||||
session.add(db_obj)
|
||||
|
||||
if cisco_apic.EXTERNAL_CIDRS in res_dict:
|
||||
self._update_list_attr(session, NetworkExtensionCidrDb, 'cidr',
|
||||
res_dict[cisco_apic.EXTERNAL_CIDRS],
|
||||
|
|
|
@ -77,6 +77,12 @@ class ApicExtensionDriver(api_plus.ExtensionDriver,
|
|||
LOG.exception("APIC AIM extend_network_dict failed")
|
||||
|
||||
def process_create_network(self, plugin_context, data, result):
|
||||
is_svi = data.get(cisco_apic.SVI, False)
|
||||
res_dict = {cisco_apic.SVI: is_svi}
|
||||
self.set_network_extn_db(plugin_context.session, result['id'],
|
||||
res_dict)
|
||||
result.update(res_dict)
|
||||
|
||||
if (data.get(cisco_apic.DIST_NAMES) and
|
||||
data[cisco_apic.DIST_NAMES].get(cisco_apic.EXTERNAL_NETWORK)):
|
||||
dn = data[cisco_apic.DIST_NAMES][cisco_apic.EXTERNAL_NETWORK]
|
||||
|
@ -85,11 +91,15 @@ class ApicExtensionDriver(api_plus.ExtensionDriver,
|
|||
except aim_exc.InvalidDNForAciResource:
|
||||
raise n_exc.InvalidInput(
|
||||
error_message=('%s is not valid ExternalNetwork DN' % dn))
|
||||
res_dict = {cisco_apic.EXTERNAL_NETWORK: dn,
|
||||
cisco_apic.NAT_TYPE:
|
||||
data.get(cisco_apic.NAT_TYPE, 'distributed'),
|
||||
cisco_apic.EXTERNAL_CIDRS:
|
||||
data.get(cisco_apic.EXTERNAL_CIDRS, ['0.0.0.0/0'])}
|
||||
if is_svi:
|
||||
res_dict = {cisco_apic.EXTERNAL_NETWORK: dn}
|
||||
else:
|
||||
res_dict = {cisco_apic.EXTERNAL_NETWORK: dn,
|
||||
cisco_apic.NAT_TYPE:
|
||||
data.get(cisco_apic.NAT_TYPE, 'distributed'),
|
||||
cisco_apic.EXTERNAL_CIDRS:
|
||||
data.get(
|
||||
cisco_apic.EXTERNAL_CIDRS, ['0.0.0.0/0'])}
|
||||
self.set_network_extn_db(plugin_context.session, result['id'],
|
||||
res_dict)
|
||||
result.setdefault(cisco_apic.DIST_NAMES, {})[
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
import copy
|
||||
import netaddr
|
||||
import re
|
||||
import sqlalchemy as sa
|
||||
|
||||
from aim.aim_lib import nat_strategy
|
||||
|
@ -69,6 +70,7 @@ from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import trunk_driver
|
|||
|
||||
LOG = log.getLogger(__name__)
|
||||
DEVICE_OWNER_SNAT_PORT = 'apic:snat-pool'
|
||||
DEVICE_OWNER_SVI_PORT = 'apic:svi'
|
||||
|
||||
ANY_FILTER_NAME = 'AnyFilter'
|
||||
ANY_FILTER_ENTRY_NAME = 'AnyFilterEntry'
|
||||
|
@ -77,6 +79,9 @@ UNROUTED_VRF_NAME = 'UnroutedVRF'
|
|||
COMMON_TENANT_NAME = 'common'
|
||||
ROUTER_SUBJECT_NAME = 'route'
|
||||
DEFAULT_SG_NAME = 'DefaultSecurityGroup'
|
||||
L3OUT_NODE_PROFILE_NAME = 'NodeProfile'
|
||||
L3OUT_IF_PROFILE_NAME = 'IfProfile'
|
||||
L3OUT_EXT_EPG = 'ExtEpg'
|
||||
|
||||
AGENT_TYPE_DVS = 'DVS agent'
|
||||
VIF_TYPE_DVS = 'dvs'
|
||||
|
@ -90,6 +95,16 @@ NO_ADDR_SCOPE = object()
|
|||
DVS_AGENT_KLASS = 'networking_vsphere.common.dvs_agent_rpc_api.DVSClientAPI'
|
||||
DEFAULT_HOST_DOMAIN = '*'
|
||||
|
||||
# TODO(kentwu): Move this to AIM utils maybe to avoid adding too much
|
||||
# APIC logic to the mechanism driver
|
||||
ACI_CHASSIS_DESCR_STRING = 'topology/pod-%s/node-%s'
|
||||
ACI_PORT_DESCR_FORMATS = ('topology/pod-(\d+)/paths-(\d+)/pathep-'
|
||||
'\[eth(\d+)/(\d+(\/\d+)*)\]')
|
||||
ACI_VPCPORT_DESCR_FORMAT = ('topology/pod-(\d+)/protpaths-(\d+)-(\d+)/pathep-'
|
||||
'\[(.*)\]')
|
||||
# TODO(kentwu): A pool of router IDs has to be put in the config file instead
|
||||
APIC_ROUTER_IDS = ['199.199.199.198', '199.199.199.199']
|
||||
|
||||
|
||||
class KeystoneNotificationEndpoint(object):
|
||||
filter_rule = oslo_messaging.NotificationFilter(
|
||||
|
@ -200,9 +215,12 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
enable_keystone_notification_purge)
|
||||
self.enable_iptables_firewall = (cfg.CONF.ml2_apic_aim.
|
||||
enable_iptables_firewall)
|
||||
self.l3_domain_dn = cfg.CONF.ml2_apic_aim.l3_domain_dn
|
||||
local_api.QUEUE_OUT_OF_PROCESS_NOTIFICATIONS = True
|
||||
self._ensure_static_resources()
|
||||
trunk_driver.register()
|
||||
self.port_desc_re = re.compile(ACI_PORT_DESCR_FORMATS)
|
||||
self.vpcport_desc_re = re.compile(ACI_VPCPORT_DESCR_FORMAT)
|
||||
|
||||
def _ensure_static_resources(self):
|
||||
session = db_api.get_writer_session()
|
||||
|
@ -439,6 +457,42 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
epg = resource
|
||||
elif isinstance(resource, aim_resource.VRF):
|
||||
vrf = resource
|
||||
elif self._is_svi(current):
|
||||
_, ext_net, _ = self._get_aim_external_stuff(current)
|
||||
# This means no DN is being provided. Then we should try to create
|
||||
# the l3out automatically
|
||||
if not ext_net:
|
||||
tenant_aname = self.name_mapper.project(session,
|
||||
current['tenant_id'])
|
||||
vrf = self._map_unrouted_vrf()
|
||||
aname = self.name_mapper.network(session, current['id'])
|
||||
dname = aim_utils.sanitize_display_name(current['name'])
|
||||
|
||||
aim_l3out = aim_resource.L3Outside(
|
||||
tenant_name=tenant_aname,
|
||||
name=aname, display_name=dname, vrf_name=vrf.name,
|
||||
l3_domain_dn=self.l3_domain_dn)
|
||||
self.aim.create(aim_ctx, aim_l3out)
|
||||
|
||||
aim_ext_net = aim_resource.ExternalNetwork(
|
||||
tenant_name=tenant_aname,
|
||||
l3out_name=aname, name=L3OUT_EXT_EPG)
|
||||
self.aim.create(aim_ctx, aim_ext_net)
|
||||
|
||||
aim_ext_subnet_ipv4 = aim_resource.ExternalSubnet(
|
||||
tenant_name=tenant_aname,
|
||||
l3out_name=aname,
|
||||
external_network_name=L3OUT_EXT_EPG, cidr='0.0.0.0/0')
|
||||
self.aim.create(aim_ctx, aim_ext_subnet_ipv4)
|
||||
aim_ext_subnet_ipv6 = aim_resource.ExternalSubnet(
|
||||
tenant_name=tenant_aname,
|
||||
l3out_name=aname,
|
||||
external_network_name=L3OUT_EXT_EPG, cidr='::/0')
|
||||
self.aim.create(aim_ctx, aim_ext_subnet_ipv6)
|
||||
|
||||
self._add_network_mapping(session, current['id'], None, None,
|
||||
vrf, aim_ext_net)
|
||||
return
|
||||
else:
|
||||
bd, epg = self._map_network(session, current)
|
||||
|
||||
|
@ -479,10 +533,15 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
if (not is_ext and
|
||||
current['name'] != original['name']):
|
||||
dname = aim_utils.sanitize_display_name(current['name'])
|
||||
bd = self._get_network_bd(mapping)
|
||||
self.aim.update(aim_ctx, bd, display_name=dname)
|
||||
epg = self._get_network_epg(mapping)
|
||||
self.aim.update(aim_ctx, epg, display_name=dname)
|
||||
if not self._is_svi(current):
|
||||
bd = self._get_network_bd(mapping)
|
||||
self.aim.update(aim_ctx, bd, display_name=dname)
|
||||
epg = self._get_network_epg(mapping)
|
||||
self.aim.update(aim_ctx, epg, display_name=dname)
|
||||
else:
|
||||
l3out = self._get_network_l3out(mapping)
|
||||
if l3out:
|
||||
self.aim.update(aim_ctx, l3out, display_name=dname)
|
||||
|
||||
if is_ext:
|
||||
_, ext_net, ns = self._get_aim_nat_strategy(current)
|
||||
|
@ -508,6 +567,20 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
# TODO(amitbose) delete L3out only if no other Neutron
|
||||
# network is using the L3out
|
||||
ns.delete_l3outside(aim_ctx, l3out)
|
||||
elif self._is_svi(current):
|
||||
l3out, ext_net, _ = self._get_aim_external_stuff(current)
|
||||
aim_l3out = self.aim.get(aim_ctx, l3out)
|
||||
if not aim_l3out:
|
||||
return
|
||||
# this means its pre-existing l3out
|
||||
if aim_l3out.monitored:
|
||||
# just delete everything under NodeProfile
|
||||
aim_l3out_np = aim_resource.L3OutNodeProfile(
|
||||
tenant_name=l3out.tenant_name, l3out_name=l3out.name,
|
||||
name=L3OUT_NODE_PROFILE_NAME)
|
||||
self.aim.delete(aim_ctx, aim_l3out_np, cascade=True)
|
||||
else:
|
||||
self.aim.delete(aim_ctx, l3out, cascade=True)
|
||||
else:
|
||||
mapping = self._get_network_mapping(session, current['id'])
|
||||
bd = self._get_network_bd(mapping)
|
||||
|
@ -527,18 +600,33 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
# mapping = network_db.aim_mapping
|
||||
mapping = self._get_network_mapping(session, network_db['id'])
|
||||
if mapping:
|
||||
bd = self._get_network_bd(mapping)
|
||||
dist_names[cisco_apic.BD] = bd.dn
|
||||
sync_state = self._merge_status(aim_ctx, sync_state, bd)
|
||||
if mapping.epg_name:
|
||||
bd = self._get_network_bd(mapping)
|
||||
dist_names[cisco_apic.BD] = bd.dn
|
||||
sync_state = self._merge_status(aim_ctx, sync_state, bd)
|
||||
|
||||
epg = self._get_network_epg(mapping)
|
||||
dist_names[cisco_apic.EPG] = epg.dn
|
||||
sync_state = self._merge_status(aim_ctx, sync_state, epg)
|
||||
epg = self._get_network_epg(mapping)
|
||||
dist_names[cisco_apic.EPG] = epg.dn
|
||||
sync_state = self._merge_status(aim_ctx, sync_state, epg)
|
||||
# SVI network with auto l3out.
|
||||
elif mapping.l3out_name:
|
||||
l3out_ext_net = self._get_network_l3out_ext_net(mapping)
|
||||
dist_names[cisco_apic.EXTERNAL_NETWORK] = l3out_ext_net.dn
|
||||
sync_state = self._merge_status(aim_ctx, sync_state,
|
||||
l3out_ext_net)
|
||||
|
||||
vrf = self._get_network_vrf(mapping)
|
||||
dist_names[cisco_apic.VRF] = vrf.dn
|
||||
sync_state = self._merge_status(aim_ctx, sync_state, vrf)
|
||||
|
||||
# SVI network with pre-existing l3out.
|
||||
if (network_db.aim_extension_mapping.svi and
|
||||
network_db.aim_extension_mapping.external_network_dn):
|
||||
_, ext_net, _ = self._get_aim_external_stuff_db(session,
|
||||
network_db)
|
||||
if ext_net:
|
||||
sync_state = self._merge_status(aim_ctx, sync_state, ext_net)
|
||||
|
||||
# REVISIT: Should the external network be persisted in the
|
||||
# mapping along with the other resources?
|
||||
if network_db.external is not None:
|
||||
|
@ -567,6 +655,15 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
ns.create_subnet(aim_ctx, l3out,
|
||||
self._subnet_to_gw_ip_mask(current))
|
||||
|
||||
# Limit 1 subnet per SVI network as each SVI interface
|
||||
# in ACI can only have 1 primary addr
|
||||
if network_db.aim_extension_mapping.svi:
|
||||
subnets_size = (session.query(models_v2.Subnet)
|
||||
.filter(models_v2.Subnet.network_id == network_id)
|
||||
.count())
|
||||
if subnets_size > 1:
|
||||
raise exceptions.OnlyOneSubnetInSVINetwork()
|
||||
|
||||
# Neutron subnets in non-external networks are mapped to AIM
|
||||
# Subnets as they are added to routers as interfaces.
|
||||
|
||||
|
@ -593,6 +690,9 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
|
||||
if (not is_ext and
|
||||
current['name'] != original['name']):
|
||||
# Nothing to be done for SVI network.
|
||||
if self._is_svi(context.network.current):
|
||||
return
|
||||
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
|
||||
|
@ -663,7 +763,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
if sub:
|
||||
dist_names[cisco_apic.SUBNET] = sub.dn
|
||||
sync_state = self._merge_status(aim_ctx, sync_state, sub)
|
||||
elif network_db.aim_mapping:
|
||||
elif network_db.aim_mapping and network_db.aim_mapping.bd_name:
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
|
||||
for gw_ip, router_id in self._subnet_router_ips(session,
|
||||
|
@ -853,13 +953,12 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
filter_by(id=subnet_db.network_id).
|
||||
one())
|
||||
|
||||
dname = aim_utils.sanitize_display_name(
|
||||
name + "-" + (subnet_db.name or subnet_db.cidr))
|
||||
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
sn = self._map_subnet(subnet_db, intf.ip_address, bd)
|
||||
|
||||
self.aim.update(aim_ctx, sn, display_name=dname)
|
||||
if network_db.aim_mapping and network_db.aim_mapping.bd_name:
|
||||
dname = aim_utils.sanitize_display_name(
|
||||
name + "-" + (subnet_db.name or subnet_db.cidr))
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
sn = self._map_subnet(subnet_db, intf.ip_address, bd)
|
||||
self.aim.update(aim_ctx, sn, display_name=dname)
|
||||
|
||||
def is_diff(old, new, attr):
|
||||
return sorted(old[attr]) != sorted(new[attr])
|
||||
|
@ -962,10 +1061,11 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
n_constants.DEVICE_OWNER_ROUTER_INTF)):
|
||||
ip_address, subnet_db, network_db = intf
|
||||
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
sn = self._map_subnet(subnet_db, intf.ip_address, bd)
|
||||
dist_names[intf.ip_address] = sn.dn
|
||||
sync_state = self._merge_status(aim_ctx, sync_state, sn)
|
||||
if network_db.aim_mapping.bd_name:
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
sn = self._map_subnet(subnet_db, intf.ip_address, bd)
|
||||
dist_names[intf.ip_address] = sn.dn
|
||||
sync_state = self._merge_status(aim_ctx, sync_state, sn)
|
||||
|
||||
scope_id = (subnet_db.subnetpool and
|
||||
subnet_db.subnetpool.address_scope_id)
|
||||
|
@ -1007,6 +1107,12 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
network_id = port['network_id']
|
||||
network_db = self.plugin._get_network(context, network_id)
|
||||
|
||||
# SVI network with pre-existing l3out is not allowed to be
|
||||
# connected to a router at this moment
|
||||
if (network_db.aim_extension_mapping.svi and
|
||||
network_db.aim_extension_mapping.external_network_dn):
|
||||
raise exceptions.PreExistingSVICannotBeConnectedToRouter()
|
||||
|
||||
# Find the address_scope(s) for the new interface.
|
||||
#
|
||||
# REVISIT: If dual-stack interfaces allowed, process each
|
||||
|
@ -1147,12 +1253,17 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
else:
|
||||
vrf = self._ensure_default_vrf(aim_ctx, intf_vrf)
|
||||
|
||||
epg = None
|
||||
# Associate or map network, depending on whether it has other
|
||||
# interfaces.
|
||||
if not net_intfs:
|
||||
# First interface for network.
|
||||
bd, epg = self._associate_network_with_vrf(
|
||||
aim_ctx, network_db, vrf, nets_to_notify)
|
||||
if network_db.aim_mapping.epg_name:
|
||||
bd, epg = self._associate_network_with_vrf(
|
||||
aim_ctx, network_db, vrf, nets_to_notify)
|
||||
elif network_db.aim_mapping.l3out_name:
|
||||
l3out, epg = self._associate_network_with_vrf(
|
||||
aim_ctx, network_db, vrf, nets_to_notify)
|
||||
else:
|
||||
# Network is already routed.
|
||||
#
|
||||
|
@ -1160,40 +1271,46 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
# to move the BD and EPG from already-routed v6 VRF to
|
||||
# newly-routed v4 VRF, and setup identity NAT for the v6
|
||||
# traffic.
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
epg = self._get_network_epg(network_db.aim_mapping)
|
||||
if network_db.aim_mapping.epg_name:
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
epg = self._get_network_epg(network_db.aim_mapping)
|
||||
elif network_db.aim_mapping.l3out_name:
|
||||
epg = self._get_network_l3out_ext_net(
|
||||
network_db.aim_mapping)
|
||||
|
||||
# Create AIM Subnet(s) for each added Neutron subnet.
|
||||
for subnet in subnets:
|
||||
gw_ip = self._ip_for_subnet(subnet, port['fixed_ips'])
|
||||
if network_db.aim_mapping.epg_name:
|
||||
# Create AIM Subnet(s) for each added Neutron subnet.
|
||||
for subnet in subnets:
|
||||
gw_ip = self._ip_for_subnet(subnet, port['fixed_ips'])
|
||||
|
||||
dname = aim_utils.sanitize_display_name(
|
||||
router['name'] + "-" +
|
||||
(subnet['name'] or subnet['cidr']))
|
||||
dname = aim_utils.sanitize_display_name(
|
||||
router['name'] + "-" +
|
||||
(subnet['name'] or subnet['cidr']))
|
||||
|
||||
sn = self._map_subnet(subnet, gw_ip, bd)
|
||||
sn.display_name = dname
|
||||
sn = self.aim.create(aim_ctx, sn)
|
||||
sn = self._map_subnet(subnet, gw_ip, bd)
|
||||
sn.display_name = dname
|
||||
sn = self.aim.create(aim_ctx, sn)
|
||||
|
||||
# Ensure network's EPG provides/consumes router's Contract.
|
||||
|
||||
contract = self._map_router(session, router, True)
|
||||
|
||||
# this could be internal or external EPG
|
||||
epg = self.aim.get(aim_ctx, epg)
|
||||
|
||||
contracts = epg.consumed_contract_names
|
||||
if contract.name not in contracts:
|
||||
contracts.append(contract.name)
|
||||
epg = self.aim.update(aim_ctx, epg,
|
||||
consumed_contract_names=contracts)
|
||||
|
||||
contracts = epg.provided_contract_names
|
||||
if contract.name not in contracts:
|
||||
contracts.append(contract.name)
|
||||
epg = self.aim.update(aim_ctx, epg,
|
||||
provided_contract_names=contracts)
|
||||
if epg:
|
||||
contracts = epg.consumed_contract_names
|
||||
if contract.name not in contracts:
|
||||
contracts.append(contract.name)
|
||||
epg = self.aim.update(aim_ctx, epg,
|
||||
consumed_contract_names=contracts)
|
||||
contracts = epg.provided_contract_names
|
||||
if contract.name not in contracts:
|
||||
contracts.append(contract.name)
|
||||
epg = self.aim.update(aim_ctx, epg,
|
||||
provided_contract_names=contracts)
|
||||
|
||||
# If external-gateway is set, handle external-connectivity changes.
|
||||
if router.gw_port_id:
|
||||
# External network is not supported for SVI network for now.
|
||||
if router.gw_port_id and not network_db.aim_extension_mapping.svi:
|
||||
net = self.plugin.get_network(context,
|
||||
router.gw_port.network_id)
|
||||
# If this is first interface-port, then that will determine
|
||||
|
@ -1244,20 +1361,24 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
# stack's scope separately, or at least raise an exception.
|
||||
scope_id = self._get_address_scope_id_for_subnets(context, subnets)
|
||||
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
epg = self._get_network_epg(network_db.aim_mapping)
|
||||
old_vrf = self._get_network_vrf(network_db.aim_mapping)
|
||||
|
||||
router_db = (session.query(l3_db.Router).
|
||||
filter_by(id=router_id).
|
||||
one())
|
||||
contract = self._map_router(session, router_db, True)
|
||||
|
||||
# Remove AIM Subnet(s) for each removed Neutron subnet.
|
||||
for subnet in subnets:
|
||||
gw_ip = self._ip_for_subnet(subnet, port['fixed_ips'])
|
||||
sn = self._map_subnet(subnet, gw_ip, bd)
|
||||
self.aim.delete(aim_ctx, sn)
|
||||
epg = None
|
||||
old_vrf = self._get_network_vrf(network_db.aim_mapping)
|
||||
if network_db.aim_mapping.epg_name:
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
epg = self._get_network_epg(network_db.aim_mapping)
|
||||
# Remove AIM Subnet(s) for each removed Neutron subnet.
|
||||
for subnet in subnets:
|
||||
gw_ip = self._ip_for_subnet(subnet, port['fixed_ips'])
|
||||
sn = self._map_subnet(subnet, gw_ip, bd)
|
||||
self.aim.delete(aim_ctx, sn)
|
||||
# SVI network with auto l3out.
|
||||
elif network_db.aim_mapping.l3out_name:
|
||||
epg = self._get_network_l3out_ext_net(network_db.aim_mapping)
|
||||
|
||||
# Find remaining routers with interfaces to this network.
|
||||
router_ids = [r[0] for r in
|
||||
|
@ -1271,7 +1392,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
# If network is no longer connected to this router, stop
|
||||
# network's EPG from providing/consuming this router's
|
||||
# Contract.
|
||||
if router_id not in router_ids:
|
||||
if router_id not in router_ids and epg:
|
||||
epg = self.aim.get(aim_ctx, epg)
|
||||
|
||||
contracts = [name for name in epg.consumed_contract_names
|
||||
|
@ -1331,7 +1452,8 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
self._cleanup_default_vrf(aim_ctx, old_vrf)
|
||||
|
||||
# If external-gateway is set, handle external-connectivity changes.
|
||||
if router_db.gw_port_id:
|
||||
# External network is not supproted for SVI network for now.
|
||||
if router_db.gw_port_id and not network_db.aim_extension_mapping.svi:
|
||||
net = self.plugin.get_network(context,
|
||||
router_db.gw_port.network_id)
|
||||
# If this was the last interface for this VRF for this
|
||||
|
@ -2015,38 +2137,61 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
# NOTE: Must only be called for networks that are not yet
|
||||
# attached to any router.
|
||||
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
epg = self._get_network_epg(network_db.aim_mapping)
|
||||
if not network_db.aim_extension_mapping.svi:
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
epg = self._get_network_epg(network_db.aim_mapping)
|
||||
tenant_name = bd.tenant_name
|
||||
else:
|
||||
l3out = self._get_network_l3out(network_db.aim_mapping)
|
||||
tenant_name = l3out.tenant_name
|
||||
|
||||
if (new_vrf.tenant_name != COMMON_TENANT_NAME and
|
||||
bd.tenant_name != new_vrf.tenant_name):
|
||||
tenant_name != new_vrf.tenant_name):
|
||||
# Move BD and EPG to new VRF's Tenant, set VRF, and make
|
||||
# sure routing is enabled.
|
||||
LOG.debug("Moving network from tenant %(old)s to tenant %(new)s",
|
||||
{'old': bd.tenant_name, 'new': new_vrf.tenant_name})
|
||||
{'old': tenant_name, 'new': new_vrf.tenant_name})
|
||||
if not network_db.aim_extension_mapping.svi:
|
||||
bd = self.aim.get(aim_ctx, bd)
|
||||
self.aim.delete(aim_ctx, bd)
|
||||
bd.tenant_name = new_vrf.tenant_name
|
||||
bd.enable_routing = True
|
||||
bd.vrf_name = new_vrf.name
|
||||
bd = self.aim.create(aim_ctx, bd)
|
||||
self._set_network_bd(network_db.aim_mapping, bd)
|
||||
|
||||
bd = self.aim.get(aim_ctx, bd)
|
||||
self.aim.delete(aim_ctx, bd)
|
||||
bd.tenant_name = new_vrf.tenant_name
|
||||
bd.enable_routing = True
|
||||
bd.vrf_name = new_vrf.name
|
||||
bd = self.aim.create(aim_ctx, bd)
|
||||
self._set_network_bd(network_db.aim_mapping, bd)
|
||||
|
||||
epg = self.aim.get(aim_ctx, epg)
|
||||
self.aim.delete(aim_ctx, epg)
|
||||
# ensure app profile exists in destination tenant
|
||||
ap = aim_resource.ApplicationProfile(
|
||||
tenant_name=new_vrf.tenant_name, name=self.ap_name)
|
||||
if not self.aim.get(aim_ctx, ap):
|
||||
self.aim.create(aim_ctx, ap)
|
||||
epg.tenant_name = new_vrf.tenant_name
|
||||
epg = self.aim.create(aim_ctx, epg)
|
||||
self._set_network_epg(network_db.aim_mapping, epg)
|
||||
epg = self.aim.get(aim_ctx, epg)
|
||||
self.aim.delete(aim_ctx, epg)
|
||||
# ensure app profile exists in destination tenant
|
||||
ap = aim_resource.ApplicationProfile(
|
||||
tenant_name=new_vrf.tenant_name, name=self.ap_name)
|
||||
if not self.aim.get(aim_ctx, ap):
|
||||
self.aim.create(aim_ctx, ap)
|
||||
epg.tenant_name = new_vrf.tenant_name
|
||||
epg = self.aim.create(aim_ctx, epg)
|
||||
self._set_network_epg(network_db.aim_mapping, epg)
|
||||
else:
|
||||
old_l3out = self.aim.get(aim_ctx, l3out)
|
||||
l3out = copy.copy(old_l3out)
|
||||
l3out.tenant_name = new_vrf.tenant_name
|
||||
l3out.vrf_name = new_vrf.name
|
||||
l3out = self.aim.create(aim_ctx, l3out)
|
||||
self._set_network_l3out(network_db.aim_mapping,
|
||||
l3out)
|
||||
for old_child in self.aim.get_subtree(aim_ctx, old_l3out):
|
||||
new_child = copy.copy(old_child)
|
||||
new_child.tenant_name = new_vrf.tenant_name
|
||||
new_child = self.aim.create(aim_ctx, new_child)
|
||||
self.aim.delete(aim_ctx, old_child)
|
||||
self.aim.delete(aim_ctx, old_l3out)
|
||||
else:
|
||||
# Just set VRF and enable routing.
|
||||
bd = self.aim.update(aim_ctx, bd, enable_routing=True,
|
||||
vrf_name=new_vrf.name)
|
||||
if not network_db.aim_extension_mapping.svi:
|
||||
bd = self.aim.update(aim_ctx, bd, enable_routing=True,
|
||||
vrf_name=new_vrf.name)
|
||||
else:
|
||||
l3out = self.aim.update(aim_ctx, l3out,
|
||||
vrf_name=new_vrf.name)
|
||||
|
||||
self._set_network_vrf(network_db.aim_mapping, new_vrf)
|
||||
|
||||
|
@ -2055,7 +2200,11 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
# Tenants have changed.
|
||||
nets_to_notify.add(network_db.id)
|
||||
|
||||
return bd, epg
|
||||
if not network_db.aim_extension_mapping.svi:
|
||||
return bd, epg
|
||||
else:
|
||||
ext_net = self._get_network_l3out_ext_net(network_db.aim_mapping)
|
||||
return l3out, ext_net
|
||||
|
||||
def _dissassociate_network_from_vrf(self, aim_ctx, network_db, old_vrf,
|
||||
nets_to_notify):
|
||||
|
@ -2078,26 +2227,47 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
LOG.debug("Moving network from tenant %(old)s to tenant %(new)s",
|
||||
{'old': old_vrf.tenant_name, 'new': new_tenant_name})
|
||||
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
bd = self.aim.get(aim_ctx, bd)
|
||||
self.aim.delete(aim_ctx, bd)
|
||||
bd.tenant_name = new_tenant_name
|
||||
bd.enable_routing = False
|
||||
bd.vrf_name = new_vrf.name
|
||||
bd = self.aim.create(aim_ctx, bd)
|
||||
self._set_network_bd(network_db.aim_mapping, bd)
|
||||
if not network_db.aim_extension_mapping.svi:
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
bd = self.aim.get(aim_ctx, bd)
|
||||
self.aim.delete(aim_ctx, bd)
|
||||
bd.tenant_name = new_tenant_name
|
||||
bd.enable_routing = False
|
||||
bd.vrf_name = new_vrf.name
|
||||
bd = self.aim.create(aim_ctx, bd)
|
||||
self._set_network_bd(network_db.aim_mapping, bd)
|
||||
|
||||
epg = self._get_network_epg(network_db.aim_mapping)
|
||||
epg = self.aim.get(aim_ctx, epg)
|
||||
self.aim.delete(aim_ctx, epg)
|
||||
epg.tenant_name = new_tenant_name
|
||||
epg = self.aim.create(aim_ctx, epg)
|
||||
self._set_network_epg(network_db.aim_mapping, epg)
|
||||
epg = self._get_network_epg(network_db.aim_mapping)
|
||||
epg = self.aim.get(aim_ctx, epg)
|
||||
self.aim.delete(aim_ctx, epg)
|
||||
epg.tenant_name = new_tenant_name
|
||||
epg = self.aim.create(aim_ctx, epg)
|
||||
self._set_network_epg(network_db.aim_mapping, epg)
|
||||
else:
|
||||
l3out = self._get_network_l3out(network_db.aim_mapping)
|
||||
old_l3out = self.aim.get(aim_ctx, l3out)
|
||||
l3out = copy.copy(old_l3out)
|
||||
l3out.tenant_name = new_tenant_name
|
||||
l3out.vrf_name = new_vrf.name
|
||||
l3out = self.aim.create(aim_ctx, l3out)
|
||||
self._set_network_l3out(network_db.aim_mapping,
|
||||
l3out)
|
||||
for old_child in self.aim.get_subtree(aim_ctx, old_l3out):
|
||||
new_child = copy.copy(old_child)
|
||||
new_child.tenant_name = new_tenant_name
|
||||
new_child = self.aim.create(aim_ctx, new_child)
|
||||
self.aim.delete(aim_ctx, old_child)
|
||||
self.aim.delete(aim_ctx, old_l3out)
|
||||
else:
|
||||
# Just set unrouted VRF and disable routing.
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
bd = self.aim.update(aim_ctx, bd, enable_routing=False,
|
||||
vrf_name=new_vrf.name)
|
||||
if not network_db.aim_extension_mapping.svi:
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
bd = self.aim.update(aim_ctx, bd, enable_routing=False,
|
||||
vrf_name=new_vrf.name)
|
||||
else:
|
||||
l3out = self._get_network_l3out(network_db.aim_mapping)
|
||||
l3out = self.aim.update(aim_ctx, l3out,
|
||||
vrf_name=new_vrf.name)
|
||||
|
||||
self._set_network_vrf(network_db.aim_mapping, new_vrf)
|
||||
|
||||
|
@ -2126,32 +2296,55 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
{'net': network_db.id,
|
||||
'old': old_vrf.tenant_name,
|
||||
'new': new_vrf.tenant_name})
|
||||
if network_db.aim_mapping.epg_name:
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
old_bd = self.aim.get(aim_ctx, bd)
|
||||
new_bd = copy.copy(old_bd)
|
||||
new_bd.tenant_name = new_vrf.tenant_name
|
||||
new_bd.vrf_name = new_vrf.name
|
||||
bd = self.aim.create(aim_ctx, new_bd)
|
||||
self._set_network_bd(network_db.aim_mapping, bd)
|
||||
for subnet in self.aim.find(
|
||||
aim_ctx, aim_resource.Subnet,
|
||||
tenant_name=old_bd.tenant_name,
|
||||
bd_name=old_bd.name):
|
||||
self.aim.delete(aim_ctx, subnet)
|
||||
subnet.tenant_name = bd.tenant_name
|
||||
subnet = self.aim.create(aim_ctx, subnet)
|
||||
self.aim.delete(aim_ctx, old_bd)
|
||||
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
old_bd = self.aim.get(aim_ctx, bd)
|
||||
new_bd = copy.copy(old_bd)
|
||||
new_bd.tenant_name = new_vrf.tenant_name
|
||||
new_bd.vrf_name = new_vrf.name
|
||||
bd = self.aim.create(aim_ctx, new_bd)
|
||||
self._set_network_bd(network_db.aim_mapping, bd)
|
||||
for subnet in self.aim.find(
|
||||
aim_ctx, aim_resource.Subnet,
|
||||
tenant_name=old_bd.tenant_name, bd_name=old_bd.name):
|
||||
self.aim.delete(aim_ctx, subnet)
|
||||
subnet.tenant_name = bd.tenant_name
|
||||
subnet = self.aim.create(aim_ctx, subnet)
|
||||
self.aim.delete(aim_ctx, old_bd)
|
||||
|
||||
epg = self._get_network_epg(network_db.aim_mapping)
|
||||
epg = self.aim.get(aim_ctx, epg)
|
||||
self.aim.delete(aim_ctx, epg)
|
||||
epg.tenant_name = new_vrf.tenant_name
|
||||
epg = self.aim.create(aim_ctx, epg)
|
||||
self._set_network_epg(network_db.aim_mapping, epg)
|
||||
epg = self._get_network_epg(network_db.aim_mapping)
|
||||
epg = self.aim.get(aim_ctx, epg)
|
||||
self.aim.delete(aim_ctx, epg)
|
||||
epg.tenant_name = new_vrf.tenant_name
|
||||
epg = self.aim.create(aim_ctx, epg)
|
||||
self._set_network_epg(network_db.aim_mapping, epg)
|
||||
# SVI network with auto l3out.
|
||||
elif network_db.aim_mapping.l3out_name:
|
||||
l3out = self._get_network_l3out(network_db.aim_mapping)
|
||||
old_l3out = self.aim.get(aim_ctx, l3out)
|
||||
l3out = copy.copy(old_l3out)
|
||||
l3out.tenant_name = new_vrf.tenant_name
|
||||
l3out.vrf_name = new_vrf.name
|
||||
l3out = self.aim.create(aim_ctx, l3out)
|
||||
self._set_network_l3out(network_db.aim_mapping,
|
||||
l3out)
|
||||
for old_child in self.aim.get_subtree(aim_ctx, old_l3out):
|
||||
new_child = copy.copy(old_child)
|
||||
new_child.tenant_name = new_vrf.tenant_name
|
||||
new_child = self.aim.create(aim_ctx, new_child)
|
||||
self.aim.delete(aim_ctx, old_child)
|
||||
self.aim.delete(aim_ctx, old_l3out)
|
||||
else:
|
||||
# New VRF is in same Tenant, so just set BD's VRF.
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
bd = self.aim.update(aim_ctx, bd, vrf_name=new_vrf.name)
|
||||
if network_db.aim_mapping.epg_name:
|
||||
# New VRF is in same Tenant, so just set BD's VRF.
|
||||
bd = self._get_network_bd(network_db.aim_mapping)
|
||||
bd = self.aim.update(aim_ctx, bd, vrf_name=new_vrf.name)
|
||||
elif network_db.aim_mapping.l3out_name:
|
||||
# New VRF is in same Tenant, so just set l3out's VRF.
|
||||
l3out = self._get_network_l3out(network_db.aim_mapping)
|
||||
l3out = self.aim.update(aim_ctx, l3out,
|
||||
vrf_name=new_vrf.name)
|
||||
|
||||
self._set_network_vrf(network_db.aim_mapping, new_vrf)
|
||||
|
||||
|
@ -2416,6 +2609,9 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
def _is_external(self, network):
|
||||
return network.get('router:external')
|
||||
|
||||
def _is_svi(self, network):
|
||||
return network.get(cisco_apic.SVI)
|
||||
|
||||
def _nat_type_to_strategy(self, nat_type):
|
||||
ns_cls = nat_strategy.DistributedNatStrategy
|
||||
if nat_type == '':
|
||||
|
@ -2427,9 +2623,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
ns.common_scope = self.apic_system_id
|
||||
return ns
|
||||
|
||||
def _get_aim_nat_strategy(self, network):
|
||||
if not self._is_external(network):
|
||||
return None, None, None
|
||||
def _get_aim_external_stuff(self, network):
|
||||
ext_net_dn = (network.get(cisco_apic.DIST_NAMES, {})
|
||||
.get(cisco_apic.EXTERNAL_NETWORK))
|
||||
if not ext_net_dn:
|
||||
|
@ -2440,19 +2634,28 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
tenant_name=aim_ext_net.tenant_name, name=aim_ext_net.l3out_name)
|
||||
return aim_l3out, aim_ext_net, self._nat_type_to_strategy(nat_type)
|
||||
|
||||
def _get_aim_nat_strategy(self, network):
|
||||
if not self._is_external(network):
|
||||
return None, None, None
|
||||
return self._get_aim_external_stuff(network)
|
||||
|
||||
def _get_aim_external_stuff_db(self, session, network_db):
|
||||
extn_db = extension_db.ExtensionDbMixin()
|
||||
extn_info = extn_db.get_network_extn_db(session, network_db.id)
|
||||
if extn_info and cisco_apic.EXTERNAL_NETWORK in extn_info:
|
||||
dn = extn_info[cisco_apic.EXTERNAL_NETWORK]
|
||||
a_ext_net = aim_resource.ExternalNetwork.from_dn(dn)
|
||||
a_l3out = aim_resource.L3Outside(
|
||||
tenant_name=a_ext_net.tenant_name,
|
||||
name=a_ext_net.l3out_name)
|
||||
ns = self._nat_type_to_strategy(
|
||||
extn_info.get(cisco_apic.NAT_TYPE))
|
||||
return a_l3out, a_ext_net, ns
|
||||
return None, None, None
|
||||
|
||||
def _get_aim_nat_strategy_db(self, session, network_db):
|
||||
if network_db.external is not None:
|
||||
extn_db = extension_db.ExtensionDbMixin()
|
||||
extn_info = extn_db.get_network_extn_db(session, network_db.id)
|
||||
if extn_info and cisco_apic.EXTERNAL_NETWORK in extn_info:
|
||||
dn = extn_info[cisco_apic.EXTERNAL_NETWORK]
|
||||
a_ext_net = aim_resource.ExternalNetwork.from_dn(dn)
|
||||
a_l3out = aim_resource.L3Outside(
|
||||
tenant_name=a_ext_net.tenant_name,
|
||||
name=a_ext_net.l3out_name)
|
||||
ns = self._nat_type_to_strategy(
|
||||
extn_info[cisco_apic.NAT_TYPE])
|
||||
return a_l3out, a_ext_net, ns
|
||||
return self._get_aim_external_stuff_db(session, network_db)
|
||||
return None, None, None
|
||||
|
||||
def _subnet_to_gw_ip_mask(self, subnet):
|
||||
|
@ -2764,6 +2967,131 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
self._is_supported_non_opflex_type(
|
||||
bound_segment[api.NETWORK_TYPE]))
|
||||
|
||||
def _update_static_path_for_svi(self, session, port_context, network,
|
||||
segment, old_path=None, new_path=None):
|
||||
if new_path and not segment:
|
||||
return
|
||||
|
||||
if segment:
|
||||
if segment.get(api.NETWORK_TYPE) in [pconst.TYPE_VLAN]:
|
||||
seg = 'vlan-%s' % segment[api.SEGMENTATION_ID]
|
||||
else:
|
||||
LOG.debug('Unsupported segmentation type for static path '
|
||||
'binding: %s',
|
||||
segment.get(api.NETWORK_TYPE))
|
||||
return
|
||||
|
||||
path = new_path
|
||||
if not path:
|
||||
path = old_path
|
||||
nodes = []
|
||||
node_paths = []
|
||||
is_vpc = False
|
||||
match = self.port_desc_re.match(path)
|
||||
if match:
|
||||
pod_id, switch, module, port = match.group(1, 2, 3, 4)
|
||||
nodes.append(switch)
|
||||
node_paths.append(ACI_CHASSIS_DESCR_STRING % (pod_id, switch))
|
||||
else:
|
||||
match = self.vpcport_desc_re.match(path)
|
||||
if match:
|
||||
pod_id, switch1, switch2, bundle = match.group(1, 2, 3, 4)
|
||||
nodes.append(switch1)
|
||||
nodes.append(switch2)
|
||||
node_paths.append(ACI_CHASSIS_DESCR_STRING % (pod_id,
|
||||
switch1))
|
||||
node_paths.append(ACI_CHASSIS_DESCR_STRING % (pod_id,
|
||||
switch2))
|
||||
is_vpc = True
|
||||
else:
|
||||
LOG.error('Unsupported static path format: %s', path)
|
||||
return
|
||||
|
||||
aim_ctx = aim_context.AimContext(db_session=session)
|
||||
l3out, ext_net, _ = self._get_aim_external_stuff(network)
|
||||
if new_path:
|
||||
aim_l3out_np = aim_resource.L3OutNodeProfile(
|
||||
tenant_name=l3out.tenant_name, l3out_name=l3out.name,
|
||||
name=L3OUT_NODE_PROFILE_NAME)
|
||||
aim_l3out_np_db = self.aim.get(aim_ctx, aim_l3out_np)
|
||||
if not aim_l3out_np_db:
|
||||
self.aim.create(aim_ctx, aim_l3out_np)
|
||||
|
||||
for idx, node_path in enumerate(node_paths):
|
||||
aim_l3out_node = aim_resource.L3OutNode(
|
||||
tenant_name=l3out.tenant_name, l3out_name=l3out.name,
|
||||
node_profile_name=L3OUT_NODE_PROFILE_NAME,
|
||||
node_path=node_path, router_id=APIC_ROUTER_IDS[idx],
|
||||
router_id_loopback=False)
|
||||
aim_l3out_node_db = self.aim.get(aim_ctx, aim_l3out_node)
|
||||
if not aim_l3out_node_db:
|
||||
self.aim.create(aim_ctx, aim_l3out_node)
|
||||
|
||||
aim_l3out_ip = aim_resource.L3OutInterfaceProfile(
|
||||
tenant_name=l3out.tenant_name, l3out_name=l3out.name,
|
||||
node_profile_name=L3OUT_NODE_PROFILE_NAME,
|
||||
name=L3OUT_IF_PROFILE_NAME)
|
||||
aim_l3out_ip_db = self.aim.get(aim_ctx, aim_l3out_ip)
|
||||
if not aim_l3out_ip_db:
|
||||
self.aim.create(aim_ctx, aim_l3out_ip)
|
||||
|
||||
subnet = (session.query(models_v2.Subnet)
|
||||
.filter(models_v2.Subnet.id ==
|
||||
network['subnets'][0]).one())
|
||||
mask = subnet['cidr'].split('/')[1]
|
||||
|
||||
plugin_context = port_context._plugin_context
|
||||
primary_ips = []
|
||||
for node in nodes:
|
||||
filters = {'network_id': [network['id']],
|
||||
'name': ['apic-svi-port:node-%s' % node]}
|
||||
svi_ports = self.plugin.get_ports(plugin_context, filters)
|
||||
if svi_ports and svi_ports[0]['fixed_ips']:
|
||||
ip = svi_ports[0]['fixed_ips'][0]['ip_address']
|
||||
primary_ips.append(ip + '/' + mask)
|
||||
else:
|
||||
attrs = {'device_id': '',
|
||||
'device_owner': DEVICE_OWNER_SVI_PORT,
|
||||
'tenant_id': network['tenant_id'],
|
||||
'name': 'apic-svi-port:node-%s' % node,
|
||||
'network_id': network['id'],
|
||||
'mac_address': n_constants.ATTR_NOT_SPECIFIED,
|
||||
'fixed_ips': [{'subnet_id':
|
||||
network['subnets'][0]}],
|
||||
'admin_state_up': False}
|
||||
port = self.plugin.create_port(plugin_context,
|
||||
{'port': attrs})
|
||||
if port and port['fixed_ips']:
|
||||
ip = port['fixed_ips'][0]['ip_address']
|
||||
primary_ips.append(ip + '/' + mask)
|
||||
else:
|
||||
LOG.error('cannot allocate a port for the SVI primary'
|
||||
' addr')
|
||||
return
|
||||
secondary_ip = subnet['gateway_ip'] + '/' + mask
|
||||
aim_l3out_if = aim_resource.L3OutInterface(
|
||||
tenant_name=l3out.tenant_name,
|
||||
l3out_name=l3out.name,
|
||||
node_profile_name=L3OUT_NODE_PROFILE_NAME,
|
||||
interface_profile_name=L3OUT_IF_PROFILE_NAME,
|
||||
interface_path=path, encap=seg,
|
||||
primary_addr_a=primary_ips[0],
|
||||
secondary_addr_a_list=[{'addr': secondary_ip}],
|
||||
primary_addr_b=primary_ips[1] if is_vpc else '',
|
||||
secondary_addr_b_list=[{'addr':
|
||||
secondary_ip}] if is_vpc else [])
|
||||
aim_l3out_if_db = self.aim.get(aim_ctx, aim_l3out_if)
|
||||
if not aim_l3out_if_db:
|
||||
self.aim.create(aim_ctx, aim_l3out_if)
|
||||
else:
|
||||
aim_l3out_if = aim_resource.L3OutInterface(
|
||||
tenant_name=l3out.tenant_name,
|
||||
l3out_name=l3out.name,
|
||||
node_profile_name=L3OUT_NODE_PROFILE_NAME,
|
||||
interface_profile_name=L3OUT_IF_PROFILE_NAME,
|
||||
interface_path=path)
|
||||
self.aim.delete(aim_ctx, aim_l3out_if)
|
||||
|
||||
def _update_static_path_for_network(self, session, network, segment,
|
||||
old_path=None, new_path=None):
|
||||
if new_path and not segment:
|
||||
|
@ -2833,9 +3161,15 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
{'host': host, 'interface': interface})
|
||||
continue
|
||||
host_link = host_link[0].path
|
||||
self._update_static_path_for_network(
|
||||
session, port_context.network.current, segment,
|
||||
**{'old_path' if remove else 'new_path': host_link})
|
||||
if self._is_svi(port_context.network.current):
|
||||
self._update_static_path_for_svi(
|
||||
session, port_context,
|
||||
port_context.network.current, segment,
|
||||
**{'old_path' if remove else 'new_path': host_link})
|
||||
else:
|
||||
self._update_static_path_for_network(
|
||||
session, port_context.network.current, segment,
|
||||
**{'old_path' if remove else 'new_path': host_link})
|
||||
static_path_updated = True
|
||||
|
||||
# acting as a fallback also
|
||||
|
@ -2847,9 +3181,15 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
host)
|
||||
return
|
||||
host_link = host_link[0].path
|
||||
self._update_static_path_for_network(
|
||||
session, port_context.network.current, segment,
|
||||
**{'old_path' if remove else 'new_path': host_link})
|
||||
if self._is_svi(port_context.network.current):
|
||||
self._update_static_path_for_svi(
|
||||
session, port_context,
|
||||
port_context.network.current, segment,
|
||||
**{'old_path' if remove else 'new_path': host_link})
|
||||
else:
|
||||
self._update_static_path_for_network(
|
||||
session, port_context.network.current, segment,
|
||||
**{'old_path' if remove else 'new_path': host_link})
|
||||
|
||||
def _release_dynamic_segment(self, port_context, use_original=False):
|
||||
top = (port_context.original_top_bound_segment if use_original
|
||||
|
@ -2894,6 +3234,8 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
return False
|
||||
|
||||
def _associate_domain(self, port_context, is_vmm=True):
|
||||
if self._is_svi(port_context.network.current):
|
||||
return
|
||||
port = port_context.current
|
||||
session = port_context._plugin_context.session
|
||||
aim_ctx = aim_context.AimContext(session)
|
||||
|
@ -2969,6 +3311,9 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
|
||||
# public interface for aim_mapping GBP policy driver also
|
||||
def disassociate_domain(self, port_context, use_original=False):
|
||||
if self._is_svi(port_context.network.current):
|
||||
return
|
||||
|
||||
btm = (port_context.original_bottom_bound_segment if use_original
|
||||
else port_context.bottom_bound_segment)
|
||||
if not btm:
|
||||
|
|
|
@ -240,3 +240,10 @@ def patched_get_locked_port_and_binding(context, port_id):
|
|||
|
||||
|
||||
ml2_db.get_locked_port_and_binding = patched_get_locked_port_and_binding
|
||||
|
||||
|
||||
from neutron.db import db_base_plugin_v2
|
||||
|
||||
|
||||
DEVICE_OWNER_SVI_PORT = 'apic:svi'
|
||||
db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS.append(DEVICE_OWNER_SVI_PORT)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue