From: Karthikeyan Krishnan Date: Wed, 29 Jul 2020 16:01:31 +0000 (+0530) Subject: Create l3vpn & asso with ext-nw failed X-Git-Tag: release/aluminium~4 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=2feb8577524f4ebe7fe53702a9fc8e602e901256;p=netvirt.git Create l3vpn & asso with ext-nw failed Issue: ======= Creation of SNAT use case via REST API fails creating vpn (Creation and Association to VPN in Single REST API Call) Using Jumbo RPC Call to create the internet BGP-VPN instance(l3vpn) and at the same request call associate the external GRE provider network also in a single REST API call is FAILED due to absence of vpn-instance OPERATIONAL data store. Solution: ========== NeutronVPN YANG model has redesigned properly to work internet VPN use case for both V4 and V6 networks. "vpn-instance-op-data" should be created/updated/removed by VPN Module only. Since it is VPN drivern OPERATIONAL data store. Have modified the NeutronVPN design to just remove the "vpn-instance-op-data" from NeutronVPN module to VPN Module. "vpn-instance-op-data" is populated based on the CONFIG vpn-instance data. Signed-off-by: Karthikeyan Krishnan Change-Id: I6ff3e54c3302eb0ee3c6f238dc27f2567ca5f72a --- diff --git a/neutronvpn/impl/src/main/java/org/opendaylight/netvirt/neutronvpn/NeutronvpnManager.java b/neutronvpn/impl/src/main/java/org/opendaylight/netvirt/neutronvpn/NeutronvpnManager.java index dc9fdfb46c..b44bef074a 100644 --- a/neutronvpn/impl/src/main/java/org/opendaylight/netvirt/neutronvpn/NeutronvpnManager.java +++ b/neutronvpn/impl/src/main/java/org/opendaylight/netvirt/neutronvpn/NeutronvpnManager.java @@ -33,6 +33,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -72,7 +73,6 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types. import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.data.LearntVpnVipToPort; 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.VpnInstanceOpDataEntry.BgpvpnType; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalNetworks; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.Networks; @@ -587,7 +587,7 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even LOG.debug("updating existing vpninstance node"); } else { builder = new VpnInstanceBuilder().withKey(new VpnInstanceKey(vpnName)).setVpnInstanceName(vpnName) - .setL2vpn(isL2Vpn).setL3vni(l3vni); + .setL2vpn(isL2Vpn).setL3vni(l3vni).setBgpvpnType(VpnInstance.BgpvpnType.InternalVPN); } if (irt != null && !irt.isEmpty()) { if (ert != null && !ert.isEmpty()) { @@ -622,7 +622,7 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even VpnTargets vpnTargets = new VpnTargetsBuilder().setVpnTarget(vpnTargetList).build(); if (rd != null && !rd.isEmpty()) { - builder.setRouteDistinguisher(rd).setVpnTargets(vpnTargets); + builder.setRouteDistinguisher(rd).setVpnTargets(vpnTargets).setBgpvpnType(VpnInstance.BgpvpnType.BGPVPN); } builder.setIpAddressFamilyConfigured(VpnInstance.IpAddressFamilyConfigured.forValue(ipVersion.choice)); @@ -2358,7 +2358,7 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even } } if (ipVersion != IpVersionChoice.UNDEFINED) { - LOG.debug("associateRouterToVpn: Updating vpnInstanceOpDataEntrywith ip address family {} for VPN {} ", + LOG.debug("associateRouterToVpn: Updating vpnInstance ip address family {} for VPN {} ", ipVersion, vpnId); neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, true); } @@ -2399,7 +2399,7 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even updateVpnForSubnet(vpnId, routerId, sn.getId(), false); } if (ipVersion != IpVersionChoice.UNDEFINED) { - LOG.debug("dissociateRouterFromVpn; Updating vpnInstanceOpDataEntry with ip address family {} for VPN {} ", + LOG.debug("dissociateRouterFromVpn; Updating vpnInstance with ip address family {} for VPN {} ", ipVersion, vpnId); neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, false); @@ -2417,6 +2417,7 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even protected List associateNetworksToVpn(@NonNull Uuid vpnId, @NonNull List networkList) { List failedNwList = new ArrayList<>(); HashSet passedNwList = new HashSet<>(); + ConcurrentMap extNwMap = new ConcurrentHashMap<>(); boolean isExternalNetwork = false; if (networkList.isEmpty()) { LOG.error("associateNetworksToVpn: Failed as given networks list is empty, VPN Id: {}", vpnId.getValue()); @@ -2440,6 +2441,7 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even return failedNwList; } Set routeTargets = vpnManager.getRtListForVpn(vpnId.getValue()); + boolean isIpFamilyUpdated = false; for (Uuid nw : networkList) { Network network = neutronvpnUtils.getNeutronNetwork(nw); if (network == null) { @@ -2464,12 +2466,11 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even + "another VPN %s", nw.getValue(), networkVpnId.getValue())); continue; } - if (NeutronvpnUtils.getIsExternal(network) && !associateExtNetworkToVpn(vpnId, network)) { - LOG.error("associateNetworksToVpn: Failed to associate Provider Network {} with VPN {}", - nw.getValue(), vpnId.getValue()); - failedNwList.add(String.format("Failed to associate Provider Network %s with VPN %s", - nw.getValue(), vpnId.getValue())); - continue; + /* Handle association of external network(s) to Internet BGP-VPN use case outside of the + * networkList iteration + */ + if (neutronvpnUtils.getIsExternal(network)) { + extNwMap.put(nw, network); } if (NeutronvpnUtils.getIsExternal(network)) { isExternalNetwork = true; @@ -2489,10 +2490,12 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even ipVersion = ipVersion.addVersion(ipVers); } } - if (ipVersion != IpVersionChoice.UNDEFINED) { - LOG.debug("associateNetworksToVpn: Updating vpnInstanceOpDataEntry with ip address family {}" + //Update vpnInstance for IP address family + if (ipVersion != IpVersionChoice.UNDEFINED && !isIpFamilyUpdated) { + LOG.debug("associateNetworksToVpn: Updating vpnInstance with ip address family {}" + " for VPN {} ", ipVersion, vpnId); neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, true); + isIpFamilyUpdated = true; } for (Subnetmap subnetmap : subnetmapList) { Uuid subnetId = subnetmap.getId(); @@ -2514,6 +2517,18 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even } } passedNwList.add(nw); + //Handle association of external network(s) to Internet BGP-VPN Instance use case + if (!extNwMap.isEmpty() || extNwMap != null) { + for (Network extNw : extNwMap.values()) { + if (!associateExtNetworkToVpn(vpnId, extNw, vpnInstance.getBgpvpnType())) { + LOG.error("associateNetworksToVpn: Failed to associate Provider External Network {} with " + + "VPN {}", extNw, vpnId.getValue()); + failedNwList.add(String.format("Failed to associate Provider External Network %s with " + + "VPN %s", extNw, vpnId.getValue())); + continue; + } + } + } } } catch (ExecutionException | InterruptedException e) { LOG.error("associateNetworksToVpn: Failed to associate VPN {} with networks {}: ", vpnId.getValue(), @@ -2529,18 +2544,17 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even return failedNwList; } - private boolean associateExtNetworkToVpn(@NonNull Uuid vpnId, @NonNull Network extNet) { + private boolean associateExtNetworkToVpn(@NonNull Uuid vpnId, @NonNull Network extNet, + VpnInstance.BgpvpnType bgpVpnType) { if (!addExternalNetworkToVpn(extNet, vpnId)) { return false; } - VpnInstanceOpDataEntry vpnOpDataEntry = neutronvpnUtils.getVpnInstanceOpDataEntryFromVpnId(vpnId.getValue()); - if (vpnOpDataEntry == null) { - LOG.error("associateExtNetworkToVpn: can not find VpnOpDataEntry for VPN {}", vpnId.getValue()); - return false; - } - if (!vpnOpDataEntry.getBgpvpnType().equals(BgpvpnType.BGPVPNInternet)) { - LOG.info("associateExtNetworkToVpn: set type {} for VPN {}", BgpvpnType.BGPVPNInternet, vpnId.getValue()); - neutronvpnUtils.updateVpnInstanceOpWithType(BgpvpnType.BGPVPNInternet, vpnId); + if (!bgpVpnType.equals(VpnInstance.BgpvpnType.InternetBGPVPN)) { + LOG.info("associateExtNetworkToVpn: External network {} is associated to VPN {}." + + "Hence set vpnInstance type to {} from {} ", extNet.key().getUuid().getValue(), + vpnId.getValue(), VpnInstance.BgpvpnType.InternetBGPVPN.getName(), + VpnInstance.BgpvpnType.BGPVPN.getName()); + neutronvpnUtils.updateVpnInstanceWithBgpVpnType(VpnInstance.BgpvpnType.InternetBGPVPN, vpnId); } //Update VpnMap with ext-nw is needed first before processing V6 internet default fallback flows List extNwList = Collections.singletonList(extNet.key().getUuid()); @@ -2582,6 +2596,7 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even protected List dissociateNetworksFromVpn(@NonNull Uuid vpnId, @NonNull List networkList) { List failedNwList = new ArrayList<>(); HashSet passedNwList = new HashSet<>(); + ConcurrentMap extNwMap = new ConcurrentHashMap<>(); if (networkList.isEmpty()) { LOG.error("dissociateNetworksFromVpn: Failed as networks list is empty"); failedNwList.add(String.format("Failed to disassociate networks from VPN %s as networks list is empty", @@ -2616,16 +2631,11 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even vpnId.getValue())); continue; } - if (NeutronvpnUtils.getIsExternal(network)) { - if (disassociateExtNetworkFromVpn(vpnId, network)) { - passedNwList.add(nw); - } else { - LOG.error("dissociateNetworksFromVpn: Failed to withdraw Provider Network {} from VPN {}", - nw.getValue(), vpnId.getValue()); - failedNwList.add(String.format("Failed to withdraw Provider Network %s from VPN %s", nw.getValue(), - vpnId.getValue())); - continue; - } + /* Handle disassociation of external network(s) from Internet BGP-VPN use case outside of the + * networkList iteration + */ + if (neutronvpnUtils.getIsExternal(network)) { + extNwMap.put(nw, network); } IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED; for (Uuid subnet : networkSubnets) { @@ -2653,11 +2663,25 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even } } if (ipVersion != IpVersionChoice.UNDEFINED) { - LOG.debug("dissociateNetworksFromVpn: Updating vpnInstanceOpDataEntryupdate with ip address family {}" + LOG.debug("dissociateNetworksFromVpn: Updating vpnInstance with ip address family {}" + " for VPN {}", ipVersion, vpnId); neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, false); } } + //Handle disassociation of external network(s) from Internet BGP-VPN Instance use case + if (!extNwMap.isEmpty() || extNwMap != null) { + for (Network extNw : extNwMap.values()) { + if (disassociateExtNetworkFromVpn(vpnId, extNw)) { + passedNwList.add(extNw.getUuid()); + } else { + LOG.error("dissociateNetworksFromVpn: Failed to withdraw External Provider Network {} from VPN {}", + extNw, vpnId.getValue()); + failedNwList.add(String.format("Failed to withdraw External Provider Network %s from VPN %s", + extNw, vpnId.getValue())); + continue; + } + } + } clearFromVpnMaps(vpnId, null, new ArrayList<>(passedNwList)); LOG.info("dissociateNetworksFromVpn: Network(s) {} disassociated from L3VPN {} successfully", passedNwList, vpnId.getValue()); @@ -2681,10 +2705,11 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even } } } - //Set VPN Type is BGPVPNExternal from BGPVPNInternet - LOG.info("disassociateExtNetworkFromVpn: set type {} for VPN {}", - VpnInstanceOpDataEntry.BgpvpnType.BGPVPNExternal, vpnId.getValue()); - neutronvpnUtils.updateVpnInstanceOpWithType(VpnInstanceOpDataEntry.BgpvpnType.BGPVPNExternal, vpnId); + ///Set VPN Type is BGPVPN from InternetBGPVPN + LOG.info("disassociateExtNetworkFromVpn: Set BGP-VPN type with {} for VPN {} and update IPv6 address family. " + + "Since external network is disassociated from VPN {}", + VpnInstance.BgpvpnType.BGPVPN, extNet, vpnId.getValue()); + neutronvpnUtils.updateVpnInstanceWithBgpVpnType(VpnInstance.BgpvpnType.BGPVPN, vpnId); IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED; for (Uuid snId : neutronvpnUtils.getPrivateSubnetsToExport(extNet, vpnId)) { Subnetmap sm = neutronvpnUtils.getSubnetmap(snId); diff --git a/neutronvpn/impl/src/main/java/org/opendaylight/netvirt/neutronvpn/NeutronvpnUtils.java b/neutronvpn/impl/src/main/java/org/opendaylight/netvirt/neutronvpn/NeutronvpnUtils.java index d54bda3285..9b7411e1fe 100644 --- a/neutronvpn/impl/src/main/java/org/opendaylight/netvirt/neutronvpn/NeutronvpnUtils.java +++ b/neutronvpn/impl/src/main/java/org/opendaylight/netvirt/neutronvpn/NeutronvpnUtils.java @@ -8,7 +8,6 @@ package org.opendaylight.netvirt.neutronvpn; import static org.opendaylight.genius.infra.Datastore.CONFIGURATION; -import static org.opendaylight.genius.infra.Datastore.OPERATIONAL; import com.google.common.base.Function; import com.google.common.collect.ImmutableBiMap; @@ -94,7 +93,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neu import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.RouterDpnListKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.router.dpn.list.DpnVpninterfacesList; 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.VpnInstanceOpDataEntryKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExtRouters; import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalSubnets; @@ -1670,34 +1668,28 @@ public class NeutronvpnUtils { } } - public void updateVpnInstanceOpWithType(VpnInstanceOpDataEntry.BgpvpnType choice, @NonNull Uuid vpn) { - String primaryRd = getVpnRd(vpn.getValue()); - if (primaryRd == null) { - LOG.debug("updateVpnInstanceOpWithType: Update BgpvpnType {} for {}." - + "Primary RD not found", choice, vpn.getValue()); - return; - } - InstanceIdentifier id = getVpnOpDataIdentifier(primaryRd); - - Optional vpnInstanceOpDataEntryOptional = - read(LogicalDatastoreType.OPERATIONAL, id); - if (!vpnInstanceOpDataEntryOptional.isPresent()) { - LOG.debug("updateVpnInstanceOpWithType: Update BgpvpnType {} for {}." - + "VpnInstanceOpDataEntry not found", choice, vpn.getValue()); - return; - } - VpnInstanceOpDataEntry vpnInstanceOpDataEntry = vpnInstanceOpDataEntryOptional.get(); - if (vpnInstanceOpDataEntry.getBgpvpnType().equals(choice)) { - LOG.debug("updateVpnInstanceOpWithType: Update BgpvpnType {} for {}." - + "VpnInstanceOpDataEntry already set", choice, vpn.getValue()); - return; - } - VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder(vpnInstanceOpDataEntry); - builder.setBgpvpnType(choice); - LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, tx -> { - tx.merge(id, builder.build()); - LOG.debug("updateVpnInstanceOpWithType: sent merge to operDS BgpvpnType {} for {}", choice, vpn.getValue()); - }), LOG, "Error updating VPN instance op {} with type {}", vpn, choice); + public void updateVpnInstanceWithBgpVpnType(VpnInstance.BgpvpnType bgpvpnType, @NonNull Uuid vpnName) { + jobCoordinator.enqueueJob("VPN-" + vpnName.getValue(), () -> { + VpnInstance vpnInstance = getVpnInstance(dataBroker, vpnName); + if (vpnInstance == null) { + LOG.error("updateVpnInstanceWithBgpVpnType: Failed to Update VpnInstance {} with BGP-VPN type {}." + + "VpnInstance is does not exist in the CONFIG. Do nothing.", vpnName.getValue(), bgpvpnType); + return Collections.emptyList(); + } + if (vpnInstance.isL2vpn()) { + LOG.error("updateVpnInstanceWithBgpVpnType: Failed to Update VpnInstance {} with BGP-VPN type {}." + + "VpnInstance is L2 instance. Do nothing.", vpnName.getValue(), bgpvpnType); + return Collections.emptyList(); + } + VpnInstanceBuilder builder = new VpnInstanceBuilder(vpnInstance); + builder.setBgpvpnType(bgpvpnType); + InstanceIdentifier vpnIdentifier = InstanceIdentifier.builder(VpnInstances.class) + .child(VpnInstance.class, new VpnInstanceKey(vpnName.getValue())).build(); + LOG.info("updateVpnInstanceWithBgpVpnType: Successfully updated the VpnInstance {} with BGP-VPN type {}", + vpnName.getValue(), bgpvpnType); + return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit( + CONFIGURATION, tx -> tx.merge(vpnIdentifier, builder.build()))); + }); } public static RouterIds getvpnInstanceRouterIds(Uuid routerId) { diff --git a/vpnmanager/api/src/main/yang/odl-l3vpn.yang b/vpnmanager/api/src/main/yang/odl-l3vpn.yang index ef1af3baeb..2b1dea7461 100644 --- a/vpnmanager/api/src/main/yang/odl-l3vpn.yang +++ b/vpnmanager/api/src/main/yang/odl-l3vpn.yang @@ -292,17 +292,17 @@ module odl-l3vpn { } leaf bgpvpn-type { type enumeration { - enum BGPVPNInternet { + enum InternetBGPVPN { value "0"; - description "BGPVPN Internet"; + description "Internet BGPVPN"; } - enum BGPVPNExternal { + enum BGPVPN { value "1"; - description "BGPVPN External"; + description "BGPVPN"; } - enum VPN { + enum InternalVPN { value "2"; - description "Default VPN"; + description "InternalVPN"; } } } diff --git a/vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnInstanceListener.java b/vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnInstanceListener.java index a77c0c9a86..adc4752ccc 100644 --- a/vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnInstanceListener.java +++ b/vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnInstanceListener.java @@ -17,9 +17,11 @@ import com.google.common.util.concurrent.MoreExecutors; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -51,9 +53,12 @@ import org.opendaylight.infrautils.jobcoordinator.JobCoordinator; import org.opendaylight.infrautils.utils.concurrent.Executors; import org.opendaylight.infrautils.utils.concurrent.LoggingFutures; import org.opendaylight.mdsal.binding.api.DataBroker; +import org.opendaylight.mdsal.binding.api.WriteTransaction; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.netvirt.bgpmanager.api.IBgpManager; import org.opendaylight.netvirt.fibmanager.api.IFibManager; import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener; +import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.ebgp.rev150901.AddressFamily; 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; @@ -61,6 +66,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.ext 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.genius.itm.rev160406.dc.gateway.ip.list.DcGatewayIpKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables; 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; @@ -88,6 +94,7 @@ public class VpnInstanceListener extends AbstractAsyncDataTreeChangeListener identifier, VpnInstance original, VpnInstance update) { LOG.trace("VPN-UPDATE: update: VPN event key: {}, value: {}.", identifier, update); - String vpnName = update.getVpnInstanceName(); - if (original != null && update != null - && original.getRouteDistinguisher() != null - && update.getRouteDistinguisher() != null - && original.getRouteDistinguisher().size() - != update.getRouteDistinguisher().size()) { - LOG.debug("VPN-UPDATE: VpnInstance:{} updated with new RDs: {} from old RDs: {}", vpnName, - update.getRouteDistinguisher(), original.getRouteDistinguisher()); - vpnUtil.updateVpnInstanceWithRdList(vpnName, update.getRouteDistinguisher()); + if (Objects.equals(original, update)) { + return; + } + jobCoordinator.enqueueJob("VPN-" + original.getVpnInstanceName(), + new UpdateVpnInstanceWorker(dataBroker, identifier, original, update)); + } + + private class UpdateVpnInstanceWorker implements Callable>> { + private final Logger log = LoggerFactory.getLogger(VpnInstanceListener.UpdateVpnInstanceWorker.class); + VpnInstance original; + VpnInstance update; + InstanceIdentifier vpnIdentifier; + DataBroker broker; + String vpnName; + + UpdateVpnInstanceWorker(DataBroker broker, + InstanceIdentifier identifier, + VpnInstance original, + VpnInstance update) { + this.broker = broker; + this.vpnIdentifier = identifier; + this.original = original; + this.update = update; + this.vpnName = update.getVpnInstanceName(); + } + + @Override + @SuppressWarnings("checkstyle:ForbidCertainMethod") + public List> call() { + WriteTransaction writeOperTxn = broker.newWriteOnlyTransaction(); + List> futures = new ArrayList<>(); + String primaryRd = vpnUtil.getVpnRd(vpnName); + if (primaryRd == null) { + log.error("{}, failed to update VPN: PrimaryRD is null for vpnName {}", LOGGING_PREFIX_UPDATE, vpnName); + return futures; + } + updateVpnInstance(writeOperTxn, primaryRd); + try { + writeOperTxn.commit().get(); + } catch (InterruptedException | ExecutionException e) { + log.error("{}, failed to update VPN: Exception in updating vpn {} rd {} ", LOGGING_PREFIX_UPDATE, + vpnName, update.getRouteDistinguisher(), e); + futures.add(Futures.immediateFailedFuture(e)); + return futures; + } + ListenableFuture> listenableFuture = Futures.allAsList(futures); + boolean isIpAddressFamilyUpdated = false; + if (original.getIpAddressFamilyConfigured() == VpnInstance.IpAddressFamilyConfigured.Undefined + && update.getIpAddressFamilyConfigured() != original.getIpAddressFamilyConfigured()) { + isIpAddressFamilyUpdated = true; + } + Futures.addCallback(listenableFuture, + new PostVpnInstanceChangeWorker(update , isIpAddressFamilyUpdated, primaryRd), + MoreExecutors.directExecutor()); + return futures; + } + + private class PostVpnInstanceChangeWorker implements FutureCallback> { + private final Logger log = LoggerFactory.getLogger(PostVpnInstanceChangeWorker.class); + VpnInstance vpnInstance; + String vpnName; + boolean isIpAddressFamilyUpdated; + String primaryRd; + + PostVpnInstanceChangeWorker(VpnInstance vpnInstance, boolean isIpAddressFamilyUpdated, String primaryRd) { + this.vpnInstance = vpnInstance; + this.vpnName = vpnInstance.getVpnInstanceName(); + this.isIpAddressFamilyUpdated = isIpAddressFamilyUpdated; + this.primaryRd = primaryRd; + } + + /** + * This implies that all the future instances have returned success. -- TODO: Confirm this + */ + @Override + public void onSuccess(List voids) { + if (!VpnUtil.isBgpVpn(vpnName, primaryRd)) { + // plain router + notifyTask(); + vpnInterfaceManager.vpnInstanceIsReady(vpnName); + return; + } + if (isIpAddressFamilyUpdated) { + //bgpvpn + notifyTask(); + vpnInterfaceManager.vpnInstanceIsReady(vpnName); + } + } + + /** + * 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. + * If rollbackworker fails, this is a double-fault. Double fault is logged and ignored. + */ + + @Override + public void onFailure(Throwable throwable) { + log.error("{} onFailure: Job for vpnInstance: {} with rd {} failed with exception:", LOGGING_PREFIX_ADD, + vpnName, primaryRd, throwable); + vpnInterfaceManager.vpnInstanceFailed(vpnName); + } + + private void notifyTask() { + vpnOpDataNotifier.notifyVpnOpDataReady(VpnOpDataSyncer.VpnOpDataType.vpnInstanceToId, + vpnInstance.getVpnInstanceName()); + vpnOpDataNotifier.notifyVpnOpDataReady(VpnOpDataSyncer.VpnOpDataType.vpnOpData, + vpnInstance.getVpnInstanceName()); + } + } + + public void updateVpnInstance(WriteTransaction writeOperTxn, String primaryRd) { + log.trace("updateVpnInstance: VPN event key: {}, value: {}.", vpnIdentifier, update); + InstanceIdentifier id = VpnUtil.buildVrfTableForPrimaryRd(primaryRd); + Optional vrfTable; + try { + vrfTable = SingleTransactionDataBroker.syncReadOptional(dataBroker, + LogicalDatastoreType.CONFIGURATION, id); + } catch (ExecutionException | InterruptedException e) { + log.trace("updateVpnInstance: Exception while reading FIB VRF Table for VPN Instance {} with " + + "Primary RD {}.", vpnName, primaryRd); + return; + } + //TODO Later if FIB VRF table is available we need to callback the vpnInstanceOpData Update to proceed + if (!vrfTable.isPresent()) { + log.error("updateVpnInstance: FIB VRF table is not present for the VPN Instance {} " + + "with Primary RD {}. Unable to Proceed VpnInstanceOpData Update event {}", + vpnName, primaryRd, update); + return; + } + List vpnInstanceUpdatedRdList = Collections.emptyList(); + boolean isBgpVrfTableUpdateRequired = false; + boolean isVpnInstanceRdUpdated = false; + //Handle VpnInstance Address Family update + int originalIpAddrFamilyValue = original.getIpAddressFamilyConfigured().getIntValue(); + int updateIpAddrFamilyValue = update.getIpAddressFamilyConfigured().getIntValue(); + if (originalIpAddrFamilyValue != updateIpAddrFamilyValue) { + log.debug("updateVpnInstance: VpnInstance: {} updated with IP address family {} from IP address " + + "family {}", vpnName, update.getIpAddressFamilyConfigured().getName(), + original.getIpAddressFamilyConfigured().getName()); + vpnUtil.setVpnInstanceOpDataWithAddressFamily(vpnName, update.getIpAddressFamilyConfigured(), + writeOperTxn); + } + //Update VpnInstanceOpData with BGPVPN to Internet BGPVPN and vice-versa + if (original.getBgpvpnType() != update.getBgpvpnType()) { + log.debug("updateVpnInstance: VpnInstance: {} updated with BGP-VPN type: {} from BGP-VPN type: {}", + vpnName, update.getBgpvpnType(), original.getBgpvpnType()); + vpnUtil.updateVpnInstanceOpDataWithVpnType(vpnName, update.getBgpvpnType(), writeOperTxn); + } + //Handle BGP-VPN Instance RD Update + if ((update.getBgpvpnType() != VpnInstance.BgpvpnType.InternalVPN)) { + if (originalIpAddrFamilyValue < updateIpAddrFamilyValue) { + isBgpVrfTableUpdateRequired = true; + } + if (original.getRouteDistinguisher().size() != update.getRouteDistinguisher().size()) { + log.debug("updateVpnInstance: VpnInstance:{} updated with new RDs: {} from old RDs: {}", vpnName, + update.getRouteDistinguisher(), original.getRouteDistinguisher()); + vpnUtil.updateVpnInstanceOpDataWithRdList(vpnName, update.getRouteDistinguisher(), writeOperTxn); + /* Update BGP Vrf entry for newly added RD. VPN Instance does not support for + * deleting the existing RDs + */ + vpnInstanceUpdatedRdList = update.getRouteDistinguisher() != null + ? new ArrayList<>(update.getRouteDistinguisher()) : new ArrayList<>(); + vpnInstanceUpdatedRdList.removeAll(original.getRouteDistinguisher()); + isBgpVrfTableUpdateRequired = true; + isVpnInstanceRdUpdated = true; + } + } + //update Bgp VrfTable + if (isBgpVrfTableUpdateRequired) { + addBgpVrfTableForVpn(update, vpnName, vpnInstanceUpdatedRdList, isVpnInstanceRdUpdated); + } } - vpnInterfaceManager.updateVpnInterfacesForUnProcessAdjancencies(vpnName); } @Override @@ -289,7 +459,6 @@ public class VpnInstanceListener extends AbstractAsyncDataTreeChangeListener opVpnTargetList = new ArrayList<>(); - builder.setBgpvpnType(VpnInstanceOpDataEntry.BgpvpnType.BGPVPNExternal); if (value.getL3vni() != null) { builder.setL3vni(value.getL3vni()); } @@ -318,9 +487,9 @@ public class VpnInstanceListener extends AbstractAsyncDataTreeChangeListener rds = value.getRouteDistinguisher(); builder.setRd(rds); - } else { - builder.setBgpvpnType(VpnInstanceOpDataEntry.BgpvpnType.VPN); } + // Get BGP-VPN type configured details from config vpn-instance + builder.setBgpvpnType(VpnInstanceOpDataEntry.BgpvpnType.forValue(value.getBgpvpnType().getIntValue())); writeOperTxn.mergeParentStructureMerge(VpnUtil.getVpnInstanceOpDataIdentifier(primaryRd), builder.build()); LOG.info("{} addVpnInstance: VpnInstanceOpData populated successfully for vpn {} rd {}", LOGGING_PREFIX_ADD, vpnInstanceName, primaryRd); @@ -504,4 +673,71 @@ public class VpnInstanceListener extends AbstractAsyncDataTreeChangeListener vpnInstanceUpdatedRdList, + boolean isVpnInstanceRdUpdated) { + String primaryRd = vpnUtil.getPrimaryRd(vpnName); + Collection vpnTargetCollection = (vpnInstance.getVpnTargets() != null) + ? vpnInstance.getVpnTargets().getVpnTarget().values() : null; + List vpnTargetList = new ArrayList(vpnTargetCollection != null ? vpnTargetCollection + : Collections.emptyList()); + List exportRTList = new ArrayList<>(); + List importRTList = new ArrayList<>(); + if (!vpnTargetList.isEmpty()) { + for (VpnTarget vpnTarget : vpnTargetList) { + if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ExportExtcommunity) { + exportRTList.add(vpnTarget.getVrfRTValue()); + } + if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.ImportExtcommunity) { + importRTList.add(vpnTarget.getVrfRTValue()); + } + if (vpnTarget.getVrfRTType() == VpnTarget.VrfRTType.Both) { + exportRTList.add(vpnTarget.getVrfRTValue()); + importRTList.add(vpnTarget.getVrfRTValue()); + } + } + } + synchronized (vpnName.intern()) { + List rds = Collections.emptyList(); + //Vpn Instance RD Update for ECMP use case + if (isVpnInstanceRdUpdated) { + rds = vpnInstanceUpdatedRdList; + } else { + rds = vpnInstance.getRouteDistinguisher() != null + ? new ArrayList<>(vpnInstance.getRouteDistinguisher()) : new ArrayList<>(); + } + for (String rd : rds) { + List irtList = rd.equals(primaryRd) ? importRTList : Collections.emptyList(); + int ipAddrFamilyConfigured = vpnInstance.getIpAddressFamilyConfigured().getIntValue(); + switch (ipAddrFamilyConfigured) { + case 10: + bgpManager.addVrf(rd, irtList, exportRTList, AddressFamily.IPV4); + bgpManager.addVrf(rd, irtList, exportRTList, AddressFamily.IPV6); + LOG.debug("addBgpVrfTableForVpn: ADD BGP VRF table for VPN {} with RD {}, ImportRTList {}, " + + "ExportRTList {} for IPv4andIPv6 AddressFamily ", vpnName, rd, irtList, exportRTList); + break; + case 6: + bgpManager.addVrf(rd, irtList, exportRTList, AddressFamily.IPV6); + LOG.debug("addBgpVrfTableForVpn: ADD BGP VRF table for VPN {} with RD {}, ImportRTList {}, " + + "ExportRTList {} for IPv6 AddressFamily ", vpnName, rd, irtList, exportRTList); + break; + case 4: + bgpManager.addVrf(rd, irtList, exportRTList, AddressFamily.IPV4); + LOG.debug("addBgpVrfTableForVpn: ADD BGP VRF table for VPN {} with RD {}, ImportRTList {}, " + + "ExportRTList {} for IPv4 AddressFamily ", vpnName, rd, irtList, exportRTList); + break; + default: + break; + } + //L2VPN Use case + if (vpnInstance.isL2vpn()) { + bgpManager.addVrf(rd, importRTList, exportRTList, AddressFamily.L2VPN); + LOG.debug("addBgpVrfTableForVpn: ADD BGP VRF table for VPN {} RD {}, ImportRTList {}, " + + "ExportRTList {} for L2VPN AddressFamily ", vpnName, rd, irtList, exportRTList); + } + } + } + } } diff --git a/vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnInterfaceManager.java b/vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnInterfaceManager.java index f703ddc010..96ad97f4f3 100755 --- a/vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnInterfaceManager.java +++ b/vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnInterfaceManager.java @@ -784,7 +784,7 @@ public class VpnInterfaceManager extends AbstractAsyncDataTreeChangeListener id = InstanceIdentifier.builder(VpnInstanceOpData.class) @@ -1930,11 +1929,9 @@ public final class VpnUtil { + "VpnInstanceOpDataEntry not found", vpnName); return false; } - LOG.debug("isBgpVpnInternet VPN {}." - + "Successfully VpnInstanceOpDataEntry.getBgpvpnType {}", + LOG.debug("isBgpVpnInternet VPN {} Successfully VpnInstanceOpDataEntry.getBgpvpnType {}", vpnName, vpnInstanceOpDataEntryOptional.get().getBgpvpnType()); - if (vpnInstanceOpDataEntryOptional.get().getBgpvpnType() == VpnInstanceOpDataEntry - .BgpvpnType.BGPVPNInternet) { + if (vpnInstanceOpDataEntryOptional.get().getBgpvpnType() == VpnInstanceOpDataEntry.BgpvpnType.InternetBGPVPN) { return true; } return false; @@ -2566,4 +2563,94 @@ public final class VpnUtil { // FIXME: is there some identifier we can use? LearntVpnVipToPortKey perhaps? return JvmGlobalLocks.getLockForString(vpnName + fixedIp); } + + public static InstanceIdentifier buildVrfTableForPrimaryRd(String primaryRd) { + InstanceIdentifier.InstanceIdentifierBuilder idBuilder = + InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(primaryRd)); + return idBuilder.build(); + } + + public void setVpnInstanceOpDataWithAddressFamily(String vpnName, + VpnInstance.IpAddressFamilyConfigured ipVersion, + WriteTransaction writeOperTxn) { + VpnInstanceOpDataEntry vpnInstanceOpDataEntry = getVpnInstanceOpDataEntryFromVpnName(vpnName); + if (vpnInstanceOpDataEntry == null) { + LOG.error("setVpnInstanceOpDataWithAddressFamily: Unable to set IP address family {} for the " + + "VPN {}. Since VpnInstanceOpData is not yet ready", ipVersion, vpnName); + return; + } + if (vpnInstanceOpDataEntry.getType() == VpnInstanceOpDataEntry.Type.L2) { + LOG.error("setVpnInstanceOpDataWithAddressFamily: Unable to set IP address family {} for the " + + "VPN {}. Since VPN type is L2 flavour. Do Nothing.", ipVersion, vpnName); + return; + } + synchronized (vpnName.intern()) { + VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder() + .setVrfId(vpnInstanceOpDataEntry.getVrfId()); + builder.setIpAddressFamilyConfigured(VpnInstanceOpDataEntry.IpAddressFamilyConfigured + .forValue(ipVersion.getIntValue())); + InstanceIdentifier id = InstanceIdentifier.builder(VpnInstanceOpData.class) + .child(VpnInstanceOpDataEntry.class, + new VpnInstanceOpDataEntryKey(vpnInstanceOpDataEntry.getVrfId())).build(); + writeOperTxn.merge(LogicalDatastoreType.OPERATIONAL, id, builder.build()); + LOG.info("setVpnInstanceOpDataWithAddressFamily: Successfully set vpnInstanceOpData with " + + "IP Address family {} for VpnInstance {}", ipVersion.getName(), vpnName); + } + } + + public void updateVpnInstanceOpDataWithVpnType(String vpnName, + VpnInstance.BgpvpnType bgpvpnType, + WriteTransaction writeOperTxn) { + VpnInstanceOpDataEntry vpnInstanceOpDataEntry = getVpnInstanceOpDataEntryFromVpnName(vpnName); + if (vpnInstanceOpDataEntry == null) { + LOG.error("updateVpnInstanceOpDataWithVpnType: VpnInstance {} with BGPVPN Type {} update Failed." + + "Since vpnInstanceOpData is not yet ready.", vpnName, bgpvpnType); + return; + } + if (vpnInstanceOpDataEntry.getType() == VpnInstanceOpDataEntry.Type.L2) { + LOG.error("updateVpnInstanceOpDataWithVpnType: Unable to update the VpnInstance {} with BGPVPN Type {}." + + "Since VPN type is L2 flavour. Do Nothing.", vpnName, bgpvpnType); + return; + } + synchronized (vpnName.intern()) { + VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder() + .setVrfId(vpnInstanceOpDataEntry.getVrfId()); + builder.setBgpvpnType(VpnInstanceOpDataEntry.BgpvpnType.forValue(bgpvpnType.getIntValue())); + InstanceIdentifier id = InstanceIdentifier.builder(VpnInstanceOpData.class) + .child(VpnInstanceOpDataEntry.class, + new VpnInstanceOpDataEntryKey(vpnInstanceOpDataEntry.getVrfId())).build(); + writeOperTxn.merge(LogicalDatastoreType.OPERATIONAL, id, builder.build()); + LOG.info("updateVpnInstanceOpDataWithVpnType: Successfully updated vpn-instance-op-data with BGPVPN type " + + "{} for the Vpn {}", bgpvpnType, vpnName); + } + } + + public VpnInstanceOpDataEntry getVpnInstanceOpDataEntryFromVpnName(String vpnName) { + String primaryRd = getVpnRd(vpnName); + if (primaryRd == null) { + LOG.error("getVpnInstanceOpDataEntryFromVpnName: Vpn Instance {} Primary RD not found", vpnName); + return null; + } + return getVpnInstanceOpData(primaryRd); + } + + public void updateVpnInstanceOpDataWithRdList(String vpnName, List updatedRdList, + WriteTransaction writeOperTxn) { + String primaryRd = getVpnRd(vpnName); + if (primaryRd == null) { + LOG.error("updateVpnInstanceOpDataWithRdList: Unable to get primary RD for the VPN {}. Skip to process " + + "the update RD list {} ", vpnName, updatedRdList); + return; + } + synchronized (vpnName.intern()) { + VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder().setVrfId(primaryRd); + builder.setRd(updatedRdList); + InstanceIdentifier id = InstanceIdentifier.builder(VpnInstanceOpData.class) + .child(VpnInstanceOpDataEntry.class, + new VpnInstanceOpDataEntryKey(primaryRd)).build(); + writeOperTxn.merge(LogicalDatastoreType.OPERATIONAL, id, builder.build()); + LOG.info("updateVpnInstanceOpDataWithRdList: Successfully updated the VPN {} with list of RDs {}", + vpnName, updatedRdList); + } + } }