Bug 7263: Spread InterVpnLinks among available DPNs 51/48951/2
authorMiguel Perez <francisco.miguel.perez@ericsson.com>
Mon, 28 Nov 2016 15:30:51 +0000 (16:30 +0100)
committerSam Hague <shague@redhat.com>
Sat, 3 Dec 2016 03:25:02 +0000 (03:25 +0000)
 + Avoids as much as possible to place InterVpnLinks
   in the same DPNs.

 + This way, BGP router will not overload one specific DPN

Change-Id: I99fccd3542d5a904369d8de91b6b4117cd070acd
Signed-off-by: Miguel Perez <francisco.miguel.perez@ericsson.com>
Signed-off-by: Sam Hague <shague@redhat.com>
vpnservice/vpnmanager/vpnmanager-api/src/main/java/org/opendaylight/netvirt/vpnmanager/api/intervpnlink/InterVpnLinkCache.java
vpnservice/vpnmanager/vpnmanager-api/src/main/java/org/opendaylight/netvirt/vpnmanager/api/intervpnlink/InterVpnLinkDataComposite.java
vpnservice/vpnmanager/vpnmanager-impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnUtil.java
vpnservice/vpnmanager/vpnmanager-impl/src/main/java/org/opendaylight/netvirt/vpnmanager/intervpnlink/InterVpnLinkListener.java
vpnservice/vpnmanager/vpnmanager-impl/src/main/java/org/opendaylight/netvirt/vpnmanager/intervpnlink/InterVpnLinkService.java [new file with mode: 0644]
vpnservice/vpnmanager/vpnmanager-impl/src/main/resources/org/opendaylight/blueprint/vpnmanager.xml

