X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=vpnservice%2Fvpnmanager%2Fvpnmanager-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetvirt%2Fvpnmanager%2FVpnInstanceListener.java;h=688b5f36ff916d261696b5a2ff28f65c93d0cf2b;hb=7b8210fb1b541318c08587a16e468bdf5c9a11ce;hp=d070be32e50bc118fb4131d4471a97d8f93eaab3;hpb=111ed48e226ed68cd25d4ac44384be640e6e002d;p=netvirt.git diff --git a/vpnservice/vpnmanager/vpnmanager-impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnInstanceListener.java b/vpnservice/vpnmanager/vpnmanager-impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnInstanceListener.java index d070be32e5..688b5f36ff 100644 --- a/vpnservice/vpnmanager/vpnmanager-impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnInstanceListener.java +++ b/vpnservice/vpnmanager/vpnmanager-impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnInstanceListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 - 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved. + * Copyright (c) 2015 - 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, @@ -12,96 +12,101 @@ import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ThreadFactoryBuilder; + +import java.math.BigInteger; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; + import org.opendaylight.controller.md.sal.binding.api.DataBroker; -import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase; import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator; +import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker; +import org.opendaylight.genius.mdsalutil.FlowEntity; +import org.opendaylight.genius.mdsalutil.InstructionInfo; +import org.opendaylight.genius.mdsalutil.MDSALUtil; +import org.opendaylight.genius.mdsalutil.MatchInfo; +import org.opendaylight.genius.mdsalutil.MetaDataUtil; +import org.opendaylight.genius.mdsalutil.NWUtil; +import org.opendaylight.genius.mdsalutil.NwConstants; +import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable; +import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata; +import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager; +import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId; import org.opendaylight.netvirt.bgpmanager.api.IBgpManager; import org.opendaylight.netvirt.fibmanager.api.IFibManager; +import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper; +import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.ebgp.rev150901.LayerType; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances; +import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.VpnTargets; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTarget; import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.ExternalTunnelList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.external.tunnel.list.ExternalTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.DcGatewayIpList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.dc.gateway.ip.list.DcGatewayIp; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.id.to.vpn.instance.VpnIds; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnTargetsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList; - -import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroute.Vpn; -import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTargetBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTargetKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.Vpn; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class VpnInstanceListener extends AbstractDataChangeListener implements AutoCloseable { +public class VpnInstanceListener extends AsyncDataTreeChangeListenerBase + implements AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(VpnInstanceListener.class); - private ListenerRegistration listenerRegistration; private final DataBroker dataBroker; private final IBgpManager bgpManager; private final IdManagerService idManager; private final VpnInterfaceManager vpnInterfaceManager; private final IFibManager fibManager; - private static final ThreadFactory threadFactory = new ThreadFactoryBuilder() - .setNameFormat("NV-VpnMgr-%d").build(); - private ExecutorService executorService = Executors.newSingleThreadExecutor(threadFactory); - private ConcurrentMap vpnOpMap = new ConcurrentHashMap(); + private final VpnOpDataSyncer vpnOpDataNotifier; + private final IMdsalApiManager mdsalManager; public VpnInstanceListener(final DataBroker dataBroker, final IBgpManager bgpManager, - final IdManagerService idManager, - final VpnInterfaceManager vpnInterfaceManager, - final IFibManager fibManager) { - super(VpnInstance.class); + final IdManagerService idManager, final VpnInterfaceManager vpnInterfaceManager, final IFibManager fibManager, + final VpnOpDataSyncer vpnOpDataSyncer, final IMdsalApiManager mdsalManager) { + super(VpnInstance.class, VpnInstanceListener.class); this.dataBroker = dataBroker; this.bgpManager = bgpManager; this.idManager = idManager; this.vpnInterfaceManager = vpnInterfaceManager; this.fibManager = fibManager; + this.vpnOpDataNotifier = vpnOpDataSyncer; + this.mdsalManager = mdsalManager; } public void start() { LOG.info("{} start", getClass().getSimpleName()); - listenerRegistration = dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, - getWildCardPath(), this, AsyncDataBroker.DataChangeScope.SUBTREE); + registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker); } - private InstanceIdentifier getWildCardPath() { + @Override + protected InstanceIdentifier getWildCardPath() { return InstanceIdentifier.create(VpnInstances.class).child(VpnInstance.class); } @Override - public void close() throws Exception { - if (listenerRegistration != null) { - listenerRegistration.close(); - listenerRegistration = null; - } - LOG.info("{} close", getClass().getSimpleName()); - } - - void notifyTaskIfRequired(String vpnName) { - Runnable notifyTask = vpnOpMap.remove(vpnName); - if (notifyTask == null) { - LOG.trace("VpnInstanceListener update: No Notify Task queued for vpnName {}", vpnName); - return; - } - executorService.execute(notifyTask); + protected VpnInstanceListener getDataTreeChangeListener() { + return VpnInstanceListener.this; } private void waitForOpRemoval(String rd, String vpnName) { - //wait till DCN for update on VPN Instance Op Data signals that vpn interfaces linked to this vpn instance is zero + //wait till DCN for update on VPN Instance Op Data signals that vpn interfaces linked to this vpn instance is + // zero //TODO(vpnteam): Entire code would need refactoring to listen only on the parent object - VPNInstance VpnInstanceOpDataEntry vpnOpEntry = null; Long intfCount = 0L; @@ -110,7 +115,7 @@ public class VpnInstanceListener extends AbstractDataChangeListener long timeout = VpnConstants.MIN_WAIT_TIME_IN_MILLISECONDS; Optional vpnOpValue = null; vpnOpValue = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, - VpnUtil.getVpnInstanceOpDataIdentifier(rd)); + VpnUtil.getVpnInstanceOpDataIdentifier(rd)); if ((vpnOpValue != null) && (vpnOpValue.isPresent())) { vpnOpEntry = vpnOpValue.get(); @@ -132,19 +137,20 @@ public class VpnInstanceListener extends AbstractDataChangeListener timeout = VpnConstants.MAX_WAIT_TIME_IN_MILLISECONDS; } LOG.info("VPNInstance removal count of interface at {} for for rd {}, vpnname {}", - intfCount, rd, vpnName); + intfCount, rd, vpnName); } LOG.info("VPNInstance removal thread waiting for {} seconds for rd {}, vpnname {}", - (timeout / 1000), rd, vpnName); + (timeout / 1000), rd, vpnName); try { Thread.sleep(timeout); } catch (InterruptedException e) { + // Ignored } // Check current interface count vpnOpValue = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, - VpnUtil.getVpnInstanceOpDataIdentifier(rd)); + VpnUtil.getVpnInstanceOpDataIdentifier(rd)); if ((vpnOpValue != null) && (vpnOpValue.isPresent())) { vpnOpEntry = vpnOpValue.get(); dpnToVpns = vpnOpEntry.getVpnToDpnList(); @@ -168,48 +174,56 @@ public class VpnInstanceListener extends AbstractDataChangeListener } else { if (retryCount > 0) { retryCount--; - LOG.info("Retrying clearing vpn with vpnname {} rd {} since current interface count {} ", vpnName, rd, currentIntfCount); + LOG.info( + "Retrying clearing vpn with vpnname {} rd {} since current interface count {} ", + vpnName, rd, currentIntfCount); if (currentIntfCount > 0) { intfCount = currentIntfCount; } else { - LOG.info("Current interface count is zero, but instance Op for vpn {} and rd {} not cleared yet. Waiting for 5 more seconds.", vpnName, rd); + LOG.info( + "Current interface count is zero, but instance Op for vpn {} and rd {} not " + + "cleared yet. Waiting for 5 more seconds.", + vpnName, rd); intfCount = 1L; } } else { - LOG.info("VPNInstance bailing out of wait loop as current interface count is {} and max retries exceeded for for vpnName {}, rd {}", - currentIntfCount, vpnName, rd); + LOG.info( + "VPNInstance bailing out of wait loop as current interface count is {} and max " + + "retries exceeded for for vpnName {}, rd {}", + currentIntfCount, vpnName, rd); break; } } + } else { + LOG.info("Retrying clearing because not all vpnInterfaces removed : current interface count {}," + + " initial count {} for rd {}, vpnname {}", currentIntfCount, intfCount, rd, vpnName); + intfCount = currentIntfCount; } } else { // There is no VPNOPEntry. Something else happened on the system ! // So let us quit and take our chances. //TODO(vpnteam): L3VPN refactoring to take care of this case. + LOG.error("VpnInstanceOpData is not present in the operational DS for rd {}, vpnname {}", rd, + vpnName); break; } } } LOG.info("Returned out of waiting for Op Data removal for rd {}, vpnname {}", rd, vpnName); } + @Override protected void remove(InstanceIdentifier identifier, VpnInstance del) { LOG.trace("Remove VPN event key: {}, value: {}", identifier, del); final String vpnName = del.getVpnInstanceName(); - final String rd = del.getIpv4Family().getRouteDistinguisher(); - final long vpnId = VpnUtil.getVpnId(dataBroker, vpnName); Optional vpnOpValue = null; + String primaryRd = VpnUtil.getPrimaryRd(del); //TODO(vpnteam): Entire code would need refactoring to listen only on the parent object - VPNInstance try { - if ((rd != null) && (!rd.isEmpty())) { - vpnOpValue = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, - VpnUtil.getVpnInstanceOpDataIdentifier(rd)); - } else { - vpnOpValue = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, - VpnUtil.getVpnInstanceOpDataIdentifier(vpnName)); - } - } catch (Exception e) { + vpnOpValue = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL, + VpnUtil.getVpnInstanceOpDataIdentifier(primaryRd)); + } catch (ReadFailedException e) { LOG.error("Exception when attempting to retrieve VpnInstanceOpDataEntry for VPN {}. ", vpnName, e); return; } @@ -221,7 +235,7 @@ public class VpnInstanceListener extends AbstractDataChangeListener DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance(); dataStoreCoordinator.enqueueJob("VPN-" + vpnName, - new DeleteVpnInstanceWorker(idManager, dataBroker, del)); + new DeleteVpnInstanceWorker(idManager, dataBroker, del)); } private class DeleteVpnInstanceWorker implements Callable>> { @@ -229,64 +243,48 @@ public class VpnInstanceListener extends AbstractDataChangeListener DataBroker broker; VpnInstance vpnInstance; - public DeleteVpnInstanceWorker(IdManagerService idManager, - DataBroker broker, - VpnInstance value) { + DeleteVpnInstanceWorker(IdManagerService idManager, + DataBroker broker, + VpnInstance value) { this.idManager = idManager; this.broker = broker; this.vpnInstance = value; } @Override - public List> call() throws Exception { + public List> call() { final String vpnName = vpnInstance.getVpnInstanceName(); - final String rd = vpnInstance.getIpv4Family().getRouteDistinguisher(); + final List rds = vpnInstance.getIpv4Family().getRouteDistinguisher(); + String primaryRd = VpnUtil.getPrimaryRd(vpnInstance); final long vpnId = VpnUtil.getVpnId(broker, vpnName); WriteTransaction writeTxn = broker.newWriteOnlyTransaction(); - if ((rd != null) && (!rd.isEmpty())) { - waitForOpRemoval(rd, vpnName); - } else { - waitForOpRemoval(vpnName, vpnName); - } + waitForOpRemoval(primaryRd, vpnName); // Clean up VpnInstanceToVpnId from Config DS VpnUtil.removeVpnIdToVpnInstance(broker, vpnId, writeTxn); VpnUtil.removeVpnInstanceToVpnId(broker, vpnName, writeTxn); - LOG.trace("Removed vpnIdentifier for rd{} vpnname {}", rd, vpnName); - if (rd != null) { - synchronized (vpnName.intern()) { - fibManager.removeVrfTable(broker, rd, null); - } - try { - bgpManager.deleteVrf(rd, false); - } catch (Exception e) { - LOG.error("Exception when removing VRF from BGP for RD {} in VPN {} exception " + e, rd, vpnName); - } - - // Clean up VPNExtraRoutes Operational DS - InstanceIdentifier vpnToExtraroute = VpnUtil.getVpnToExtrarouteIdentifier(rd); - Optional optVpnToExtraroute = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, vpnToExtraroute); - if (optVpnToExtraroute.isPresent()) { - VpnUtil.removeVpnExtraRouteForVpn(broker, rd, writeTxn); - } - - // Clean up VPNInstanceOpDataEntry - VpnUtil.removeVpnOpInstance(broker, rd, writeTxn); - } else { - // Clean up FIB Entries Config DS - synchronized (vpnName.intern()) { - fibManager.removeVrfTable(broker, vpnName, null); - } - // Clean up VPNExtraRoutes Operational DS - InstanceIdentifier vpnToExtraroute = VpnUtil.getVpnToExtrarouteIdentifier(vpnName); - Optional optVpnToExtraroute = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, vpnToExtraroute); - if (optVpnToExtraroute.isPresent()) { - VpnUtil.removeVpnExtraRouteForVpn(broker, vpnName, writeTxn); - } + LOG.trace("Removed vpnIdentifier for rd{} vpnname {}", primaryRd, vpnName); + // Clean up FIB Entries Config DS + synchronized (vpnName.intern()) { + fibManager.removeVrfTable(broker, primaryRd, null); + } + if (VpnUtil.isBgpVpn(vpnName, primaryRd)) { + rds.parallelStream().forEach(rd -> bgpManager.deleteVrf(rd, false)); + } + // Clean up VPNExtraRoutes Operational DS + InstanceIdentifier vpnToExtraroute = VpnExtraRouteHelper.getVpnToExtrarouteVpnIdentifier(vpnName); + Optional optVpnToExtraroute = VpnUtil.read(broker, + LogicalDatastoreType.OPERATIONAL, vpnToExtraroute); + if (optVpnToExtraroute.isPresent()) { + VpnUtil.removeVpnExtraRouteForVpn(broker, vpnName, writeTxn); + } - // Clean up VPNInstanceOpDataEntry - VpnUtil.removeVpnOpInstance(broker, vpnName, writeTxn); + if (VpnUtil.isL3VpnOverVxLan(vpnInstance.getL3vni())) { + removeExternalTunnelDemuxFlows(vpnName); } + + // Clean up VPNInstanceOpDataEntry + VpnUtil.removeVpnOpInstance(broker, primaryRd, writeTxn); // Clean up PrefixToInterface Operational DS VpnUtil.removePrefixToInterfaceForVpnId(broker, vpnId, writeTxn); @@ -304,20 +302,18 @@ public class VpnInstanceListener extends AbstractDataChangeListener @Override protected void update(InstanceIdentifier identifier, - VpnInstance original, VpnInstance update) { + VpnInstance original, VpnInstance update) { LOG.trace("Update VPN event key: {}, value: {}", identifier, update); } @Override protected void add(final InstanceIdentifier identifier, final VpnInstance value) { LOG.trace("Add VPN event key: {}, value: {}", identifier, value); - final VpnAfConfig config = value.getIpv4Family(); - final String rd = config.getRouteDistinguisher(); final String vpnName = value.getVpnInstanceName(); DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance(); dataStoreCoordinator.enqueueJob("VPN-" + vpnName, - new AddVpnInstanceWorker(idManager, vpnInterfaceManager, dataBroker, value)); + new AddVpnInstanceWorker(idManager, vpnInterfaceManager, dataBroker, value)); } private class AddVpnInstanceWorker implements Callable>> { @@ -326,10 +322,10 @@ public class VpnInstanceListener extends AbstractDataChangeListener VpnInstance vpnInstance; DataBroker broker; - public AddVpnInstanceWorker(IdManagerService idManager, - VpnInterfaceManager vpnInterfaceManager, - DataBroker broker, - VpnInstance value) { + AddVpnInstanceWorker(IdManagerService idManager, + VpnInterfaceManager vpnInterfaceManager, + DataBroker broker, + VpnInstance value) { this.idManager = idManager; this.vpnInterfaceManager = vpnInterfaceManager; this.broker = broker; @@ -340,8 +336,6 @@ public class VpnInstanceListener extends AbstractDataChangeListener public List> call() throws Exception { // If another renderer(for eg : CSS) needs to be supported, check can be performed here // to call the respective helpers. - final VpnAfConfig config = vpnInstance.getIpv4Family(); - final String rd = config.getRouteDistinguisher(); WriteTransaction writeConfigTxn = broker.newWriteOnlyTransaction(); WriteTransaction writeOperTxn = broker.newWriteOnlyTransaction(); addVpnInstance(vpnInstance, writeConfigTxn, writeOperTxn); @@ -355,118 +349,181 @@ public class VpnInstanceListener extends AbstractDataChangeListener List> futures = new ArrayList<>(); futures.add(writeConfigTxn.submit()); ListenableFuture> listenableFuture = Futures.allAsList(futures); - if (rd != null) { - Futures.addCallback(listenableFuture, - new AddBgpVrfWorker(config , vpnInstance.getVpnInstanceName())); - } + Futures.addCallback(listenableFuture, + new PostAddVpnInstanceWorker(vpnInstance , vpnInstance.getVpnInstanceName())); return futures; } } + // TODO Clean up the exception handling + @SuppressWarnings("checkstyle:IllegalCatch") private void addVpnInstance(VpnInstance value, WriteTransaction writeConfigTxn, - WriteTransaction writeOperTxn) { + WriteTransaction writeOperTxn) { VpnAfConfig config = value.getIpv4Family(); - String rd = config.getRouteDistinguisher(); String vpnInstanceName = value.getVpnInstanceName(); long vpnId = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME, vpnInstanceName); - LOG.trace("VPN instance to ID generated."); + if (vpnId == 0) { + LOG.error( + "Unable to fetch label from Id Manager. Bailing out of adding operational data for Vpn Instance {}", + value.getVpnInstanceName()); + LOG.error( + "Unable to fetch label from Id Manager. Bailing out of adding operational data for Vpn Instance {}", + value.getVpnInstanceName()); + return; + } + LOG.info("VPN Id {} generated for VpnInstanceName {}", vpnId, vpnInstanceName); + String primaryRd = VpnUtil.getPrimaryRd(value); org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance - vpnInstanceToVpnId = VpnUtil.getVpnInstanceToVpnId(vpnInstanceName, vpnId, (rd != null) ? rd - : vpnInstanceName); + vpnInstanceToVpnId = VpnUtil.getVpnInstanceToVpnId(vpnInstanceName, vpnId, primaryRd); if (writeConfigTxn != null) { writeConfigTxn.put(LogicalDatastoreType.CONFIGURATION, - VpnUtil.getVpnInstanceToVpnIdIdentifier(vpnInstanceName), - vpnInstanceToVpnId, true); + VpnUtil.getVpnInstanceToVpnIdIdentifier(vpnInstanceName), + vpnInstanceToVpnId, true); } else { - TransactionUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, - VpnUtil.getVpnInstanceToVpnIdIdentifier(vpnInstanceName), - vpnInstanceToVpnId, TransactionUtil.DEFAULT_CALLBACK); + TransactionUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, + VpnUtil.getVpnInstanceToVpnIdIdentifier(vpnInstanceName), + vpnInstanceToVpnId, TransactionUtil.DEFAULT_CALLBACK); } VpnIds vpnIdToVpnInstance = VpnUtil.getVpnIdToVpnInstance(vpnId, value.getVpnInstanceName(), - (rd != null) ? rd : value.getVpnInstanceName(), (rd != null)/*isExternalVpn*/); + primaryRd, VpnUtil.isBgpVpn(vpnInstanceName, primaryRd)); if (writeConfigTxn != null) { writeConfigTxn.put(LogicalDatastoreType.CONFIGURATION, - VpnUtil.getVpnIdToVpnInstanceIdentifier(vpnId), - vpnIdToVpnInstance, true); + VpnUtil.getVpnIdToVpnInstanceIdentifier(vpnId), + vpnIdToVpnInstance, true); } else { - TransactionUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, - VpnUtil.getVpnIdToVpnInstanceIdentifier(vpnId), - vpnIdToVpnInstance, TransactionUtil.DEFAULT_CALLBACK); + TransactionUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, + VpnUtil.getVpnIdToVpnInstanceIdentifier(vpnId), + vpnIdToVpnInstance, TransactionUtil.DEFAULT_CALLBACK); } try { String cachedTransType = fibManager.getConfTransType(); - LOG.trace("Value for confTransportType is " + cachedTransType); if (cachedTransType.equals("Invalid")) { try { fibManager.setConfTransType("L3VPN", "VXLAN"); } catch (Exception e) { - LOG.trace("Exception caught setting the cached value for transportType"); - LOG.error(e.getMessage()); + LOG.error("Exception caught setting the L3VPN tunnel transportType", e); } } else { - LOG.trace(":cached val is neither unset/invalid. NO-op."); + LOG.trace("Configured tunnel transport type for L3VPN as {}", cachedTransType); } } catch (Exception e) { - LOG.error(e.getMessage()); + LOG.error("Error when trying to retrieve tunnel transport type for L3VPN ", e); } - if (rd == null) { - VpnInstanceOpDataEntryBuilder builder = - new VpnInstanceOpDataEntryBuilder().setVrfId(vpnInstanceName).setVpnId(vpnId) - .setVpnInstanceName(vpnInstanceName); - if (writeOperTxn != null) { - writeOperTxn.merge(LogicalDatastoreType.OPERATIONAL, - VpnUtil.getVpnInstanceOpDataIdentifier(vpnInstanceName), - builder.build(), true); - } else { - TransactionUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, - VpnUtil.getVpnInstanceOpDataIdentifier(vpnInstanceName), - builder.build(), TransactionUtil.DEFAULT_CALLBACK); + VpnInstanceOpDataEntryBuilder builder = + new VpnInstanceOpDataEntryBuilder().setVrfId(primaryRd).setVpnId(vpnId) + .setVpnInstanceName(vpnInstanceName); + if (VpnUtil.isBgpVpn(vpnInstanceName, primaryRd)) { + List opVpnTargetList = new ArrayList<>(); + if (value.getL3vni() != null) { + builder.setL3vni(value.getL3vni()); } - } else { - VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder() - .setVrfId(rd).setVpnId(vpnId).setVpnInstanceName(vpnInstanceName); - - if (writeOperTxn != null) { - writeOperTxn.merge(LogicalDatastoreType.OPERATIONAL, - VpnUtil.getVpnInstanceOpDataIdentifier(rd), - builder.build(), true); - } else { - TransactionUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, - VpnUtil.getVpnInstanceOpDataIdentifier(rd), - builder.build(), TransactionUtil.DEFAULT_CALLBACK); + VpnTargets vpnTargets = config.getVpnTargets(); + if (vpnTargets != null) { + List vpnTargetList = vpnTargets.getVpnTarget(); + if (vpnTargetList != null) { + for (VpnTarget vpnTarget : vpnTargetList) { + VpnTargetBuilder vpnTargetBuilder = + new VpnTargetBuilder().setKey(new VpnTargetKey(vpnTarget.getKey().getVrfRTValue())) + .setVrfRTType(org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn + .instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTarget.VrfRTType + .forValue(vpnTarget.getVrfRTType().getIntValue())).setVrfRTValue( + vpnTarget.getVrfRTValue()); + opVpnTargetList.add(vpnTargetBuilder.build()); + } + } } + VpnTargetsBuilder vpnTargetsBuilder = new VpnTargetsBuilder().setVpnTarget(opVpnTargetList); + builder.setVpnTargets(vpnTargetsBuilder.build()); + } + if (writeOperTxn != null) { + writeOperTxn.merge(LogicalDatastoreType.OPERATIONAL, + VpnUtil.getVpnInstanceOpDataIdentifier(primaryRd), + builder.build(), true); + } else { + TransactionUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, + VpnUtil.getVpnInstanceOpDataIdentifier(primaryRd), + builder.build(), TransactionUtil.DEFAULT_CALLBACK); } + LOG.info("VpnInstanceOpData populated successfully for vpn {} rd {}", vpnInstanceName, primaryRd); } - - private class AddBgpVrfWorker implements FutureCallback> { - VpnAfConfig config; + private class PostAddVpnInstanceWorker implements FutureCallback> { + VpnInstance vpnInstance; String vpnName; - public AddBgpVrfWorker(VpnAfConfig config, String vpnName) { - this.config = config; + PostAddVpnInstanceWorker(VpnInstance vpnInstance, String vpnName) { + this.vpnInstance = vpnInstance; this.vpnName = vpnName; } /** - * @param voids * This implies that all the future instances have returned success. -- TODO: Confirm this */ @Override public void onSuccess(List voids) { - String rd = config.getRouteDistinguisher(); - if (rd != null) { - List vpnTargetList = config.getVpnTargets().getVpnTarget(); + /* + if rd is null, then its either a router vpn instance (or) a vlan external network vpn instance. + if rd is non-null, then it is a bgpvpn instance + */ + VpnAfConfig config = vpnInstance.getIpv4Family(); + List rd = config.getRouteDistinguisher(); + if ((rd == null) || addBgpVrf(voids)) { + notifyTask(); + vpnInterfaceManager.vpnInstanceIsReady(vpnName); + } + VpnInstanceOpDataEntry vpnInstanceOpDataEntry = VpnUtil.getVpnInstanceOpData(dataBroker, + VpnUtil.getPrimaryRd(vpnInstance)); + + // bind service on each tunnel interface + //TODO (KIRAN): Add a new listener to handle creation of new DC-GW binding and deletion of existing DC-GW. + if (VpnUtil.isL3VpnOverVxLan(vpnInstance.getL3vni())) { //Handled for L3VPN Over VxLAN + for (String tunnelInterfaceName: getDcGatewayTunnelInterfaceNameList()) { + VpnUtil.bindService(vpnInstance.getVpnInstanceName(), tunnelInterfaceName, dataBroker, + true/*isTunnelInterface*/); + } - List ertList = new ArrayList(); - List irtList = new ArrayList(); + // install flow + List mkMatches = new ArrayList<>(); + mkMatches.add(new MatchTunnelId(BigInteger.valueOf(vpnInstance.getL3vni()))); + + List instructions = + Arrays.asList(new InstructionWriteMetadata(MetaDataUtil.getVpnIdMetadata(vpnInstanceOpDataEntry + .getVpnId()), MetaDataUtil.METADATA_MASK_VRFID), + new InstructionGotoTable(NwConstants.L3_GW_MAC_TABLE)); + + for (BigInteger dpnId: NWUtil.getOperativeDPNs(dataBroker)) { + String flowRef = getFibFlowRef(dpnId, NwConstants.L3VNI_EXTERNAL_TUNNEL_DEMUX_TABLE, + vpnName, VpnConstants.DEFAULT_FLOW_PRIORITY); + FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, + NwConstants.L3VNI_EXTERNAL_TUNNEL_DEMUX_TABLE, flowRef, VpnConstants.DEFAULT_FLOW_PRIORITY, + "VxLAN VPN Tunnel Bind Service", 0, 0, NwConstants.COOKIE_VM_FIB_TABLE, + mkMatches, instructions); + mdsalManager.installFlow(dpnId, flowEntity); + } + /////////////////////// + } + } + + // TODO Clean up the exception handling + @SuppressWarnings("checkstyle:IllegalCatch") + private boolean addBgpVrf(List voids) { + VpnAfConfig config = vpnInstance.getIpv4Family(); + List rds = config.getRouteDistinguisher(); + String primaryRd = VpnUtil.getPrimaryRd(dataBroker, vpnName); + List vpnTargetList = config.getVpnTargets().getVpnTarget(); + + List ertList = new ArrayList<>(); + List irtList = new ArrayList<>(); + + if (vpnTargetList != null) { for (VpnTarget vpnTarget : vpnTargetList) { if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) { ertList.add(vpnTarget.getVrfRTValue()); @@ -479,19 +536,33 @@ public class VpnInstanceListener extends AbstractDataChangeListener irtList.add(vpnTarget.getVrfRTValue()); } } - + } else { + LOG.error("vpn target list is empty, cannot add BGP VPN {} VRF {}", this.vpnName, primaryRd); + return false; + } + //Advertise all the rds and check if primary Rd advertisement fails + long primaryRdAddFailed = rds.parallelStream().filter(rd -> { try { - bgpManager.addVrf(rd, irtList, ertList); + bgpManager.addVrf(rd, irtList, ertList, LayerType.LAYER3); } catch (Exception e) { - LOG.error("Exception when adding VRF to BGP", e); - return; + LOG.error("Exception when adding VRF {} to BGP {}. Exception {}", rd, vpnName, e); + return rd.equals(primaryRd); } - vpnInterfaceManager.handleVpnsExportingRoutes(this.vpnName, rd); + return false; + }).count(); + if (primaryRdAddFailed == 1) { + return false; } + vpnInterfaceManager.handleVpnsExportingRoutes(this.vpnName, primaryRd); + return true; + } + + private void notifyTask() { + vpnOpDataNotifier.notifyVpnOpDataReady(VpnOpDataSyncer.VpnOpDataType.vpnInstanceToId, vpnName); + vpnOpDataNotifier.notifyVpnOpDataReady(VpnOpDataSyncer.VpnOpDataType.vpnOpData, vpnName); } + /** - * - * @param throwable * This method is used to handle failure callbacks. * If more retry needed, the retrycount is decremented and mainworker is executed again. * After retries completed, rollbackworker is executed. @@ -500,19 +571,19 @@ public class VpnInstanceListener extends AbstractDataChangeListener @Override public void onFailure(Throwable throwable) { - LOG.warn("Job: failed with exception: {}", throwable.getStackTrace()); + LOG.error("Job for vpnInstance: {} failed with exception: {}", vpnName ,throwable); + vpnInterfaceManager.vpnInstanceFailed(vpnName); } } public boolean isVPNConfigured() { - InstanceIdentifier vpnsIdentifier = - InstanceIdentifier.builder(VpnInstances.class).build(); + InstanceIdentifier vpnsIdentifier = InstanceIdentifier.builder(VpnInstances.class).build(); Optional optionalVpns = TransactionUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, - vpnsIdentifier); - if (!optionalVpns.isPresent() || - optionalVpns.get().getVpnInstance() == null || - optionalVpns.get().getVpnInstance().isEmpty()) { + vpnsIdentifier); + if (!optionalVpns.isPresent() + || optionalVpns.get().getVpnInstance() == null + || optionalVpns.get().getVpnInstance().isEmpty()) { LOG.trace("No VPNs configured."); return false; } @@ -523,10 +594,82 @@ public class VpnInstanceListener extends AbstractDataChangeListener protected VpnInstanceOpDataEntry getVpnInstanceOpData(String rd) { InstanceIdentifier id = VpnUtil.getVpnInstanceOpDataIdentifier(rd); Optional vpnInstanceOpData = - TransactionUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id); - if(vpnInstanceOpData.isPresent()) { + TransactionUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id); + if (vpnInstanceOpData.isPresent()) { return vpnInstanceOpData.get(); } return null; } + + private List getDcGatewayTunnelInterfaceNameList() { + List tunnelInterfaceNameList = new ArrayList<>(); + + InstanceIdentifier dcGatewayIpListInstanceIdentifier = InstanceIdentifier + .create(DcGatewayIpList.class); + Optional dcGatewayIpListOptional = VpnUtil.read(dataBroker, + LogicalDatastoreType.CONFIGURATION, dcGatewayIpListInstanceIdentifier); + if (!dcGatewayIpListOptional.isPresent()) { + LOG.info("No DC gateways configured."); + return tunnelInterfaceNameList; + } + List dcGatewayIps = dcGatewayIpListOptional.get().getDcGatewayIp(); + + InstanceIdentifier externalTunnelListId = InstanceIdentifier + .create(ExternalTunnelList.class); + + Optional externalTunnelListOptional = VpnUtil.read(dataBroker, + LogicalDatastoreType.OPERATIONAL, externalTunnelListId); + if (externalTunnelListOptional.isPresent()) { + List externalTunnels = externalTunnelListOptional.get().getExternalTunnel(); + + List externalTunnelIpList = new ArrayList<>(); + for (ExternalTunnel externalTunnel: externalTunnels) { + externalTunnelIpList.add(externalTunnel.getDestinationDevice()); + } + + List dcGatewayIpList = new ArrayList<>(); + for (DcGatewayIp dcGatewayIp: dcGatewayIps) { + dcGatewayIpList.add(dcGatewayIp.getIpAddress().getIpv4Address().toString()); + } + + // Find all externalTunnelIps present in dcGateWayIpList + List externalTunnelIpsInDcGatewayIpList = new ArrayList<>(); + for (String externalTunnelIp: externalTunnelIpList) { + for (String dcGateWayIp: dcGatewayIpList) { + if (externalTunnelIp.contentEquals(dcGateWayIp)) { + externalTunnelIpsInDcGatewayIpList.add(externalTunnelIp); + } + } + } + + + for (String externalTunnelIpsInDcGatewayIp: externalTunnelIpsInDcGatewayIpList) { + for (ExternalTunnel externalTunnel: externalTunnels) { + if (externalTunnel.getDestinationDevice().contentEquals(externalTunnelIpsInDcGatewayIp)) { + tunnelInterfaceNameList.add(externalTunnel.getTunnelInterfaceName()); + } + } + } + + } + + return tunnelInterfaceNameList; + } + + private String getFibFlowRef(BigInteger dpnId, short tableId, String vpnName, int priority) { + return VpnConstants.FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + + NwConstants.FLOWID_SEPARATOR + vpnName + NwConstants.FLOWID_SEPARATOR + priority; + } + + private void removeExternalTunnelDemuxFlows(String vpnName) { + LOG.info("Removing external tunnel flows for vpn {}", vpnName); + for (BigInteger dpnId: NWUtil.getOperativeDPNs(dataBroker)) { + LOG.debug("Removing external tunnel flows for vpn {} from dpn {}", vpnName, dpnId); + String flowRef = getFibFlowRef(dpnId, NwConstants.L3VNI_EXTERNAL_TUNNEL_DEMUX_TABLE, + vpnName, VpnConstants.DEFAULT_FLOW_PRIORITY); + FlowEntity flowEntity = VpnUtil.buildFlowEntity(dpnId, + NwConstants.L3VNI_EXTERNAL_TUNNEL_DEMUX_TABLE, flowRef); + mdsalManager.removeFlow(flowEntity); + } + } }