use the provider mappings for external group
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / ha / WeightedCentralizedSwitchScheduler.java
index dd33e2fd660cce5d591e7ca229ee08fb9caec306..86bf977355494dc7c75f6e6c8715e238b4fab7d5 100644 (file)
@@ -11,6 +11,8 @@ package org.opendaylight.netvirt.natservice.ha;
 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
 
+import com.google.common.base.Optional;
+
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -20,22 +22,32 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
+
+import javax.annotation.Nullable;
 import javax.inject.Inject;
 import javax.inject.Singleton;
+
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 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.SingleTransactionDataBroker;
 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
 import org.opendaylight.netvirt.natservice.api.CentralizedSwitchScheduler;
+import org.opendaylight.netvirt.natservice.api.NatSwitchCache;
+import org.opendaylight.netvirt.natservice.api.NatSwitchCacheListener;
+import org.opendaylight.netvirt.natservice.api.SwitchInfo;
 import org.opendaylight.netvirt.natservice.internal.NatUtil;
 import org.opendaylight.netvirt.vpnmanager.api.IVpnFootprintService;
 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.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExtRouters;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.NaptSwitches;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitch;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
@@ -48,40 +60,58 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @Singleton
-public class WeightedCentralizedSwitchScheduler implements CentralizedSwitchScheduler {
+public class WeightedCentralizedSwitchScheduler implements CentralizedSwitchScheduler, NatSwitchCacheListener {
     private static final Logger LOG = LoggerFactory.getLogger(WeightedCentralizedSwitchScheduler.class);
     private static final Integer INITIAL_SWITCH_WEIGHT = Integer.valueOf(0);
 
-    private final Map<BigInteger,Integer> switchWeightsMap = new ConcurrentHashMap<>();
+    private final Map<String, Map<BigInteger,Integer>> providerSwitchWeightsMap = new ConcurrentHashMap<>();
     private final Map<String,String> subnetIdToRouterPortMap = new ConcurrentHashMap<>();
     private final Map<String,String> subnetIdToElanInstanceMap = new ConcurrentHashMap<>();
     private final DataBroker dataBroker;
     private final ManagedNewTransactionRunner txRunner;
     private final OdlInterfaceRpcService interfaceManager;
     private final IVpnFootprintService vpnFootprintService;
+    private final NatserviceConfig.NatMode natMode;
 
     @Inject
-    public WeightedCentralizedSwitchScheduler(DataBroker dataBroker, OdlInterfaceRpcService interfaceManager,
-            IVpnFootprintService vpnFootprintService) {
+    public WeightedCentralizedSwitchScheduler(final DataBroker dataBroker,
+            final OdlInterfaceRpcService interfaceManager,
+            final IVpnFootprintService vpnFootprintService, final NatserviceConfig config,
+            final NatSwitchCache natSwitchCache) {
         this.dataBroker = dataBroker;
         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
         this.interfaceManager = interfaceManager;
         this.vpnFootprintService = vpnFootprintService;
+        if (config != null) {
+            this.natMode = config.getNatMode();
+        } else {
+            this.natMode = NatserviceConfig.NatMode.Controller;
+        }
+        natSwitchCache.register(this);
     }
 
     @Override
     public boolean scheduleCentralizedSwitch(Routers router) {
-        BigInteger nextSwitchId = getSwitchWithLowestWeight();
+        String providerNet = NatUtil.getElanInstancePhysicalNetwok(router.getNetworkId().getValue(),dataBroker);
+        BigInteger nextSwitchId = getSwitchWithLowestWeight(providerNet);
+        if (nextSwitchId == BigInteger.valueOf(0)) {
+            LOG.error("In scheduleCentralizedSwitch, unable to schedule the router {} as there is no available switch.",
+                    router.getRouterName());
+            return false;
+        }
+
         LOG.info("scheduleCentralizedSwitch for router {} on switch {}", router.getRouterName(), nextSwitchId);
         String routerName = router.getRouterName();
         RouterToNaptSwitchBuilder routerToNaptSwitchBuilder =
                 new RouterToNaptSwitchBuilder().setRouterName(routerName);
-        RouterToNaptSwitch id = routerToNaptSwitchBuilder.setPrimarySwitchId(nextSwitchId).build();
+        RouterToNaptSwitch id = routerToNaptSwitchBuilder.setPrimarySwitchId(nextSwitchId)
+                .setEnableSnat(router.isEnableSnat()).build();
         addToDpnMaps(routerName, router.getSubnetIds(), nextSwitchId);
         try {
             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
                     getNaptSwitchesIdentifier(routerName), id);
-            switchWeightsMap.put(nextSwitchId,switchWeightsMap.get(nextSwitchId) + 1);
+            Map<BigInteger,Integer> switchWeightMap = providerSwitchWeightsMap.get(providerNet);
+            switchWeightMap.put(nextSwitchId,switchWeightMap.get(nextSwitchId) + 1);
 
         } catch (TransactionCommitFailedException e) {
             LOG.error("ScheduleCentralizedSwitch failed for {}", routerName);
@@ -99,19 +129,47 @@ public class WeightedCentralizedSwitchScheduler implements CentralizedSwitchSche
         BigInteger primarySwitchId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, newRouter.getRouterName());
         addToDpnMaps(routerName, addedSubnetIds, primarySwitchId);
         deleteFromDpnMaps(routerName, deletedSubnetIds, primarySwitchId);
+        try {
+            InstanceIdentifier<RouterToNaptSwitch> id  = NatUtil.buildNaptSwitchIdentifier(routerName);
+            RouterToNaptSwitch routerToNaptSwitch = SingleTransactionDataBroker.syncRead(dataBroker,
+                    LogicalDatastoreType.CONFIGURATION, id);
+            boolean isSnatEnabled = newRouter.isEnableSnat();
+            List<ExternalIps> updateExternalIps = newRouter.getExternalIps();
+            if (updateExternalIps == null || updateExternalIps.isEmpty()) {
+                isSnatEnabled = false;
+            }
+            if (isSnatEnabled != routerToNaptSwitch.isEnableSnat()) {
+                RouterToNaptSwitchBuilder routerToNaptSwitchBuilder =
+                        new RouterToNaptSwitchBuilder(routerToNaptSwitch);
+                routerToNaptSwitchBuilder.setEnableSnat(isSnatEnabled);
+                SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
+                        getNaptSwitchesIdentifier(routerName), routerToNaptSwitchBuilder.build());
+            }
+        } catch (ReadFailedException e) {
+            LOG.error("updateCentralizedSwitch ReadFailedException for {}", routerName);
+        } catch (TransactionCommitFailedException e) {
+            LOG.error("updateCentralizedSwitch TransactionCommitFailedException for {}", routerName);
+        }
         return true;
     }
 
     @Override
     public boolean releaseCentralizedSwitch(Routers router) {
+        String providerNet = NatUtil.getElanInstancePhysicalNetwok(router.getNetworkId().getValue(),dataBroker);
         String routerName = router.getRouterName();
         BigInteger primarySwitchId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
+        if (primarySwitchId == null || primarySwitchId == BigInteger.valueOf(0)) {
+            LOG.info("releaseCentralizedSwitch: NAPT Switch is not allocated for router {}", router.getRouterName());
+            return false;
+        }
+
         LOG.info("releaseCentralizedSwitch for router {} from switch {}", router.getRouterName(), primarySwitchId);
         deleteFromDpnMaps(routerName, router.getSubnetIds(), primarySwitchId);
         try {
             SingleTransactionDataBroker.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
                     getNaptSwitchesIdentifier(routerName));
-            switchWeightsMap.put(primarySwitchId,switchWeightsMap.get(primarySwitchId) - 1);
+            Map<BigInteger,Integer> switchWeightMap = providerSwitchWeightsMap.get(providerNet);
+            switchWeightMap.put(primarySwitchId, switchWeightMap.get(primarySwitchId) - 1);
         } catch (TransactionCommitFailedException e) {
             return false;
         }
@@ -154,8 +212,6 @@ public class WeightedCentralizedSwitchScheduler implements CentralizedSwitchSche
         }
     }
 
