Support for SNAT and DNAT features in L3 forwarding services.
[vpnservice.git] / natservice / natservice-impl / src / main / java / org / opendaylight / vpnservice / natservice / internal / NAPTSwitchSelector.java
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NAPTSwitchSelector.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NAPTSwitchSelector.java
new file mode 100644 (file)
index 0000000..b7fd7d3
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * 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.vpnservice.natservice.internal;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceOpData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ExtRouters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.NaptSwitches;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.NaptSwitchesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitchBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+
+public class NAPTSwitchSelector {
+    private static final Logger LOG = LoggerFactory.getLogger(NAPTSwitchSelector.class);
+
+    private DataBroker dataBroker;
+    public NAPTSwitchSelector(DataBroker dataBroker) {
+        this.dataBroker = dataBroker;
+    }
+
+    BigInteger selectNewNAPTSwitch(String routerName) {
+        LOG.info("NAT Service : Select a new NAPT switch for router {}", routerName);
+        Map<BigInteger, Integer> naptSwitchWeights = constructNAPTSwitches();
+        List<BigInteger> routerSwitches = getDpnsForVpn(routerName);
+        if(routerSwitches.isEmpty()) {
+            LOG.debug("NAT Service : No dpns that are part of router {}", routerName);
+            LOG.warn("NAT Service : NAPT switch selection stopped due to no dpns scenario for router {}", routerName);
+            return BigInteger.ZERO;
+        }
+
+        Set<SwitchWeight> switchWeights = new TreeSet<>();
+        for(BigInteger dpn : routerSwitches) {
+            if(naptSwitchWeights.get(dpn) != null) {
+                switchWeights.add(new SwitchWeight(dpn, naptSwitchWeights.get(dpn)));
+            } else {
+                switchWeights.add(new SwitchWeight(dpn, 0));
+            }
+        }
+
+        BigInteger primarySwitch;
+
+        if(!switchWeights.isEmpty()) {
+
+            LOG.debug("NAT Service : Current switch weights for router {} - {}", routerName, switchWeights);
+
+            Iterator<SwitchWeight> it = switchWeights.iterator();
+            List<RouterToNaptSwitch> routerToNaptSwitchList = new ArrayList<>();
+            RouterToNaptSwitchBuilder routerToNaptSwitchBuilder = new RouterToNaptSwitchBuilder().setRouterName(routerName);
+            if ( switchWeights.size() == 1 )
+            {
+                SwitchWeight singleSwitchWeight = null;
+                while(it.hasNext() ) {
+                    singleSwitchWeight = it.next();
+                }
+                primarySwitch = singleSwitchWeight.getSwitch();
+                routerToNaptSwitchBuilder.setPrimarySwitchId(primarySwitch);
+                routerToNaptSwitchList.add(routerToNaptSwitchBuilder.build());
+                NaptSwitches naptSwitches = new NaptSwitchesBuilder().setRouterToNaptSwitch(routerToNaptSwitchList).build();
+                MDSALUtil.syncWrite( dataBroker, LogicalDatastoreType.OPERATIONAL, getNaptSwitchesIdentifier(), naptSwitches);
+
+                LOG.debug( "NAT Service : successful addition of RouterToNaptSwitch to napt-switches container for single switch" );
+                return primarySwitch;
+            }
+            else
+            {
+                SwitchWeight firstSwitchWeight = null;
+                while(it.hasNext() ) {
+                    firstSwitchWeight = it.next();
+                }
+                primarySwitch = firstSwitchWeight.getSwitch();
+                routerToNaptSwitchBuilder.setPrimarySwitchId(primarySwitch);
+                routerToNaptSwitchList.add(routerToNaptSwitchBuilder.build());
+                NaptSwitches naptSwitches = new NaptSwitchesBuilder().setRouterToNaptSwitch(routerToNaptSwitchList).build();
+                MDSALUtil.syncWrite( dataBroker, LogicalDatastoreType.OPERATIONAL, getNaptSwitchesIdentifier(), naptSwitches);
+
+                LOG.debug( "NAT Service : successful addition of RouterToNaptSwitch to napt-switches container");
+                return primarySwitch;
+            }
+        } else {
+
+                primarySwitch = BigInteger.ZERO;
+
+                LOG.debug("NAT Service : switchWeights empty, primarySwitch: {} ", primarySwitch);
+                return primarySwitch;
+        }
+
+
+    }
+
+    private Map<BigInteger, Integer> constructNAPTSwitches() {
+        Optional<NaptSwitches> optNaptSwitches = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, getNaptSwitchesIdentifier());
+        Map<BigInteger, Integer> switchWeights = new HashMap<>();
+
+        if(optNaptSwitches.isPresent()) {
+            NaptSwitches naptSwitches = optNaptSwitches.get();
+            List<RouterToNaptSwitch> routerToNaptSwitches = naptSwitches.getRouterToNaptSwitch();
+
+            for(RouterToNaptSwitch naptSwitch : routerToNaptSwitches) {
+                BigInteger primarySwitch = naptSwitch.getPrimarySwitchId();
+                //update weight
+                Integer weight = switchWeights.get(primarySwitch);
+                if(weight == null) {
+                    switchWeights.put(primarySwitch, 1);
+                } else {
+                    switchWeights.put(primarySwitch, ++weight);
+                }
+            }
+        }
+        return switchWeights;
+    }
+
+    private InstanceIdentifier<NaptSwitches> getNaptSwitchesIdentifier() {
+        return InstanceIdentifier.create(NaptSwitches.class);
+    }
+
+    public List<BigInteger> getDpnsForVpn(String routerName ) {
+        LOG.debug( "getVpnToDpnList called for RouterName {}", routerName );
+
+        InstanceIdentifier<VpnInstanceOpDataEntry> id = InstanceIdentifier.builder(VpnInstanceOpData.class)
+                .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(routerName))
+                .build();
+
+        List<BigInteger> dpnsInVpn = new ArrayList<>();
+        Optional<VpnInstanceOpDataEntry> vpnInstanceOpData = NatUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
+
+        if(vpnInstanceOpData.isPresent()) {
+            LOG.debug( "NATService : getVpnToDpnList able to fetch vpnInstanceOpData" );
+            VpnInstanceOpDataEntry vpnInstanceOpDataEntry = vpnInstanceOpData.get();
+            List<VpnToDpnList> vpnDpnList = vpnInstanceOpDataEntry.getVpnToDpnList();
+            if(vpnDpnList != null) {
+                for(VpnToDpnList vpnToDpn: vpnDpnList) {
+                    dpnsInVpn.add(vpnToDpn.getDpnId());
+                }
+            }
+        }
+
+        LOG.debug( "getVpnToDpnList returning vpnDpnList {}", dpnsInVpn);
+        return dpnsInVpn;
+    }
+
+    private static class SwitchWeight implements Comparable<SwitchWeight>
+    {
+        private BigInteger swich;
+        private int weight;
+
+        public SwitchWeight( BigInteger swich, int weight )
+        {
+            this.swich = swich;
+            this.weight = weight;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((swich == null) ? 0 : swich.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            SwitchWeight other = (SwitchWeight) obj;
+            if (swich == null) {
+                if (other.swich != null)
+                    return false;
+            } else if (!swich.equals(other.swich))
+                return false;
+            return true;
+        }
+
+        public BigInteger getSwitch() {
+            return swich;
+        }
+
+        public int getWeight() { 
+            return weight;
+        }
+
+        public void incrementWeight() {
+            ++ weight;
+        }
+
+        @Override
+        public int compareTo(SwitchWeight o) {
+            return o.getWeight() - weight;
+        }
+    }
+}