index afaebfdef1c9d58822e37ccb48c597445053bf73..30aef21d6c5476111943ef592795e55460cff30f 100644 (file)
@@ -8,6 +8,8 @@
 
 package org.opendaylight.netvirt.vpnmanager.api.intervpnlink;
 
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
@@ -169,6 +171,10 @@ public class InterVpnLinkCache {
     private static void addToEndpointCache(InterVpnLinkDataComposite iVpnLink) {
         ConcurrentHashMap<String, InterVpnLinkDataComposite> cache =
             (ConcurrentHashMap<String, InterVpnLinkDataComposite>) CacheUtil.getCache(ENDPOINT_2_IVPNLINK_CACHE_NAME);
+        if ( cache == null ) {
+            LOG.warn("Cache {} is not ready", ENDPOINT_2_IVPNLINK_CACHE_NAME);
+            return;
+        }
         if ( iVpnLink.getFirstEndpointIpAddr().isPresent() ) {
             cache.put(iVpnLink.getFirstEndpointIpAddr().get(), iVpnLink);
         }
@@ -180,6 +186,10 @@ public class InterVpnLinkCache {
     private static void addToVpnUuidCache(InterVpnLinkDataComposite iVpnLink) {
         ConcurrentHashMap<String, InterVpnLinkDataComposite> cache =
             (ConcurrentHashMap<String, InterVpnLinkDataComposite>) CacheUtil.getCache(UUID_2_IVPNLINK_CACHE_NAME);
+        if ( cache == null ) {
+            LOG.warn("Cache {} is not ready", UUID_2_IVPNLINK_CACHE_NAME);
+            return;
+        }
         if ( iVpnLink.getFirstEndpointVpnUuid().isPresent() ) {
             cache.put(iVpnLink.getFirstEndpointVpnUuid().get(), iVpnLink);
         }
@@ -191,6 +201,10 @@ public class InterVpnLinkCache {
     private static void addToIVpnLinkNameCache(InterVpnLinkDataComposite iVpnLink) {
         ConcurrentHashMap<String, InterVpnLinkDataComposite> cache =
             (ConcurrentHashMap<String, InterVpnLinkDataComposite>) CacheUtil.getCache(IVPNLINK_NAME_2_IVPNLINK_CACHE_NAME);
+        if ( cache == null ) {
+            LOG.warn("Cache {} is not ready", IVPNLINK_NAME_2_IVPNLINK_CACHE_NAME);
+            return;
+        }
         cache.put(iVpnLink.getInterVpnLinkName(), iVpnLink);
         if ( iVpnLink.getSecondEndpointIpAddr().isPresent() ) {
             cache.put(iVpnLink.getSecondEndpointIpAddr().get(), iVpnLink);
@@ -200,13 +214,21 @@ public class InterVpnLinkCache {
     public static void removeInterVpnLinkFromCache(InterVpnLink iVpnLink) {
         ConcurrentHashMap<String, InterVpnLink> cache =
             (ConcurrentHashMap<String, InterVpnLink>) CacheUtil.getCache(ENDPOINT_2_IVPNLINK_CACHE_NAME);
-        cache.remove(iVpnLink.getFirstEndpoint().getIpAddress().getValue());
-        cache.remove(iVpnLink.getSecondEndpoint().getIpAddress().getValue());
+        if ( cache != null ) {
+            cache.remove(iVpnLink.getFirstEndpoint().getIpAddress().getValue());
+            cache.remove(iVpnLink.getSecondEndpoint().getIpAddress().getValue());
+        } else {
+            LOG.warn("Cache {} is not ready", ENDPOINT_2_IVPNLINK_CACHE_NAME);
+        }
 
         ConcurrentHashMap<String, InterVpnLink> cache2 =
             (ConcurrentHashMap<String, InterVpnLink>) CacheUtil.getCache(UUID_2_IVPNLINK_CACHE_NAME);
-        cache2.remove(iVpnLink.getFirstEndpoint().getVpnUuid().getValue());
-        cache2.remove(iVpnLink.getSecondEndpoint().getVpnUuid().getValue());
+        if ( cache2 != null ) {
+            cache2.remove(iVpnLink.getFirstEndpoint().getVpnUuid().getValue());
+            cache2.remove(iVpnLink.getSecondEndpoint().getVpnUuid().getValue());
+        } else {
+            LOG.warn("Cache {} is not ready", UUID_2_IVPNLINK_CACHE_NAME);
+        }
     }
 
 
@@ -225,13 +247,21 @@ public class InterVpnLinkCache {
     private static void removeFromInterVpnLinkNameCache(InterVpnLinkDataComposite iVpnLinkComposite) {
         ConcurrentHashMap<String, InterVpnLinkDataComposite> cache =
             (ConcurrentHashMap<String, InterVpnLinkDataComposite>) CacheUtil.getCache(IVPNLINK_NAME_2_IVPNLINK_CACHE_NAME);
-        cache.remove(iVpnLinkComposite.getInterVpnLinkName());
+        if ( cache != null ) {
+            cache.remove(iVpnLinkComposite.getInterVpnLinkName());
+        } else {
+            LOG.warn("removeFromInterVpnLinkNameCache: Cache {} is not ready", IVPNLINK_NAME_2_IVPNLINK_CACHE_NAME);
+        }
     }
 
 
     private static void removeFromVpnUuidCache(InterVpnLinkDataComposite iVpnLinkComposite) {
         ConcurrentHashMap<String, InterVpnLink> cache =
             (ConcurrentHashMap<String, InterVpnLink>) CacheUtil.getCache(UUID_2_IVPNLINK_CACHE_NAME);
+        if ( cache == null ) {
+            LOG.warn("removeFromVpnUuidCache: Cache {} is not ready", UUID_2_IVPNLINK_CACHE_NAME);
+            return;
+        }
         Optional<String> opt1stEndpointUuid = iVpnLinkComposite.getFirstEndpointVpnUuid();
         if ( opt1stEndpointUuid.isPresent() ) {
             cache.remove(opt1stEndpointUuid.get());
@@ -244,6 +274,10 @@ public class InterVpnLinkCache {
     private static void removeFromEndpointIpAddressCache(InterVpnLinkDataComposite iVpnLinkComposite) {
         ConcurrentHashMap<String, InterVpnLink> cache =
             (ConcurrentHashMap<String, InterVpnLink>) CacheUtil.getCache(ENDPOINT_2_IVPNLINK_CACHE_NAME);
+        if ( cache == null ) {
+            LOG.warn("removeFromVpnUuidCache: Cache {} is not ready", ENDPOINT_2_IVPNLINK_CACHE_NAME);
+            return;
+        }
         Optional<String> opt1stEndpointIpAddr = iVpnLinkComposite.getFirstEndpointIpAddr();
         if ( opt1stEndpointIpAddr.isPresent() ) {
             cache.remove(opt1stEndpointIpAddr.get());
@@ -260,24 +294,29 @@ public class InterVpnLinkCache {
     public static Optional<InterVpnLinkDataComposite> getInterVpnLinkByName(String iVpnLinkName) {
         ConcurrentHashMap<String, InterVpnLinkDataComposite> cache =
             (ConcurrentHashMap<String, InterVpnLinkDataComposite>) CacheUtil.getCache(IVPNLINK_NAME_2_IVPNLINK_CACHE_NAME);
-        InterVpnLinkDataComposite iVpnLink = cache.get(iVpnLinkName);
-        return (iVpnLink == null) ? Optional.<InterVpnLinkDataComposite>absent() : Optional.of(iVpnLink);
+        return (cache == null) ? Optional.<InterVpnLinkDataComposite>absent()
+                               : Optional.fromNullable(cache.get(iVpnLinkName));
     }
 
     public static Optional<InterVpnLinkDataComposite> getInterVpnLinkByEndpoint(String endpointIp) {
         LOG.trace("Checking if {} is configured as an InterVpnLink endpoint", endpointIp);
         ConcurrentHashMap<String, InterVpnLinkDataComposite> cache =
             (ConcurrentHashMap<String, InterVpnLinkDataComposite>) CacheUtil.getCache(ENDPOINT_2_IVPNLINK_CACHE_NAME);
-        InterVpnLinkDataComposite iVpnLink = cache.get(endpointIp);
-        LOG.trace("_XX IP {} is configured for InterVpnLink {}", endpointIp, iVpnLink);
-        return (iVpnLink == null) ? Optional.<InterVpnLinkDataComposite>absent() : Optional.of(iVpnLink);
+        return (cache == null) ? Optional.<InterVpnLinkDataComposite>absent()
+                               : Optional.fromNullable(cache.get(endpointIp));
     }
 
     public static Optional<InterVpnLinkDataComposite> getInterVpnLinkByVpnId(String vpnId) {
-        ConcurrentHashMap<String, InterVpnLinkDataComposite> cache2 =
+        ConcurrentHashMap<String, InterVpnLinkDataComposite> cache =
+            (ConcurrentHashMap<String, InterVpnLinkDataComposite>) CacheUtil.getCache(UUID_2_IVPNLINK_CACHE_NAME);
+        return (cache == null) ? Optional.<InterVpnLinkDataComposite>absent() : Optional.fromNullable(cache.get(vpnId));
+    }
+
+    public static List<InterVpnLinkDataComposite> getAllInterVpnLinks() {
+        ConcurrentHashMap<String, InterVpnLinkDataComposite> cache =
             (ConcurrentHashMap<String, InterVpnLinkDataComposite>) CacheUtil.getCache(UUID_2_IVPNLINK_CACHE_NAME);
-        InterVpnLinkDataComposite iVpnLink = cache2.get(vpnId);
-        return (iVpnLink == null) ? Optional.<InterVpnLinkDataComposite>absent() : Optional.of(iVpnLink);
+        return (cache == null) ? Collections.<InterVpnLinkDataComposite>emptyList()
+                               : Collections.list(cache.elements());
     }
 
 }
index d58339a0a2eabe64d1af7abc044aee83d71d88ac..a34bc9ab74f804de6b8c56df7eea49ab05ba1e10 100755 (executable)
@@ -8,18 +8,20 @@
 
 package org.opendaylight.netvirt.vpnmanager.api.intervpnlink;
 
+import com.google.common.base.Optional;
 import java.math.BigInteger;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkState;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkState.State;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.links.InterVpnLink;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import com.google.common.base.Optional;
 
 /**
  * It holds all info about an InterVpnLink, combining both configurational
- * and stateful
+ * and stateful.
  */
 public class InterVpnLinkDataComposite {
 
@@ -28,42 +30,44 @@ public class InterVpnLinkDataComposite {
     private InterVpnLink interVpnLinkCfg;
     private InterVpnLinkState interVpnLinkState;
 
-    public InterVpnLinkDataComposite(InterVpnLink iVpnLink) {
-        this.interVpnLinkCfg = iVpnLink;
+    public InterVpnLinkDataComposite(InterVpnLink interVpnLink) {
+        this.interVpnLinkCfg = interVpnLink;
     }
 
-    public InterVpnLinkDataComposite(InterVpnLinkState iVpnLinkState) {
-        this.interVpnLinkState = iVpnLinkState;
+    public InterVpnLinkDataComposite(InterVpnLinkState interVpnLinkState) {
+        this.interVpnLinkState = interVpnLinkState;
     }
 
-    public InterVpnLinkDataComposite(InterVpnLink iVpnLink, InterVpnLinkState iVpnLinkState) {
-        this.interVpnLinkCfg = iVpnLink;
-        this.interVpnLinkState = iVpnLinkState;
+    public InterVpnLinkDataComposite(InterVpnLink interVpnLink, InterVpnLinkState interVpnLinkState) {
+        this.interVpnLinkCfg = interVpnLink;
+        this.interVpnLinkState = interVpnLinkState;
     }
 
     public InterVpnLink getInterVpnLinkConfig() {
         return this.interVpnLinkCfg;
     }
 
-    public void setInterVpnLinkConfig(InterVpnLink iVpnLink) {
-        this.interVpnLinkCfg = iVpnLink;
+    public void setInterVpnLinkConfig(InterVpnLink interVpnLink) {
+        this.interVpnLinkCfg = interVpnLink;
     }
 
     public InterVpnLinkState getInterVpnLinkState() {
         return this.interVpnLinkState;
     }
 
-    public void setInterVpnLinkState(InterVpnLinkState iVpnLinkState) {
-        this.interVpnLinkState = iVpnLinkState;
+    public void setInterVpnLinkState(InterVpnLinkState interVpnLinkState) {
+        this.interVpnLinkState = interVpnLinkState;
     }
 
     public boolean isComplete() {
-        return interVpnLinkCfg != null && interVpnLinkState != null;
+        return interVpnLinkCfg != null && interVpnLinkState != null
+                 && interVpnLinkState.getFirstEndpointState() != null
+                 && interVpnLinkState.getSecondEndpointState() != null;
     }
 
     public Optional<InterVpnLinkState.State> getState() {
         return this.interVpnLinkState == null ? Optional.absent()
-                                              : Optional.of(this.interVpnLinkState.getState());
+                                              : Optional.fromNullable(this.interVpnLinkState.getState());
     }
 
     public boolean isActive() {
@@ -87,7 +91,7 @@ public class InterVpnLinkDataComposite {
 
     public boolean isSecondEndpointIpAddr(String endpointIp) {
         return interVpnLinkCfg != null
-               &&interVpnLinkCfg.getSecondEndpoint().getIpAddress().getValue().equals(endpointIp);
+               && interVpnLinkCfg.getSecondEndpoint().getIpAddress().getValue().equals(endpointIp);
     }
 
     public boolean isIpAddrTheOtherVpnEndpoint(String ipAddr, String vpnUuid) {
@@ -115,6 +119,12 @@ public class InterVpnLinkDataComposite {
         return Optional.of(this.interVpnLinkCfg.getFirstEndpoint().getIpAddress().getValue());
     }
 
+    public List<BigInteger> getFirstEndpointDpns() {
+        return ( !isComplete() || this.interVpnLinkState.getFirstEndpointState().getDpId() == null )
+                   ? Collections.<BigInteger>emptyList()
+                   : this.interVpnLinkState.getFirstEndpointState().getDpId();
+    }
+
     public Optional<String> getSecondEndpointVpnUuid() {
         if ( !isComplete() ) {
             return Optional.absent();
@@ -129,13 +139,20 @@ public class InterVpnLinkDataComposite {
         return Optional.of(this.interVpnLinkCfg.getSecondEndpoint().getIpAddress().getValue());
     }
 
+    public List<BigInteger> getSecondEndpointDpns() {
+        return (!isComplete() || this.interVpnLinkState.getSecondEndpointState().getDpId() == null )
+                    ? Collections.<BigInteger>emptyList()
+                    : this.interVpnLinkState.getSecondEndpointState().getDpId();
+    }
+
     public Optional<Long> getEndpointLportTagByIpAddr(String endpointIp) {
         if ( !isComplete() ) {
             return Optional.absent();
         }
 
-        return isFirstEndpointIpAddr(endpointIp) ? Optional.of(interVpnLinkState.getFirstEndpointState().getLportTag())
-                                                 : Optional.of(interVpnLinkState.getSecondEndpointState().getLportTag());
+        return isFirstEndpointIpAddr(endpointIp)
+                    ? Optional.fromNullable(interVpnLinkState.getFirstEndpointState().getLportTag())
+                    : Optional.fromNullable(interVpnLinkState.getSecondEndpointState().getLportTag());
     }
 
     public Optional<Long> getOtherEndpointLportTagByVpnName(String vpnName) {
@@ -159,9 +176,8 @@ public class InterVpnLinkDataComposite {
     }
 
     public List<BigInteger> getEndpointDpnsByVpnName(String vpnUuid) {
-        List<BigInteger> result = new ArrayList<>();
-        if ( !isComplete()) {
-            return result;
+        if ( !isComplete() ) {
+            return new ArrayList<>();
         }
 
         return isFirstEndpointVpnName(vpnUuid) ? interVpnLinkState.getFirstEndpointState().getDpId()
@@ -170,7 +186,7 @@ public class InterVpnLinkDataComposite {
 
     public List<BigInteger> getOtherEndpointDpnsByVpnName(String vpnUuid) {
         List<BigInteger> result = new ArrayList<>();
-        if ( !isComplete()) {
+        if ( !isComplete() ) {
             return result;
         }
 
index cb379e6541f2b0329fc2ce461eb6a86994d21a3b..d7ed71fa25c311461d0231c8ceebc2054c231556 100755 (executable)
@@ -646,13 +646,9 @@ public class VpnUtil {
                 .setInterfaceName(interfaceName).setRouterName(routerName).build();
     }
 
-    static VpnInstanceOpDataEntry getVpnInstanceOpData(DataBroker broker, String rd) {
+    public static VpnInstanceOpDataEntry getVpnInstanceOpData(DataBroker broker, String rd) {
         InstanceIdentifier<VpnInstanceOpDataEntry> id = VpnUtil.getVpnInstanceOpDataIdentifier(rd);
-        Optional<VpnInstanceOpDataEntry> vpnInstanceOpData = read(broker, LogicalDatastoreType.OPERATIONAL, id);
-        if (vpnInstanceOpData.isPresent()) {
-            return vpnInstanceOpData.get();
-        }
-        return null;
+        return read(broker, LogicalDatastoreType.OPERATIONAL, id).orNull();
     }
 
     static VpnInstanceOpDataEntry getVpnInstanceOpDataFromCache(DataBroker broker, String rd) {
index 6eeb0c51e11f9fce33a1998f68092f23bd371cbf..4a6486bff3ad5a21c9d2a81bad04ada571778a9b 100755 (executable)
@@ -14,12 +14,12 @@ import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import java.math.BigInteger;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
-
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
@@ -83,9 +83,10 @@ public class InterVpnLinkListener extends AsyncDataTreeChangeListenerBase<InterV
         implements  AutoCloseable {
 
     private static final Logger LOG = LoggerFactory.getLogger(InterVpnLinkListener.class);
-    private static final String NBR_OF_DPNS_PROPERTY_NAME = "vpnservice.intervpnlink.number.dpns";
+
     private static final long INVALID_ID = 0;
 
+    private final InterVpnLinkService ivpnLinkService;
     private final DataBroker dataBroker;
     private final IMdsalApiManager mdsalManager;
     private final IdManagerService idManager;
@@ -104,7 +105,9 @@ public class InterVpnLinkListener extends AsyncDataTreeChangeListenerBase<InterV
     public InterVpnLinkListener(final DataBroker dataBroker, final IdManagerService idManager,
                                 final IMdsalApiManager mdsalManager, final IBgpManager bgpManager,
                                 final IFibManager fibManager, final NotificationPublishService notifService,
-                                final VpnFootprintService vpnFootprintService, final VpnOpDataSyncer vpnOpDataSyncer) {
+                                final InterVpnLinkService interVpnLinkService,
+                                final VpnFootprintService vpnFootprintService,
+                                final VpnOpDataSyncer vpnOpDataSyncer) {
         super(InterVpnLink.class, InterVpnLinkListener.class);
         this.dataBroker = dataBroker;
         this.idManager = idManager;
@@ -112,6 +115,7 @@ public class InterVpnLinkListener extends AsyncDataTreeChangeListenerBase<InterV
         this.bgpManager = bgpManager;
         this.fibManager = fibManager;
         this.notificationsService = notifService;
+        this.ivpnLinkService = interVpnLinkService;
         this.vpnFootprintService = vpnFootprintService;
         this.vpnOpDataSyncer = vpnOpDataSyncer;
     }
@@ -211,8 +215,7 @@ public class InterVpnLinkListener extends AsyncDataTreeChangeListenerBase<InterV
             }
         }
 
-        int numberOfDpns = Integer.getInteger(NBR_OF_DPNS_PROPERTY_NAME, 1);
-        List<BigInteger> firstDpnList = VpnUtil.pickRandomDPNs(dataBroker, numberOfDpns, null);
+        List<BigInteger> firstDpnList = ivpnLinkService.selectSuitableDpns(add);
         if (firstDpnList != null && !firstDpnList.isEmpty()) {
             List<BigInteger> secondDpnList = firstDpnList;
 
@@ -254,9 +257,11 @@ public class InterVpnLinkListener extends AsyncDataTreeChangeListenerBase<InterV
             Long firstVpnLportTag = allocateVpnLinkLportTag(key.getName() + vpn1Name);
             Long secondVpnLportTag = allocateVpnLinkLportTag(key.getName() + vpn2Name);
             FirstEndpointState firstEndPointState =
-                new FirstEndpointStateBuilder().setVpnUuid(vpn1Uuid).setLportTag(firstVpnLportTag).build();
+                new FirstEndpointStateBuilder().setVpnUuid(vpn1Uuid).setLportTag(firstVpnLportTag)
+                                               .setDpId(Collections.emptyList()).build();
             SecondEndpointState secondEndPointState =
-                new SecondEndpointStateBuilder().setVpnUuid(vpn2Uuid).setLportTag(secondVpnLportTag).build();
+                new SecondEndpointStateBuilder().setVpnUuid(vpn2Uuid).setLportTag(secondVpnLportTag)
+                                                .setDpId(Collections.emptyList()).build();
             InterVpnLinkUtil.updateInterVpnLinkState(dataBroker, add.getName(), InterVpnLinkState.State.Error,
                                                      firstEndPointState, secondEndPointState);
         }
@@ -289,6 +294,10 @@ public class InterVpnLinkListener extends AsyncDataTreeChangeListenerBase<InterV
         // Retrieving all Routers
         InstanceIdentifier<Routers> routersIid = InstanceIdentifier.builder(Neutron.class).child(Routers.class).build();
         Optional<Routers> routerOpData = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routersIid);
+        if ( !routerOpData.isPresent() ) {
+
+            return;
+        }
         List<Router> routers = routerOpData.get().getRouter();
         for ( Router router : routers ) {
             String vpnId = routerXL3VpnMap.get(router.getUuid().getValue());
@@ -296,8 +305,11 @@ public class InterVpnLinkListener extends AsyncDataTreeChangeListenerBase<InterV
                 LOG.warn("Could not find suitable VPN for router {}", router.getUuid());
                 continue; // with next router
             }
-            for ( Routes route : router.getRoutes() ) {
-                handleStaticRoute(vpnId, route, iVpnLink);
+            List<Routes> routerRoutes = router.getRoutes();
+            if ( routerRoutes != null ) {
+                for ( Routes route : routerRoutes ) {
+                    handleStaticRoute(vpnId, route, iVpnLink);
+                }
             }
         }
     }
diff --git a/vpnservice/vpnmanager/vpnmanager-impl/src/main/java/org/opendaylight/netvirt/vpnmanager/intervpnlink/InterVpnLinkService.java b/vpnservice/vpnmanager/vpnmanager-impl/src/main/java/org/opendaylight/netvirt/vpnmanager/intervpnlink/InterVpnLinkService.java
new file mode 100644 (file)
index 0000000..fa00e0a
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2016 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,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.vpnmanager.intervpnlink;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.genius.mdsalutil.NWUtil;
+import org.opendaylight.netvirt.vpnmanager.VpnUtil;
+import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
+import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
+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.vpn.instance.op.data.entry.VpnTargets;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTarget;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.links.InterVpnLink;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InterVpnLinkService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(InterVpnLinkService.class);
+    private static final String NBR_OF_DPNS_PROPERTY_NAME = "vpnservice.intervpnlink.number.dpns";
+
+    private final DataBroker dataBroker;
+
+    public InterVpnLinkService(final DataBroker dataBroker) {
+        this.dataBroker = dataBroker;
+    }
+
+    /**
+     * Retrieves a list of randomly selected DPNs avoiding to select DPNs
+     * where there is already an InterVpnLink of the same group (i.e., an
+     * InterVpnLink that links similar L3VPNs).
+     *
+     * @param interVpnLink InterVpnLink to find suitable DPNs for.
+     * @return the list of the selected DPN Ids
+     */
+    public List<BigInteger> selectSuitableDpns(InterVpnLink interVpnLink) {
+        int numberOfDpns = Integer.getInteger(NBR_OF_DPNS_PROPERTY_NAME, 1);
+        List<BigInteger> dpnIdPool = NWUtil.getOperativeDPNs(dataBroker);
+        LOG.trace("selectSuitableDpns for {} with numberOfDpns={} and availableDpns={}",
+                  interVpnLink.getName(), numberOfDpns, dpnIdPool);
+        int poolSize = dpnIdPool.size();
+        if (poolSize <= numberOfDpns) {
+            // You requested more than there is, I give you all I have.
+            return dpnIdPool;
+        }
+
+        List<InterVpnLinkDataComposite> allInterVpnLinks = InterVpnLinkCache.getAllInterVpnLinks();
+
+        // 1st criteria is to select those DPNs where there is no InterVpnLink at all
+        List<BigInteger> dpnsWithNoIVL = findDPNsWithNoInterVpnLink(dpnIdPool, allInterVpnLinks);
+        if ( dpnsWithNoIVL.size() >= numberOfDpns ) {
+            return dpnsWithNoIVL.subList(0, numberOfDpns); // Best case scenario
+        }
+
+        // Not enough. 2nd criteria is to avoid DPNs where there are InterVpnLinks of the same group
+        List<BigInteger> result = new ArrayList<>(dpnsWithNoIVL);
+        dpnIdPool.removeAll(result);
+        int pendingDPNs = numberOfDpns - result.size();
+
+        List<BigInteger> dpnsToAvoid = findDpnsWithSimilarIVpnLinks(interVpnLink, allInterVpnLinks);
+        result.addAll(dpnIdPool.stream().filter(dpId -> dpnsToAvoid == null || !dpnsToAvoid.contains(dpId))
+                               .limit(pendingDPNs).collect(Collectors.toList()));
+
+        int currentNbrOfItems = result.size();
+        if (currentNbrOfItems < numberOfDpns) {
+            // Still not enough. 3rd criteria: whatever is available
+            dpnIdPool.removeAll(result);
+            pendingDPNs = numberOfDpns - currentNbrOfItems;
+            result.addAll(dpnIdPool.subList(0, Math.max(dpnIdPool.size(), pendingDPNs)));
+        }
+        return result;
+    }
+
+    /*
+     * Given a list of Dpn Ids and a list of InterVpnLinks, this method finds
+     * the DPNs in the first list where no InterVpnLink is instantiated there.
+     *
+     * @param dpnList A list of DPN IDs
+     * @param interVpnLinks A List of InterVpnLinks to avoid
+     *
+     * @return the list of available DPNs among the specified ones
+     */
+    private List<BigInteger> findDPNsWithNoInterVpnLink(List<BigInteger> dpnList,
+                                                        List<InterVpnLinkDataComposite> interVpnLinks) {
+        List<BigInteger> occupiedDpns = new ArrayList<>();
+        for ( InterVpnLinkDataComposite ivl : interVpnLinks ) {
+            if ( ivl.isActive() ) {
+                occupiedDpns.addAll(ivl.getFirstEndpointDpns());
+                occupiedDpns.addAll(ivl.getSecondEndpointDpns());
+            }
+        }
+
+        List<BigInteger> result = new ArrayList<>(dpnList);
+        result.removeAll(occupiedDpns);
+        return result;
+    }
+
+    /*
+     * Given an InterVpnLink, this method finds those DPNs where there is an
+     * InterVpnLink of the same group. Two InterVpnLinks are in the same group
+     * if they link 2 L3VPNs that are from the same group, and 2 L3VPNs are in
+     * the same group if their iRTs match.
+     *
+     * @param interVpnLink InterVpnLink to be checked
+     * @return the list of dpnIds where the specified InterVpnLink should not
+     *     be installed
+     */
+    private List<BigInteger> findDpnsWithSimilarIVpnLinks(InterVpnLink interVpnLink,
+                                                          List<InterVpnLinkDataComposite> allInterVpnLinks) {
+        List<InterVpnLinkDataComposite> sameGroupInterVpnLinks = findInterVpnLinksSameGroup(interVpnLink,
+                                                                                            allInterVpnLinks);
+        Set<BigInteger> resultDpns = new HashSet<>();
+        for ( InterVpnLinkDataComposite ivl : sameGroupInterVpnLinks ) {
+            resultDpns.addAll(ivl.getFirstEndpointDpns());
+            resultDpns.addAll(ivl.getSecondEndpointDpns());
+        }
+        return new ArrayList<>(resultDpns);
+    }
+
+    private List<String> getRts(VpnInstanceOpDataEntry vpnInstance, VpnTarget.VrfRTType rtType) {
+        String name = vpnInstance.getVpnInstanceName();
+        VpnTargets targets = vpnInstance.getVpnTargets();
+        if (targets == null) {
+            LOG.trace("vpn targets not available for {}", name);
+            return new ArrayList<>();
+        }
+        List<VpnTarget> vpnTargets = targets.getVpnTarget();
+        if (vpnTargets == null) {
+            LOG.trace("vpnTarget values not available for {}", name);
+            return new ArrayList<>();
+        }
+        return vpnTargets.stream()
+                         .filter(target-> target.getVrfRTType().equals(rtType) ||
+                                          target.getVrfRTType().equals(VpnTarget.VrfRTType.Both))
+                         .map(target-> target.getVrfRTValue())
+                         .collect(Collectors.toList());
+    }
+
+    private List<String> getIRTsByVpnName(String vpnName) {
+        String vpn1Rd = VpnUtil.getVpnRd(dataBroker, vpnName);
+        final VpnInstanceOpDataEntry vpnInstance = VpnUtil.getVpnInstanceOpData(dataBroker, vpn1Rd);
+        return getRts(vpnInstance, VpnTarget.VrfRTType.ImportExtcommunity);
+    }
+
+    private boolean haveSameIRTs(List<String> irts1, List<String> irts2) {
+        if ( irts1 == null && irts2 == null ) {
+            return true;
+        }
+        if ( (irts1 == null && irts2 != null) || (irts1 != null && irts2 == null) ) {
+            return false;
+        }
+        if ( irts1.size() != irts2.size() ) {
+            return false;
+        }
+        irts1.sort(/*comparator*/ null);
+        irts2.sort(/*comparator*/ null);
+        return irts1.equals(irts2);
+    }
+
+    public List<InterVpnLinkDataComposite> findInterVpnLinksSameGroup(InterVpnLink ivpnLinkToMatch,
+                                                                      List<InterVpnLinkDataComposite> interVpnLinks) {
+
+        List<String> vpnToMatch1IRTs = getIRTsByVpnName(ivpnLinkToMatch.getFirstEndpoint().getVpnUuid().getValue());
+        List<String> vpnToMatch2IRTs = getIRTsByVpnName(ivpnLinkToMatch.getSecondEndpoint().getVpnUuid().getValue());
+
+        Predicate<InterVpnLinkDataComposite> areSameGroup = (ivl) -> {
+            if ( ivl.getInterVpnLinkName().equals(ivpnLinkToMatch.getName())) {
+                return false; // ivl and ivpnLinlToMatch are the same InterVpnLink
+            }
+            String vpn1Name = ivl.getFirstEndpointVpnUuid().orNull();
+            String vpn2Name = ivl.getSecondEndpointVpnUuid().orNull();
+            if ( vpn1Name == null ) {
+                return false;
+            }
+            List<String> vpn1IRTs = getIRTsByVpnName(vpn1Name);
+            List<String> vpn2IRTs = getIRTsByVpnName(vpn2Name);
+            return (haveSameIRTs(vpn1IRTs, vpnToMatch1IRTs) && haveSameIRTs(vpn2IRTs, vpnToMatch2IRTs)
+                    || (haveSameIRTs(vpn1IRTs, vpnToMatch2IRTs) && haveSameIRTs(vpn2IRTs, vpnToMatch1IRTs)) );
+        };
+
+        return interVpnLinks.stream().filter(areSameGroup).collect(Collectors.toList());
+    }
+
+}
index f231ac459a88c26a2da9e53c9fc3b34a5f414abd..05e0951e04b7c3d980c90a5dcd35cc18aa669a01 100644 (file)
   <service ref="vpnRpcServiceImpl"
            interface="org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.VpnRpcService" />
 
+  <bean id="interVpnLinkService"
+        class="org.opendaylight.netvirt.vpnmanager.intervpnlink.InterVpnLinkService">
+    <argument ref="dataBroker" />
+  </bean>
+
   <bean id="interVpnLinkListener"
         class="org.opendaylight.netvirt.vpnmanager.intervpnlink.InterVpnLinkListener"
         init-method="start" destroy-method="close">
     <argument ref="bgpmanager" />
     <argument ref="fibManager" />
     <argument ref="notificationPublishService" />
+    <argument ref="interVpnLinkService" />
     <argument ref="vpnFootprintService" />
     <argument ref="vpnOpDataSyncer" />
   </bean>