-
-
     private void deleteFromDpnMaps(String routerName, List<Uuid> deletedSubnetIds, BigInteger primarySwitchId) {
         if (deletedSubnetIds == null || deletedSubnetIds.isEmpty()) {
             LOG.debug("deleteFromDpnMaps no subnets associated with {}", routerName);
@@ -174,7 +230,8 @@ public class WeightedCentralizedSwitchScheduler implements CentralizedSwitchSche
                     vpnFootprintService.updateVpnToDpnMapping(primarySwitchId, routerName, primaryRd,
                             routerPort, null, false);
                     NatUtil.removeFromNeutronRouterDpnsMap(routerName, primarySwitchId, tx);
-                    NatUtil.removeFromDpnRoutersMap(dataBroker, routerName, routerName, interfaceManager, tx);
+                    NatUtil.removeFromDpnRoutersMap(dataBroker, routerName, routerName,
+                            primarySwitchId, interfaceManager, tx);
                     if (subnetIdToElanInstanceMap.containsKey(subnetUuid.getValue())) {
                         String elanInstanceName = subnetIdToElanInstanceMap.remove(subnetUuid.getValue());
                         NatUtil.removePseudoPortFromElanDpn(elanInstanceName, elanInstanceName, primarySwitchId,
@@ -188,44 +245,99 @@ public class WeightedCentralizedSwitchScheduler implements CentralizedSwitchSche
     }
 
     @Override
-    public boolean addSwitch(BigInteger dpnId) {
-        /* Initialize the switch in the map with weight 0 */
-        LOG.info("addSwitch: Adding {} dpnId to switchWeightsMap", dpnId);
-        switchWeightsMap.put(dpnId, INITIAL_SWITCH_WEIGHT);
-        return true;
+    public void switchAddedToCache(SwitchInfo switchInfo) {
+        boolean scheduleRouters = (providerSwitchWeightsMap.size() == 0) ? true : false;
+        for (String providerNet : switchInfo.getProviderNets()) {
+            Map<BigInteger,Integer> switchWeightMap = providerSwitchWeightsMap.get(providerNet);
+            if (providerSwitchWeightsMap.get(providerNet) == null) {
+                switchWeightMap = new ConcurrentHashMap<>();
+                providerSwitchWeightsMap.put(providerNet, switchWeightMap);
+            }
+            LOG.info("addSwitch: Adding {} dpnId with provider mapping {} to switchWeightsMap",
+                    switchInfo.getDpnId(), providerNet);
+            switchWeightMap.put(switchInfo.getDpnId(), INITIAL_SWITCH_WEIGHT);
+        }
+        if (natMode == NatserviceConfig.NatMode.Conntrack && scheduleRouters) {
+            Optional<ExtRouters> optRouters;
+            try {
+                optRouters = SingleTransactionDataBroker.syncReadOptional(dataBroker,
+                        LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(ExtRouters.class));
+            } catch (ReadFailedException e) {
+                LOG.error("addSwitch: Error reading external routers", e);
+                return;
+            }
 
+            if (optRouters.isPresent()) {
+                // Get the list of routers and verify if any routers do not have primarySwitch allocated.
+                for (Routers router : optRouters.get().getRouters()) {
+                    List<ExternalIps> externalIps = router.getExternalIps();
+                    if (router.isEnableSnat() && externalIps != null && !externalIps.isEmpty()) {
+                        // Check if the primarySwitch is allocated for the router.
+                        if (!isPrimarySwitchAllocatedForRouter(router.getRouterName())) {
+                            scheduleCentralizedSwitch(router);
+                        }
+                    }
+                }
+            }
+        }
+        return;
+    }
+
+    private boolean isPrimarySwitchAllocatedForRouter(String routerName) {
+        InstanceIdentifier<RouterToNaptSwitch> routerToNaptSwitch =
+                NatUtil.buildNaptSwitchRouterIdentifier(routerName);
+        try {
+            RouterToNaptSwitch rtrToNapt = SingleTransactionDataBroker.syncRead(dataBroker,
+                    LogicalDatastoreType.CONFIGURATION, routerToNaptSwitch);
+            BigInteger dpnId = rtrToNapt.getPrimarySwitchId();
+            if (dpnId == null || dpnId.equals(BigInteger.ZERO)) {
+                return false;
+            }
+        } catch (ReadFailedException e) {
+            LOG.error("isPrimarySwitchAllocatedForRouter: Error reading RouterToNaptSwitch model", e);
+            return false;
+        }
+        return true;
     }
 
     @Override
-    public boolean removeSwitch(BigInteger dpnId) {
+    public void switchRemovedFromCache(SwitchInfo switchInfo) {
+        BigInteger dpnId = switchInfo.getDpnId();
         LOG.info("removeSwitch: Removing {} dpnId to switchWeightsMap", dpnId);
-        if (!INITIAL_SWITCH_WEIGHT.equals(switchWeightsMap.get(dpnId))) {
-            NaptSwitches naptSwitches = getNaptSwitches(dataBroker);
-            for (RouterToNaptSwitch routerToNaptSwitch : naptSwitches.getRouterToNaptSwitch()) {
-                if (dpnId.equals(routerToNaptSwitch.getPrimarySwitchId())) {
-                    Routers router = NatUtil.getRoutersFromConfigDS(dataBroker, routerToNaptSwitch.getRouterName());
-                    releaseCentralizedSwitch(router);
-                    switchWeightsMap.remove(dpnId);
-                    scheduleCentralizedSwitch(router);
-                    break;
+        for (Map.Entry<String,Map<BigInteger,Integer>> providerNet : providerSwitchWeightsMap.entrySet()) {
+            Map<BigInteger,Integer> switchWeightMap = providerNet.getValue();
+            if (natMode == NatserviceConfig.NatMode.Conntrack
+                    && !INITIAL_SWITCH_WEIGHT.equals(switchWeightMap.get(dpnId))) {
+                NaptSwitches naptSwitches = getNaptSwitches();
+                for (RouterToNaptSwitch routerToNaptSwitch : naptSwitches.getRouterToNaptSwitch()) {
+                    if (dpnId.equals(routerToNaptSwitch.getPrimarySwitchId())) {
+                        Routers router = NatUtil.getRoutersFromConfigDS(dataBroker, routerToNaptSwitch.getRouterName());
+                        releaseCentralizedSwitch(router);
+                        scheduleCentralizedSwitch(router);
+                        break;
+                    }
                 }
             }
-        } else {
-            switchWeightsMap.remove(dpnId);
+            switchWeightMap.remove(dpnId);
         }
-        return true;
+        return;
     }
 
-    public static NaptSwitches getNaptSwitches(DataBroker dataBroker) {
+    private NaptSwitches getNaptSwitches() {
         InstanceIdentifier<NaptSwitches> id = InstanceIdentifier.builder(NaptSwitches.class).build();
         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
                 LogicalDatastoreType.CONFIGURATION, id).orNull();
     }
 
-    private BigInteger getSwitchWithLowestWeight() {
+    private BigInteger getSwitchWithLowestWeight(String providerNet) {
         int lowestWeight = Integer.MAX_VALUE;
         BigInteger nextSwitchId = BigInteger.valueOf(0);
-        for (Entry<BigInteger, Integer> entry : switchWeightsMap.entrySet()) {
+        Map<BigInteger,Integer> switchWeightMap = providerSwitchWeightsMap.get(providerNet);
+        if (null == switchWeightMap) {
+            LOG.error("No switch have the provider mapping {}", providerNet);
+            return nextSwitchId;
+        }
+        for (Entry<BigInteger, Integer> entry : switchWeightMap.entrySet()) {
             BigInteger dpnId = entry.getKey();
             Integer weight = entry.getValue();
             if (lowestWeight > weight) {
@@ -233,6 +345,8 @@ public class WeightedCentralizedSwitchScheduler implements CentralizedSwitchSche
                 nextSwitchId =  dpnId;
             }
         }
+        LOG.info("getSwitchWithLowestWeight: switchWeightsMap {}, returning nextSwitchId {} ",
+                providerSwitchWeightsMap, nextSwitchId);
         return nextSwitchId;
     }
 
@@ -246,15 +360,25 @@ public class WeightedCentralizedSwitchScheduler implements CentralizedSwitchSche
                 new SubnetmapKey(subnetId)).build();
     }
 
-    @Override
-    public boolean getCentralizedSwitch(String routerName) {
-        // TODO Auto-generated method stub
-        return false;
+    @Nullable
+    public BigInteger getCentralizedSwitch(String routerName) {
+        try {
+            Optional<RouterToNaptSwitch> naptSwitches = SingleTransactionDataBroker
+                    .syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
+                            getNaptSwitchesIdentifier(routerName));
+            if (!naptSwitches.isPresent()) {
+                LOG.info("No Napt switch is scheduled for {}", routerName);
+                return null;
+            }
+            return naptSwitches.get().getPrimarySwitchId();
+        } catch (ReadFailedException e) {
+            LOG.error("Error reading RouterToNaptSwitch model", e);
+            return null;
+        }
     }
 
-    public static List<Uuid> getUpdatedSubnetIds(
-            List<Uuid> updatedSubnetIds,
-            List<Uuid> currentSubnetIds) {
+    @Nullable
+    private static List<Uuid> getUpdatedSubnetIds(List<Uuid> updatedSubnetIds, List<Uuid> currentSubnetIds) {
         if (updatedSubnetIds == null) {
             return null;
         }