Merge "LLDP monitor interval update fixes"
authorVishal Thapar <vishal.thapar@ericsson.com>
Tue, 26 Apr 2016 17:05:59 +0000 (17:05 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Tue, 26 Apr 2016 17:05:59 +0000 (17:05 +0000)
53 files changed:
features/pom.xml
features/src/main/features/features.xml
fibmanager/fibmanager-api/pom.xml
fibmanager/fibmanager-api/src/main/yang/fib-rpc.yang [new file with mode: 0644]
fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibConstants.java [new file with mode: 0644]
fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibManager.java
fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibManagerProvider.java
fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibRpcServiceImpl.java [new file with mode: 0644]
fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibUtil.java
fibmanager/fibmanager-impl/src/test/java/org/opendaylight/vpnservice/fibmanager/test/FibManagerTest.java
natservice/natservice-api/pom.xml [new file with mode: 0644]
natservice/natservice-api/src/main/yang/odl-nat.yang [new file with mode: 0644]
natservice/natservice-impl/pom.xml [new file with mode: 0644]
natservice/natservice-impl/src/main/config/default-config.xml [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/DpnInVpnListener.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/EventDispatcher.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalNetworkListener.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalNetworksChangeListener.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalRoutersListener.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/FloatingIPHandler.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/FloatingIPListener.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/IPAddress.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/InterfaceStateEventListener.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NAPTEntryEvent.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NAPTSwitchSelector.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptEventHandler.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptFlowRemovedEventHandler.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptManager.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptPacketInHandler.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptSwitchHA.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatConstants.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatNodeEventListener.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatServiceProvider.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatUtil.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/RouterPortsListener.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/SNATDefaultRouteProgrammer.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/SessionAddress.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/VpnFloatingIpHandler.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/natservice/impl/rev160111/NATServiceModule.java [new file with mode: 0644]
natservice/natservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/natservice/impl/rev160111/NATServiceModuleFactory.java [new file with mode: 0644]
natservice/natservice-impl/src/main/yang/natservice-impl.yang [new file with mode: 0644]
natservice/natservice-impl/src/test/java/org/opendaylight/vpnservice/natservice/internal/test/ExternalNetworksChangeListenerTest.java [new file with mode: 0644]
natservice/natservice-impl/src/test/java/org/opendaylight/vpnservice/natservice/internal/test/NaptManagerTest.java [new file with mode: 0644]
natservice/pom.xml [new file with mode: 0644]
pom.xml
vpnmanager/vpnmanager-api/src/main/yang/odl-l3vpn.yang
vpnmanager/vpnmanager-api/src/main/yang/vpn-rpc.yang [new file with mode: 0644]
vpnmanager/vpnmanager-impl/src/main/java/org/opendaylight/vpnservice/VpnConstants.java
vpnmanager/vpnmanager-impl/src/main/java/org/opendaylight/vpnservice/VpnInterfaceManager.java
vpnmanager/vpnmanager-impl/src/main/java/org/opendaylight/vpnservice/VpnManager.java
vpnmanager/vpnmanager-impl/src/main/java/org/opendaylight/vpnservice/VpnSubnetRouteHandler.java
vpnmanager/vpnmanager-impl/src/main/java/org/opendaylight/vpnservice/VpnUtil.java
vpnmanager/vpnmanager-impl/src/test/java/org/opendaylight/vpnservice/test/VpnSubnetRouteHandlerTest.java

index c5ece44da22229bd78bdd54daef410f42734855f..0cd96d4a4946349e361a2d518290741c22f55258 100644 (file)
@@ -364,6 +364,23 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
       <artifactId>dhcpservice-api</artifactId>
       <version>${vpnservices.version}</version>
     </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>natservice-impl</artifactId>
+      <version>${vpnservices.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>natservice-impl</artifactId>
+      <version>${vpnservices.version}</version>
+      <classifier>config</classifier>
+      <type>xml</type>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>natservice-api</artifactId>
+      <version>${vpnservices.version}</version>
+    </dependency>
     <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>neutronvpn-api</artifactId>
index f441332c1b60b35c1bbc2c18d0acab138290520e..d92ccda954c749d5cf3bd2a18a5d1fb32862c019 100644 (file)
@@ -41,6 +41,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
     <bundle>mvn:org.opendaylight.vpnservice/itm-api/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.vpnservice/neutronvpn-api/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.vpnservice/dhcpservice-api/{{VERSION}}</bundle>
+    <bundle>mvn:org.opendaylight.vpnservice/natservice-api/{{VERSION}}</bundle>
   </feature>
   <feature name='odl-vpnservice-impl' version='${project.version}' description='OpenDaylight :: vpnservice :: impl '>
     <feature version='${mdsal.version}'>odl-mdsal-broker</feature>
@@ -70,6 +71,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
     <bundle>mvn:org.opendaylight.vpnservice/dhcpservice-impl/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.vpnservice/elanmanager-api/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.vpnservice/elanmanager-impl/{{VERSION}}</bundle>
+    <bundle>mvn:org.opendaylight.vpnservice/natservice-impl/{{VERSION}}</bundle>
 
     <!--<bundle>mvn:org.opendaylight.vpnservice.third-party/org.apache.thriftlib/1.1.0-SNAPSHOT</bundle>-->
     <bundle>wrap:mvn:org.apache.thrift/libthrift/0.9.1$overwrite=merge&amp;Bundle-Version=0.9.1&amp;Export-Package=*;-noimport:=true;version="0.9.1"</bundle>
@@ -88,6 +90,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
     <configfile finalname="neutronvpn-impl-default-config.xml">mvn:org.opendaylight.vpnservice/neutronvpn-impl/{{VERSION}}/xml/config</configfile>
     <configfile finalname="dhcpservice-impl-default-config.xml">mvn:org.opendaylight.vpnservice/dhcpservice-impl/{{VERSION}}/xml/config</configfile>
     <configfile finalname="elanmanager-impl-default-config.xml">mvn:org.opendaylight.vpnservice/elanmanager-impl/{{VERSION}}/xml/config</configfile>
+    <configfile finalname="natservice-impl-default-config.xml">mvn:org.opendaylight.vpnservice/natservice-impl/{{VERSION}}/xml/config</configfile>
 
   </feature>
   <feature name='odl-vpnservice-impl-rest' version='${project.version}' description='OpenDaylight :: vpnservice :: impl :: REST '>
index d698dec9b79bdec28f71133aefede34be0be6f0f..4296f0055ad87a7a8a01b9bb1e8fb41fe082afaf 100644 (file)
@@ -54,5 +54,10 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>config-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.openflowplugin.model</groupId>
+      <artifactId>model-flow-base</artifactId>
+      <version>${openflowplugin.version}</version>
+    </dependency>
   </dependencies>
 </project>
diff --git a/fibmanager/fibmanager-api/src/main/yang/fib-rpc.yang b/fibmanager/fibmanager-api/src/main/yang/fib-rpc.yang
new file mode 100644 (file)
index 0000000..9a76379
--- /dev/null
@@ -0,0 +1,57 @@
+module fib-rpc {\r
+    namespace "urn:opendaylight:vpnservice:fib:rpc";\r
+    prefix "fib-rpc";\r
+\r
+    import ietf-inet-types {\r
+        prefix inet;\r
+        revision-date "2010-09-24";\r
+    }\r
+\r
+    import opendaylight-flow-types {\r
+        prefix offlow;\r
+        revision-date "2013-10-26";\r
+    }\r
+\r
+    revision "2016-01-21" {\r
+        description "FIB Servicer RPC Module";\r
+    }\r
+\r
+    /* RPCs */\r
+\r
+    rpc create-fib-entry {\r
+        description "to install FIB/LFIB/TST routes on specified dpn with given instructions";\r
+        input {\r
+            leaf source-dpid {\r
+                type uint64;\r
+            }\r
+            leaf vpn-name {\r
+                type string;\r
+            }\r
+            leaf service-id {\r
+                type uint32;\r
+            }\r
+            leaf ip-address {\r
+                type string;\r
+            }\r
+            uses offlow:instruction-list;\r
+        }\r
+    }\r
+\r
+    rpc remove-fib-entry {\r
+        description "to remove FIB/LFIB/TST routes from specified dpn";\r
+        input {\r
+            leaf source-dpid {\r
+                type uint64;\r
+            }\r
+            leaf vpn-name {\r
+                type string;\r
+            }\r
+            leaf service-id {\r
+                type uint32;\r
+            }\r
+            leaf ip-address {\r
+                type string;\r
+            }\r
+        }\r
+    }\r
+}
\ No newline at end of file
diff --git a/fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibConstants.java b/fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibConstants.java
new file mode 100644 (file)
index 0000000..52dc950
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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.fibmanager;
+
+import java.math.BigInteger;
+
+public class FibConstants {
+    static final BigInteger COOKIE_VM_LFIB_TABLE = new BigInteger("8000002", 16);
+    static final BigInteger COOKIE_VM_FIB_TABLE =  new BigInteger("8000003", 16);
+    static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
+    static final int DEFAULT_FIB_FLOW_PRIORITY = 10;
+    static final String FLOWID_PREFIX = "L3.";
+    static final String VPN_IDPOOL_NAME = "vpnservices";
+    static final String SEPARATOR = ".";
+}
index b9906ada2455bb32e8062d80fd2088bb09802d9f..769038f1cae35ebb43367547565317cc1b4d18b7 100644 (file)
@@ -56,6 +56,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instanc
 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.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.to.extraroute.Vpn;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.to.extraroute.VpnKey;
@@ -93,6 +94,7 @@ public class FibManager extends AbstractDataChangeListener<VrfEntry> implements
   private NexthopManager nextHopManager;
   private ItmRpcService itmManager;
   private OdlInterfaceRpcService interfaceManager;
+  private IdManagerService idManager;
   private static final BigInteger COOKIE_VM_LFIB_TABLE = new BigInteger("8000002", 16);
   private static final BigInteger COOKIE_VM_FIB_TABLE =  new BigInteger("8000003", 16);
   private static final int DEFAULT_FIB_FLOW_PRIORITY = 10;
@@ -140,6 +142,10 @@ public class FibManager extends AbstractDataChangeListener<VrfEntry> implements
       this.interfaceManager = ifManager;
   }
 
+  public void setIdManager(IdManagerService idManager) {
+      this.idManager = idManager;
+  }
+
   private void registerListener(final DataBroker db) {
     try {
       listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
@@ -159,19 +165,19 @@ public class FibManager extends AbstractDataChangeListener<VrfEntry> implements
   @Override
   protected void add(final InstanceIdentifier<VrfEntry> identifier,
                      final VrfEntry vrfEntry) {
-    LOG.trace("key: " + identifier + ", value=" + vrfEntry );
+    LOG.trace("Add key: " + identifier + ", value=" + vrfEntry );
     createFibEntries(identifier, vrfEntry);
   }
 
   @Override
   protected void remove(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry) {
-    LOG.trace("key: " + identifier + ", value=" + vrfEntry);
+    LOG.trace("Remove key: " + identifier + ", value=" + vrfEntry);
     deleteFibEntries(identifier, vrfEntry);
   }
 
   @Override
   protected void update(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update) {
-    LOG.trace("key: " + identifier + ", original=" + original + ", update=" + update );
+    LOG.trace("Update key: " + identifier + ", original=" + original + ", update=" + update );
     createFibEntries(identifier, update);
   }
 
@@ -182,12 +188,13 @@ public class FibManager extends AbstractDataChangeListener<VrfEntry> implements
     Preconditions.checkNotNull(vrfEntry, "VrfEntry cannot be null or empty!");
 
     VpnInstanceOpDataEntry vpnInstance = getVpnInstance(vrfTableKey.getRouteDistinguisher());
-    Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available!");
-    Preconditions.checkNotNull(vpnInstance.getVpnId(), "Vpn Instance with rd " + vpnInstance.getVrfId() + "has null vpnId!");
+    Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
+    Preconditions.checkNotNull(vpnInstance.getVpnId(), "Vpn Instance with rd " + vpnInstance.getVrfId() + " has null vpnId!");
 
     Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
     Long vpnId = vpnInstance.getVpnId();
-    RdToElanOpEntry rdToElanOpEntry = getRdToElanOpEntry(broker, vrfTableKey.getRouteDistinguisher(),
+    String rd = vrfTableKey.getRouteDistinguisher();
+    RdToElanOpEntry rdToElanOpEntry = getRdToElanOpEntry(broker, rd,
             vrfEntry.getDestPrefix());
     if (rdToElanOpEntry!=null) {
         if (vpnToDpnList!=null) {
@@ -198,14 +205,14 @@ public class FibManager extends AbstractDataChangeListener<VrfEntry> implements
         return;
     }
     BigInteger localDpnId = createLocalFibEntry(vpnInstance.getVpnId(),
-              vrfTableKey.getRouteDistinguisher(), vrfEntry);
+            rd, vrfEntry);
     if (vpnToDpnList != null) {
-      for (VpnToDpnList curDpn : vpnToDpnList) {
-        if (!curDpn.getDpnId().equals(localDpnId)) {
-          createRemoteFibEntry(localDpnId, curDpn.getDpnId(), vpnInstance.getVpnId(),
-                               vrfTableKey, vrfEntry);
+        for (VpnToDpnList curDpn : vpnToDpnList) {
+            if (!curDpn.getDpnId().equals(localDpnId)) {
+                createRemoteFibEntry(localDpnId, curDpn.getDpnId(), vpnInstance.getVpnId(),
+                        vrfTableKey, vrfEntry);
+            }
         }
-      }
     }
   }
 
@@ -213,12 +220,22 @@ public class FibManager extends AbstractDataChangeListener<VrfEntry> implements
                                        long vpnId, VrfEntry vrfEntry){
       makeSubnetRouteFlow(dpnId);
       List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
-      List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
       Long elanTag = rdToElanOpEntry.getElanTag();
+
       instructions.add(new InstructionInfo(InstructionType.write_metadata,  new BigInteger[] { (BigInteger.valueOf(elanTag)).shiftLeft(24), MetaDataUtil.METADATA_MASK_SERVICE }));
       instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NwConstants.L3_SUBNET_ROUTE_TABLE }));
       makeConnectedRoute(dpnId,vpnId,vrfEntry,rdToElanOpEntry.getRd(),
               instructions,NwConstants.ADD_FLOW);
+
+      List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+      // reinitialize instructions list for LFIB Table
+      instructions = new ArrayList<InstructionInfo>();
+
+      actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
+      instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
+      instructions.add(new InstructionInfo(InstructionType.write_metadata,  new BigInteger[] { (BigInteger.valueOf(elanTag)).shiftLeft(24), MetaDataUtil.METADATA_MASK_SERVICE }));
+      instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NwConstants.L3_SUBNET_ROUTE_TABLE }));
+
       makeLFibTableEntry(dpnId,vrfEntry.getLabel(),instructions,
               vrfEntry.getNextHopAddress(),NwConstants.ADD_FLOW);
       // TODO makeTunnelTableEntry();
@@ -567,7 +584,11 @@ public class FibManager extends AbstractDataChangeListener<VrfEntry> implements
           LOG.trace("Clean up vpn interface {} from dpn {} to vpn {} list.", ifName, prefixInfo.getDpnId(), rd);
           FibUtil.delete(broker, LogicalDatastoreType.OPERATIONAL,
                          FibUtil.getVpnInterfaceIdentifier(ifName));
-       }
+      }
+
+      FibUtil.releaseId(idManager, FibConstants.VPN_IDPOOL_NAME,
+         FibUtil.getNextHopLabelKey(rd, vrfEntry.getDestPrefix()));
+
   }
 
   private void deleteFibEntries(final InstanceIdentifier<VrfEntry> identifier,
@@ -576,8 +597,12 @@ public class FibManager extends AbstractDataChangeListener<VrfEntry> implements
     Preconditions.checkNotNull(vrfTableKey, "VrfTablesKey cannot be null or empty!");
     Preconditions.checkNotNull(vrfEntry, "VrfEntry cannot be null or empty!");
 
+    String rd  = vrfTableKey.getRouteDistinguisher();
     VpnInstanceOpDataEntry vpnInstance = getVpnInstance(vrfTableKey.getRouteDistinguisher());
-    Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available!");
+    if (vpnInstance == null) {
+        LOG.debug("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
+        return;
+    }
     Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
     RdToElanOpEntry rdToElanOpEntry= getRdToElanOpEntry(broker,vrfTableKey.getRouteDistinguisher(),
             vrfEntry.getDestPrefix());
@@ -595,16 +620,18 @@ public class FibManager extends AbstractDataChangeListener<VrfEntry> implements
         InstanceIdentifier<RdToElanOpEntry> id = getRdToElanOpEntryDataPath(vrfTableKey.getRouteDistinguisher(),
                 vrfEntry.getDestPrefix());
         MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL,id);
+        FibUtil.releaseId(idManager,FibConstants.VPN_IDPOOL_NAME,
+                FibUtil.getNextHopLabelKey(rd, vrfEntry.getDestPrefix()));
         return;
     }
     BigInteger localDpnId = deleteLocalFibEntry(vpnInstance.getVpnId(),
-              vrfTableKey.getRouteDistinguisher(), vrfEntry);
+            vrfTableKey.getRouteDistinguisher(), vrfEntry);
     if (vpnToDpnList != null) {
-      for (VpnToDpnList curDpn : vpnToDpnList) {
-        if (!curDpn.getDpnId().equals(localDpnId)) {
-          deleteRemoteRoute(localDpnId, curDpn.getDpnId(), vpnInstance.getVpnId(), vrfTableKey, vrfEntry);
+        for (VpnToDpnList curDpn : vpnToDpnList) {
+            if (!curDpn.getDpnId().equals(localDpnId)) {
+                deleteRemoteRoute(localDpnId, curDpn.getDpnId(), vpnInstance.getVpnId(), vrfTableKey, vrfEntry);
+            }
         }
-      }
     }
     //The flow/group entry has been deleted from config DS; need to clean up associated operational
     //DS entries in VPN Op DS, VpnInstanceOpData and PrefixToInterface to complete deletion
@@ -778,6 +805,17 @@ public class FibManager extends AbstractDataChangeListener<VrfEntry> implements
       Optional<VrfTables> vrfTable = FibUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
       if (vrfTable.isPresent()) {
         for (VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
+                        /* Handle subnet routes here */
+            RdToElanOpEntry rdToElanOpEntry= getRdToElanOpEntry(broker, rd,
+                    vrfEntry.getDestPrefix());
+            if (rdToElanOpEntry != null) {
+                LOG.trace("Cleaning subnetroute {} on dpn {} for vpn {} : cleanUpDpnForVpn", vrfEntry.getDestPrefix(),
+                        dpnId, rd);
+                makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null, NwConstants.DEL_FLOW);
+                makeLFibTableEntry(dpnId, vrfEntry.getLabel(), null,
+                        vrfEntry.getNextHopAddress(),NwConstants.DEL_FLOW);
+                continue;
+            }
           // Passing null as we don't know the dpn
           // to which prefix is attached at this point
           deleteRemoteRoute(null, dpnId, vpnId, vrfTable.get().getKey(), vrfEntry);
index 382e6172f32c3afef36cf422f626919a249cbdfc..62f9e7111c0f22bf2f57adbd64b2eb752366f731 100644 (file)
@@ -12,9 +12,12 @@ import java.util.List;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
 import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
 import org.opendaylight.fibmanager.api.IFibManager;
 import org.opendaylight.vpnmanager.api.IVpnManager;
 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.FibRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService;
@@ -33,6 +36,8 @@ public class FibManagerProvider implements BindingAwareProvider, IFibManager, Au
   private ItmRpcService itmManager;
   private OdlInterfaceRpcService interfaceManager;
   private FibNodeCapableListener fibNcListener;
+  private RpcProviderRegistry rpcProviderRegistry;
+  private RpcRegistration<FibRpcService> rpcRegistration;
 
   @Override
   public void onSessionInitiated(ProviderContext session) {
@@ -50,7 +55,10 @@ public class FibManagerProvider implements BindingAwareProvider, IFibManager, Au
       fibManager.setNextHopManager(nexthopManager);
       fibManager.setITMRpcService(itmManager);
       fibManager.setInterfaceManager(interfaceManager);
+      fibManager.setIdManager(idManager);
       fibNcListener = new FibNodeCapableListener(dataBroker, fibManager);
+      FibRpcService fibRpcService = new FibRpcServiceImpl(dataBroker, mdsalManager, this);
+      rpcRegistration = getRpcProviderRegistry().addRpcImplementation(FibRpcService.class, fibRpcService);
     } catch (Exception e) {
       LOG.error("Error initializing services", e);
     }
@@ -109,4 +117,13 @@ public class FibManagerProvider implements BindingAwareProvider, IFibManager, Au
   public void deleteStaticRoute(String prefix, String rd) {
     this.vpnmanager.delExtraRoute(prefix, rd, null);
   }
+
+  public void setRpcProviderRegistry(RpcProviderRegistry rpcProviderRegistry) {
+    this.rpcProviderRegistry = rpcProviderRegistry;
+  }
+
+  private RpcProviderRegistry getRpcProviderRegistry() {
+    return rpcProviderRegistry;
+  }
+
 }
diff --git a/fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibRpcServiceImpl.java b/fibmanager/fibmanager-impl/src/main/java/org/opendaylight/vpnservice/fibmanager/FibRpcServiceImpl.java
new file mode 100644 (file)
index 0000000..c8afe58
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * 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.fibmanager;
+
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.fibmanager.api.IFibManager;
+import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
+import org.opendaylight.vpnservice.mdsalutil.ActionType;
+import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
+import org.opendaylight.vpnservice.mdsalutil.InstructionType;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
+import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
+import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
+import org.opendaylight.vpnservice.mdsalutil.NwConstants;
+import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceOpData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceToVpnId;
+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.VpnInstanceOpDataEntryBuilder;
+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.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddressesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddressesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.CreateFibEntryInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.RemoveFibEntryInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.FibRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+
+import static org.opendaylight.vpnservice.fibmanager.FibConstants.*;
+
+public class FibRpcServiceImpl implements FibRpcService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(FibRpcServiceImpl.class);
+    private IMdsalApiManager mdsalManager;
+    private DataBroker broker;
+    private IFibManager fibManager;
+
+    public FibRpcServiceImpl(DataBroker broker, IMdsalApiManager mdsalManager, IFibManager fibManager) {
+        this.broker = broker;
+        this.mdsalManager = mdsalManager;
+        this.fibManager = fibManager;
+    }
+
+
+    /**
+     * to install FIB routes on specified dpn with given instructions
+     *
+     */
+    public Future<RpcResult<Void>> createFibEntry(CreateFibEntryInput input) {
+
+        BigInteger dpnId = input.getSourceDpid();
+        String vpnName = input.getVpnName();
+        long vpnId = getVpnId(broker, vpnName);
+        String ipAddress = input.getIpAddress();
+        LOG.info("Create custom FIB entry - {} on dpn {} for VPN {} ", ipAddress, dpnId, vpnName);
+        List<Instruction> instructions = input.getInstruction();
+
+        makeLocalFibEntry(vpnId, dpnId, ipAddress, instructions);
+        updateVpnToDpnAssociation(vpnId, dpnId, ipAddress, vpnName);
+
+        return Futures.immediateFuture(RpcResultBuilder.<Void>success().build());
+    }
+
+    /**
+     * to remove FIB/LFIB/TST routes from specified dpn
+     *
+     */
+    public Future<RpcResult<Void>> removeFibEntry(RemoveFibEntryInput input) {
+
+        BigInteger dpnId = input.getSourceDpid();
+        String vpnName = input.getVpnName();
+        long vpnId = getVpnId(broker, vpnName);
+        long serviceId = input.getServiceId();
+        String ipAddress = input.getIpAddress();
+
+        LOG.info("Delete custom FIB entry - {} on dpn {} for VPN {} ", ipAddress, dpnId, vpnName);
+
+        removeLocalFibEntry(dpnId, vpnId, ipAddress);
+        //removeLFibTableEntry(dpnId, serviceId);
+        //removeTunnelTableEntry(dpnId, serviceId);
+        removeFromVpnDpnAssociation(vpnId, dpnId, ipAddress, vpnName);
+
+        return Futures.immediateFuture(RpcResultBuilder.<Void>success().build());
+    }
+
+    private void removeLocalFibEntry(BigInteger dpnId, long vpnId, String ipPrefix) {
+        String values[] = ipPrefix.split("/");
+        String ipAddress = values[0];
+        int prefixLength = (values.length == 1) ? 0 : Integer.parseInt(values[1]);
+        LOG.debug("Removing route from DPN. ip {} masklen {}", ipAddress, prefixLength);
+        InetAddress destPrefix = null;
+        try {
+          destPrefix = InetAddress.getByName(ipAddress);
+        } catch (UnknownHostException e) {
+          LOG.error("UnknowHostException in removeRoute. Failed  to remove Route for ipPrefix {}", ipAddress);
+          return;
+        }
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+
+        matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+            BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                                  new long[] { 0x0800L }));
+
+        if(prefixLength != 0) {
+            matches.add(new MatchInfo(MatchFieldType.ipv4_destination, new String[] {
+                    destPrefix.getHostAddress(), Integer.toString(prefixLength) }));
+        }
+
+        String flowRef = getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, vpnId, ipAddress);
+
+
+        int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength;
+        Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_FIB_TABLE, flowRef,
+                                               priority, flowRef, 0, 0,
+                                               COOKIE_VM_FIB_TABLE, matches, null);
+
+        mdsalManager.removeFlow(dpnId, flowEntity);
+
+        LOG.debug("FIB entry for route {} on dpn {} removed successfully", ipAddress, dpnId);
+    }
+
+    private void removeLFibTableEntry(BigInteger dpnId, long serviceId) {
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                                  new long[] { 0x8847L }));
+        matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
+
+        String flowRef = getFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, serviceId, "");
+
+        LOG.debug("removing LFib entry with flow ref {}", flowRef);
+
+        Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
+                                               DEFAULT_FIB_FLOW_PRIORITY, flowRef, 0, 0,
+                                               COOKIE_VM_LFIB_TABLE, matches, null);
+
+        mdsalManager.removeFlow(dpnId, flowEntity);
+
+        LOG.debug("LFIB Entry for dpID : {} label : {} removed successfully {}",dpnId, serviceId);
+    }
+
+    private void removeTunnelTableEntry(BigInteger dpnId, long serviceId) {
+        LOG.info("remove terminatingServiceActions called with DpnId = {} and label = {}", dpnId , serviceId);
+        List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+        // Matching metadata
+        mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
+        Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
+                                               getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""),
+                                               5, String.format("%s:%d","TST Flow Entry ",serviceId), 0, 0,
+                                               COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)), mkMatches, null);
+        mdsalManager.removeFlow(dpnId, flowEntity);
+        LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully {}",dpnId, serviceId);
+    }
+
+    private void makeTunnelTableEntry(BigInteger dpnId, long serviceId, List<Instruction> customInstructions) {
+        List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+
+        LOG.info("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}", dpnId , serviceId);
+
+        mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
+
+        Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
+                        getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""), 5, String.format("%s:%d","TST Flow Entry ",serviceId),
+                        0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)),mkMatches, customInstructions);
+
+        mdsalManager.installFlow(dpnId, terminatingServiceTableFlowEntity);
+    }
+
+    private long getIpAddress(byte[] rawIpAddress) {
+        return (((rawIpAddress[0] & 0xFF) << (3 * 8)) + ((rawIpAddress[1] & 0xFF) << (2 * 8))
+                + ((rawIpAddress[2] & 0xFF) << (1 * 8)) + (rawIpAddress[3] & 0xFF)) & 0xffffffffL;
+    }
+
+    private void makeLocalFibEntry(long vpnId, BigInteger dpnId, String ipPrefix, List<Instruction> customInstructions) {
+        String values[] = ipPrefix.split("/");
+        String ipAddress = values[0];
+        int prefixLength = (values.length == 1) ? 0 : Integer.parseInt(values[1]);
+        LOG.debug("Adding route to DPN. ip {} masklen {}", ipAddress, prefixLength);
+        InetAddress destPrefix = null;
+        try {
+          destPrefix = InetAddress.getByName(ipAddress);
+        } catch (UnknownHostException e) {
+          LOG.error("UnknowHostException in addRoute. Failed  to add Route for ipPrefix {}", ipAddress);
+          return;
+        }
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+
+        matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+            BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                                  new long[] { 0x0800L }));
+
+        if(prefixLength != 0) {
+          matches.add(new MatchInfo(MatchFieldType.ipv4_destination, new String[] {
+             destPrefix.getHostAddress(), Integer.toString(prefixLength) }));
+        }
+
+        String flowRef = getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, vpnId, ipAddress);
+
+
+        int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength;
+        Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_FIB_TABLE, flowRef,
+                                               priority, flowRef, 0, 0,
+                                               COOKIE_VM_FIB_TABLE, matches, customInstructions);
+
+        mdsalManager.installFlow(dpnId, flowEntity);
+
+        LOG.debug("FIB entry for route {} on dpn {} installed successfully", ipAddress, dpnId);
+    }
+
+    private void makeLFibTableEntry(BigInteger dpId, long serviceId, List<Instruction> customInstructions) {
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x8847L }));
+        matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
+
+        List<Instruction> instructions = new ArrayList<Instruction>();
+        List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+        actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
+        Instruction writeInstruction = new InstructionInfo(InstructionType.write_actions, actionsInfos).buildInstruction(0);
+        instructions.add(writeInstruction);
+        instructions.addAll(customInstructions);
+
+        // Install the flow entry in L3_LFIB_TABLE
+        String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, serviceId, "");
+
+        Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
+                DEFAULT_FIB_FLOW_PRIORITY, flowRef, 0, 0,
+                COOKIE_VM_LFIB_TABLE, matches, instructions);
+
+        mdsalManager.installFlow(dpId, flowEntity);
+
+        LOG.debug("LFIB Entry for dpID {} : label : {} modified successfully {}",dpId, serviceId );
+    }
+
+    private String getFlowRef(BigInteger dpnId, short tableId, long id, String ipAddress) {
+        return new StringBuilder(64).append(FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
+            .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
+            .append(id).append(NwConstants.FLOWID_SEPARATOR).append(ipAddress).toString();
+    }
+
+    private synchronized void updateVpnToDpnAssociation(long vpnId, BigInteger dpnId, String ipAddr, String vpnName) {
+        LOG.debug("Updating VPN to DPN list for dpn : {} for VPN: {} with ip: {}",
+                dpnId, vpnName, ipAddr);
+        String routeDistinguisher = getVpnRd(broker, vpnName);
+        String rd = (routeDistinguisher == null) ? vpnName : routeDistinguisher;
+        InstanceIdentifier<VpnToDpnList> id = getVpnToDpnListIdentifier(rd, dpnId);
+        Optional<VpnToDpnList> dpnInVpn = MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
+        org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses
+            ipAddress = new IpAddressesBuilder().setIpAddress(ipAddr).build();
+
+        if (dpnInVpn.isPresent()) {
+            MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, id.child(
+                org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance
+                    .op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses.class,
+                    new IpAddressesKey(ipAddr)), ipAddress);
+        } else {
+            MDSALUtil.syncUpdate(broker, LogicalDatastoreType.OPERATIONAL,
+                                    getVpnInstanceOpDataIdentifier(rd),
+                                    getVpnInstanceOpData(rd, vpnId));
+            VpnToDpnListBuilder vpnToDpnList = new VpnToDpnListBuilder().setDpnId(dpnId);
+            List<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data
+                .vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses> ipAddresses =  new ArrayList<>();
+            ipAddresses.add(ipAddress);
+            MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, id,
+                              vpnToDpnList.setIpAddresses(ipAddresses).build());
+            LOG.debug("populate FIB on new dpn {} for VPN {}", dpnId, vpnName);
+            fibManager.populateFibOnNewDpn(dpnId, vpnId, rd);
+        }
+    }
+
+    private synchronized void removeFromVpnDpnAssociation(long vpnId, BigInteger dpnId, String ipAddr, String vpnName) {
+        LOG.debug("Removing association of VPN to DPN list for dpn : {} for VPN: {} with ip: {}",
+                dpnId, vpnName, ipAddr);
+        String routeDistinguisher = getVpnRd(broker, vpnName);
+        String rd = (routeDistinguisher == null) ? vpnName : routeDistinguisher;
+        InstanceIdentifier<VpnToDpnList> id = getVpnToDpnListIdentifier(rd, dpnId);
+        Optional<VpnToDpnList> dpnInVpn = MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
+        if (dpnInVpn.isPresent()) {
+            List<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data
+                .vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses> ipAddresses = dpnInVpn.get().getIpAddresses();
+            org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses
+                    ipAddress = new IpAddressesBuilder().setIpAddress(ipAddr).build();
+
+            if (ipAddresses != null && ipAddresses.remove(ipAddress)) {
+                if (ipAddresses.isEmpty()) {
+                    List<VpnInterfaces> vpnInterfaces = dpnInVpn.get().getVpnInterfaces();
+                    if(vpnInterfaces ==null || vpnInterfaces.isEmpty()) {
+                        //Clean up the dpn
+                        LOG.debug("Cleaning up dpn {} from VPN {}", dpnId, vpnName);
+                        MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, id);
+                        fibManager.cleanUpDpnForVpn(dpnId, vpnId, rd);
+                    }
+                } else {
+                    delete(broker, LogicalDatastoreType.OPERATIONAL, id.child(
+                        org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data
+                            .vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses.class,
+                            new IpAddressesKey(ipAddr)));
+                }
+            }
+        }
+    }
+
+    //TODO: Below Util methods to be removed once VpnUtil methods are exposed in api bundle
+    public static String getVpnRd(DataBroker broker, String vpnName) {
+
+        InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> id
+                = getVpnInstanceToVpnIdIdentifier(vpnName);
+        Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> vpnInstance
+                = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
+
+        String rd = null;
+        if(vpnInstance.isPresent()) {
+            rd = vpnInstance.get().getVrfId();
+        }
+        return rd;
+    }
+
+    static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance>
+                                                                                         getVpnInstanceToVpnIdIdentifier(String vpnName) {
+        return InstanceIdentifier.builder(VpnInstanceToVpnId.class)
+                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance.class,
+                        new org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstanceKey(vpnName)).build();
+    }
+
+
+    static InstanceIdentifier<VpnToDpnList> getVpnToDpnListIdentifier(String rd, BigInteger dpnId) {
+        return InstanceIdentifier.builder(VpnInstanceOpData.class)
+            .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(rd))
+            .child(VpnToDpnList.class, new VpnToDpnListKey(dpnId)).build();
+    }
+
+    static InstanceIdentifier<VpnInstanceOpDataEntry> getVpnInstanceOpDataIdentifier(String rd) {
+        return InstanceIdentifier.builder(VpnInstanceOpData.class)
+            .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(rd)).build();
+    }
+
+    static VpnInstanceOpDataEntry getVpnInstanceOpData(String rd, long vpnId) {
+        return new VpnInstanceOpDataEntryBuilder().setVrfId(rd).setVpnId(vpnId).build();
+    }
+
+    static <T extends DataObject> void delete(DataBroker broker, LogicalDatastoreType datastoreType,
+            InstanceIdentifier<T> path) {
+        WriteTransaction tx = broker.newWriteOnlyTransaction();
+        tx.delete(datastoreType, path);
+        tx.submit();
+    }
+
+    static long getVpnId(DataBroker broker, String vpnName) {
+
+        InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> id
+            = getVpnInstanceToVpnIdIdentifier(vpnName);
+        Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> vpnInstance
+            = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
+
+        long vpnId = -1;
+        if(vpnInstance.isPresent()) {
+            vpnId = vpnInstance.get().getVpnId();
+        }
+        return vpnId;
+    }
+
+}
index 1c13444093e8cc3620308452511e3cfc7972f06e..0ba7296d38902663a2dac3505942b8eb5fd4cdcc 100644 (file)
@@ -21,12 +21,19 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.Adjacencies
 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.Adjacency;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
 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.idmanager.rev150403.IdManagerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.ReleaseIdInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.ReleaseIdInputBuilder;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.math.BigInteger;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
 
 public class FibUtil {
     private static final Logger LOG = LoggerFactory.getLogger(FibUtil.class);
@@ -100,6 +107,24 @@ public class FibUtil {
                         new org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.to.extraroute.vpn.ExtrarouteKey(ipPrefix)).build();
     }
 
+    static String getNextHopLabelKey(String rd, String prefix){
+        String key = rd + FibConstants.SEPARATOR + prefix;
+        return key;
+    }
+
+    static void releaseId(IdManagerService idManager, String poolName, String idKey) {
+        ReleaseIdInput idInput = new ReleaseIdInputBuilder().setPoolName(poolName).setIdKey(idKey).build();
+        try {
+            Future<RpcResult<Void>> result = idManager.releaseId(idInput);
+            RpcResult<Void> rpcResult = result.get();
+            if(!rpcResult.isSuccessful()) {
+                LOG.warn("RPC Call to Get Unique Id returned with Errors {}", rpcResult.getErrors());
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.warn("Exception when getting Unique Id for key {}", idKey, e);
+        }
+    }
+
     static final FutureCallback<Void> DEFAULT_CALLBACK =
             new FutureCallback<Void>() {
                 public void onSuccess(Void result) {
index 89841010dd808e0e5c56ed614e4257738f51a102..84487410c313e75acebbe682fd5d9e8f06a4668e 100644 (file)
@@ -34,6 +34,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instanc
 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.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.FibEntries;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTables;
@@ -133,6 +134,9 @@ public class FibManagerTest {
                 return null;
               }
 
+              @Override
+              public List<IpAddresses> getIpAddresses() { return null; }
+
               @Override
               public VpnToDpnListKey getKey() {
                 return new VpnToDpnListKey(Dpn);
diff --git a/natservice/natservice-api/pom.xml b/natservice/natservice-api/pom.xml
new file mode 100644 (file)
index 0000000..5d8b0c8
--- /dev/null
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <groupId>org.opendaylight.vpnservice</groupId>
+    <artifactId>config-parent</artifactId>
+    <version>0.3.0-SNAPSHOT</version>
+    <relativePath>../../commons/config-parent</relativePath>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.opendaylight.vpnservice</groupId>
+  <artifactId>natservice-api</artifactId>
+  <version>${vpnservices.version}</version>
+  <packaging>bundle</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.opendaylight.mdsal</groupId>
+      <artifactId>yang-binding</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-common</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.mdsal.model</groupId>
+      <artifactId>ietf-inet-types</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.mdsal.model</groupId>
+      <artifactId>ietf-yang-types-20130715</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>config-api</artifactId>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/natservice/natservice-api/src/main/yang/odl-nat.yang b/natservice/natservice-api/src/main/yang/odl-nat.yang
new file mode 100644 (file)
index 0000000..b56bda2
--- /dev/null
@@ -0,0 +1,138 @@
+module odl-nat {
+    namespace "urn:opendaylight:vpnservice:natservice";
+    prefix odl-nat;
+
+    import ietf-yang-types { prefix "yang"; /*revision-date 2013-07-15; */}
+    import ietf-inet-types { prefix "inet"; }
+
+    revision "2016-01-11" {
+        description "NAT Manager module";
+    }
+
+    container external-networks {
+        list networks  {
+            key id;
+            leaf id {
+                type yang:uuid;
+            }
+            leaf vpnid { type yang:uuid; }
+            leaf-list router-ids { type yang:uuid; }
+        }
+   }
+
+   container ext-routers {
+       list routers {
+           key router-name;
+           leaf router-name { type string; }
+           leaf network-id { type yang:uuid; }
+           leaf enable-snat { type boolean; }
+           leaf-list external-ips {
+                type string; //format - ipaddress\prefixlength
+           }
+           leaf-list subnet-ids { type yang:uuid; }
+           leaf ext_gw_mac_address { type string; }
+       }
+   }
+
+
+    grouping external-interface-info {
+        leaf internal-ip { type string; }
+        leaf external-ip { type string; }
+        leaf label { type uint16; config false; }
+    }
+
+    container floating-ip-info {
+        config true;
+        list router-ports {
+            key router-id;
+            leaf router-id { type string; }
+            leaf external-network-id { type yang:uuid; }
+            list ports {
+                key port-name;
+                leaf port-name { type string; }
+                list ip-mapping {
+                    key "internal-ip";
+                    uses external-interface-info;
+                }
+            }
+        }
+    }
+
+    container napt-switches {
+        list router-to-napt-switch {
+            key router-name;
+            leaf router-name { type string; }
+            leaf primary-switch-id { type uint64; }
+        }
+    }
+
+    grouping ip-port-entity {
+        leaf ip-address { type string; }
+        leaf port-num { type uint16; }
+    }
+
+    typedef protocol-types {
+        type enumeration {
+              enum TCP;
+              enum UDP;
+        }
+    }
+
+    container intext-ip-port-map {
+        config true;
+        list ip-port-mapping {
+            key router-id;
+            leaf router-id { type uint32; }
+            list intext-ip-protocol-type {
+                key protocol;
+                leaf protocol { type protocol-types; }
+                list ip-port-map {
+                    key ip-port-internal;
+                    description "internal to external ip-port mapping";
+                    leaf ip-port-internal { type string; }
+                    container ip-port-external {
+                       uses ip-port-entity;
+                    }
+                }
+            }
+         }
+    }
+
+    container snatint-ip-port-map {
+        list intip-port-map {
+            key router-id;
+            leaf router-id { type uint32; }
+            list ip-port {
+                key internal-ip;
+                leaf internal-ip { type string; }
+                list int-ip-proto-type {
+                    key protocol;
+                    leaf protocol { type protocol-types; }
+                    leaf-list ports { type uint16; }
+                }
+            }
+        }
+    }
+
+     container intext-ip-map {
+         config false;
+         list ip-mapping {
+             key segment-id;
+             leaf segment-id { type uint32; }
+             list ip-map {
+                 key internal-ip;
+                 leaf internal-ip { type string; }
+                 leaf external-ip { type string; }
+                 leaf label {type uint32;}
+             }
+         }
+     }
+
+     container router-id-name {
+       list routerIds {
+           key router-id;
+           leaf router-id {type uint32;}
+           leaf router-name { type string; }
+       }
+    }
+}
diff --git a/natservice/natservice-impl/pom.xml b/natservice/natservice-impl/pom.xml
new file mode 100644 (file)
index 0000000..d40b99b
--- /dev/null
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: --><!--
+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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <parent>
+    <groupId>org.opendaylight.vpnservice</groupId>
+    <artifactId>config-parent</artifactId>
+    <version>0.3.0-SNAPSHOT</version>
+    <relativePath>../../commons/config-parent</relativePath>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.opendaylight.vpnservice</groupId>
+  <artifactId>natservice-impl</artifactId>
+  <version>${vpnservices.version}</version>
+  <packaging>bundle</packaging>
+  <properties>
+    <powermock.version>1.6.4</powermock.version>
+    <mockitoall.version>1.10.19</mockitoall.version>
+</properties>
+  <dependencies>
+    <dependency>
+      <groupId>org.opendaylight.vpnservice</groupId>
+      <artifactId>natservice-api</artifactId>
+      <version>${vpnservices.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>mdsalutil-api</artifactId>
+      <version>${vpnservices.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.vpnservice</groupId>
+      <artifactId>itm-api</artifactId>
+      <version>${vpnservices.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>bgpmanager-api</artifactId>
+      <version>${vpnservices.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.vpnservice</groupId>
+      <artifactId>vpnmanager-api</artifactId>
+      <version>${vpnservices.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.vpnservice</groupId>
+      <artifactId>fibmanager-api</artifactId>
+      <version>${vpnservices.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.vpnservice</groupId>
+      <artifactId>idmanager-api</artifactId>
+      <version>${vpnservices.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>interfacemgr-api</artifactId>
+      <version>${vpnservices.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>neutronvpn-api</artifactId>
+      <version>${vpnservices.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.openflowplugin.model</groupId>
+      <artifactId>model-flow-service</artifactId>
+      <version>${openflowplugin.version}</version>
+    </dependency>
+    <dependency>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>sal-binding-broker-impl</artifactId>
+    </dependency>
+    <dependency>
+        <groupId>commons-net</groupId>
+        <artifactId>commons-net</artifactId>
+    </dependency>
+  <!--  Only for unit-test -->
+   <dependency>
+        <groupId>org.mockito</groupId>
+        <artifactId>mockito-all</artifactId>
+        <version>${mockitoall.version}</version>
+        <scope>test</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.powermock</groupId>
+        <artifactId>powermock-api-mockito</artifactId>
+        <version>${powermock.version}</version>
+        <scope>test</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.powermock</groupId>
+        <artifactId>powermock-module-junit4</artifactId>
+        <version>${powermock.version}</version>
+        <scope>test</scope>
+        </dependency>
+    </dependencies> 
+
+</project>
diff --git a/natservice/natservice-impl/src/main/config/default-config.xml b/natservice/natservice-impl/src/main/config/default-config.xml
new file mode 100644 (file)
index 0000000..4f5f357
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+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
+-->
+<snapshot>
+  <required-capabilities>
+      <capability>urn:opendaylight:params:xml:ns:yang:natservice:impl?module=natservice-impl&amp;revision=2016-01-11</capability>
+      <capability>urn:opendaylight:params:xml:ns:yang:mdsalutil:api?module=odl-mdsalutil&amp;revision=2015-04-10</capability>
+      <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&amp;revision=2013-10-28</capability>
+      <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&amp;revision=2013-10-28</capability>
+      <capability>urn:opendaylight:params:xml:ns:yang:bgpmanager:api?module=bgpmanager-api&amp;revision=2015-04-20</capability>
+      <capability>urn:opendaylight:params:xml:ns:yang:mdsalutil:api?module=odl-mdsalutil&amp;revision=2015-04-10</capability>
+  </required-capabilities>
+  <configuration>
+
+    <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+      <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+        <module>
+          <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:natservice:impl">prefix:natservice-impl</type>
+          <name>natservice-default</name>
+          <broker>
+            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
+            <name>binding-osgi-broker</name>
+          </broker>
+          <rpc-registry>
+            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
+            <name>binding-rpc-broker</name>
+          </rpc-registry>
+          <bgpmanager>
+            <type xmlns:bgpmanager="urn:opendaylight:params:xml:ns:yang:bgpmanager:api">bgpmanager:bgpmanager-api</type>
+            <name>bgpmanager</name>
+          </bgpmanager>
+          <notification-service>
+            <type xmlns:bindingimpl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">bindingimpl:binding-new-notification-service</type>
+            <name>binding-notification-adapter</name>
+          </notification-service>
+          <mdsalutil>
+            <type xmlns:mdsalutil="urn:opendaylight:params:xml:ns:yang:mdsalutil:api">mdsalutil:odl-mdsalutil</type>
+            <name>mdsalutil-service</name>
+          </mdsalutil>
+        </module>
+      </modules>
+    </data>
+  </configuration>
+</snapshot>
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/DpnInVpnListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/DpnInVpnListener.java
new file mode 100644 (file)
index 0000000..d033b80
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * 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.List;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.vpnservice.mdsalutil.*;
+import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+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.group.types.rev131018.GroupTypes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.AddDpnEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.add.dpn.event.AddEventData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.RemoveDpnEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.remove.dpn.event.RemoveEventData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.OdlL3vpnListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.Routers;
+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.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+
+public class DpnInVpnListener implements OdlL3vpnListener {
+    private static final Logger LOG = LoggerFactory.getLogger(DpnInVpnListener.class);
+    private DataBroker dataBroker;
+    private SNATDefaultRouteProgrammer defaultRouteProgrammer;
+    private NaptSwitchHA naptSwitchHA;
+    private IMdsalApiManager mdsalManager;
+    private IdManagerService idManager;
+
+    public DpnInVpnListener(DataBroker dataBroker) {
+        this.dataBroker = dataBroker;
+    }
+
+    void setDefaultProgrammer(SNATDefaultRouteProgrammer defaultRouteProgrammer) {
+        this.defaultRouteProgrammer = defaultRouteProgrammer;
+    }
+
+    void setNaptSwitchHA(NaptSwitchHA switchHA) {
+        naptSwitchHA = switchHA;
+    }
+
+    void setMdsalManager(IMdsalApiManager mdsalManager) {
+        this.mdsalManager = mdsalManager;
+    }
+
+    public void setIdManager(IdManagerService idManager) {
+        this.idManager = idManager;
+    }
+
+    public void onAddDpnEvent(AddDpnEvent notification) {
+        AddEventData eventData =  notification.getAddEventData();
+        BigInteger dpnId = eventData.getDpnId();
+        String vpnName = eventData.getVpnName();
+        LOG.info("Received add dpn {} in vpn {} event", dpnId, vpnName);
+        String  routerId = NatUtil.getRouterIdfromVpnId(dataBroker, vpnName);
+        if (routerId != null) {
+            //check router is associated to external network
+            InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerId);
+            Optional<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
+            if (routerData.isPresent()) {
+                Uuid networkId = routerData.get().getNetworkId();
+                if(networkId != null) {
+                    LOG.debug("Router {} is associated with ext nw {}", routerId, networkId);
+                    long vpnId = NatUtil.readVpnId(dataBroker, vpnName);
+                    if(vpnId != NatConstants.INVALID_ID) {
+                        //Install default entry in FIB to SNAT table
+                        LOG.debug("Installing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName);
+                        defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId);
+                    } else {
+                        LOG.debug("Add DPN Event: Could not read vpnId for vpnName {}", vpnName);
+                    }
+                    if (routerData.get().isEnableSnat()) {
+                        LOG.info("SNAT enabled for router {}", routerId);
+                        handleSNATForDPN(dpnId, routerId);
+                    } else {
+                        LOG.info("SNAT is not enabled for router {} to handle addDPN event {}", routerId, dpnId);
+                    }
+                }
+            }
+        }
+    }
+
+    void handleSNATForDPN(BigInteger dpnId, String routerName) {
+        //Check if primary and secondary switch are selected, If not select the role
+        //Install select group to NAPT switch
+        //Install default miss entry to NAPT switch
+        BigInteger naptSwitch;
+        try {
+            Long routerId = NatUtil.getVpnId(dataBroker, routerName);
+            if (routerId == NatConstants.INVALID_ID) {
+                LOG.error("Invalid routerId returned for routerName {}",routerName);
+                return;
+            }
+            BigInteger naptId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
+            if (naptId == null) {
+                LOG.debug("No Naptswitch is selected for router {}", routerName);
+
+                naptSwitch = dpnId;
+                boolean naptstatus = naptSwitchHA.updateNaptSwitch(routerName, naptSwitch);
+                if(!naptstatus) {
+                    LOG.error("Failed to update newNaptSwitch {} for routername {}",naptSwitch,routerName);
+                    return;
+                }
+                LOG.debug("Switch {} is elected as NaptSwitch for router {}", dpnId, routerName);
+
+                //installing group
+                List<BucketInfo> bucketInfo = naptSwitchHA.handleGroupInPrimarySwitch();
+                naptSwitchHA.installSnatGroupEntry(naptSwitch,bucketInfo,routerName);
+
+            }  else {
+                LOG.debug("Napt switch is already elected for router {}"
+                        , naptId, routerName);
+                naptSwitch = naptId;
+
+                //installing group
+                List<BucketInfo> bucketInfo = naptSwitchHA.handleGroupInNeighborSwitches(dpnId, routerName, naptSwitch);
+                if (bucketInfo == null) {
+                    return;
+                }
+                naptSwitchHA.installSnatGroupEntry(dpnId, bucketInfo, routerName);
+
+            }
+            // Install miss entry pointing to group
+            long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
+            FlowEntity flowEntity = naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId,NatConstants.ADD_FLOW);
+            if (flowEntity == null) {
+                LOG.debug("Failed to populate flowentity for router {} with dpnId {} groupIs {}",routerName,dpnId,groupId);
+                return;
+            }
+            LOG.debug("Sucessfully installed flow for dpnId {} router {} group {}",dpnId,routerName,groupId);
+            mdsalManager.installFlow(flowEntity);
+        } catch (Exception ex) {
+            LOG.error("Exception in handleSNATForDPN method : {}",ex);
+        }
+    }
+
+    public void onRemoveDpnEvent(RemoveDpnEvent notification) {
+        RemoveEventData eventData = notification.getRemoveEventData();
+        BigInteger dpnId = eventData.getDpnId();
+        String vpnName = eventData.getVpnName();
+        LOG.info("Received remove dpn {} in vpn {} event", dpnId, vpnName);
+        String  routerId = NatUtil.getRouterIdfromVpnId(dataBroker, vpnName);
+        if (routerId != null) {
+            //check router is associated to external network
+            InstanceIdentifier<Routers> id = NatUtil.buildRouterIdentifier(routerId);
+            Optional<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
+            if (routerData.isPresent()) {
+                Uuid networkId = routerData.get().getNetworkId();
+                if(networkId != null) {
+                    LOG.debug("Router {} is associated with ext nw {}", routerId, networkId);
+                    long vpnId = NatUtil.readVpnId(dataBroker, vpnName);
+                    if(vpnId != NatConstants.INVALID_ID) {
+                        //Remove default entry in FIB
+                        LOG.debug("Removing default route in FIB on dpn {} for vpn {} ...", dpnId, vpnName);
+                        defaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId);
+                    } else {
+                        LOG.debug("Remove DPN Event: Could not read vpnId for vpnName {}", vpnName);
+                    }
+                    if (routerData.get().isEnableSnat()) {
+                        LOG.info("SNAT enabled for router {}", routerId);
+                        removeSNATFromDPN(dpnId,routerId);
+                    } else {
+                        LOG.info("SNAT is not enabled for router {} to handle removeDPN event {}", routerId, dpnId);
+                    }
+                }
+            }
+        }
+    }
+
+    void removeSNATFromDPN(BigInteger dpnId, String routerName) {
+        //irrespective of naptswitch or non-naptswitch, SNAT default miss entry need to be removed
+        //remove miss entry to NAPT switch
+        long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
+        FlowEntity flowEntity = null;
+        try {
+            flowEntity = naptSwitchHA.buildSnatFlowEntity(dpnId, routerName, groupId, NatConstants.DEL_FLOW);
+            if (flowEntity == null) {
+                LOG.debug("Failed to populate flowentity for router {} with dpnId {} groupIs {}",routerName,dpnId,groupId);
+                return;
+            }
+            LOG.debug("NAT Service : Removing default SNAT miss entry flow entity {}",flowEntity);
+            mdsalManager.removeFlow(flowEntity);
+
+        } catch (Exception ex) {
+            LOG.debug("NAT Service : Failed to remove default SNAT miss entry flow entity {} : {}",flowEntity,ex);
+            return;
+        }
+        LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}", dpnId, routerName);
+
+        //remove group
+        try {
+            GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName,
+                    GroupTypes.GroupAll, null);
+            LOG.info("NAT Service : Removing NAPT GroupEntity:{}", groupEntity);
+            mdsalManager.removeGroup(groupEntity);
+        } catch (Exception ex) {
+            LOG.debug("NAT Service : Failed to remove group entity {} : {}",flowEntity,ex);
+            return;
+        }
+        LOG.debug("NAT Service : Removed default SNAT miss entry flow for dpnID {} with routername {}", dpnId, routerName);
+    }
+}
\ No newline at end of file
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/EventDispatcher.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/EventDispatcher.java
new file mode 100644 (file)
index 0000000..d0aad9a
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.util.concurrent.BlockingQueue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class EventDispatcher implements Runnable {
+    private BlockingQueue<NAPTEntryEvent> eventQueue;
+    private NaptEventHandler naptEventHandler;
+    private static final Logger LOG = LoggerFactory.getLogger(NaptManager.class);
+
+    EventDispatcher(BlockingQueue<NAPTEntryEvent> eventQueue, NaptEventHandler naptEventHandler){
+        this.eventQueue = eventQueue;
+        this.naptEventHandler = naptEventHandler;
+    }
+
+    public void addNaptEvent(NAPTEntryEvent naptEntryEvent){
+        this.eventQueue.add(naptEntryEvent);
+    }
+
+    public void run(){
+        while(true) {
+            try {
+                NAPTEntryEvent event = eventQueue.take();
+                naptEventHandler.handleEvent(event);
+            } catch (InterruptedException e) {
+                LOG.error("EventDispatcher : Error in handling the event queue : ", e.getMessage());
+                e.printStackTrace();
+            }
+        }
+    }
+}
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalNetworkListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalNetworkListener.java
new file mode 100644 (file)
index 0000000..621aea2
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * 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 org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ExternalNetworks;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.Networks;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+
+import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
+import org.opendaylight.vpnservice.mdsalutil.ActionType;
+import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
+import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
+import org.opendaylight.vpnservice.mdsalutil.InstructionType;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
+import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
+import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
+import org.opendaylight.vpnservice.mdsalutil.NwConstants;
+import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import com.google.common.base.Optional;
+
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.List;
+import java.util.ArrayList;
+
+
+public class ExternalNetworkListener extends org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener<Networks> implements AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(ExternalNetworkListener.class);
+    private ListenerRegistration<DataChangeListener> listenerRegistration;
+    private final DataBroker broker;
+    private IMdsalApiManager mdsalManager;
+
+    public ExternalNetworkListener (final DataBroker db) {
+        super(Networks.class);
+        broker = db;
+        //registerListener(db);
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (listenerRegistration != null) {
+            try {
+                listenerRegistration.close();
+            } catch (final Exception e) {
+                LOG.error("Error when cleaning up DataChangeListener.", e);
+            }
+            listenerRegistration = null;
+        }
+        LOG.info("ExternalNetwork Listener Closed");
+    }
+
+    private void registerListener(final DataBroker db) {
+        try {
+            listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
+                    getWildCardPath(), ExternalNetworkListener.this, AsyncDataBroker.DataChangeScope.SUBTREE);
+        } catch (final Exception e) {
+            LOG.error("External Network DataChange listener registration fail!", e);
+            throw new IllegalStateException("External Network registration Listener failed.", e);
+        }
+    }
+
+    private InstanceIdentifier<Networks> getWildCardPath() {
+        return InstanceIdentifier.create(ExternalNetworks.class).child(Networks.class);
+    }
+
+    public void setMdsalManager(IMdsalApiManager mdsalManager) {
+        this.mdsalManager = mdsalManager;
+    }
+
+    @Override
+    protected void add(final InstanceIdentifier<Networks> identifier,
+                       final Networks nw) {
+        LOG.trace("External Network add mapping method - key: " + identifier + ", value=" + nw );
+        processExternalNwAdd(identifier, nw);
+    }
+
+    @Override
+    protected void remove(InstanceIdentifier<Networks> identifier, Networks nw) {
+        LOG.trace("External Network remove mapping method - key: " + identifier + ", value=" + nw );
+        processExternalNwDel(identifier, nw);
+    }
+
+    @Override
+    protected void update(InstanceIdentifier<Networks> identifier, Networks original, Networks update) {
+        LOG.trace("External Network update mapping method - key: " + identifier + ", original=" + original + ", update=" + update );
+        //check if a new router has been added or an already existing router has been deleted from the external nw to router association
+        List<Uuid> oldRtrs = original.getRouterIds();
+        List<Uuid> newRtrs = update.getRouterIds();
+        if (oldRtrs != newRtrs) {
+            //handle both addition and removal of routers
+            for (Uuid rtr : newRtrs) {
+                if (oldRtrs.contains(rtr)) {
+                    oldRtrs.remove(rtr);
+                } else {
+                    // new router case
+                    //Routers added need to have the corresponding default Fib entry added to the switches in the router
+                    String routerId = rtr.getValue();
+                    addOrDelDefFibRouteToSNAT(routerId, true);
+
+                }
+            }
+
+            //Routers removed need to have the corresponding default Fib entry removed from the switches in the router
+            for (Uuid rtr : oldRtrs) {
+                String routerId = rtr.getValue();
+                addOrDelDefFibRouteToSNAT(routerId, false);
+            }
+        }
+    }
+
+    private void processExternalNwAdd(final InstanceIdentifier<Networks> identifier,
+                                      final Networks network) {
+        LOG.trace("Add event - key: {}, value: {}", identifier, network);
+        List<Uuid> routerList = network.getRouterIds();
+
+        if(routerList == null) {
+            LOG.debug("No routers associated with external network {}", identifier);
+            return;
+        }
+
+        for(Uuid router: routerList) {
+            String routerId = router.getValue();
+            addOrDelDefFibRouteToSNAT(routerId, true);
+        }
+    }
+
+    private void processExternalNwDel(final InstanceIdentifier<Networks> identifier,
+                                      final Networks network) {
+        LOG.trace("Add event - key: {}, value: {}", identifier, network);
+        List<Uuid> routerList = network.getRouterIds();
+
+        for(Uuid router: routerList) {
+            String routerId = router.getValue();
+            addOrDelDefFibRouteToSNAT(routerId, false);
+        }
+    }
+
+    private void addOrDelDefFibRouteToSNAT(String routerId, boolean create) {
+        //Router ID is used as the internal VPN's name, hence the vrf-id in VpnInstance Op DataStore
+        InstanceIdentifier<VpnInstanceOpDataEntry> id = NatUtil.getVpnInstanceOpDataIdentifier(routerId);
+        Optional<VpnInstanceOpDataEntry> vpnInstOp = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
+        if (vpnInstOp.isPresent()) {
+            List<VpnToDpnList> dpnListInVpn = vpnInstOp.get().getVpnToDpnList();
+            for (VpnToDpnList dpn : dpnListInVpn) {
+                BigInteger dpnId = dpn.getDpnId();
+                long vpnId = NatUtil.readVpnId(broker, vpnInstOp.get().getVrfId());
+                if (create == true) {
+                    installDefNATRouteInDPN(dpnId, vpnId);
+                } else {
+                    removeDefNATRouteInDPN(dpnId, vpnId);
+                }
+            }
+        }
+    }
+
+    private FlowEntity buildDefNATFlowEntity(BigInteger dpId, long vpnId) {
+
+        InetAddress defaultIP = null;
+
+        try {
+            defaultIP = InetAddress.getByName("0.0.0.0");
+
+        } catch (UnknownHostException e) {
+            LOG.error("UnknowHostException in buildDefNATFlowEntity. Failed  to build FIB Table Flow for Default Route to NAT table ");
+            return null;
+        }
+
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x0800L }));
+
+        //add match for default route "0.0.0.0/0"
+        //matches.add(new MatchInfo(MatchFieldType.ipv4_src, new long[] {
+        //        NatUtil.getIpAddress(defaultIP.getAddress()), 0 }));
+
+        //add match for vrfid
+        matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+                BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+        List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+        instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.PSNAT_TABLE }));
+
+        String flowRef = NatUtil.getFlowRef(dpId, NatConstants.L3_FIB_TABLE, defaultIP);
+
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.L3_FIB_TABLE, flowRef,
+                NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_DNAT_TABLE, matches, instructions);
+
+        return flowEntity;
+
+
+    }
+
+    private void installDefNATRouteInDPN(BigInteger dpnId, long vpnId) {
+        FlowEntity flowEntity = buildDefNATFlowEntity(dpnId, vpnId);
+        if(flowEntity == null) {
+            LOG.error("Flow entity received is NULL. Cannot proceed with installation of Default NAT flow");
+            return;
+        }
+        mdsalManager.installFlow(flowEntity);
+    }
+
+    private void removeDefNATRouteInDPN(BigInteger dpnId, long vpnId) {
+        FlowEntity flowEntity = buildDefNATFlowEntity(dpnId, vpnId);
+        if(flowEntity == null) {
+            LOG.error("Flow entity received is NULL. Cannot proceed with installation of Default NAT flow");
+            return;
+        }
+        mdsalManager.removeFlow(flowEntity);
+    }
+
+
+}
\ No newline at end of file
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalNetworksChangeListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalNetworksChangeListener.java
new file mode 100644 (file)
index 0000000..3207b5c
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * 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 com.google.common.base.Optional;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.vpnservice.datastoreutils.AsyncDataTreeChangeListenerBase;
+import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
+import org.opendaylight.vpnservice.mdsalutil.ActionType;
+import org.opendaylight.vpnservice.mdsalutil.BucketInfo;
+import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
+import org.opendaylight.vpnservice.mdsalutil.GroupEntity;
+import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
+import org.opendaylight.vpnservice.mdsalutil.InstructionType;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
+import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
+import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
+import org.opendaylight.vpnservice.mdsalutil.NwConstants;
+import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+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.vpnservice.interfacemgr.rpcs.rev151003.GetDpidFromInterfaceInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetDpidFromInterfaceInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetDpidFromInterfaceOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ExtRouters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ExternalNetworks;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.IntextIpMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPorts;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.Ports;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.ports.IpMapping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.NaptSwitches;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.Routers;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.RoutersKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.Networks;
+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.RouterToNaptSwitchKey;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import org.opendaylight.bgpmanager.api.IBgpManager;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.FibRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.VpnRpcService;
+
+/**
+ * Created by ESUMAMS on 1/21/2016.
+ */
+public class ExternalNetworksChangeListener extends AsyncDataTreeChangeListenerBase<Networks, ExternalNetworksChangeListener>
+{
+    private static final Logger LOG = LoggerFactory.getLogger( ExternalNetworksChangeListener.class);
+
+    private ListenerRegistration<DataChangeListener> listenerRegistration;
+    private final DataBroker dataBroker;
+    private IMdsalApiManager mdsalManager;
+    //private VpnFloatingIpHandler vpnFloatingIpHandler;
+    private FloatingIPListener floatingIpListener;
+    private ExternalRoutersListener externalRouterListener;
+    private OdlInterfaceRpcService interfaceManager;
+    private NaptManager naptManager;
+
+    private IBgpManager bgpManager;
+    private VpnRpcService vpnService;
+    private FibRpcService fibService;
+
+
+    private ExternalRoutersListener externalRoutersListener;
+
+    void setMdsalManager(IMdsalApiManager mdsalManager) {
+        this.mdsalManager = mdsalManager;
+    }
+
+    void setInterfaceManager(OdlInterfaceRpcService interfaceManager) {
+        this.interfaceManager = interfaceManager;
+    }
+
+    void setFloatingIpListener(FloatingIPListener floatingIpListener) {
+        this.floatingIpListener = floatingIpListener;
+    }
+
+    void setExternalRoutersListener(ExternalRoutersListener externalRoutersListener) {
+        this.externalRouterListener = externalRoutersListener;
+    }
+
+    public void setBgpManager(IBgpManager bgpManager) {
+        this.bgpManager = bgpManager;
+    }
+
+    public void setNaptManager(NaptManager naptManager) {
+        this.naptManager = naptManager;
+    }
+
+    public void setVpnService(VpnRpcService vpnService) {
+        this.vpnService = vpnService;
+    }
+
+    public void setFibService(FibRpcService fibService) {
+        this.fibService = fibService;
+    }
+
+    public void setListenerRegistration(ListenerRegistration<DataChangeListener> listenerRegistration) {
+        this.listenerRegistration = listenerRegistration;
+    }
+
+    public ExternalNetworksChangeListener(final DataBroker dataBroker ) {
+        super( Networks.class, ExternalNetworksChangeListener.class );
+        this.dataBroker = dataBroker;
+    }
+
+
+    protected InstanceIdentifier<Networks> getWildCardPath() {
+        return InstanceIdentifier.create(ExternalNetworks.class).child(Networks.class);
+    }
+
+
+    @Override
+    protected void add(InstanceIdentifier<Networks> identifier, Networks networks) {
+
+    }
+
+    @Override
+    protected ExternalNetworksChangeListener getDataTreeChangeListener() {
+        return ExternalNetworksChangeListener.this;
+    }
+
+    @Override
+    protected void remove(InstanceIdentifier<Networks> identifier, Networks networks) {
+        if( identifier == null || networks == null || networks.getRouterIds().isEmpty() ) {
+            LOG.info( "ExternalNetworksChangeListener:remove:: returning without processing since networks/identifier is null"  );
+            return;
+        }
+
+        for( Uuid routerId: networks.getRouterIds() ) {
+            String routerName = routerId.toString();
+
+            InstanceIdentifier<RouterToNaptSwitch> routerToNaptSwitchInstanceIdentifier =
+                    getRouterToNaptSwitchInstanceIdentifier( routerName);
+
+            MDSALUtil.syncDelete( dataBroker, LogicalDatastoreType.OPERATIONAL, routerToNaptSwitchInstanceIdentifier );
+
+            LOG.debug( "ExternalNetworksChangeListener:delete:: successful deletion of data in napt-switches container" );
+        }
+    }
+
+    private static InstanceIdentifier<RouterToNaptSwitch> getRouterToNaptSwitchInstanceIdentifier( String routerName ) {
+
+        return  InstanceIdentifier.builder( NaptSwitches.class )
+                        .child( RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerName)).build();
+
+    }
+
+    public void close() throws Exception {
+        if (listenerRegistration != null) {
+            try {
+                listenerRegistration.close();
+            }
+            catch (final Exception e) {
+                LOG.error("Error when cleaning up ExternalNetworksChangeListener.", e);
+            }
+
+            listenerRegistration = null;
+        }
+        LOG.debug("ExternalNetworksChangeListener Closed");
+    }
+
+
+    @Override
+    protected void update(InstanceIdentifier<Networks> identifier, Networks original, Networks update) {
+        //Check for VPN disassociation
+        Uuid originalVpn = original.getVpnid();
+        Uuid updatedVpn = update.getVpnid();
+        if(originalVpn == null && updatedVpn != null) {
+            //external network is dis-associated from L3VPN instance
+            associateExternalNetworkWithVPN(update);
+            //Install the VPN related FIB entries
+            installVpnFibEntries(update, updatedVpn.getValue());
+        } else if(originalVpn != null && updatedVpn == null) {
+            //external network is associated with vpn
+            disassociateExternalNetworkFromVPN(update, originalVpn.getValue());
+            //Remove the SNAT entries
+            removeSnatEntries(original, original.getId());
+        }
+    }
+
+    private void installVpnFibEntries(Networks update, String vpnName){
+        List<Uuid> routerUuids = update.getRouterIds();
+        for(Uuid routerUuid :routerUuids){
+            InstanceIdentifier<Routers> routerInstanceIndentifier = InstanceIdentifier.builder(ExtRouters.class).child
+                    (Routers.class, new RoutersKey(routerUuid.getValue())).build();
+            Optional<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInstanceIndentifier);
+            if(!routerData.isPresent()){
+                continue;
+            }
+            String routerName = routerData.get().getRouterName();
+            List<String> externalIps = routerData.get().getExternalIps();
+            InstanceIdentifier<RouterToNaptSwitch> routerToNaptSwitch = NatUtil.buildNaptSwitchRouterIdentifier(routerName);
+            Optional<RouterToNaptSwitch> rtrToNapt = NatUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, routerToNaptSwitch);
+            if(!rtrToNapt.isPresent()) {
+                LOG.debug("Unable to retrieve the Primary switch DPN ID");
+                continue;
+            }
+            BigInteger naptSwitchDpnId = rtrToNapt.get().getPrimarySwitchId();
+            for(String externalIp: externalIps) {
+                externalRouterListener.advToBgpAndInstallFibAndTsFlows(naptSwitchDpnId, NatConstants.INBOUND_NAPT_TABLE, vpnName, NatUtil.getVpnId(dataBroker, routerName), externalIp,
+                        vpnService, fibService, bgpManager, dataBroker, LOG);
+            }
+        }
+    }
+
+    private void removeSnatEntries(Networks original, Uuid networkUuid){
+        List<Uuid> routerUuids = original.getRouterIds();
+        for(Uuid routerUuid :routerUuids){
+            InstanceIdentifier<Routers> routerInstanceIndentifier = InstanceIdentifier.builder(ExtRouters.class).child
+                    (Routers.class, new RoutersKey(routerUuid.getValue())).build();
+            Optional<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routerInstanceIndentifier);
+            List<String> externalIps = null;
+            if(!routerData.isPresent()){
+                continue;
+            }
+            externalIps = routerData.get().getExternalIps();
+            externalRouterListener.handleDisableSnat(routerUuid.getValue(), networkUuid, externalIps);
+        }
+    }
+
+    private void associateExternalNetworkWithVPN(Networks network) {
+        List<Uuid> routerIds = network.getRouterIds();
+        for(Uuid routerId : routerIds) {
+            //long router = NatUtil.getVpnId(dataBroker, routerId.getValue());
+
+            InstanceIdentifier<RouterPorts> routerPortsId = NatUtil.getRouterPortsId(routerId.getValue());
+            Optional<RouterPorts> optRouterPorts = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routerPortsId);
+            if(!optRouterPorts.isPresent()) {
+                LOG.debug("Could not read Router Ports data object with id: {} to handle associate ext nw {}", routerId, network.getId());
+                continue;
+            }
+            RouterPorts routerPorts = optRouterPorts.get();
+            List<Ports> interfaces = routerPorts.getPorts();
+            for(Ports port : interfaces) {
+                String portName = port.getPortName();
+                BigInteger dpnId = getDpnForInterface(interfaceManager, portName);
+                if(dpnId.equals(BigInteger.ZERO)) {
+                    LOG.debug("DPN not found for {}, skip handling of ext nw {} association", portName, network.getId());
+                    continue;
+                }
+                List<IpMapping> ipMapping = port.getIpMapping();
+                for(IpMapping ipMap : ipMapping) {
+                    String externalIp = ipMap.getExternalIp();
+                    //remove all VPN related entries
+                    floatingIpListener.createNATFlowEntries(dpnId, portName, routerId.getValue(), network.getId(), ipMap.getInternalIp(), externalIp);
+                }
+            }
+        }
+
+        // SNAT
+        for(Uuid routerId : routerIds) {
+            LOG.debug("NAT Service : associateExternalNetworkWithVPN() for routerId {}",  routerId);
+            Uuid networkId = network.getId();
+            if(networkId == null) {
+                LOG.error("NAT Service : networkId is null for the router ID {}", routerId);
+                return;
+            }
+            final String vpnName = network.getVpnid().getValue();
+            if(vpnName == null) {
+                LOG.error("NAT Service : No VPN associated with ext nw {} for router {}", networkId, routerId);
+                return;
+            }
+
+            BigInteger dpnId = new BigInteger("0");
+            InstanceIdentifier<RouterToNaptSwitch> routerToNaptSwitch = NatUtil.buildNaptSwitchRouterIdentifier(routerId.getValue());
+            Optional<RouterToNaptSwitch> rtrToNapt = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, routerToNaptSwitch );
+            if(rtrToNapt.isPresent()) {
+                dpnId = rtrToNapt.get().getPrimarySwitchId();
+            }
+            LOG.debug("NAT Service : got primarySwitch as dpnId{} ", dpnId);
+
+            Long routerIdentifier = NatUtil.getVpnId(dataBroker, routerId.getValue());
+            InstanceIdentifierBuilder<org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMapping> idBuilder =
+                            InstanceIdentifier.builder(IntextIpMap.class).child(org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMapping.class, new org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMappingKey(routerIdentifier));
+            InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMapping> id = idBuilder.build();
+            Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMapping> ipMapping = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
+            if (ipMapping.isPresent()) {
+                  List<IpMap> ipMaps = ipMapping.get().getIpMap();
+                  for (IpMap ipMap : ipMaps) {
+                      String externalIp = ipMap.getExternalIp();
+                      LOG.debug("NAT Service : got externalIp as {}", externalIp);
+                      LOG.debug("NAT Service : About to call advToBgpAndInstallFibAndTsFlows for dpnId {}, vpnName {} and externalIp {}", dpnId, vpnName, externalIp);
+                      externalRouterListener.advToBgpAndInstallFibAndTsFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, vpnName, NatUtil.getVpnId(dataBroker, routerId.getValue()), externalIp, vpnService, fibService, bgpManager, dataBroker, LOG);
+                  }
+            } else {
+                LOG.warn("NAT Service : No ipMapping present fot the routerId {}", routerId);
+            }
+
+            long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
+            // Install 47 entry to point to 21
+            if(vpnId != -1) {
+                LOG.debug("NAT Service : Calling externalRouterListener installNaptPfibEntry for donId {} and vpnId {}", dpnId, vpnId);
+                externalRouterListener.installNaptPfibEntry(dpnId, vpnId);
+            }
+
+        }
+
+    }
+
+    private void disassociateExternalNetworkFromVPN(Networks network, String vpnName) {
+        List<Uuid> routerIds = network.getRouterIds();
+
+        //long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
+        for(Uuid routerId : routerIds) {
+            //long router = NatUtil.getVpnId(dataBroker, routerId.getValue());
+
+            InstanceIdentifier<RouterPorts> routerPortsId = NatUtil.getRouterPortsId(routerId.getValue());
+            Optional<RouterPorts> optRouterPorts = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routerPortsId);
+            if(!optRouterPorts.isPresent()) {
+                LOG.debug("Could not read Router Ports data object with id: {} to handle disassociate ext nw {}", routerId, network.getId());
+                continue;
+            }
+            RouterPorts routerPorts = optRouterPorts.get();
+            List<Ports> interfaces = routerPorts.getPorts();
+            for(Ports port : interfaces) {
+                String portName = port.getPortName();
+                BigInteger dpnId = getDpnForInterface(interfaceManager, portName);
+                if(dpnId.equals(BigInteger.ZERO)) {
+                    LOG.debug("DPN not found for {}, skip handling of ext nw {} disassociation", portName, network.getId());
+                    continue;
+                }
+                List<IpMapping> ipMapping = port.getIpMapping();
+                for(IpMapping ipMap : ipMapping) {
+                    String externalIp = ipMap.getExternalIp();
+                    floatingIpListener.removeNATFlowEntries(dpnId, portName, vpnName, routerId.getValue(), network.getId(), ipMap.getInternalIp(), externalIp);
+                }
+            }
+        }
+    }
+
+    public static BigInteger getDpnForInterface(OdlInterfaceRpcService interfaceManagerRpcService, String ifName) {
+        BigInteger nodeId = BigInteger.ZERO;
+        try {
+            GetDpidFromInterfaceInput
+                    dpIdInput =
+                    new GetDpidFromInterfaceInputBuilder().setIntfName(ifName).build();
+            Future<RpcResult<GetDpidFromInterfaceOutput>>
+                    dpIdOutput =
+                    interfaceManagerRpcService.getDpidFromInterface(dpIdInput);
+            RpcResult<GetDpidFromInterfaceOutput> dpIdResult = dpIdOutput.get();
+            if (dpIdResult.isSuccessful()) {
+                nodeId = dpIdResult.getResult().getDpid();
+            } else {
+                LOG.error("Could not retrieve DPN Id for interface {}", ifName);
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Exception when getting dpn for interface {}", ifName,  e);
+        }
+        return nodeId;
+    }
+
+}
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalRoutersListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/ExternalRoutersListener.java
new file mode 100644 (file)
index 0000000..3e6db2d
--- /dev/null
@@ -0,0 +1,1130 @@
+/*
+ * 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.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import org.opendaylight.bgpmanager.api.IBgpManager;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.FibRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.VpnRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.IpPortMapping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.CreateFibEntryInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.CreateFibEntryInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.RemoveVpnLabelInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.RemoveFibEntryInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.RemoveVpnLabelInputBuilder;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.vpnservice.datastoreutils.AsyncDataTreeChangeListenerBase;
+import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
+import org.opendaylight.vpnservice.mdsalutil.ActionType;
+import org.opendaylight.vpnservice.mdsalutil.BucketInfo;
+import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
+import org.opendaylight.vpnservice.mdsalutil.GroupEntity;
+import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
+import org.opendaylight.vpnservice.mdsalutil.InstructionType;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
+import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
+import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
+import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.vpnservice.mdsalutil.NwConstants;
+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.action.types.rev131112.action.action.OutputActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
+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.idmanager.rev150403.AllocateIdInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetTunnelInterfaceNameInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetTunnelInterfaceNameOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ExtRouters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.RouterIdName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.Routers;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.RoutersKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMapBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMapKey;
+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.router.id.name.RouterIds;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.router.id.name.RouterIdsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.router.id.name.RouterIdsKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.Subnetmaps;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.subnetmaps.Subnetmap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
+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.vpn.instance.op.data.entry.VpnToDpnList;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Created by EYUGSAR on 2/20/2016.
+ */
+
+public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Routers, ExternalRoutersListener>{
+
+    private static final Logger LOG = LoggerFactory.getLogger( ExternalRoutersListener.class);
+    private static long label;
+    private ListenerRegistration<DataChangeListener> listenerRegistration;
+    private final DataBroker dataBroker;
+    private IMdsalApiManager mdsalManager;
+    private ItmRpcService itmManager;
+    private OdlInterfaceRpcService interfaceManager;
+    private IdManagerService idManager;
+    private NaptManager naptManager;
+    private NAPTSwitchSelector naptSwitchSelector;
+    private IBgpManager bgpManager;
+    private VpnRpcService vpnService;
+    private FibRpcService fibService;
+    private SNATDefaultRouteProgrammer defaultRouteProgrammer;
+    private static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
+    static final BigInteger COOKIE_VM_LFIB_TABLE = new BigInteger("8000022", 16);
+
+    public void setMdsalManager(IMdsalApiManager mdsalManager) {
+        this.mdsalManager = mdsalManager;
+    }
+
+    public void setItmManager(ItmRpcService itmManager) {
+        this.itmManager = itmManager;
+    }
+
+    public void setIdManager(IdManagerService idManager) {
+        this.idManager = idManager;
+        createGroupIdPool();
+    }
+
+    void setDefaultProgrammer(SNATDefaultRouteProgrammer defaultRouteProgrammer) {
+        this.defaultRouteProgrammer = defaultRouteProgrammer;
+    }
+
+
+    public void setInterfaceManager(OdlInterfaceRpcService interfaceManager) {
+        this.interfaceManager = interfaceManager;
+    }
+
+    public void setNaptManager(NaptManager naptManager) {
+        this.naptManager = naptManager;
+    }
+
+    public void setNaptSwitchSelector(NAPTSwitchSelector naptSwitchSelector) {
+        this.naptSwitchSelector = naptSwitchSelector;
+    }
+
+    public void setBgpManager(IBgpManager bgpManager) {
+        this.bgpManager = bgpManager;
+    }
+
+    public void setVpnService(VpnRpcService vpnService) {
+        this.vpnService = vpnService;
+    }
+
+    public void setFibService(FibRpcService fibService) {
+        this.fibService = fibService;
+    }
+
+    public ExternalRoutersListener(DataBroker dataBroker )
+    {
+        super( Routers.class, ExternalRoutersListener.class );
+        this.dataBroker = dataBroker;
+    }
+
+    @Override
+    protected void add(InstanceIdentifier<Routers> identifier, Routers routers) {
+
+        LOG.info( "Add external router event for {}", routers.getRouterName() );
+
+        LOG.info("Installing NAT default route on all dpns part of router {}", routers.getRouterName());
+        addOrDelDefFibRouteToSNAT(routers.getRouterName(), true);
+
+        if( !routers.isEnableSnat()) {
+            LOG.info( "SNAT is disabled for external router {} ", routers.getRouterName());
+            return;
+        }
+
+        // Populate the router-id-name container
+        String routerName = routers.getRouterName();
+        Long routerId = NatUtil.getVpnId(dataBroker, routerName);
+        RouterIds rtrs = new RouterIdsBuilder().setKey(new RouterIdsKey(routerId)).setRouterId(routerId).setRouterName(routerName).build();
+        MDSALUtil.syncWrite( dataBroker, LogicalDatastoreType.CONFIGURATION, getRoutersIdentifier(routerId), rtrs);
+
+        handleEnableSnat(routerName);
+    }
+
+    public void handleEnableSnat(String routerName){
+        LOG.info("Handling SNAT for router {}", routerName);
+
+        // Allocate Primary Napt Switch for this router
+        BigInteger primarySwitchId = naptSwitchSelector.selectNewNAPTSwitch(routerName);
+
+        LOG.debug("NAT Service : About to create and install outbound miss entry in Primary Switch {} for router {}", primarySwitchId, routerName);
+        // write metadata and punt
+        installOutboundMissEntry(routerName, primarySwitchId);
+        // Now install entries in SNAT tables to point to Primary for each router
+        List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
+        for(BigInteger dpnId : switches) {
+              // Handle switches and NAPT switches separately
+              if( dpnId != primarySwitchId ) {
+                   LOG.debug("NAT Service : Handle Ordinary switch");
+                   handleSwitches(dpnId, routerName, primarySwitchId);
+              } else {
+                   LOG.debug("NAT Service : Handle NAPT switch");
+                   handlePrimaryNaptSwitch(dpnId, routerName, primarySwitchId);
+              }
+        }
+
+        // call registerMapping Api
+        long segmentId = NatUtil.getVpnId(dataBroker, routerName);
+        LOG.debug("NAT Service : Preparing to call registerMapping for routerName {} and Id {}", routerName, segmentId);
+
+        List<Uuid> subnetList = null;
+        List<String> externalIps = null;
+
+        InstanceIdentifier<Routers> id = InstanceIdentifier
+                .builder(ExtRouters.class)
+                .child(Routers.class, new RoutersKey(routerName))
+                .build();
+
+        Optional<Routers> extRouters = read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
+
+        if(extRouters.isPresent())
+        {
+            LOG.debug("NAT Service : Fetching values from extRouters model");
+            Routers routerEntry= extRouters.get();
+            subnetList = routerEntry.getSubnetIds();
+            externalIps = routerEntry.getExternalIps();
+            int counter = 0;
+            int extIpCounter = externalIps.size();
+            LOG.debug("NAT Service : counter values before looping counter {} and extIpCounter {}", counter, extIpCounter);
+            for(Uuid subnet : subnetList) {
+                  LOG.debug("NAT Service : Looping internal subnets for subnet {}", subnet);
+                  InstanceIdentifier<Subnetmap> subnetmapId = InstanceIdentifier
+                         .builder(Subnetmaps.class)
+                         .child(Subnetmap.class, new SubnetmapKey(subnet))
+                         .build();
+                  Optional<Subnetmap> sn = read(dataBroker, LogicalDatastoreType.CONFIGURATION, subnetmapId);
+                  if(sn.isPresent()){
+                      // subnets
+                      Subnetmap subnetmapEntry = sn.get();
+                      String subnetString = subnetmapEntry.getSubnetIp();
+                      String[] subnetSplit = subnetString.split("/");
+                      String subnetIp = subnetSplit[0];
+                      String subnetPrefix = "0";
+                      if(subnetSplit.length ==  2) {
+                          subnetPrefix = subnetSplit[1];
+                      }
+                      IPAddress subnetAddr = new IPAddress(subnetIp, Integer.parseInt(subnetPrefix));
+                      LOG.debug("NAT Service : subnetAddr is {} and subnetPrefix is {}", subnetAddr.getIpAddress(), subnetAddr.getPrefixLength());
+                      //externalIps
+                      LOG.debug("NAT Service : counter values counter {} and extIpCounter {}", counter, extIpCounter);
+                      if(extIpCounter != 0) {
+                            if(counter < extIpCounter) {
+                                   String[] IpSplit = externalIps.get(counter).split("/");
+                                   String externalIp = IpSplit[0];
+                                   String extPrefix = Short.toString(NatConstants.DEFAULT_PREFIX);
+                                   if(IpSplit.length==2) {
+                                       extPrefix = IpSplit[1];
+                                   }
+                                   IPAddress externalIpAddr = new IPAddress(externalIp, Integer.parseInt(extPrefix));
+                                   LOG.debug("NAT Service : externalIp is {} and extPrefix  is {}", externalIpAddr.getIpAddress(), externalIpAddr.getPrefixLength());
+                                   naptManager.registerMapping(segmentId, subnetAddr, externalIpAddr);
+                                   LOG.debug("NAT Service : Called registerMapping for subnetIp {}, prefix {}, externalIp {}. prefix {}", subnetIp, subnetPrefix,
+                                            externalIp, extPrefix);
+
+                                   String externalIpAddrPrefix = externalIpAddr.getIpAddress() + "/" + externalIpAddr.getPrefixLength();
+                                   LOG.debug("NAT Service : Calling handleSnatReverseTraffic for primarySwitchId {}, routerName {} and externalIpAddPrefix {}", primarySwitchId, routerName, externalIpAddrPrefix);
+                                   handleSnatReverseTraffic(primarySwitchId, segmentId, externalIpAddrPrefix);
+
+                            } else {
+                                   counter = 0;    //Reset the counter which runs on externalIps for round-robbin effect
+                                   LOG.debug("NAT Service : Counter on externalIps got reset");
+                                   String[] IpSplit = externalIps.get(counter).split("/");
+                                   String externalIp = IpSplit[0];
+                                   String extPrefix = Short.toString(NatConstants.DEFAULT_PREFIX);
+                                   if(IpSplit.length==2) {
+                                       extPrefix = IpSplit[1];
+                                   }
+                                   IPAddress externalIpAddr = new IPAddress(externalIp, Integer.parseInt(extPrefix));
+                                   LOG.debug("NAT Service : externalIp is {} and extPrefix  is {}", externalIpAddr.getIpAddress(), externalIpAddr.getPrefixLength());
+                                   naptManager.registerMapping(segmentId, subnetAddr, externalIpAddr);
+                                   LOG.debug("NAT Service : Called registerMapping for subnetIp {}, prefix {}, externalIp {}. prefix {}", subnetIp, subnetPrefix,
+                                            externalIp, extPrefix);
+
+                                   String externalIpAddrPrefix = externalIpAddr.getIpAddress() + "/" + externalIpAddr.getPrefixLength();
+                                   LOG.debug("NAT Service : Calling handleSnatReverseTraffic for primarySwitchId {}, routerName {} and externalIpAddPrefix {}", primarySwitchId, routerName, externalIpAddrPrefix);
+                                   handleSnatReverseTraffic(primarySwitchId, segmentId, externalIpAddrPrefix);
+
+                            }
+                      }
+                      counter++;
+                      LOG.debug("NAT Service : Counter on externalIps incremented to {}", counter);
+
+                  } else {
+                      LOG.warn("NAT Service : No internal subnets present in extRouters Model");
+                  }
+            }
+        }
+
+        LOG.info("NAT Service : handleEnableSnat() Exit");
+    }
+
+    private void addOrDelDefFibRouteToSNAT(String routerName, boolean create) {
+        //Router ID is used as the internal VPN's name, hence the vrf-id in VpnInstance Op DataStore
+        InstanceIdentifier<VpnInstanceOpDataEntry> id = NatUtil.getVpnInstanceOpDataIdentifier(routerName);
+        Optional<VpnInstanceOpDataEntry> vpnInstOp = NatUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
+        if (vpnInstOp.isPresent()) {
+            List<VpnToDpnList> dpnListInVpn = vpnInstOp.get().getVpnToDpnList();
+            if(dpnListInVpn == null) {
+                LOG.debug("Current no dpns part of router {} to program default NAT route", routerName);
+                return;
+            }
+            long vpnId = NatUtil.readVpnId(dataBroker, routerName);
+            if(vpnId == NatConstants.INVALID_ID) {
+                LOG.error("Could not retrieve router Id for {} to program default NAT route in FIB", routerName);
+                return;
+            }
+            for (VpnToDpnList dpn : dpnListInVpn) {
+                BigInteger dpnId = dpn.getDpnId();
+                if (create == true) {
+                    //installDefNATRouteInDPN(dpnId, vpnId);
+                    defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId);
+                } else {
+                    //removeDefNATRouteInDPN(dpnId, vpnId);
+                    defaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId);
+                }
+            }
+        }
+    }
+
+    public static <T extends DataObject> Optional<T> read(DataBroker broker, LogicalDatastoreType datastoreType, InstanceIdentifier<T> path)
+    {
+        ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
+
+        Optional<T> result = Optional.absent();
+        try
+        {
+            result = tx.read(datastoreType, path).get();
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException(e);
+        }
+
+        return result;
+    }
+
+    public void close() throws Exception
+    {
+        if (listenerRegistration != null)
+        {
+            try
+            {
+                listenerRegistration.close();
+            }
+            catch (final Exception e)
+            {
+                LOG.error("Error when cleaning up ExternalRoutersListener.", e);
+            }
+
+            listenerRegistration = null;
+        }
+        LOG.debug("ExternalRoutersListener Closed");
+    }
+
+    protected void installOutboundMissEntry(String routerName, BigInteger primarySwitchId) {
+        long routerId = NatUtil.getVpnId(dataBroker, routerName);
+        LOG.debug("NAT Service : Router ID from getVpnId {}", routerId);
+        if(routerId != NatConstants.INVALID_ID) {
+            LOG.debug("NAT Service : Creating miss entry on primary {}, for router {}", primarySwitchId, routerId);
+            createOutboundTblEntry(primarySwitchId, routerId);
+        } else {
+            LOG.error("NAT Service : Unable to fetch Router Id  for RouterName {}, failed to createAndInstallMissEntry", routerName);
+        }
+    }
+
+    public String getFlowRefOutbound(BigInteger dpnId, short tableId, long routerID) {
+        return new StringBuilder().append(NatConstants.NAPT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR).
+                append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString();
+    }
+
+    public BigInteger getCookieOutboundFlow(long routerId) {
+        return NatConstants.COOKIE_OUTBOUND_NAPT_TABLE.add(new BigInteger("0110001", 16)).add(
+                BigInteger.valueOf(routerId));
+    }
+
+    protected FlowEntity buildOutboundFlowEntity(BigInteger dpId, long routerId) {
+        LOG.debug("NAT Service : buildOutboundFlowEntity called for dpId {} and routerId{}", dpId, routerId);
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x0800L }));
+        matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+                BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+        List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+        List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+        actionsInfos.add(new ActionInfo(ActionType.punt_to_controller, new String[] {}));
+        instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
+        instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] { BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+        String flowRef = getFlowRefOutbound(dpId, NatConstants.OUTBOUND_NAPT_TABLE, routerId);
+        BigInteger cookie = getCookieOutboundFlow(routerId);
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.OUTBOUND_NAPT_TABLE, flowRef,
+                5, flowRef, 0, 0,
+                cookie, matches, instructions);
+        LOG.debug("NAT Service : returning flowEntity {}", flowEntity);
+        return flowEntity;
+    }
+
+    public void createOutboundTblEntry(BigInteger dpnId, long routerId) {
+        LOG.debug("NAT Service : createOutboundTblEntry called for dpId {} and routerId {}", dpnId, routerId);
+        FlowEntity flowEntity = buildOutboundFlowEntity(dpnId, routerId);
+        LOG.debug("NAT Service : Installing flow {}", flowEntity);
+        mdsalManager.installFlow(flowEntity);
+    }
+
+    protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
+        try {
+            Future<RpcResult<GetTunnelInterfaceNameOutput>> result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
+                                                                                 .setSourceDpid(srcDpId)
+                                                                                 .setDestinationDpid(dstDpId).build());
+            RpcResult<GetTunnelInterfaceNameOutput> rpcResult = result.get();
+            if(!rpcResult.isSuccessful()) {
+                LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
+            } else {
+                return rpcResult.getResult().getInterfaceName();
+            }
+        } catch (InterruptedException | ExecutionException | NullPointerException e) {
+            LOG.warn("NAT Service : Exception when getting tunnel interface Id for tunnel between {} and  {}", srcDpId, dstDpId);
+        }
+
+        return null;
+    }
+
+    protected List<ActionInfo> getEgressActionsForInterface(String ifName, long routerId) {
+        LOG.debug("NAT Service : getEgressActionsForInterface called for interface {}", ifName);
+        List<ActionInfo> listActionInfo = new ArrayList<ActionInfo>();
+        try {
+            Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
+                interfaceManager.getEgressActionsForInterface(
+                    new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).setTunnelKey(routerId).build());
+            RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
+            if(!rpcResult.isSuccessful()) {
+                LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}", ifName, rpcResult.getErrors());
+            } else {
+                List<Action> actions =
+                    rpcResult.getResult().getAction();
+                for (Action action : actions) {
+                    org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action actionClass = action.getAction();
+                    if (actionClass instanceof OutputActionCase) {
+                        listActionInfo.add(new ActionInfo(ActionType.output,
+                                                          new String[] {((OutputActionCase)actionClass).getOutputAction()
+                                                                            .getOutputNodeConnector().getValue()}));
+                    } else if (actionClass instanceof PushVlanActionCase) {
+                        listActionInfo.add(new ActionInfo(ActionType.push_vlan, new String[] {}));
+                    } else if (actionClass instanceof SetFieldCase) {
+                        if (((SetFieldCase)actionClass).getSetField().getVlanMatch() != null) {
+                            int vlanVid = ((SetFieldCase)actionClass).getSetField().getVlanMatch().getVlanId().getVlanId().getValue();
+                            listActionInfo.add(new ActionInfo(ActionType.set_field_vlan_vid,
+                                                              new String[] { Long.toString(vlanVid) }));
+                        }
+                    }
+                }
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.warn("Exception when egress actions for interface {}", ifName, e);
+        }
+        return listActionInfo;
+    }
+
+    protected void installSnatMissEntry(BigInteger dpnId, List<BucketInfo> bucketInfo, String routerName) {
+        LOG.debug("NAT Service : installSnatMissEntry called for dpnId {} with primaryBucket {} ", dpnId, bucketInfo.get(0));
+        // Install the select group
+        long groupId = createGroupId(getGroupIdKey(routerName));
+        GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll, bucketInfo);
+        LOG.debug("NAT Service : installing the SNAT to NAPT GroupEntity:{}", groupEntity);
+        mdsalManager.installGroup(groupEntity);
+        // Install miss entry pointing to group
+        FlowEntity flowEntity = buildSnatFlowEntity(dpnId, routerName, groupId);
+        mdsalManager.installFlow(flowEntity);
+    }
+
+    public FlowEntity buildSnatFlowEntity(BigInteger dpId, String routerName, long groupId) {
+
+        LOG.debug("NAT Service : buildSnatFlowEntity is called for dpId {}, routerName {} and groupId {}", dpId, routerName, groupId );
+        long routerId = NatUtil.getVpnId(dataBroker, routerName);
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x0800L }));
+        matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+                BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+
+        List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+        List<ActionInfo> actionsInfo = new ArrayList<ActionInfo>();
+
+        ActionInfo actionSetField = new ActionInfo(ActionType.set_field_tunnel_id, new BigInteger[] {
+                        BigInteger.valueOf(routerId)}) ;
+        actionsInfo.add(actionSetField);
+        LOG.debug("NAT Service : Setting the tunnel to the list of action infos {}", actionsInfo);
+        actionsInfo.add(new ActionInfo(ActionType.group, new String[] {String.valueOf(groupId)}));
+        instructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfo));
+        String flowRef = getFlowRefSnat(dpId, NatConstants.PSNAT_TABLE, routerName);
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
+                NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_SNAT_TABLE, matches, instructions);
+
+        LOG.debug("NAT Service : Returning SNAT Flow Entity {}", flowEntity);
+        return flowEntity;
+    }
+
+    // TODO : Replace this with ITM Rpc once its available with full functionality
+    protected void installTerminatingServiceTblEntry(BigInteger dpnId, String routerName) {
+        LOG.debug("NAT Service : creating entry for Terminating Service Table for switch {}, routerName {}", dpnId, routerName);
+        FlowEntity flowEntity = buildTsFlowEntity(dpnId, routerName);
+        mdsalManager.installFlow(flowEntity);
+
+    }
+
+    private FlowEntity buildTsFlowEntity(BigInteger dpId, String routerName) {
+
+        BigInteger routerId = BigInteger.valueOf (NatUtil.getVpnId(dataBroker, routerName));
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x0800L }));
+        matches.add(new MatchInfo(MatchFieldType.tunnel_id, new  BigInteger[] {routerId }));
+
+        List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+        instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]
+                { routerId, MetaDataUtil.METADATA_MASK_VRFID }));
+        instructions.add(new InstructionInfo(InstructionType.goto_table, new long[]
+                { NatConstants.OUTBOUND_NAPT_TABLE }));
+        String flowRef = getFlowRefTs(dpId, NatConstants.TERMINATING_SERVICE_TABLE, routerId.longValue());
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.TERMINATING_SERVICE_TABLE, flowRef,
+                NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_TS_TABLE, matches, instructions);
+        return flowEntity;
+    }
+
+    public String getFlowRefTs(BigInteger dpnId, short tableId, long routerID) {
+        return new StringBuilder().append(NatConstants.NAPT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR).
+                append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString();
+    }
+
+    public static String getFlowRefSnat(BigInteger dpnId, short tableId, String routerID) {
+        return new StringBuilder().append(NatConstants.SNAT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR).
+            append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString();
+    }
+
+    private String getGroupIdKey(String routerName){
+        String groupIdKey = new String("snatmiss." + routerName);
+        return groupIdKey;
+    }
+
+    protected long createGroupId(String groupIdKey) {
+        AllocateIdInput getIdInput = new AllocateIdInputBuilder()
+            .setPoolName(NatConstants.SNAT_IDPOOL_NAME).setIdKey(groupIdKey)
+            .build();
+        try {
+            Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
+            RpcResult<AllocateIdOutput> rpcResult = result.get();
+            return rpcResult.getResult().getIdValue();
+        } catch (NullPointerException | InterruptedException | ExecutionException e) {
+            LOG.trace("",e);
+        }
+        return 0;
+    }
+
+    protected void createGroupIdPool() {
+        CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
+            .setPoolName(NatConstants.SNAT_IDPOOL_NAME)
+            .setLow(NatConstants.SNAT_ID_LOW_VALUE)
+            .setHigh(NatConstants.SNAT_ID_HIGH_VALUE)
+            .build();
+        try {
+            Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
+                if ((result != null) && (result.get().isSuccessful())) {
+                    LOG.debug("NAT Service : Created GroupIdPool");
+                } else {
+                    LOG.error("NAT Service : Unable to create GroupIdPool");
+                }
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Failed to create PortPool for NAPT Service",e);
+        }
+    }
+
+    protected void handleSwitches (BigInteger dpnId, String routerName, BigInteger primarySwitchId) {
+        LOG.debug("NAT Service : Installing SNAT miss entry in switch {}", dpnId);
+        List<ActionInfo> listActionInfoPrimary = new ArrayList<>();
+        String ifNamePrimary = getTunnelInterfaceName( dpnId, primarySwitchId);
+        List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
+        long routerId = NatUtil.getVpnId(dataBroker, routerName);
+
+        if(ifNamePrimary != null) {
+            LOG.debug("NAT Service : On Non- Napt switch , Primary Tunnel interface is {}", ifNamePrimary);
+            listActionInfoPrimary = getEgressActionsForInterface(ifNamePrimary, routerId);
+        }
+        BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
+
+        listBucketInfo.add(0, bucketPrimary);
+        installSnatMissEntry(dpnId, listBucketInfo, routerName);
+
+    }
+
+    protected void handlePrimaryNaptSwitch (BigInteger dpnId, String routerName, BigInteger primarySwitchId) {
+
+           /*
+            * Primary NAPT Switch â€“ bucket Should always point back to its own Outbound Table
+            */
+
+            LOG.debug("NAT Service : Installing SNAT miss entry in Primary NAPT switch {} ", dpnId);
+
+            List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
+            List<ActionInfo> listActionInfoPrimary =  new ArrayList<ActionInfo>();
+            listActionInfoPrimary.add(new ActionInfo(ActionType.nx_resubmit, new String[]{String.valueOf(NatConstants.TERMINATING_SERVICE_TABLE)}));
+            BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
+            listBucketInfo.add(0, bucketPrimary);
+
+            long routerId = NatUtil.getVpnId(dataBroker, routerName);
+
+            installSnatMissEntry(dpnId, listBucketInfo, routerName);
+            installTerminatingServiceTblEntry(dpnId, routerName);
+            installNaptPfibEntry(dpnId, routerId);
+
+    }
+
+    public void installNaptPfibEntry(BigInteger dpnId, long routerId) {
+        LOG.debug("NAT Service : installNaptPfibEntry called for dpnId {} and routerId {} ", dpnId, routerId);
+        FlowEntity flowEntity = buildNaptPfibFlowEntity(dpnId, routerId);
+        mdsalManager.installFlow(flowEntity);
+    }
+
+    public FlowEntity buildNaptPfibFlowEntity(BigInteger dpId, long routerId) {
+
+        LOG.debug("NAT Service : buildNaptPfibFlowEntity is called for dpId {}, routerId {}", dpId, routerId );
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x0800L }));
+        matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+                BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+        ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
+        ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
+        listActionInfo.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NatConstants.L3_FIB_TABLE) }));
+        instructionInfo.add(new InstructionInfo(InstructionType.apply_actions, listActionInfo));
+
+        String flowRef = getFlowRefTs(dpId, NatConstants.NAPT_PFIB_TABLE, routerId);
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.NAPT_PFIB_TABLE, flowRef,
+                NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_SNAT_TABLE, matches, instructionInfo);
+
+        LOG.debug("NAT Service : Returning NaptPFib Flow Entity {}", flowEntity);
+        return flowEntity;
+    }
+
+    private void handleSnatReverseTraffic(BigInteger dpnId, long routerId, String externalIp) {
+        LOG.debug("NAT Service : handleSnatReverseTraffic() entry for DPN ID, routerId, externalIp : {}", dpnId, routerId, externalIp);
+        Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
+        if(networkId == null) {
+            LOG.error("NAT Service : networkId is null for the router ID {}", routerId);
+            return;
+        }
+        final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
+        if(vpnName == null) {
+            LOG.error("NAT Service : No VPN associated with ext nw {} to handle add external ip configuration {} in router {}",
+                    networkId, externalIp, routerId);
+            return;
+        }
+        advToBgpAndInstallFibAndTsFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, vpnName, routerId, externalIp, vpnService, fibService, bgpManager, dataBroker, LOG);
+        LOG.debug("NAT Service : handleSnatReverseTraffic() exit for DPN ID, routerId, externalIp : {}", dpnId, routerId, externalIp);
+    }
+
+    public void advToBgpAndInstallFibAndTsFlows(final BigInteger dpnId, final short tableId, final String vpnName, final long routerId, final String externalIp,
+                                                VpnRpcService vpnService, final FibRpcService fibService, final IBgpManager bgpManager, final DataBroker dataBroker,
+                                                final Logger log){
+        LOG.debug("NAT Service : advToBgpAndInstallFibAndTsFlows() entry for DPN ID {}, tableId {}, vpnname {} and externalIp {}", dpnId, tableId, vpnName, externalIp);
+        //Generate VPN label for the external IP
+        GenerateVpnLabelInput labelInput = new GenerateVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
+        Future<RpcResult<GenerateVpnLabelOutput>> labelFuture = vpnService.generateVpnLabel(labelInput);
+
+        //On successful generation of the VPN label, advertise the route to the BGP and install the FIB routes.
+        ListenableFuture<RpcResult<Void>> future = Futures.transform(JdkFutureAdapters.listenInPoolThread(labelFuture), new AsyncFunction<RpcResult<GenerateVpnLabelOutput>, RpcResult<Void>>() {
+
+            @Override
+            public ListenableFuture<RpcResult<Void>> apply(RpcResult<GenerateVpnLabelOutput> result) throws Exception {
+                if (result.isSuccessful()) {
+                    LOG.debug("NAT Service : inside apply with result success");
+                    GenerateVpnLabelOutput output = result.getResult();
+                    long label = output.getLabel();
+
+                    //Inform BGP
+                    String rd = NatUtil.getVpnRd(dataBroker, vpnName);
+                    String nextHopIp = NatUtil.getEndpointIpAddressForDPN(dataBroker, dpnId);
+                    NatUtil.addPrefixToBGP(bgpManager, rd, externalIp, nextHopIp, label, log);
+
+                    //Get IPMaps from the DB for the router ID
+                    List<IpMap> dbIpMaps = naptManager.getIpMapList(dataBroker, routerId);
+
+                    for (IpMap dbIpMap : dbIpMaps) {
+                        String dbExternalIp = dbIpMap.getExternalIp();
+                        //Select the IPMap, whose external IP is the IP for which FIB is installed
+                        if (externalIp.contains(dbExternalIp)) {
+                            String dbInternalIp = dbIpMap.getInternalIp();
+                            IpMapKey dbIpMapKey = dbIpMap.getKey();
+                            IpMap newIpm = new IpMapBuilder().setKey(dbIpMapKey).setInternalIp(dbInternalIp).setExternalIp(dbExternalIp).setLabel(label).build();
+                            MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, naptManager.getIpMapIdentifier(routerId, dbInternalIp), newIpm);
+                        }
+                    }
+
+                    //Install custom FIB routes
+                    List<Instruction> customInstructions = new ArrayList<>();
+                    customInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[]{tableId}).buildInstruction(0));
+                    makeTunnelTableEntry(dpnId, label, customInstructions);
+                    makeLFibTableEntry(dpnId, label, customInstructions);
+
+                    CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId)
+                            .setIpAddress(externalIp).setServiceId(label).setInstruction(customInstructions).build();
+                    Future<RpcResult<Void>> future = fibService.createFibEntry(input);
+                    return JdkFutureAdapters.listenInPoolThread(future);
+                } else {
+                    LOG.error("NAT Service : inside apply with result failed");
+                    String errMsg = String.format("Could not retrieve the label for prefix %s in VPN %s, %s", externalIp, vpnName, result.getErrors());
+                    return Futures.immediateFailedFuture(new RuntimeException(errMsg));
+                }
+            }
+        });
+
+            Futures.addCallback(future, new FutureCallback<RpcResult<Void>>() {
+
+                @Override
+                public void onFailure(Throwable error) {
+                    log.error("NAT Service : Error in generate label or fib install process", error);
+                }
+
+                @Override
+                public void onSuccess(RpcResult<Void> result) {
+                    if (result.isSuccessful()) {
+                        log.info("NAT Service : Successfully installed custom FIB routes for prefix {}", externalIp);
+                    } else {
+                        log.error("NAT Service : Error in rpc call to create custom Fib entries for prefix {} in DPN {}, {}", externalIp, dpnId, result.getErrors());
+                    }
+                }
+            });
+     }
+
+    private void makeLFibTableEntry(BigInteger dpId, long serviceId, List<Instruction> customInstructions) {
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x8847L }));
+        matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
+
+        List<Instruction> instructions = new ArrayList<Instruction>();
+        List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+        actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
+        Instruction writeInstruction = new InstructionInfo(InstructionType.write_actions, actionsInfos).buildInstruction(0);
+        instructions.add(writeInstruction);
+        instructions.addAll(customInstructions);
+
+        // Install the flow entry in L3_LFIB_TABLE
+        String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, serviceId, "");
+
+        Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
+                10, flowRef, 0, 0,
+                COOKIE_VM_LFIB_TABLE, matches, instructions);
+
+        mdsalManager.installFlow(dpId, flowEntity);
+
+        LOG.debug("NAT Service : LFIB Entry for dpID {} : label : {} modified successfully {}",dpId, serviceId );
+    }
+
+    private void makeTunnelTableEntry(BigInteger dpnId, long serviceId, List<Instruction> customInstructions) {
+        List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+
+        LOG.debug("NAT Service : Create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}", dpnId , serviceId);
+
+        mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
+
+        Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
+                getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""), 5, String.format("%s:%d","TST Flow Entry ",serviceId),
+                0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)),mkMatches, customInstructions);
+
+        mdsalManager.installFlow(dpnId, terminatingServiceTableFlowEntity);
+    }
+
+    protected InstanceIdentifier<RouterIds> getRoutersIdentifier(long routerId) {
+        InstanceIdentifier<RouterIds> id = InstanceIdentifier.builder(
+                RouterIdName.class).child(RouterIds.class, new RouterIdsKey(routerId)).build();
+        return id;
+    }
+
+    private String getFlowRef(BigInteger dpnId, short tableId, long id, String ipAddress) {
+        return new StringBuilder(64).append(NatConstants.SNAT_FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
+                .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
+                .append(id).append(NwConstants.FLOWID_SEPARATOR).append(ipAddress).toString();
+    }
+
+    @Override
+    protected void update(InstanceIdentifier<Routers> identifier, Routers original, Routers update) {
+        boolean originalSNATEnabled = original.isEnableSnat();
+        boolean updatedSNATEnabled = update.isEnableSnat();
+        if(originalSNATEnabled != updatedSNATEnabled) {
+            if(originalSNATEnabled) {
+                //SNAT disabled for the router
+                String routerName = original.getRouterName();
+                Uuid networkUuid = original.getNetworkId();
+                List<String> externalIps = original.getExternalIps();
+                LOG.info("NAT Service : SNAT disabled for Router {}", routerName);
+                handleDisableSnat(routerName, networkUuid, externalIps);
+            } else {
+                String routerName = original.getRouterName();
+                LOG.info("NAT Service : SNAT enabled for Router {}", original.getRouterName());
+                handleEnableSnat(routerName);
+            }
+        }
+    }
+
+    @Override
+    protected void remove(InstanceIdentifier<Routers> identifier, Routers router) {
+        LOG.trace("NAT Service : Router delete method");
+        {
+        /*
+            ROUTER DELETE SCENARIO
+            1) Get the router ID from the event.
+            2) Build the cookie information from the router ID.
+            3) Get the primary and secondary switch DPN IDs using the router ID from the model.
+            4) Build the flow with the cookie value.
+            5) Delete the flows which matches the cookie information from the NAPT outbound, inbound tables.
+            6) Remove the flows from the other switches which points to the primary and secondary switches for the flows related the router ID.
+            7) Get the list of external IP address maintained for the router ID.
+            8) Use the NaptMananager removeMapping API to remove the list of IP addresses maintained.
+            9) Withdraw the corresponding routes from the BGP.
+         */
+
+            if (identifier == null || router == null) {
+                LOG.info("++++++++++++++NAT Service : ExternalRoutersListener:remove:: returning without processing since routers is null");
+                return;
+            }
+
+            String routerName = router.getRouterName();
+            LOG.info("Removing default NAT route from FIB on all dpns part of router {} ", routerName);
+            addOrDelDefFibRouteToSNAT(routerName, false);
+            Uuid networkUuid = router.getNetworkId();
+            List<String> externalIps = router.getExternalIps();
+            handleDisableSnat(routerName, networkUuid, externalIps);
+        }
+    }
+
+    public void handleDisableSnat(String routerName, Uuid networkUuid, List<String> externalIps){
+        LOG.info("NAT Service : handleDisableSnat() Entry");
+        Long routerId = NatUtil.getVpnId(dataBroker, routerName);
+
+        BigInteger naptSwitchDpnId = null;
+        InstanceIdentifier<RouterToNaptSwitch> routerToNaptSwitch = NatUtil.buildNaptSwitchRouterIdentifier(routerName);
+        Optional<RouterToNaptSwitch> rtrToNapt = read(dataBroker, LogicalDatastoreType.OPERATIONAL, routerToNaptSwitch );
+        if(rtrToNapt.isPresent()) {
+            naptSwitchDpnId = rtrToNapt.get().getPrimarySwitchId();
+        }
+        LOG.debug("NAT Service : got primarySwitch as dpnId{} ", naptSwitchDpnId);
+
+        removeNaptFlowsFromActiveSwitch(routerId, routerName, naptSwitchDpnId);
+        removeFlowsFromNonActiveSwitches(routerName, naptSwitchDpnId);
+        advToBgpAndRemoveFibAndTsFlows(naptSwitchDpnId, routerId, networkUuid, externalIps);
+
+        //Use the NaptMananager removeMapping API to remove the entire list of IP addresses maintained for the router ID.
+        LOG.debug("NAT Service : Remove the Internal to external IP address maintained for the router ID {} in the DS", routerId);
+        naptManager.removeMapping(routerId);
+
+        LOG.info("NAT Service : handleDisableSnat() Exit");
+    }
+
+    public void removeNaptFlowsFromActiveSwitch(long routerId, String routerName, BigInteger dpnId){
+        LOG.debug("NAT Service : Remove NAPT flows from Active switch");
+        BigInteger cookieSnatFlow = NatUtil.getCookieNaptFlow(routerId);
+
+        //Remove the PSNAT entry which forwards the packet to Terminating Service table
+        String pSNatFlowRef = getFlowRefSnat(dpnId, NatConstants.PSNAT_TABLE, routerName);
+        FlowEntity pSNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.PSNAT_TABLE, pSNatFlowRef);
+
+        LOG.info("NAT Service : Remove the flow in the " + NatConstants.PSNAT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+        mdsalManager.removeFlow(pSNatFlowEntity);
+
+        //Remove the group entry which resubmits the packet to the Terminating Service table or to the out port accordingly.
+        long groupId = createGroupId(getGroupIdKey(routerName));
+        List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
+        GroupEntity pSNatGroupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll, listBucketInfo);
+
+        LOG.info("NAT Service : Remove the group {} for the active switch with the DPN ID {} and router ID {}", groupId, dpnId, routerId);
+        mdsalManager.removeGroup(pSNatGroupEntity);
+
+        //Remove the Terminating Service table entry which forwards the packet to Outbound NAPT Table
+        String tsFlowRef = getFlowRefTs(dpnId, NatConstants.TERMINATING_SERVICE_TABLE, routerId);
+        FlowEntity tsNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.TERMINATING_SERVICE_TABLE, tsFlowRef);
+
+        LOG.info("NAT Service : Remove the flow in the " + NatConstants.TERMINATING_SERVICE_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+        mdsalManager.removeFlow(tsNatFlowEntity);
+
+        //Remove the Outbound flow entry which forwards the packet to FIB Table
+        String outboundNatFlowRef = getFlowRefOutbound(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, routerId);
+        FlowEntity outboundNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, outboundNatFlowRef);
+
+        LOG.info("NAT Service : Remove the flow in the " + NatConstants.OUTBOUND_NAPT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+        mdsalManager.removeFlow(outboundNatFlowEntity);
+
+        //Remove the NAPT PFIB TABLE which forwards the packet to FIB Table
+        String natPfibFlowRef = getFlowRefTs(dpnId, NatConstants.NAPT_PFIB_TABLE, routerId);
+        FlowEntity natPfibFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.NAPT_PFIB_TABLE, natPfibFlowRef);
+
+        LOG.info("NAT Service : Remove the flow in the " + NatConstants.NAPT_PFIB_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+        mdsalManager.removeFlow(natPfibFlowEntity);
+
+        //For the router ID get the internal IP , internal port and the corresponding external IP and external Port.
+        IpPortMapping ipPortMapping = NatUtil.getIportMapping(dataBroker, routerId);
+        if(ipPortMapping == null){
+            LOG.error("NAT Service : Unable to retrieve the IpPortMapping");
+            return;
+        }
+
+        List<IntextIpProtocolType> intextIpProtocolTypes = ipPortMapping.getIntextIpProtocolType();
+        for(IntextIpProtocolType intextIpProtocolType : intextIpProtocolTypes){
+            List<IpPortMap> ipPortMaps = intextIpProtocolType.getIpPortMap();
+            for(IpPortMap ipPortMap : ipPortMaps){
+                String ipPortInternal = ipPortMap.getIpPortInternal();
+                String[] ipPortParts = ipPortInternal.split(":");
+                if(ipPortParts.length != 2) {
+                    LOG.error("NAT Service : Unable to retrieve the Internal IP and port");
+                    return;
+                }
+                String internalIp = ipPortParts[0];
+                String internalPort = ipPortParts[1];
+
+                //Build the flow for the outbound NAPT table
+                String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, String.valueOf(routerId), internalIp, Integer.valueOf(internalPort));
+                FlowEntity outboundNaptFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, cookieSnatFlow, switchFlowRef);
+
+                LOG.info("NAT Service : Remove the flow in the " + NatConstants.OUTBOUND_NAPT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+                mdsalManager.removeFlow(outboundNaptFlowEntity);
+
+                IpPortExternal ipPortExternal = ipPortMap.getIpPortExternal();
+                String externalIp = ipPortExternal.getIpAddress();
+                int externalPort = ipPortExternal.getPortNum();
+
+                //Build the flow for the inbound NAPT table
+                switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NatConstants.INBOUND_NAPT_TABLE, String.valueOf(routerId), externalIp, externalPort);
+                FlowEntity inboundNaptFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.INBOUND_NAPT_TABLE, cookieSnatFlow, switchFlowRef);
+
+                LOG.info("NAT Service : Remove the flow in the " + NatConstants.INBOUND_NAPT_TABLE + " for the active active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+                mdsalManager.removeFlow(inboundNaptFlowEntity);
+            }
+        }
+    }
+
+    public void removeFlowsFromNonActiveSwitches(String routerName, BigInteger naptSwitchDpnId){
+        LOG.debug("NAT Service : Remove NAPT related flows from non active switches");
+
+        //Remove the flows from the other switches which points to the primary and secondary switches for the flows related the router ID.
+        List<VpnToDpnList> allSwitchList = NatUtil.getVpnToDpnList(dataBroker, routerName);
+        Long routerId = NatUtil.getVpnId(dataBroker, routerName);
+        for (VpnToDpnList eachSwitch : allSwitchList) {
+            BigInteger dpnId = eachSwitch.getDpnId();
+            if (naptSwitchDpnId != dpnId) {
+                LOG.info("NAT Service : Handle Ordinary switch");
+
+                //Remove the PSNAT entry which forwards the packet to Terminating Service table
+                String pSNatFlowRef = getFlowRefSnat(dpnId, NatConstants.PSNAT_TABLE, String.valueOf(routerName));
+                FlowEntity pSNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.PSNAT_TABLE, pSNatFlowRef);
+
+                LOG.info("Remove the flow in the " + NatConstants.PSNAT_TABLE + " for the non active switch with the DPN ID {} and router ID {}", dpnId, routerId);
+                mdsalManager.removeFlow(pSNatFlowEntity);
+
+                //Remove the group entry which resubmits the packet to the Terminating Service table or to the out port accordingly.
+                long groupId = createGroupId(getGroupIdKey(routerName));
+                List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
+                GroupEntity pSNatGroupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll, listBucketInfo);
+
+                LOG.info("NAT Service : Remove the group {} for the non active switch with the DPN ID {} and router ID {}", groupId, dpnId, routerId);
+                mdsalManager.removeGroup(pSNatGroupEntity);
+
+            }
+        }
+    }
+
+    public void advToBgpAndRemoveFibAndTsFlows(final BigInteger dpnId, Long routerId, Uuid networkUuid, List<String> externalIps){
+        //Withdraw the corresponding routes from the BGP.
+        //Get the network ID using the router ID.
+        LOG.debug("NAT Service : Advertise to BGP and remove routes");
+        if(networkUuid == null ){
+            LOG.error("NAT Service : networkId is null");
+            return;
+        }
+
+        //Get the VPN Name using the network ID
+        final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkUuid, LOG);
+        if (vpnName == null) {
+            LOG.error("No VPN associated with ext nw {} for the router {}",
+                    networkUuid, routerId);
+            return;
+        }
+
+        //Inform BGP about the route removal
+        String rd = NatUtil.getVpnRd(dataBroker, vpnName);
+        String prefix = "32";
+        NatUtil.removePrefixFromBGP(bgpManager, rd, prefix, LOG);
+
+        //Remove custom FIB routes
+        //Future<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
+        final String externalIp = externalIps.get(0);
+
+        //Get IPMaps from the DB for the router ID
+        List<IpMap> dbIpMaps = naptManager.getIpMapList(dataBroker, routerId);
+        if(dbIpMaps == null ){
+            LOG.error("NAT Service : IPMaps is null");
+            return;
+        }
+
+        long tempLabel = -1;
+        for(IpMap dbIpMap: dbIpMaps) {
+            String dbExternalIp = dbIpMap.getExternalIp();
+            //Select the IPMap, whose external IP is the IP for which FIB is installed
+            if (externalIp.contains(dbExternalIp)) {
+                tempLabel = dbIpMap.getLabel();
+                break;
+            }
+        }
+        if(tempLabel == -1){
+            LOG.error("NAT Service : Label is null");
+            return;
+        }
+
+        final long label = tempLabel;
+        RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp + "/" +
+                NatConstants.DEFAULT_PREFIX).setServiceId(label).build();
+        Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
+
+        ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+
+            @Override
+            public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
+                //Release label
+                if (result.isSuccessful()) {
+                    removeTunnelTableEntry(dpnId, label);
+                    removeLFibTableEntry(dpnId, label);
+                    RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
+                    Future<RpcResult<Void>> labelFuture = vpnService.removeVpnLabel(labelInput);
+                    return JdkFutureAdapters.listenInPoolThread(labelFuture);
+                } else {
+                    String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors());
+                    LOG.error(errMsg);
+                    return Futures.immediateFailedFuture(new RuntimeException(errMsg));
+                }
+            }
+
+        });
+
+        Futures.addCallback(labelFuture, new FutureCallback<RpcResult<Void>>() {
+
+            @Override
+            public void onFailure(Throwable error) {
+                LOG.error("NAT Service : Error in removing the label or custom fib entries", error);
+            }
+
+            @Override
+            public void onSuccess(RpcResult<Void> result) {
+                if (result.isSuccessful()) {
+                    LOG.debug("NAT Service : Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName);
+                } else {
+                    LOG.error("NAT Service : Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors());
+                }
+            }
+        });
+    }
+
+    private void removeTunnelTableEntry(BigInteger dpnId, long serviceId) {
+        LOG.info("NAT Service : remove terminatingServiceActions called with DpnId = {} and label = {}", dpnId , serviceId);
+        List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+        // Matching metadata
+        mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
+        Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
+                getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""),
+                5, String.format("%s:%d","TST Flow Entry ",serviceId), 0, 0,
+                COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)), mkMatches, null);
+        mdsalManager.removeFlow(dpnId, flowEntity);
+        LOG.debug("NAT Service : Terminating service Entry for dpID {} : label : {} removed successfully {}",dpnId, serviceId);
+    }
+
+    private void removeLFibTableEntry(BigInteger dpnId, long serviceId) {
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x8847L }));
+        matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
+
+        String flowRef = getFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, serviceId, "");
+
+        LOG.debug("NAT Service : removing LFib entry with flow ref {}", flowRef);
+
+        Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
+                10, flowRef, 0, 0,
+                COOKIE_VM_LFIB_TABLE, matches, null);
+
+        mdsalManager.removeFlow(dpnId, flowEntity);
+
+        LOG.debug("NAT Service : LFIB Entry for dpID : {} label : {} removed successfully {}",dpnId, serviceId);
+    }
+
+    public static GroupEntity buildGroupEntity(BigInteger dpnId, long groupId) {
+        GroupEntity groupEntity = new GroupEntity(dpnId);
+        groupEntity.setGroupId(groupId);
+        return groupEntity;
+    }
+
+    protected InstanceIdentifier<Routers> getWildCardPath()
+    {
+        return InstanceIdentifier.create(ExtRouters.class).child(Routers.class);
+    }
+
+    @Override
+    protected ExternalRoutersListener getDataTreeChangeListener()
+    {
+        return ExternalRoutersListener.this;
+    }
+
+}
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/FloatingIPHandler.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/FloatingIPHandler.java
new file mode 100644 (file)
index 0000000..8b1ad65
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+
+public interface FloatingIPHandler {
+
+    void onAddFloatingIp(BigInteger dpnId, String routerId, Uuid networkId, String interfaceName, String externalIp,
+                         String internalIp);
+
+    void onRemoveFloatingIp(BigInteger dpnId, String routerId, Uuid networkId, String externalIp, String internalIp,
+                            long label);
+
+}
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/FloatingIPListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/FloatingIPListener.java
new file mode 100644 (file)
index 0000000..7b43c59
--- /dev/null
@@ -0,0 +1,649 @@
+/*
+ * 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 org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
+import org.opendaylight.vpnservice.mdsalutil.ActionType;
+import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
+import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
+import org.opendaylight.vpnservice.mdsalutil.InstructionType;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
+import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
+import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
+import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.Routers;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.FloatingIpInfo;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPorts;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.Ports;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.PortsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.PortsKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.ports.IpMapping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.ports.IpMappingBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.ports.IpMappingKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.Networks;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.NetworksKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ExternalNetworks;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetDpidFromInterfaceInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetDpidFromInterfaceInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetDpidFromInterfaceOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by emhamla on 1/18/2016.
+ */
+public class FloatingIPListener extends org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener<IpMapping> implements AutoCloseable{
+    private static final Logger LOG = LoggerFactory.getLogger(FloatingIPListener.class);
+    private ListenerRegistration<DataChangeListener> listenerRegistration;
+    private final DataBroker broker;
+    private OdlInterfaceRpcService interfaceManager;
+    private IMdsalApiManager mdsalManager;
+    private FloatingIPHandler handler;
+
+
+    public FloatingIPListener (final DataBroker db) {
+        super(IpMapping.class);
+        broker = db;
+        registerListener(db);
+    }
+
+    void setFloatingIpHandler(FloatingIPHandler handler) {
+        this.handler = handler;
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (listenerRegistration != null) {
+            try {
+                listenerRegistration.close();
+            } catch (final Exception e) {
+                LOG.error("Error when cleaning up DataChangeListener.", e);
+            }
+            listenerRegistration = null;
+        }
+        LOG.info("FloatingIP Listener Closed");
+    }
+
+    private void registerListener(final DataBroker db) {
+        try {
+            listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
+                    getWildCardPath(), FloatingIPListener.this, AsyncDataBroker.DataChangeScope.SUBTREE);
+        } catch (final Exception e) {
+            LOG.error("FloatingIP DataChange listener registration fail!", e);
+            throw new IllegalStateException("FloatingIP Listener registration Listener failed.", e);
+        }
+    }
+
+    private InstanceIdentifier<IpMapping> getWildCardPath() {
+        return InstanceIdentifier.create(FloatingIpInfo.class).child(RouterPorts.class).child(Ports.class).child(IpMapping.class);
+    }
+
+    public void setInterfaceManager(OdlInterfaceRpcService interfaceManager) {
+        this.interfaceManager = interfaceManager;
+    }
+
+    public void setMdsalManager(IMdsalApiManager mdsalManager) {
+        this.mdsalManager = mdsalManager;
+    }
+
+    @Override
+    protected void add(final InstanceIdentifier<IpMapping> identifier,
+                       final IpMapping mapping) {
+        LOG.trace("FloatingIPListener add ip mapping method - key: " + identifier + ", value=" + mapping );
+        processFloatingIPAdd(identifier, mapping);
+    }
+
+    @Override
+    protected void remove(InstanceIdentifier<IpMapping> identifier, IpMapping mapping) {
+        LOG.trace("FloatingIPListener remove ip mapping method - key: " + identifier + ", value=" + mapping );
+        processFloatingIPDel(identifier, mapping);
+    }
+
+    @Override
+    protected void update(InstanceIdentifier<IpMapping> identifier, IpMapping original, IpMapping update) {
+        LOG.trace("FloatingIPListener update ip mapping method - key: " + identifier + ", original=" + original + ", update=" + update );
+    }
+
+    public static BigInteger getDpnForInterface(OdlInterfaceRpcService interfaceManagerRpcService, String ifName) {
+        BigInteger nodeId = BigInteger.ZERO;
+        try {
+            GetDpidFromInterfaceInput
+                    dpIdInput =
+                    new GetDpidFromInterfaceInputBuilder().setIntfName(ifName).build();
+            Future<RpcResult<GetDpidFromInterfaceOutput>>
+                    dpIdOutput =
+                    interfaceManagerRpcService.getDpidFromInterface(dpIdInput);
+            RpcResult<GetDpidFromInterfaceOutput> dpIdResult = dpIdOutput.get();
+            if (dpIdResult.isSuccessful()) {
+                nodeId = dpIdResult.getResult().getDpid();
+            } else {
+                LOG.error("Could not retrieve DPN Id for interface {}", ifName);
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Exception when getting dpn for interface {}", ifName,  e);
+        }
+        return nodeId;
+    }
+
+    private FlowEntity buildPreDNATFlowEntity(BigInteger dpId, String internalIp, String externalIp, long routerId, long vpnId) {
+
+        LOG.info("Bulding DNAT Flow entity for ip {} ", externalIp);
+
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x0800L }));
+
+        matches.add(new MatchInfo(MatchFieldType.ipv4_destination, new String[] {
+                externalIp, "32" }));
+
+//        matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+//                BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+        List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+        actionsInfos.add(new ActionInfo(ActionType.set_destination_ip, new String[]{ internalIp, "32" }));
+
+        List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+        instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] { BigInteger.valueOf
+                (routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+        instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
+        instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.DNAT_TABLE }));
+
+        String flowRef = NatUtil.getFlowRef(dpId, NatConstants.PDNAT_TABLE, externalIp);
+
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PDNAT_TABLE, flowRef,
+                NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_DNAT_TABLE, matches, instructions);
+
+        return flowEntity;
+    }
+
+
+
+    private FlowEntity buildDNATFlowEntity(BigInteger dpId, String internalIp, String externalIp, long routerId) {
+
+        LOG.info("Bulding DNAT Flow entity for ip {} ", externalIp);
+
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+                BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x0800L }));
+
+        matches.add(new MatchInfo(MatchFieldType.ipv4_destination, new String[] {
+        //        externalIp, "32" }));
+                  internalIp, "32" }));
+
+        List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+//        actionsInfos.add(new ActionInfo(ActionType.set_destination_ip, new String[]{ internalIp, "32" }));
+
+        List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+//        instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] { BigInteger.valueOf
+//                (routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+        actionsInfos.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NatConstants.L3_FIB_TABLE) }));
+        instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
+        //instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.L3_FIB_TABLE }));
+
+        String flowRef = NatUtil.getFlowRef(dpId, NatConstants.DNAT_TABLE, externalIp);
+
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.DNAT_TABLE, flowRef,
+                NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_DNAT_TABLE, matches, instructions);
+
+        return flowEntity;
+
+    }
+
+    private FlowEntity buildPreSNATFlowEntity(BigInteger dpId, String internalIp, String externalIp, long vpnId, long routerId) {
+
+        LOG.info("Building PSNAT Flow entity for ip {} ", internalIp);
+
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x0800L }));
+
+        matches.add(new MatchInfo(MatchFieldType.ipv4_source, new String[] {
+                internalIp, "32" }));
+
+        matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+                BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+        List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+        actionsInfos.add(new ActionInfo(ActionType.set_source_ip, new String[]{ externalIp, "32" }));
+
+        List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+        instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] { BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
+        instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
+        instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.SNAT_TABLE }));
+
+        String flowRef = NatUtil.getFlowRef(dpId, NatConstants.PSNAT_TABLE, internalIp);
+
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
+                NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_DNAT_TABLE, matches, instructions);
+
+        return flowEntity;
+    }
+
+    private FlowEntity buildSNATFlowEntity(BigInteger dpId, String internalIp, String externalIp, long vpnId, String macAddress) {
+
+        LOG.info("Building SNAT Flow entity for ip {} ", internalIp);
+
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+                BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x0800L }));
+
+        matches.add(new MatchInfo(MatchFieldType.ipv4_source, new String[] {
+        //        internalIp, "32" }));
+                  externalIp, "32" }));
+
+        List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+//        actionsInfos.add(new ActionInfo(ActionType.set_source_ip, new String[]{ externalIp, "32" }));
+
+        //TODO: Set external gateway mac address
+        if(!Strings.isNullOrEmpty(macAddress)) {
+            LOG.debug("Setting ext gw mac address {} in SNAT {} flow action", macAddress, internalIp);
+            actionsInfos.add(new ActionInfo(ActionType.set_field_eth_dest, new String[]{ macAddress }));
+        }
+
+        List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+        //instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] { BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
+        actionsInfos.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NatConstants.L3_FIB_TABLE) }));
+        instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
+        //instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.L3_FIB_TABLE }));
+
+        String flowRef = NatUtil.getFlowRef(dpId, NatConstants.SNAT_TABLE, internalIp);
+
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.SNAT_TABLE, flowRef,
+                NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_DNAT_TABLE, matches, instructions);
+
+        return flowEntity;
+
+
+    }
+
+    private void createDNATTblEntry(BigInteger dpnId, String internalIp, String externalIp, long routerId, long vpnId) {
+        FlowEntity pFlowEntity = buildPreDNATFlowEntity(dpnId, internalIp, externalIp, routerId, vpnId );
+        mdsalManager.installFlow(pFlowEntity);
+
+        FlowEntity flowEntity = buildDNATFlowEntity(dpnId, internalIp, externalIp, routerId);
+        mdsalManager.installFlow(flowEntity);
+    }
+
+    private void removeDNATTblEntry(BigInteger dpnId, String internalIp, String externalIp, long routerId) {
+        FlowEntity pFlowEntity = buildPreDNATDeleteFlowEntity(dpnId, internalIp, externalIp, routerId );
+        mdsalManager.removeFlow(pFlowEntity);
+
+        FlowEntity flowEntity = buildDNATDeleteFlowEntity(dpnId, internalIp, externalIp, routerId);
+        mdsalManager.removeFlow(flowEntity);
+    }
+
+    private void createSNATTblEntry(BigInteger dpnId, String internalIp, String externalIp, long vpnId, long routerId, String macAddress) {
+        FlowEntity pFlowEntity = buildPreSNATFlowEntity(dpnId, internalIp, externalIp, vpnId , routerId);
+        mdsalManager.installFlow(pFlowEntity);
+
+        FlowEntity flowEntity = buildSNATFlowEntity(dpnId, internalIp, externalIp, vpnId, macAddress);
+        mdsalManager.installFlow(flowEntity);
+
+    }
+
+    private void removeSNATTblEntry(BigInteger dpnId, String internalIp, String externalIp) {
+        FlowEntity pFlowEntity = buildPreSNATDeleteFlowEntity(dpnId, internalIp, externalIp);
+        mdsalManager.removeFlow(pFlowEntity);
+
+        FlowEntity flowEntity = buildSNATDeleteFlowEntity(dpnId, internalIp, externalIp);
+        mdsalManager.removeFlow(flowEntity);
+
+    }
+
+    private Uuid getExtNetworkId(final InstanceIdentifier<RouterPorts> pIdentifier) {
+        Optional<RouterPorts> rtrPort = NatUtil.read(broker, LogicalDatastoreType.CONFIGURATION, pIdentifier);
+        if(!rtrPort.isPresent()) {
+            LOG.error("Unable to read router port entry for {}", pIdentifier);
+            return null;
+        }
+
+        Uuid extNwId = rtrPort.get().getExternalNetworkId();
+        return extNwId;
+    }
+
+    private long getVpnId(Uuid extNwId) {
+        InstanceIdentifier<Networks> nwId = InstanceIdentifier.builder(ExternalNetworks.class).child(Networks.class, new NetworksKey(extNwId)).build();
+        Optional<Networks> nw = NatUtil.read(broker, LogicalDatastoreType.CONFIGURATION, nwId);
+        if(!nw.isPresent()) {
+            LOG.error("Unable to read external network for {}", extNwId);
+            return NatConstants.INVALID_ID;
+        }
+
+        Uuid vpnUuid = nw.get().getVpnid();
+        if(vpnUuid == null) {
+            return NatConstants.INVALID_ID;
+        }
+
+        //Get the id using the VPN UUID (also vpn instance name)
+        return NatUtil.readVpnId(broker, vpnUuid.getValue());
+
+    }
+
+    private void processFloatingIPAdd(final InstanceIdentifier<IpMapping> identifier,
+                                      final IpMapping mapping) {
+        LOG.trace("Add event - key: {}, value: {}", identifier, mapping);
+
+        final String routerId = identifier.firstKeyOf(RouterPorts.class).getRouterId();
+        final PortsKey pKey = identifier.firstKeyOf(Ports.class);
+        String interfaceName = pKey.getPortName();
+
+        InstanceIdentifier<RouterPorts> pIdentifier = identifier.firstIdentifierOf(RouterPorts.class);
+        createNATFlowEntries(interfaceName, mapping, pIdentifier, routerId);
+    }
+
+    private void processFloatingIPDel(final InstanceIdentifier<IpMapping> identifier,
+                                      final IpMapping mapping) {
+        LOG.trace("Del event - key: {}, value: {}", identifier, mapping);
+
+        final String routerId = identifier.firstKeyOf(RouterPorts.class).getRouterId();
+        final PortsKey pKey = identifier.firstKeyOf(Ports.class);
+        String interfaceName = pKey.getPortName();
+
+        InstanceIdentifier<RouterPorts> pIdentifier = identifier.firstIdentifierOf(RouterPorts.class);
+        removeNATFlowEntries(interfaceName, mapping, pIdentifier, routerId);
+    }
+
+    private InetAddress getInetAddress(String ipAddr) {
+        InetAddress ipAddress = null;
+        try {
+            ipAddress = InetAddress.getByName(ipAddr);
+        } catch (UnknownHostException e) {
+            LOG.error("UnknowHostException for ip {}", ipAddr);
+        }
+        return ipAddress;
+    }
+
+    private boolean validateIpMapping(IpMapping mapping) {
+        return getInetAddress(mapping.getInternalIp()) != null &&
+                    getInetAddress(mapping.getExternalIp()) != null;
+    }
+
+    void createNATFlowEntries(String interfaceName, final IpMapping mapping,
+                              final InstanceIdentifier<RouterPorts> pIdentifier, final String routerName) {
+        if(!validateIpMapping(mapping)) {
+            LOG.warn("Not a valid ip addresses in the mapping {}", mapping);
+            return;
+        }
+
+        //Get the DPN on which this interface resides
+        BigInteger dpnId = getDpnForInterface(interfaceManager, interfaceName);
+
+        if(dpnId.equals(BigInteger.ZERO)) {
+             LOG.error("No DPN for interface {}. NAT flow entries for ip mapping {} will not be installed", 
+                     interfaceName, mapping);
+             return;
+        }
+
+        long routerId = NatUtil.getVpnId(broker, routerName);
+        if(routerId == NatConstants.INVALID_ID) {
+            LOG.warn("Could not retrieve router id for {} to create NAT Flow entries", routerName);
+            return;
+        }
+
+        Uuid extNwId = getExtNetworkId(pIdentifier);
+        if(extNwId == null) {
+            LOG.error("External network associated with interface {} could not be retrieved", interfaceName);
+            LOG.error("NAT flow entries will not be installed {}", mapping);
+            return;
+        }
+        long vpnId = getVpnId(extNwId);
+        if(vpnId < 0) {
+            LOG.error("No VPN associated with Ext nw {}. Unable to create SNAT table entry for fixed ip {}", 
+                    extNwId, mapping.getInternalIp());
+            return;
+        }
+
+        //Create the DNAT and SNAT table entries
+        createDNATTblEntry(dpnId, mapping.getInternalIp(), mapping.getExternalIp(), routerId, vpnId);
+
+
+        String macAddr = getExternalGatewayMacAddress(routerName);
+        createSNATTblEntry(dpnId, mapping.getInternalIp(), mapping.getExternalIp(), vpnId, routerId, macAddr);
+
+        handler.onAddFloatingIp(dpnId, routerName, extNwId, interfaceName, mapping.getExternalIp(), mapping
+                .getInternalIp());
+    }
+
+    void createNATFlowEntries(BigInteger dpnId,  String interfaceName, String routerName, Uuid externalNetworkId, String internalIp, String externalIp) {
+        long routerId = NatUtil.getVpnId(broker, routerName);
+        if(routerId == NatConstants.INVALID_ID) {
+            LOG.warn("Could not retrieve router id for {} to create NAT Flow entries", routerName);
+            return;
+        }
+        long vpnId = getVpnId(externalNetworkId);
+        if(vpnId < 0) {
+            LOG.error("Unable to create SNAT table entry for fixed ip {}", internalIp);
+            return;
+        }
+        //Create the DNAT and SNAT table entries
+        createDNATTblEntry(dpnId, internalIp, externalIp, routerId, vpnId);
+
+        String macAddr = getExternalGatewayMacAddress(routerName);
+        createSNATTblEntry(dpnId, internalIp, externalIp, vpnId, routerId, macAddr);
+
+        handler.onAddFloatingIp(dpnId, routerName, externalNetworkId, interfaceName, externalIp, internalIp);
+    }
+
+    private String getExternalGatewayMacAddress(String routerName) {
+        InstanceIdentifier<Routers> routersIdentifier = NatUtil.buildRouterIdentifier(routerName);
+        Optional<Routers> optRouters = NatUtil.read(broker, LogicalDatastoreType.CONFIGURATION, routersIdentifier);
+        if(optRouters.isPresent()) {
+            Routers routers = optRouters.get();
+            return routers.getExtGwMacAddress();
+        }
+        return "";
+    }
+
+    void removeNATFlowEntries(String interfaceName, final IpMapping mapping,
+                              final InstanceIdentifier<RouterPorts> pIdentifier, final String routerName) {
+
+        //Get the DPN on which this interface resides
+        BigInteger dpnId = getDpnForInterface(interfaceManager, interfaceName);
+        if(dpnId.equals(BigInteger.ZERO)) {
+            LOG.info("Abort processing Floating ip configuration. No DPN for port : {}", interfaceName);
+            return;
+        }
+
+        long routerId = NatUtil.getVpnId(broker, routerName);
+        if(routerId == NatConstants.INVALID_ID) {
+            LOG.warn("Could not retrieve router id for {} to remove NAT Flow entries", routerName);
+            return;
+        }
+
+        //Delete the DNAT and SNAT table entries
+        removeDNATTblEntry(dpnId, mapping.getInternalIp(), mapping.getExternalIp(), routerId);
+
+//        Uuid extNwId = getExtNetworkId(pIdentifier);
+//        if(extNwId == null) {
+//            LOG.error("External network associated with interface {} could not be retrieved", interfaceName);
+//            return;
+//        }
+//        long vpnId = getVpnId(extNwId);
+//        if(vpnId < 0) {
+//            LOG.error("No VPN associated with ext nw {}. Unable to delete SNAT table entry for fixed ip {}", 
+//                    extNwId, mapping.getInternalIp());
+//            return;
+//        }
+        removeSNATTblEntry(dpnId, mapping.getInternalIp(), mapping.getExternalIp());
+
+        long label = getOperationalIpMapping(routerName, interfaceName, mapping.getInternalIp());
+        if(label < 0) {
+            LOG.error("Could not retrieve label for prefix {} in router {}", mapping.getInternalIp(), routerId);
+            return;
+        }
+        //Uuid extNwId = getExtNetworkId(pIdentifier);
+        Uuid extNwId = getExternalNetworkForRouter(routerName);
+        if(extNwId == null) {
+            LOG.error("External network associated with router {} could not be retrieved", routerName);
+            return;
+        }
+        handler.onRemoveFloatingIp(dpnId, routerName, extNwId, mapping.getExternalIp(), mapping.getInternalIp(), (int) label);
+        removeOperationalDS(routerName, interfaceName, mapping.getInternalIp(), mapping.getExternalIp());
+
+    }
+
+    void removeNATFlowEntries(BigInteger dpnId, String interfaceName, String vpnName, String routerName, Uuid externalNetworkId, String internalIp, String externalIp) {
+        long routerId = NatUtil.getVpnId(broker, routerName);
+        if(routerId == NatConstants.INVALID_ID) {
+            LOG.warn("Could not retrieve router id for {} to create NAT Flow entries", routerName);
+            return;
+        }
+        //Delete the DNAT and SNAT table entries
+        removeDNATTblEntry(dpnId, internalIp, externalIp, routerId);
+
+//        long vpnId = getVpnId(externalNetworkId);
+//        if(vpnId < 0) {
+//            LOG.error("Unable to delete SNAT table entry for fixed ip {}", internalIp);
+//            return;
+//        }
+        removeSNATTblEntry(dpnId, internalIp, externalIp);
+
+        long label = getOperationalIpMapping(routerName, interfaceName, internalIp);
+        if(label < 0) {
+            LOG.error("Could not retrieve label for prefix {} in router {}", internalIp, routerId);
+            return;
+        }
+        //handler.onRemoveFloatingIp(dpnId, routerName, externalNetworkId, externalIp, internalIp, (int)label);
+        ((VpnFloatingIpHandler)handler).cleanupFibEntries(dpnId, vpnName, externalIp, label);
+        removeOperationalDS(routerName, interfaceName, internalIp, externalIp);
+    }
+
+    private long getOperationalIpMapping(String routerId, String interfaceName, String internalIp) {
+        InstanceIdentifier<IpMapping> ipMappingIdentifier = NatUtil.getIpMappingIdentifier(routerId, interfaceName, internalIp);
+        Optional<IpMapping> ipMapping = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, ipMappingIdentifier);
+        if(ipMapping.isPresent()) {
+            return ipMapping.get().getLabel();
+        }
+        return NatConstants.INVALID_ID;
+    }
+
+    private Uuid getExternalNetworkForRouter(String routerId) {
+        InstanceIdentifier<RouterPorts> identifier = NatUtil.getRouterPortsId(routerId);
+        Optional<RouterPorts> optRouterPorts = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, identifier);
+        if(optRouterPorts.isPresent()) {
+            RouterPorts routerPorts = optRouterPorts.get();
+            return routerPorts.getExternalNetworkId();
+        }
+        return null;
+    }
+
+    void updateOperationalDS(String routerId, String interfaceName, int label, String internalIp, String externalIp) {
+
+        LOG.info("Updating operational DS for floating ip config : {} with label {}", internalIp, label);
+        InstanceIdentifier<Ports> portsId = NatUtil.getPortsIdentifier(routerId, interfaceName);
+        Optional<Ports> optPorts = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, portsId);
+        IpMapping ipMapping = new IpMappingBuilder().setKey(new IpMappingKey(internalIp)).setInternalIp(internalIp)
+                .setExternalIp(externalIp).setLabel(label).build();
+        if(optPorts.isPresent()) {
+            LOG.debug("Ports {} entry already present. Updating ipmapping for internal ip {}", interfaceName, internalIp);
+            MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, portsId.child(IpMapping.class, new IpMappingKey(internalIp)), ipMapping);
+        } else {
+            LOG.debug("Adding Ports entry {} along with ipmapping {}", interfaceName, internalIp);
+            List<IpMapping> ipMappings = new ArrayList<>();
+            ipMappings.add(ipMapping);
+            Ports ports = new PortsBuilder().setKey(new PortsKey(interfaceName)).setPortName(interfaceName).setIpMapping(ipMappings).build();
+            MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, portsId, ports);
+        }
+    }
+
+    void removeOperationalDS(String routerId, String interfaceName, String internalIp, String externalIp) {
+        LOG.info("Remove operational DS for floating ip config: {}", internalIp);
+        InstanceIdentifier<IpMapping> ipMappingId = NatUtil.getIpMappingIdentifier(routerId, interfaceName, internalIp);
+        MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, ipMappingId);
+    }
+
+    private FlowEntity buildPreDNATDeleteFlowEntity(BigInteger dpId, String internalIp, String externalIp, long routerId) {
+
+        LOG.info("Bulding Delete DNAT Flow entity for ip {} ", externalIp);
+
+        String flowRef = NatUtil.getFlowRef(dpId, NatConstants.PDNAT_TABLE, externalIp);
+
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PDNAT_TABLE, flowRef,
+                NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_DNAT_TABLE, null, null);
+
+        return flowEntity;
+    }
+
+
+
+    private FlowEntity buildDNATDeleteFlowEntity(BigInteger dpId, String internalIp, String externalIp, long routerId) {
+
+        LOG.info("Bulding Delete DNAT Flow entity for ip {} ", externalIp);
+
+        String flowRef = NatUtil.getFlowRef(dpId, NatConstants.DNAT_TABLE, externalIp);
+
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.DNAT_TABLE, flowRef,
+                NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_DNAT_TABLE, null, null);
+
+        return flowEntity;
+
+    }
+
+    private FlowEntity buildPreSNATDeleteFlowEntity(BigInteger dpId, String internalIp, String externalIp) {
+
+        LOG.info("Building Delete PSNAT Flow entity for ip {} ", internalIp);
+
+        String flowRef = NatUtil.getFlowRef(dpId, NatConstants.PSNAT_TABLE, internalIp);
+
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
+                NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_DNAT_TABLE, null, null);
+
+        return flowEntity;
+    }
+
+    private FlowEntity buildSNATDeleteFlowEntity(BigInteger dpId, String internalIp, String externalIp) {
+
+        LOG.info("Building Delete SNAT Flow entity for ip {} ", internalIp);
+
+        String flowRef = NatUtil.getFlowRef(dpId, NatConstants.SNAT_TABLE, internalIp);
+
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.SNAT_TABLE, flowRef,
+                NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_DNAT_TABLE, null, null);
+
+        return flowEntity;
+
+
+    }
+}
+
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/IPAddress.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/IPAddress.java
new file mode 100644 (file)
index 0000000..fd8698c
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+public class IPAddress {
+
+    private String ipAddress;
+    private int prefixLength;
+
+    public String getIpAddress() {
+        return ipAddress;
+    }
+    public int getPrefixLength() {
+        return prefixLength;
+    }
+
+    public IPAddress(String ipAddress, int prefixLength) {
+        this.ipAddress = ipAddress;
+        this.prefixLength = prefixLength;
+    }
+}
\ No newline at end of file
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/InterfaceStateEventListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/InterfaceStateEventListener.java
new file mode 100644 (file)
index 0000000..169e452
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * 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 com.google.common.collect.Lists;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.vpnservice.mdsalutil.*;
+import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.vpnservice.mdsalutil.packet.IPProtocols;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
+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.vpnservice.natservice.rev160111.NaptSwitches;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPorts;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.Ports;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.ports.IpMapping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ProtocolTypes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal;
+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.RouterToNaptSwitchKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.GetFixedIPsForNeutronPortInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.GetFixedIPsForNeutronPortOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.NeutronvpnService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+public class InterfaceStateEventListener extends AbstractDataChangeListener<Interface> implements AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(InterfaceStateEventListener.class);
+    private ListenerRegistration<DataChangeListener> listenerRegistration;
+    private final DataBroker dataBroker;
+    private IMdsalApiManager mdsalManager;
+    private FloatingIPListener floatingIPListener;
+    private NaptManager naptManager;
+    private NeutronvpnService neutronVpnService;
+
+    public InterfaceStateEventListener(final DataBroker db){
+        super(Interface.class);
+        dataBroker = db;
+        registerListener(db);
+    }
+
+    public void setMdsalManager(IMdsalApiManager mdsalManager) {
+        this.mdsalManager = mdsalManager;
+    }
+
+    public void setFloatingIpListener(FloatingIPListener floatingIPListener) {
+        this.floatingIPListener = floatingIPListener;
+    }
+
+    public void setNeutronVpnService(NeutronvpnService neutronVpnService) {
+        this.neutronVpnService = neutronVpnService;
+    }
+
+    public void setNaptManager(NaptManager naptManager) {
+        this.naptManager = naptManager;
+
+    }
+
+    private void registerListener(final DataBroker db) {
+        try {
+            listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
+                    getWildCardPath(), InterfaceStateEventListener.this, AsyncDataBroker.DataChangeScope.SUBTREE);
+        } catch (final Exception e) {
+            LOG.error("Interface DataChange listener registration failed", e);
+            throw new IllegalStateException("Nexthop Manager registration Listener failed.", e);
+        }
+    }
+
+    private InstanceIdentifier<Interface> getWildCardPath() {
+        return InstanceIdentifier.create(InterfacesState.class).child(Interface.class);
+    }
+
+    @Override
+    protected void remove(InstanceIdentifier<Interface> identifier, Interface delintrf) {
+        LOG.trace("NAT Service : Interface {} removed event received", delintrf);
+        try {
+            if (delintrf != null) {
+                String interfaceName = delintrf.getName();
+                LOG.trace("NAT Service : Port removed event received for interface {} ", interfaceName);
+
+                BigInteger dpnId = NatUtil.getDpIdFromInterface(delintrf);
+                LOG.trace("NAT Service : PORT_REMOVE: Interface {} down in Dpn {}", interfaceName, dpnId);
+
+                String routerName = getRouterIdForPort(dataBroker, interfaceName);
+                if (routerName != null) {
+                    processInterfaceRemoved(interfaceName, routerName);
+                    removeSnatEntriesForPort(interfaceName,routerName);
+                } else {
+                    LOG.debug("NAT Service : PORT_REMOVE: Router Id is null either Interface {} is not associated " +
+                                "to router or failed to retrieve routerId due to exception", interfaceName);
+                }
+            }
+        } catch(Exception e) {
+            LOG.error("NAT Service : Exception caught in InterfaceOperationalStateRemove : {}", e);
+        }
+    }
+
+    @Override
+    protected void update(InstanceIdentifier<Interface> identifier, Interface original, Interface update) {
+        LOG.trace("NAT Service : Operation Interface update event - Old: {}, New: {}", original, update);
+        String interfaceName = update.getName();
+        if (update.getOperStatus().equals(Interface.OperStatus.Up)) {
+            LOG.trace("NAT Service : Port UP event received for interface {} ", interfaceName);
+        } else if (update.getOperStatus().equals(Interface.OperStatus.Down)) {
+            try {
+                LOG.trace("NAT Service : Port DOWN event received for interface {} ", interfaceName);
+
+                BigInteger dpnId = NatUtil.getDpIdFromInterface(update);
+                LOG.trace("NAT Service : PORT_DOWN: Interface {} down in Dpn {}", interfaceName, dpnId);
+
+                String routerName = getRouterIdForPort(dataBroker, interfaceName);
+                if (routerName != null) {
+                    removeSnatEntriesForPort(interfaceName,routerName);
+                } else {
+                    LOG.debug("NAT Service : PORT_DOWN: Router Id is null, either Interface {} is not associated " +
+                            "to router or failed to retrieve routerId due to exception", interfaceName);
+                }
+            } catch (Exception ex) {
+                LOG.error("NAT Service : Exception caught in InterfaceOperationalStateDown : {}",ex);
+            }
+        }
+    }
+
+    @Override
+    protected void add(InstanceIdentifier<Interface> identifier, Interface intrf) {
+        LOG.trace("NAT Service : Interface {} up event received", intrf);
+        try {
+            String interfaceName = intrf.getName();
+            LOG.trace("NAT Service : Port added event received for interface {} ", interfaceName);
+            String routerId = getRouterIdForPort(dataBroker,interfaceName);
+            if (routerId != null) {
+                processInterfaceAdded(interfaceName, routerId);
+            }
+        } catch (Exception ex) {
+        LOG.error("NAT Service : Exception caught in Interface Operational State Up event: {}", ex);
+        }
+    }
+
+    private void removeSnatEntriesForPort(String interfaceName,String routerName) {
+        Long routerId = NatUtil.getVpnId(dataBroker, routerName);
+        if (routerId == NatConstants.INVALID_ID) {
+            LOG.error("NAT Service : routerId not found for routername {}",routerName);
+            return;
+        }
+        BigInteger naptSwitch = getNaptSwitchforRouter(dataBroker,routerName);
+        if (naptSwitch == null) {
+            LOG.error("NAT Service : NaptSwitch is not elected for router {} with Id {}",routerName,routerId);
+            return;
+        }
+        //getInternalIp for port
+        List<String> fixedIps = getFixedIpsForPort(interfaceName);
+        if (fixedIps == null) {
+            LOG.debug("NAT Service : Internal Ips not found for InterfaceName {} in router {} with id {}",interfaceName,routerName,routerId);
+            return;
+        }
+        List<ProtocolTypes> protocolTypesList = getPortocolList();
+        for (String internalIp : fixedIps) {
+            LOG.debug("NAT Service : Internal Ip retrieved for interface {} is {} in router with Id {}",interfaceName,internalIp,routerId);
+            for(ProtocolTypes protocol : protocolTypesList) {
+                List<Integer> portList = NatUtil.getInternalIpPortListInfo(dataBroker, routerId, internalIp, protocol);
+                if (portList != null) {
+                    for (Integer portnum : portList) {
+                        //build and remove the flow in outbound table
+                        try {
+                            removeNatFlow(naptSwitch, NatConstants.OUTBOUND_NAPT_TABLE, routerId, internalIp, portnum);
+                        } catch (Exception ex) {
+                            LOG.error("NAT Service : Failed to remove snat flow for internalIP {} with Port {} protocol {} for routerId {} " +
+                                    "in OUTBOUNDTABLE of NaptSwitch {}: {}",internalIp,portnum,protocol,routerId,naptSwitch,ex);
+                        }
+                        //Get the external IP address and the port from the model
+                        NAPTEntryEvent.Protocol proto = protocol.toString().equals(ProtocolTypes.TCP.toString()) ? NAPTEntryEvent.Protocol.TCP : NAPTEntryEvent.Protocol.UDP;
+                        IpPortExternal ipPortExternal = NatUtil.getExternalIpPortMap(dataBroker, routerId,
+                                internalIp, String.valueOf(portnum), proto);
+                        if (ipPortExternal == null) {
+                            LOG.error("Mapping for internalIp {} with port {} is not found in router with Id {}",internalIp,portnum,routerId);
+                            return;
+                        }
+                        String externalIpAddress = ipPortExternal.getIpAddress();
+                        Integer portNumber = ipPortExternal.getPortNum();
+
+                        //build and remove the flow in inboundtable
+                        try {
+                            removeNatFlow(naptSwitch, NatConstants.INBOUND_NAPT_TABLE,routerId, externalIpAddress, portNumber);
+                        } catch (Exception ex) {
+                            LOG.error("NAT Service : Failed to remove snat flow internalIP {} with Port {} protocol {} for routerId {} " +
+                                    "in INBOUNDTABLE of naptSwitch {} : {}",externalIpAddress,portNumber,protocol,routerId,naptSwitch,ex);
+                        }
+
+                        String internalIpPort = internalIp + ":" + portnum;
+                        // delete the entry from IntExtIpPortMap DS
+                        try {
+                            naptManager.removeFromIpPortMapDS(routerId, internalIpPort, proto);
+                            naptManager.removePortFromPool(internalIpPort,externalIpAddress);
+                        } catch (Exception ex){
+                            LOG.error("NAPT Service : releaseIpExtPortMapping failed, Removal of ipportmap {} for router {} failed {}" ,
+                                    internalIpPort, routerId, ex);
+                        }
+                    }
+                    // delete the entry from SnatIntIpPortMap DS
+                    LOG.debug("NAT Service : Removing InternalIp :{} portlist :{} for protocol :{} of router {}",internalIp,portList,protocol,routerId);
+                    naptManager.removeFromSnatIpPortDS(routerId,internalIp);
+                } else {
+                    LOG.debug("NAT Service : No {} session for interface {} with internalIP {} in router with id {}",protocol,interfaceName,internalIp,routerId);
+                }
+            }
+        }
+    }
+
+    private String getRouterIdForPort(DataBroker dataBroker,String interfaceName) {
+        String vpnName = null, routerName = null;
+        if (NatUtil.isVpnInterfaceConfigured(dataBroker, interfaceName)) {
+            //getVpnInterface
+            VpnInterface vpnInterface = null;
+            try {
+                vpnInterface = NatUtil.getConfiguredVpnInterface(dataBroker, interfaceName);
+            } catch (Exception ex) {
+                LOG.error("NAT Service : Unable to process for interface {} as it is not configured", interfaceName);
+            }
+            if (vpnInterface != null) {
+                //getVpnName
+                try {
+                    vpnName = vpnInterface.getVpnInstanceName();
+                    LOG.debug("NAT Service : Retrieved VpnName {}", vpnName);
+                } catch (Exception e) {
+                    LOG.error("NAT Service : Unable to get vpnname for vpninterface {} - {}", vpnInterface, e);
+                }
+                if (vpnName != null) {
+                    try {
+                        routerName = NatUtil.getRouterIdfromVpnId(dataBroker, vpnName);
+                    } catch (Exception e) {
+                        LOG.error("NAT Service : Unable to get routerId for vpnName {} - {}", vpnName, e);
+                    }
+                    if (routerName != null) {
+                        //check router is associated to external network
+                        if (NatUtil.isSnatEnabledForRouterId(dataBroker, routerName)) {
+                            LOG.debug("NAT Service : Retreived Router Id {} for vpnname {} associated to interface {}",
+                                    routerName,vpnName,interfaceName);
+                            return routerName;
+                        } else {
+                            LOG.info("NAT Service : Interface {} associated to routerId {} is not associated to external network",
+                                    interfaceName, routerName);
+                        }
+                    } else {
+                        LOG.debug("Router is not associated to vpnname {} for interface {}",vpnName,interfaceName);
+                    }
+                } else {
+                    LOG.debug("NAT Service : vpnName not found for vpnInterface {} of port {}",vpnInterface,interfaceName);
+                }
+            }
+        } else {
+            LOG.debug("NAT Service : Interface {} is not a vpninterface",interfaceName);
+        }
+        return null;
+    }
+
+    private List<ProtocolTypes> getPortocolList() {
+        List<ProtocolTypes> protocollist = Lists.newArrayList();
+        protocollist.add(ProtocolTypes.TCP);
+        protocollist.add(ProtocolTypes.UDP);
+        return protocollist;
+    }
+
+    private BigInteger getNaptSwitchforRouter(DataBroker broker,String routerName) {
+        InstanceIdentifier<RouterToNaptSwitch> rtrNaptSw = InstanceIdentifier.builder(NaptSwitches.class).child
+                (RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerName)).build();
+        Optional<RouterToNaptSwitch> routerToNaptSwitchData = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, rtrNaptSw);
+        if (routerToNaptSwitchData.isPresent()) {
+            RouterToNaptSwitch routerToNaptSwitchInstance = routerToNaptSwitchData.get();
+            return routerToNaptSwitchInstance.getPrimarySwitchId();
+        }
+        return null;
+    }
+
+    private void removeNatFlow(BigInteger dpnId, short tableId,Long routerId, String ipAddress,int ipPort) {
+
+        String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(routerId), ipAddress, ipPort);
+        FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, tableId, switchFlowRef);
+
+        mdsalManager.removeFlow(snatFlowEntity);
+        LOG.debug("NAT Service : Removed the flow in table {} for the switch with the DPN ID {} for router {} ip {} port {}",
+                tableId,dpnId,routerId,ipAddress,ipPort);
+    }
+
+    private void processInterfaceAdded(String portName, String rtrId) {
+        LOG.trace("Processing Interface Add Event for interface {}", portName);
+        String routerId = getRouterIdForPort(dataBroker, portName);
+        List<IpMapping> ipMappingList = getIpMappingForPortName(portName, routerId);
+        if (ipMappingList == null || ipMappingList.isEmpty()) {
+            LOG.trace("Ip Mapping list is empty/null for portname {}", portName);
+            return;
+        }
+        InstanceIdentifier<RouterPorts> pIdentifier = NatUtil.buildRouterPortsIdentifier(routerId);
+        for (IpMapping ipMapping : ipMappingList) {
+            floatingIPListener.createNATFlowEntries(portName, ipMapping, pIdentifier, routerId);
+        }
+    }
+
+    private void processInterfaceRemoved(String portName, String rtrId) {
+        LOG.trace("Processing Interface Removed Event for interface {}", portName);
+        String routerId = getRouterIdForPort(dataBroker, portName);
+        List<IpMapping> ipMappingList = getIpMappingForPortName(portName, routerId);
+        if (ipMappingList == null || ipMappingList.isEmpty()) {
+            LOG.trace("Ip Mapping list is empty/null for portName {}", portName);
+            return;
+        }
+        InstanceIdentifier<RouterPorts> pIdentifier = NatUtil.buildRouterPortsIdentifier(routerId);
+        for (IpMapping ipMapping : ipMappingList) {
+            floatingIPListener.removeNATFlowEntries(portName, ipMapping, pIdentifier, routerId);
+        }
+    }
+
+    private List<IpMapping> getIpMappingForPortName(String portName, String routerId) {
+        InstanceIdentifier<Ports> portToIpMapIdentifier = NatUtil.buildPortToIpMapIdentifier(routerId, portName);
+        Optional<Ports> port = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, portToIpMapIdentifier);
+        if(!port.isPresent()) {
+            LOG.error("NAT Service : Unable to read router port entry for router ID {} and port name {}", routerId, portName);
+            return null;
+        }
+        List<IpMapping> ipMappingList = port.get().getIpMapping();
+        return ipMappingList;
+    }
+
+    private List<String> getFixedIpsForPort (String interfname) {
+        LOG.debug("getFixedIpsForPort method is called for interface {}",interfname);
+        try {
+            Future<RpcResult<GetFixedIPsForNeutronPortOutput>> result =
+                    neutronVpnService.getFixedIPsForNeutronPort(new GetFixedIPsForNeutronPortInputBuilder()
+                            .setPortId(new Uuid(interfname)).build());
+
+            RpcResult<GetFixedIPsForNeutronPortOutput> rpcResult = result.get();
+            if(!rpcResult.isSuccessful()) {
+                LOG.warn("NAT Service : RPC Call to GetFixedIPsForNeutronPortOutput returned with Errors {}", rpcResult.getErrors());
+            } else {
+                return rpcResult.getResult().getFixedIPs();
+            }
+        } catch (InterruptedException | ExecutionException | NullPointerException ex ) {
+            LOG.error("NAT Service : Exception while receiving fixedIps for port {}",interfname);
+        }
+        return null;
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (listenerRegistration != null) {
+            try {
+                listenerRegistration.close();
+            } catch (final Exception e) {
+                LOG.error("NAT Service : Error when cleaning up DataChangeListener.", e);
+            }
+            listenerRegistration = null;
+        }
+        LOG.info("NAT Service : Interface listener Closed");
+    }
+}
\ No newline at end of file
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NAPTEntryEvent.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NAPTEntryEvent.java
new file mode 100644 (file)
index 0000000..8a16f7c
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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;
+
+
+public class NAPTEntryEvent {
+    private String ipAddress;
+    private int portNumber;
+    private Long routerId;
+    private Operation op;
+    private Protocol protocol;
+
+    public String getIpAddress() {
+        return ipAddress;
+    }
+
+    public int getPortNumber() {
+        return portNumber;
+    }
+
+    public Long getRouterId() {
+        return routerId;
+    }
+
+    public Operation getOperation() {
+        return op;
+    }
+
+    public Protocol getProtocol() {
+        return protocol;
+    }
+
+    NAPTEntryEvent(String ipAddress, int portNumber, Long routerId, Operation op, Protocol protocol){
+        this.ipAddress = ipAddress;
+        this.portNumber = portNumber;
+        this.routerId = routerId;
+        this.op = op;
+        this.protocol = protocol;
+    }
+
+    public enum Operation{
+        ADD, DELETE;
+    }
+
+    public enum Protocol{
+        TCP, UDP;
+    }
+}
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;
+        }
+    }
+}
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptEventHandler.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptEventHandler.java
new file mode 100644 (file)
index 0000000..f8e2a67
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * 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 org.opendaylight.vpnservice.mdsalutil.*;
+import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.vpnservice.mdsalutil.packet.IPProtocols;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.List;
+import java.util.ArrayList;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NaptEventHandler {
+    private NaptManager naptManager;
+    private static final Logger LOG = LoggerFactory.getLogger(NaptEventHandler.class);
+    private static IMdsalApiManager mdsalManager;
+    private DataBroker dataBroker;
+
+    public NaptEventHandler(final DataBroker dataBroker) {
+        this.dataBroker = dataBroker;
+    }
+
+    public void setMdsalManager(IMdsalApiManager mdsalManager) {
+        this.mdsalManager = mdsalManager;
+    }
+
+    public void setNaptManager(NaptManager naptManager) {
+        this.naptManager = naptManager;
+    }
+
+
+    public void handleEvent(NAPTEntryEvent naptEntryEvent){
+    /*
+            Flow programming logic of the OUTBOUND NAPT TABLE :
+            1) Get the internal IP address, port number, router ID from the event.
+            2) Use the NAPT service getExternalAddressMapping() to get the External IP and the port.
+            3) Build the flow for replacing the Internal IP and port with the External IP and port.
+              a) Write the matching criteria.
+              b) Match the router ID in the metadata.
+              d) Write the VPN ID to the metadata.
+              e) Write the other data.
+              f) Set the apply actions instruction with the action setfield.
+            4) Write the flow to the OUTBOUND NAPT Table and forward to FIB table for routing the traffic.
+
+            Flow programming logic of the INBOUND NAPT TABLE :
+            Same as Outbound table logic except that :
+            1) Build the flow for replacing the External IP and port with the Internal IP and port.
+            2) Match the VPN ID in the metadata.
+            3) Write the router ID to the metadata.
+            5) Write the flow to the INBOUND NAPT Table and forward to FIB table for routing the traffic.
+    */
+        Long routerId = naptEntryEvent.getRouterId();
+        LOG.info("NAT Service : handleEvent() entry for IP {}, port {}, routerID {}", naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
+        BigInteger dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
+        if(dpnId == null ){
+            LOG.error("NAT Service : dpnId is null");
+            return;
+        }
+        if(naptEntryEvent.getOperation() == NAPTEntryEvent.Operation.ADD) {
+            LOG.debug("NAT Service : Inside Add operation of NaptEventHandler");
+
+            //Get the external network ID from the ExternalRouter model
+            Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
+            if(networkId == null ){
+                LOG.error("NAT Service : networkId is null");
+                return;
+            }
+
+            //Get the VPN ID from the ExternalNetworks model
+            Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
+            if(vpnUuid == null ){
+                LOG.error("NAT Service : vpnUuid is null");
+                return;
+            }
+            Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
+
+            //Get the internal IpAddress, internal port number from the event
+            String internalIpAddress = naptEntryEvent.getIpAddress();
+            int internalPort = naptEntryEvent.getPortNumber();
+            SessionAddress internalAddress = new SessionAddress(internalIpAddress, internalPort);
+            NAPTEntryEvent.Protocol protocol = naptEntryEvent.getProtocol();
+
+            //Get the external IP address for the corresponding internal IP address
+            SessionAddress externalAddress = naptManager.getExternalAddressMapping(routerId, internalAddress, naptEntryEvent.getProtocol());
+            if(externalAddress == null ){
+                LOG.error("NAT Service : externalAddress is null");
+                return;
+            }
+
+            //Build and install the NAPT translation flows in the Outbound and Inbound NAPT tables
+            buildAndInstallNatFlows(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, vpnId, routerId, internalAddress, externalAddress, protocol);
+            buildAndInstallNatFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, vpnId, routerId, externalAddress, internalAddress, protocol);
+
+        }else{
+            LOG.debug("NAT Service : Inside delete Operation of NaptEventHandler");
+            removeNatFlows(dpnId, routerId, naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber());
+        }
+
+        LOG.info("NAT Service : handleNaptEvent() exited for IP, port, routerID : {}", naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
+    }
+
+    public static void buildAndInstallNatFlows(BigInteger dpnId, short tableId, long vpnId, long routerId, SessionAddress actualSourceAddress,
+                                         SessionAddress translatedSourceAddress, NAPTEntryEvent.Protocol protocol){
+        LOG.debug("NAT Service : Build and install NAPT flows in InBound and OutBound tables for dpnId {} and routerId {}", dpnId, routerId);
+        //Build the flow for replacing the actual IP and port with the translated IP and port.
+        String actualIp = actualSourceAddress.getIpAddress();
+        int actualPort = actualSourceAddress.getPortNumber();
+        String translatedIp = translatedSourceAddress.getIpAddress();
+        String translatedPort = String.valueOf(translatedSourceAddress.getPortNumber());
+        int idleTimeout=0;
+        if(tableId == NatConstants.OUTBOUND_NAPT_TABLE) {
+            idleTimeout = NatConstants.DEFAULT_NAPT_IDLE_TIMEOUT;
+        }
+        long metaDataValue = routerId;
+        String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(metaDataValue), actualIp, actualPort);
+
+        FlowEntity snatFlowEntity = MDSALUtil.buildFlowEntity(dpnId, tableId, switchFlowRef, NatConstants.DEFAULT_NAPT_FLOW_PRIORITY, NatConstants.NAPT_FLOW_NAME,
+                idleTimeout, 0, NatUtil.getCookieNaptFlow(metaDataValue), buildAndGetMatchInfo(actualIp, actualPort, tableId, protocol, routerId, vpnId),
+                buildAndGetSetActionInstructionInfo(translatedIp, translatedPort, routerId, vpnId, tableId, protocol));
+
+        snatFlowEntity.setSendFlowRemFlag(true);
+
+        LOG.debug("NAT Service : Installing the NAPT flow in the table {} for the switch with the DPN ID {} ", tableId, dpnId);
+        mdsalManager.installFlow(snatFlowEntity);
+    }
+
+    private static List<MatchInfo> buildAndGetMatchInfo(String ip, int port, short tableId, NAPTEntryEvent.Protocol protocol, long segmentId, long vpnId){
+        MatchInfo ipMatchInfo = null;
+        MatchInfo portMatchInfo = null;
+        MatchInfo protocolMatchInfo = null;
+        InetAddress ipAddress = null;
+        String ipAddressAsString = null;
+        try {
+            ipAddress = InetAddress.getByName(ip);
+            ipAddressAsString = ipAddress.getHostAddress();
+
+        } catch (UnknownHostException e) {
+            LOG.error("NAT Service : UnknowHostException in buildAndGetMatchInfo. Failed  to build NAPT Flow for  ip {}", ipAddress);
+            return null;
+        }
+
+        MatchInfo metaDataMatchInfo;
+        if(tableId == NatConstants.OUTBOUND_NAPT_TABLE){
+            ipMatchInfo = new MatchInfo(MatchFieldType.ipv4_source, new String[] {ipAddressAsString, "32" });
+            if(protocol == NAPTEntryEvent.Protocol.TCP) {
+                protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.TCP.intValue()});
+                portMatchInfo = new MatchInfo(MatchFieldType.tcp_src, new long[]{port});
+            } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
+                protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.UDP.intValue()});
+                portMatchInfo = new MatchInfo(MatchFieldType.udp_src, new long[]{port});
+            }
+            metaDataMatchInfo = new MatchInfo(MatchFieldType.metadata, new BigInteger[]{BigInteger.valueOf(segmentId), MetaDataUtil.METADATA_MASK_VRFID});
+        }else{
+            ipMatchInfo = new MatchInfo(MatchFieldType.ipv4_destination, new String[] {ipAddressAsString, "32" });         
+            if(protocol == NAPTEntryEvent.Protocol.TCP) {
+                protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.TCP.intValue()});
+                portMatchInfo = new MatchInfo(MatchFieldType.tcp_dst, new long[]{port});
+            } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
+                protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.UDP.intValue()});
+                portMatchInfo = new MatchInfo(MatchFieldType.udp_dst, new long[]{port});
+            }
+            metaDataMatchInfo = new MatchInfo(MatchFieldType.metadata, new BigInteger[]{BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID});
+        }
+        ArrayList<MatchInfo> matchInfo = new ArrayList<>();
+        matchInfo.add(new MatchInfo(MatchFieldType.eth_type, new long[] { 0x0800L }));
+        matchInfo.add(ipMatchInfo);
+        matchInfo.add(protocolMatchInfo);
+        matchInfo.add(portMatchInfo);
+        matchInfo.add(metaDataMatchInfo);
+        return matchInfo;
+    }
+
+    private static List<InstructionInfo> buildAndGetSetActionInstructionInfo(String ipAddress, String port, long routerId, long vpnId, short tableId, NAPTEntryEvent.Protocol protocol) {
+        ActionInfo ipActionInfo = null;
+        ActionInfo portActionInfo = null;
+        ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
+        ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
+
+        if(tableId == NatConstants.OUTBOUND_NAPT_TABLE){
+            ipActionInfo = new ActionInfo(ActionType.set_source_ip, new String[] {ipAddress});
+            if(protocol == NAPTEntryEvent.Protocol.TCP) {
+               portActionInfo = new ActionInfo( ActionType.set_tcp_source_port, new String[] {port});
+            } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
+               portActionInfo = new ActionInfo( ActionType.set_udp_source_port, new String[] {port});
+            }
+            instructionInfo.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]{BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID}));
+        }else{
+            ipActionInfo = new ActionInfo(ActionType.set_destination_ip, new String[] {ipAddress});
+            if(protocol == NAPTEntryEvent.Protocol.TCP) {
+               portActionInfo = new ActionInfo( ActionType.set_tcp_destination_port, new String[] {port});
+            } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
+               portActionInfo = new ActionInfo( ActionType.set_udp_destination_port, new String[] {port});
+            }
+            instructionInfo.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]{BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID}));
+        }
+
+        listActionInfo.add(ipActionInfo);
+        listActionInfo.add(portActionInfo);
+
+        instructionInfo.add(new InstructionInfo(InstructionType.apply_actions, listActionInfo));
+        instructionInfo.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.NAPT_PFIB_TABLE }));
+
+        return instructionInfo;
+    }
+
+    private void removeNatFlows(BigInteger dpnId, long routerId, String externalIp, int externalPort){
+        LOG.debug("NAT Service : Remove NAPT flows for dpnId {} and routerId {}", dpnId, routerId);
+
+        //Build the flow with the externalPort IP and port as the match info.
+        String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NatConstants.INBOUND_NAPT_TABLE, String.valueOf(routerId), externalIp, externalPort);
+        FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.INBOUND_NAPT_TABLE, switchFlowRef);
+        LOG.debug("NAT Service : Remove the flow in the table {} for the switch with the DPN ID {}", NatConstants.INBOUND_NAPT_TABLE, dpnId);
+        mdsalManager.removeFlow(snatFlowEntity);
+
+    }
+
+}
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptFlowRemovedEventHandler.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptFlowRemovedEventHandler.java
new file mode 100644 (file)
index 0000000..f0206e2
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * 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 org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAdded;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeErrorNotification;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeExperimenterErrorNotification;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SwitchFlowRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.RemovedReasonFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.TcpMatchFields;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.UdpMatchFields;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer4Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4Match;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.TcpMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.UdpMatch;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
+import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import java.math.BigInteger;
+
+public class NaptFlowRemovedEventHandler implements SalFlowListener{
+    private DataBroker dataBroker;
+    private final EventDispatcher naptEventdispatcher;
+    private static final Logger LOG = LoggerFactory.getLogger(NaptFlowRemovedEventHandler.class);
+    private final NaptPacketInHandler naptPacketInHandler;
+    private IMdsalApiManager mdsalManager;
+    private NaptManager naptManager;
+
+    public NaptFlowRemovedEventHandler(EventDispatcher eventDispatcher, DataBroker dataBroker, NaptPacketInHandler handler,
+                                       IMdsalApiManager mdsalManager, NaptManager naptManager) {
+        this.naptEventdispatcher = eventDispatcher;
+        this.dataBroker = dataBroker;
+        this.naptPacketInHandler = handler;
+        this.mdsalManager = mdsalManager;
+        this.naptManager = naptManager;
+    }
+
+    @Override
+    public void onSwitchFlowRemoved(SwitchFlowRemoved switchFlowRemoved) {
+
+/*
+        If the removed flow is from the OUTBOUND NAPT table :
+        1) Get the ActionInfo of the flow.
+        2) From the ActionInfo of the flow get the internal IP address, port and the protocol.
+        3) Get the Metadata matching info of the flow.
+        4) From the Metadata matching info of the flow get router ID.
+        5) Querry the container intext-ip-port-map using the router ID
+           and the internal IP address, port to get the external IP address, port
+        6) Instantiate an NaptEntry event and populate the external IP address, port and the router ID.
+        7) Place the NaptEntry event to the queue.
+*/
+
+        short tableId = switchFlowRemoved.getTableId();
+        RemovedReasonFlags removedReasonFlag = switchFlowRemoved.getRemovedReason();
+
+        if (tableId == NatConstants.OUTBOUND_NAPT_TABLE) {
+            LOG.info("NaptFlowRemovedEventHandler : onSwitchFlowRemoved() entry");
+
+            //Get the internal internal IP address and the port number from the IPv4 match.
+            Ipv4Prefix internalIpv4Address = null;
+            Layer3Match layer3Match = switchFlowRemoved.getMatch().getLayer3Match();
+            if (layer3Match instanceof Ipv4Match) {
+                Ipv4Match internalIpv4Match = (Ipv4Match) layer3Match;
+                internalIpv4Address = internalIpv4Match.getIpv4Source();
+            }
+            if(internalIpv4Address == null){
+                LOG.error("NaptFlowRemovedEventHandler : Matching internal IP is null while retrieving the value from the Outbound NAPT flow");
+                return;
+            }
+            //Get the internal IP as a string
+            String internalIpv4AddressAsString = internalIpv4Address.getValue();
+            String[] internalIpv4AddressParts = internalIpv4AddressAsString.split("/");
+            String internalIpv4HostAddress = null;
+            if(internalIpv4AddressParts.length >= 1){
+                internalIpv4HostAddress = internalIpv4AddressParts[0];
+            }
+
+            //Get the protocol from the layer4 match
+            NAPTEntryEvent.Protocol protocol = null;
+            Integer internalPortNumber = null;
+            Layer4Match layer4Match = switchFlowRemoved.getMatch().getLayer4Match();
+            if (layer4Match instanceof TcpMatch) {
+                TcpMatchFields tcpMatchFields = (TcpMatchFields)layer4Match;
+                internalPortNumber = tcpMatchFields.getTcpSourcePort().getValue();
+                protocol = NAPTEntryEvent.Protocol.TCP;
+            }else if (layer4Match instanceof UdpMatch){
+                UdpMatchFields udpMatchFields = (UdpMatchFields)layer4Match;
+                internalPortNumber = udpMatchFields.getUdpSourcePort().getValue();
+                protocol = NAPTEntryEvent.Protocol.UDP;
+            }
+            if(protocol == null){
+                LOG.error("NaptFlowRemovedEventHandler : Matching protocol is null while retrieving the value from the Outbound NAPT flow");
+                return;
+            }
+
+            //Get the router ID from the metadata.
+            Long routerId;
+            BigInteger metadata = switchFlowRemoved.getMatch().getMetadata().getMetadata();
+            if(MetaDataUtil.getNatRouterIdFromMetadata(metadata) != 0) {
+                routerId = MetaDataUtil.getNatRouterIdFromMetadata(metadata);
+            }else {
+                LOG.error("NaptFlowRemovedEventHandler : Null exception while retrieving routerId");
+                return;
+            }
+
+            //Get the external IP address and the port from the model
+            IpPortExternal ipPortExternal = NatUtil.getExternalIpPortMap(dataBroker, routerId, internalIpv4HostAddress, internalPortNumber.toString(), protocol);
+            if(ipPortExternal == null){
+                LOG.error("NaptFlowRemovedEventHandler : IpPortExternal is null while querried from the model");
+                return;
+            }
+            String externalIpAddress = ipPortExternal.getIpAddress();
+            int externalPortNumber = ipPortExternal.getPortNum();
+
+            //Create an NAPT event and place it in the queue.
+            NAPTEntryEvent naptEntryEvent =  new NAPTEntryEvent(externalIpAddress, externalPortNumber, routerId, NAPTEntryEvent.Operation.DELETE, protocol);
+            naptEventdispatcher.addNaptEvent(naptEntryEvent);
+
+            //Get the DPN ID from the Node
+            InstanceIdentifier<Node> nodeRef = switchFlowRemoved.getNode().getValue().firstIdentifierOf(Node.class);
+            String dpn = nodeRef.firstKeyOf(Node.class).getId().getValue();
+            BigInteger dpnId = getDpnId(dpn);
+
+            //Inform the MDSAL manager to inform about the flow removal.
+            String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(metadata), internalIpv4HostAddress, internalPortNumber);
+            LOG.debug("NaptFlowRemovedEventHandler : DPN ID {}, Metadata {}, SwitchFlowRef {}, internalIpv4HostAddress{}", dpnId, metadata, switchFlowRef, internalIpv4AddressAsString);
+            FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, tableId, switchFlowRef);
+            mdsalManager.removeFlow(snatFlowEntity);
+
+            if(removedReasonFlag.isIDLETIMEOUT()) {
+                LOG.debug("Received flow removed notification due to idleTimeout of flow from switch for flowref {}",switchFlowRef);
+                //Remove the SourceIP:Port key from the Napt packet handler map.
+                String internalIpPortKey = internalIpv4HostAddress + ":" + internalPortNumber;
+                naptPacketInHandler.removeIncomingPacketMap(internalIpPortKey);
+
+                //Remove the mapping of internal fixed ip/port to external ip/port from the datastore.
+                SessionAddress internalSessionAddress = new SessionAddress(internalIpv4HostAddress, internalPortNumber);
+                naptManager.releaseIpExtPortMapping(routerId, internalSessionAddress, protocol);
+            } else {
+                LOG.debug("Received flow removed notification due to flowdelete from switch for flowref {}",switchFlowRef);
+            }
+
+            LOG.info("NaptFlowRemovedEventHandler : onSwitchFlowRemoved() exit");
+        }
+    }
+
+    private BigInteger getDpnId(String node) {
+        //openflow:1]
+        String temp[] = node.split(":");
+        BigInteger dpnId = new BigInteger(temp[1]);
+        return dpnId;
+
+    }
+
+    @Override
+    public void onFlowAdded(FlowAdded arg0) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void onFlowRemoved(FlowRemoved arg0) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void onFlowUpdated(FlowUpdated arg0) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void onNodeErrorNotification(NodeErrorNotification arg0) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void onNodeExperimenterErrorNotification(NodeExperimenterErrorNotification arg0) {
+        // TODO Auto-generated method stub
+
+    }
+
+}
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptManager.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptManager.java
new file mode 100644 (file)
index 0000000..db6a2b1
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * 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
+ */
+
+/*
+ * Created eyugsar 2016/12/1
+ */
+
+package org.opendaylight.vpnservice.natservice.internal;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Future;
+import java.util.concurrent.ExecutionException;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.net.util.SubnetUtils;
+import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
+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.vpnservice.idmanager.rev150403.AllocateIdInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.ReleaseIdInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.ReleaseIdInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.IntextIpMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.IntextIpPortMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ProtocolTypes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.SnatintIpPortMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.Ports;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.PortsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMapping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMappingKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMapBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMapKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.IpPortMapping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.IpPortMappingKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolTypeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMapBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMapKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternalBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.router.id.name.RouterIds;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.IntipPortMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.IntipPortMapKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPort;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPortKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoTypeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoTypeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+
+public class NaptManager  {
+    private static final Logger LOG = LoggerFactory.getLogger(NaptManager.class);
+    private final DataBroker broker;
+    private IdManagerService idManager;
+    private static final long LOW_PORT = 49152L;
+    private static final long HIGH_PORT = 65535L;
+    private static boolean EXTSUBNET_FLAG = false;
+    private static boolean NEXT_EXTIP_FLAG = false;
+
+    public NaptManager(final DataBroker db) {
+        this.broker = db;
+    }
+
+    public void setIdManager(IdManagerService idManager) {
+        this.idManager = idManager;
+    }
+
+    protected void createNaptPortPool(String PoolName) {
+         LOG.debug("NAPT Service : createPortPool requested for : {}", PoolName);
+         CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
+             .setPoolName(PoolName)
+             .setLow(LOW_PORT)
+             .setHigh(HIGH_PORT)
+             .build();
+         try {
+             Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
+             if ((result != null) && (result.get().isSuccessful())) {
+                 LOG.debug("NAPT Service : Created PortPool");
+             } else {
+                 LOG.error("NAPT Service : Unable to create PortPool");
+             }
+         } catch (InterruptedException | ExecutionException e) {
+             LOG.error("Failed to create PortPool for NAPT Service",e);
+         }
+    }
+
+     // 1. napt service functions
+     /**
+      * this method is used to inform this service of what external IP address to be used
+      * as mapping when requested one for the internal IP address given in the input
+      * @param segmentId â€“ segmentation in which the mapping to be used. Eg; routerid
+      * @param internal subnet prefix or ip address
+      * @param external subnet prefix or ip address
+      */
+
+      public void registerMapping(long segmentId, IPAddress internal, IPAddress external) {
+
+          LOG.debug("NAPT Service : registerMapping called with segmentid {}, internalIp {}, prefix {}, externalIp {} and prefix {} ", segmentId, internal.getIpAddress(),
+                internal.getPrefixLength(), external.getIpAddress(), external.getPrefixLength());
+        // Create Pool per ExternalIp and not for all IPs in the subnet. Create new Pools during getExternalAddressMapping if exhausted.
+        String externalIpPool;
+        if (external.getPrefixLength() !=0 && external.getPrefixLength() != NatConstants.DEFAULT_PREFIX) {  // subnet case
+            String externalSubnet = new StringBuilder(64).append(external.getIpAddress()).append("/").append(external.getPrefixLength()).toString();
+            LOG.debug("NAPT Service : externalSubnet is : {}", externalSubnet);
+            SubnetUtils subnetUtils = new SubnetUtils(externalSubnet);
+            SubnetInfo subnetInfo = subnetUtils.getInfo();
+            externalIpPool = subnetInfo.getLowAddress();
+        } else {  // ip case
+            externalIpPool = external.getIpAddress();
+        }
+        createNaptPortPool(externalIpPool);
+
+        // Store the ip to ip map in Operational DS
+        String internalIp = internal.getIpAddress();
+        if(internal.getPrefixLength() != 0) {
+            internalIp =  new StringBuilder(64).append(internal.getIpAddress()).append("/").append(internal.getPrefixLength()).toString();
+        }
+        String externalIp = external.getIpAddress();
+        if(external.getPrefixLength() != 0) {
+            externalIp =  new StringBuilder(64).append(external.getIpAddress()).append("/").append(external.getPrefixLength()).toString();
+        }
+        IpMap ipm = new IpMapBuilder().setKey(new IpMapKey(internalIp)).setInternalIp(internalIp).setExternalIp(externalIp).build();
+        MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, getIpMapIdentifier(segmentId, internalIp), ipm);
+        LOG.debug("NAPT Service : registerMapping exit after updating DS with internalIP {}, externalIP {}", internalIp, externalIp);
+     }
+
+
+     /**
+      * method to get external ip/port mapping when provided with internal ip/port pair
+      * If already a mapping exist for the given input, then the existing mapping is returned
+      * instead of overwriting with new ip/port pair.
+      * @param segmentId
+      * @param sourceAddress - internal ip address/port pair
+      * @return external ip address/port
+      */
+     public SessionAddress getExternalAddressMapping(long segmentId, SessionAddress sourceAddress, NAPTEntryEvent.Protocol protocol) {
+         LOG.debug("NAPT Service : getExternalAddressMapping called with segmentId {}, internalIp {} and port {}",
+                 segmentId, sourceAddress.getIpAddress(), sourceAddress.getPortNumber());
+        /*
+         1. Get Internal IP, Port in IP:Port format
+         2. Inside DB with routerId get the list of entries and check if it matches with existing IP:Port
+         3. If True return SessionAddress of ExternalIp and Port
+         4. Else check ip Map and Form the ExternalIp and Port and update DB and then return ExternalIp and Port
+         */
+
+         //SessionAddress externalIpPort = new SessionAddress();
+         String internalIpPort = new StringBuilder(64).append(sourceAddress.getIpAddress()).append(":").append(sourceAddress.getPortNumber()).toString();
+
+         // First check existing Port Map.
+         SessionAddress existingIpPort = checkIpPortMap(segmentId, internalIpPort ,protocol);
+         if(existingIpPort != null) {
+             // populate externalIpPort from IpPortMap and return
+             LOG.debug("NAPT Service : getExternalAddressMapping successfully returning existingIpPort as {} and {}", existingIpPort.getIpAddress(), existingIpPort.getPortNumber());
+             return existingIpPort;
+         } else {
+             // Now check in ip-map
+             String externalIp = checkIpMap(segmentId, sourceAddress.getIpAddress());
+             if(externalIp == null) {
+                 LOG.error("NAPT Service : getExternalAddressMapping, Unexpected error, internal to external ip map does not exist");
+                 return null;
+             } else {
+                 /* Logic assuming internalIp is always ip and not subnet
+                  * case 1: externalIp is ip
+                  *        a) goto externalIp pool and getPort and return
+                  *        b) else return error
+                  * case 2: externalIp is subnet
+                  *        a) Take first externalIp and goto that Pool and getPort
+                  *             if port -> return
+                  *             else Take second externalIp and create that Pool and getPort
+                  *             if port ->return
+                  *             else
+                  *             Continue same with third externalIp till we exhaust subnet
+                  *        b) Nothing worked return error
+                  */
+                 SubnetUtils externalIpSubnet;
+                 List<String> allIps = new ArrayList<String>();
+                 String subnetPrefix = "/" + String.valueOf(NatConstants.DEFAULT_PREFIX);
+                 if( !externalIp.contains(subnetPrefix) ) {
+                    EXTSUBNET_FLAG = true;
+                    externalIpSubnet = new SubnetUtils(externalIp);
+                    allIps = Arrays.asList(externalIpSubnet.getInfo().getAllAddresses());
+                    LOG.debug("NAPT Service : total count of externalIps available {}", externalIpSubnet.getInfo().getAddressCount());
+                 } else {
+                     LOG.debug("NAPT Service : getExternalAddress single ip case");
+                     if(externalIp.contains(subnetPrefix)) {
+                         String[] externalIpSplit = externalIp.split("/");
+                         String extIp = externalIpSplit[0];
+                         externalIp = extIp; //remove /32 what we got from checkIpMap
+                     }
+                     allIps.add(externalIp);
+                 }
+
+                 for(String extIp : allIps) {
+                    LOG.info("NAPT Service : Looping externalIPs with externalIP now as {}", extIp);
+                    if(NEXT_EXTIP_FLAG) {
+                        createNaptPortPool(extIp);
+                        LOG.debug("NAPT Service : Created Pool for next Ext IP {}", extIp);
+                    }
+                    AllocateIdInput getIdInput = new AllocateIdInputBuilder()
+                        .setPoolName(extIp).setIdKey(internalIpPort)
+                        .build();
+                     try {
+                        Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
+                        RpcResult<AllocateIdOutput> rpcResult;
+                        if ((result != null) && (result.get().isSuccessful())) {
+                            LOG.debug("NAPT Service : Got id from idManager");
+                            rpcResult = result.get();
+                        } else {
+                            LOG.error("NAPT Service : getExternalAddressMapping, idManager could not allocate id retry if subnet");
+                            if(!EXTSUBNET_FLAG) {
+                                LOG.error("NAPT Service : getExternalAddressMapping returning null for single IP case, may be ports exhausted");
+                                return null;
+                            }
+                            LOG.debug("NAPT Service : Could be ports exhausted case, try with another externalIP if possible");
+                            NEXT_EXTIP_FLAG = true;
+                            continue;
+                        }
+                        int extPort= rpcResult.getResult().getIdValue().intValue();
+                        SessionAddress externalIpPort = new SessionAddress(extIp, extPort);
+                        // Write to ip-port-map before returning
+                        IpPortExternalBuilder ipExt = new IpPortExternalBuilder();
+                        IpPortExternal ipPortExt = ipExt.setIpAddress(extIp).setPortNum(extPort).build();
+                        IpPortMap ipm = new IpPortMapBuilder().setKey(new IpPortMapKey(internalIpPort))
+                                .setIpPortInternal(internalIpPort).setIpPortExternal(ipPortExt).build();
+                        LOG.debug("NAPT Service : getExternalAddressMapping writing into ip-port-map with externalIP {} and port {}",
+                                ipPortExt.getIpAddress(), ipPortExt.getPortNum());
+                        try {
+                            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION,
+                                           getIpPortMapIdentifier(segmentId, internalIpPort, protocol), ipm);
+                        } catch (UncheckedExecutionException uee) {
+                            LOG.error("NAPT Service : Failed to write into ip-port-map with exception {}", uee.getMessage() );
+                        }
+
+                         // Write to snat-internal-ip-port-info
+                         String internalIpAddress = sourceAddress.getIpAddress();
+                         int ipPort = sourceAddress.getPortNumber();
+                         ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
+                         List<Integer> portList = NatUtil.getInternalIpPortListInfo(broker,segmentId,internalIpAddress,protocolType);
+                         if (portList == null) {
+                             portList = Lists.newArrayList();
+                         }
+                         portList.add(ipPort);
+
+                         IntIpProtoTypeBuilder builder = new IntIpProtoTypeBuilder();
+                         IntIpProtoType intIpProtocolType = builder.setKey(new IntIpProtoTypeKey(protocolType)).setPorts(portList).build();
+                         try {
+                             MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, NatUtil.buildSnatIntIpPortIdentifier(segmentId, internalIpAddress, protocolType), intIpProtocolType);
+                         } catch (Exception ex) {
+                             LOG.error("NAPT Service : Failed to write into snat-internal-ip-port-info with exception {}", ex.getMessage() );
+                         }
+
+                         LOG.debug("NAPT Service : getExternalAddressMapping successfully returning externalIP {} and port {}",
+                                         externalIpPort.getIpAddress(), externalIpPort.getPortNumber());
+                        return externalIpPort;
+                    } catch(InterruptedException | ExecutionException  e) {
+                        LOG.error("NAPT Service : getExternalAddressMapping, Exception caught  {}",e);
+                        return null;
+                    }
+                }// end of for loop
+             }// end of else ipmap present
+         }// end of else check ipmap
+         LOG.error("NAPT Service: getExternalAddressMapping returning null, nothing worked or externalIPs exhausted");
+         return null;
+     }
+
+
+     /**
+      * release the existing mapping of internal ip/port to external ip/port pair
+      * if no mapping exist for given internal ip/port, it returns false
+      * @param segmentId
+      * @param address
+      * @return true if mapping exist and the mapping is removed successfully
+      */
+
+     public boolean releaseAddressMapping(long segmentId, SessionAddress address, NAPTEntryEvent.Protocol protocol) {
+
+         LOG.debug("NAPT Service : releaseAddressMapping called with segmentId {}, internalIP {}, port {}", segmentId, address.getIpAddress(), address.getPortNumber());
+         // delete entry from IpPort Map and IP Map if exists
+         String internalIpPort = new StringBuilder(64).append(address.getIpAddress()).append(":").append(address.getPortNumber()).toString();
+         SessionAddress existingIpPort = checkIpPortMap(segmentId, internalIpPort, protocol);
+         if(existingIpPort != null) {
+             // delete the entry from IpPortMap DS
+             try {
+                 removeFromIpPortMapDS(segmentId, internalIpPort , protocol);
+             } catch (Exception e){
+                 LOG.error("NAPT Service : releaseAddressMapping failed, Removal of ipportmap {} for router {} failed {}" , internalIpPort, segmentId, e);
+                 return false;
+             }
+         } else {
+             LOG.error("NAPT Service : releaseAddressMapping failed, segmentId {} and internalIpPort {} not found in IpPortMap DS", segmentId, internalIpPort);
+             return false;
+         }
+         String existingIp = checkIpMap(segmentId, address.getIpAddress());
+         if(existingIp != null) {
+             // delete the entry from IpMap DS
+             try {
+                 removeFromIpMapDS(segmentId, address.getIpAddress());
+             } catch (Exception e){
+                 LOG.error("NAPT Service : Removal of  ipmap {} for router {} failed {}" , address.getIpAddress(), segmentId, e);
+                 return false;
+             }
+             //delete the entry from snatIntIpportinfo
+             try {
+                 removeFromSnatIpPortDS(segmentId, address.getIpAddress());
+             } catch (Exception e){
+                 LOG.error("NAPT Service : releaseAddressMapping failed, Removal of snatipportmap {} for router {} failed {}" , address.getIpAddress(), segmentId, e);
+                 return false;
+             }
+         } else {
+             LOG.error("NAPT Service : releaseAddressMapping failed, segmentId {} and internalIpPort {} not found in IpMap DS", segmentId, internalIpPort);
+             return false;
+         }
+         // Finally release port from idmanager
+         removePortFromPool(internalIpPort, existingIpPort.getIpAddress());
+
+         LOG.debug("NAPT Service : Exit of releaseAddressMapping successfully for segmentId {} and internalIpPort {}", segmentId, internalIpPort);  
+         return true;
+
+     }
+
+    protected void releaseIpExtPortMapping(long segmentId, SessionAddress address, NAPTEntryEvent.Protocol protocol) {
+        String internalIpPort = address.getIpAddress() + ":" + address.getPortNumber();
+        SessionAddress existingIpPort = checkIpPortMap(segmentId, internalIpPort, protocol);
+        if(existingIpPort != null) {
+            // delete the entry from IpPortMap DS
+            try {
+                removeFromIpPortMapDS(segmentId, internalIpPort , protocol);
+                // Finally release port from idmanager
+                removePortFromPool(internalIpPort, existingIpPort.getIpAddress());
+            } catch (Exception e){
+                LOG.error("NAPT Service : releaseAddressMapping failed, Removal of ipportmap {} for router {} failed {}" ,
+                        internalIpPort, segmentId, e);
+            }
+        } else {
+            LOG.error("NAPT Service : releaseIpExtPortMapping failed, segmentId {} and internalIpPort {} not found in IpPortMap DS", segmentId, internalIpPort);
+        }
+
+        //delete the entry of port for InternalIp from snatIntIpportMappingDS
+        try {
+            removeSnatIntIpPortDS(segmentId,address, protocol);
+        } catch (Exception e){
+            LOG.error("NAPT Service : releaseSnatIpPortMapping failed, Removal of snatipportmap {} for router {} failed {}" ,
+                    address.getIpAddress(), segmentId, e);
+        }
+    }
+
+     /**
+      * removes the internal ip to external ip mapping if present
+      * @param segmentId
+      * @return true if successfully removed
+      */
+     public boolean removeMapping(long segmentId) {
+         try {
+             removeIpMappingForRouterID(segmentId);
+         } catch (Exception e){
+             LOG.error("NAPT Service : Removal of  IPMapping for router {} failed {}" , segmentId, e);
+             return false;
+         }
+
+         //TODO :  This is when router is deleted then cleanup the entries in tables, ports etc - Delete scenarios
+        return false;
+     }
+
+     // 2. Utility functions
+     protected InstanceIdentifier<IpMap> getIpMapIdentifier(long segid, String internal) {
+         InstanceIdentifier<IpMap> id = InstanceIdentifier.builder(
+                 IntextIpMap.class).child(IpMapping.class, new IpMappingKey(segid)).child(IpMap.class, new IpMapKey(internal)).build();
+         return id;
+     }
+
+    public static List<IpMap> getIpMapList(DataBroker broker, Long routerId) {
+        InstanceIdentifier id = getIpMapList(routerId);
+        Optional<IpMapping> ipMappingListData = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
+        if (ipMappingListData.isPresent()) {
+            IpMapping ipMapping = ipMappingListData.get();
+            return ipMapping.getIpMap();
+        }
+        return null;
+    }
+
+    protected static InstanceIdentifier<IpMapping> getIpMapList(long routerId) {
+        InstanceIdentifier<IpMapping> id = InstanceIdentifier.builder(
+                IntextIpMap.class).child(IpMapping.class, new IpMappingKey(routerId)).build();
+        return id;
+    }
+
+     protected InstanceIdentifier<IpPortMap> getIpPortMapIdentifier(long segid, String internal, NAPTEntryEvent.Protocol protocol) {
+         ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
+         InstanceIdentifier<IpPortMap> id = InstanceIdentifier.builder(
+                 IntextIpPortMap.class).child(IpPortMapping.class, new IpPortMappingKey(segid)).child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType)).
+                 child(IpPortMap.class, new IpPortMapKey(internal)).build();
+         return id;
+     }
+
+     protected SessionAddress checkIpPortMap(long segmentId, String internalIpPort, NAPTEntryEvent.Protocol protocol) {
+
+         LOG.debug("NAPT Service : checkIpPortMap called with segmentId {} and internalIpPort {}", segmentId, internalIpPort);
+         ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
+         // check if ip-port-map node is there
+         InstanceIdentifierBuilder<IntextIpProtocolType> idBuilder =
+                         InstanceIdentifier.builder(IntextIpPortMap.class).child(IpPortMapping.class, new IpPortMappingKey(segmentId)).child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType));
+         InstanceIdentifier<IntextIpProtocolType> id = idBuilder.build();
+         Optional<IntextIpProtocolType> intextIpProtocolType = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
+         if (intextIpProtocolType.isPresent()) {
+               List<IpPortMap> ipPortMaps = intextIpProtocolType.get().getIpPortMap();
+               for (IpPortMap ipPortMap : ipPortMaps) {
+                    if (ipPortMap.getIpPortInternal().equals(internalIpPort)) {
+                       LOG.debug("NAPT Service : IpPortMap : {}", ipPortMap);
+                       SessionAddress externalIpPort = new SessionAddress(ipPortMap.getIpPortExternal().getIpAddress(),
+                                ipPortMap.getIpPortExternal().getPortNum());
+                       LOG.debug("NAPT Service : checkIpPortMap returning successfully externalIP {} and port {}",
+                               externalIpPort.getIpAddress(), externalIpPort.getPortNumber());
+                       return externalIpPort;
+                    }
+               }
+         }
+         // return null if not found
+         LOG.error("NAPT Service : no-entry in checkIpPortMap, returning NULL [should be OK] for segmentId {} and internalIPPort {}", segmentId, internalIpPort);
+         return null;
+     }
+
+     protected String checkIpMap(long segmentId, String internalIp) {
+
+         LOG.debug("NAPT Service : checkIpMap called with segmentId {} and internalIp {}", segmentId, internalIp);
+         String externalIp;
+         // check if ip-map node is there
+         InstanceIdentifierBuilder<IpMapping> idBuilder =
+                         InstanceIdentifier.builder(IntextIpMap.class).child(IpMapping.class, new IpMappingKey(segmentId));
+         InstanceIdentifier<IpMapping> id = idBuilder.build();
+         Optional<IpMapping> ipMapping = MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
+         if (ipMapping.isPresent()) {
+               List<IpMap> ipMaps = ipMapping.get().getIpMap();
+               for (IpMap ipMap : ipMaps) {
+                    if (ipMap.getInternalIp().equals(internalIp)) {
+                       LOG.debug("NAPT Service : IpMap : {}", ipMap);
+                       externalIp = ipMap.getExternalIp().toString();
+                       LOG.debug("NAPT Service : checkIpMap successfully returning externalIp {}", externalIp );
+                       return externalIp;
+                    } else if (ipMap.getInternalIp().contains("/")) { // subnet case
+                        SubnetUtils subnetUtils = new SubnetUtils(ipMap.getInternalIp());
+                        SubnetInfo subnetInfo = subnetUtils.getInfo();
+                        if (subnetInfo.isInRange(internalIp)) {
+                            LOG.debug("NAPT Service : internalIp {} found to be IpMap of internalIpSubnet {}", internalIp, ipMap.getInternalIp());
+                            externalIp = ipMap.getExternalIp().toString();
+                            LOG.debug("NAPT Service : checkIpMap successfully returning externalIp {}", externalIp );
+                            return externalIp;
+                        }
+                    }
+               }
+         }
+         // return null if not found
+         LOG.error("NAPT Service : checkIpMap failed, returning NULL for segmentId {} and internalIp {}", segmentId, internalIp);
+         return null;
+      }
+
+    protected void removeSnatIntIpPortDS(long segmentId, SessionAddress address,NAPTEntryEvent.Protocol protocol) {
+        LOG.trace("NAPT Service : removeSnatIntIpPortDS method called for IntIpport {} of router {} ",address,segmentId);
+        ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
+        List<Integer> portList = NatUtil.getInternalIpPortListInfo(broker,segmentId,address.getIpAddress(),protocolType);
+        if (portList == null || portList.isEmpty() || !portList.contains(address.getPortNumber())) {
+           LOG.debug("Internal IP {} for port {} entry not found in SnatIntIpPort DS",address.getIpAddress(),address.getPortNumber());
+           return;
+        }
+        LOG.trace("NAPT Service : PortList {} retrieved for InternalIp {} of router {}",portList,address.getIpAddress(),segmentId);
+        Integer port = address.getPortNumber();
+        portList.remove(port);
+
+        IntIpProtoTypeBuilder builder = new IntIpProtoTypeBuilder();
+        IntIpProtoType intIpProtocolType = builder.setKey(new IntIpProtoTypeKey(protocolType)).setPorts(portList).build();
+        try {
+            MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, NatUtil.buildSnatIntIpPortIdentifier(segmentId, address.getIpAddress(), protocolType), intIpProtocolType);
+        } catch (Exception ex) {
+            LOG.error("NAPT Service : Failed to write into snat-internal-ip-port-info with exception {}", ex.getMessage() );
+        }
+        LOG.debug("NAPT Service : Removing SnatIp {} Port {} of router {} from SNATIntIpport datastore : {}"
+                ,address.getIpAddress(),address.getPortNumber(),segmentId);
+    }
+
+    protected void removeFromSnatIpPortDS(long segmentId, String internalIp) {
+        InstanceIdentifier<IpPort> intIp = InstanceIdentifier.builder(SnatintIpPortMap.class).child
+                (IntipPortMap.class, new IntipPortMapKey(segmentId)).child(IpPort.class, new IpPortKey(internalIp)).build();
+        // remove from SnatIpPortDS
+        LOG.debug("NAPT Service : Removing SnatIpPort from datastore : {}", intIp);
+        MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, intIp);
+
+    }
+
+    protected void removeFromIpPortMapDS(long segmentId, String internalIpPort, NAPTEntryEvent.Protocol protocol) {
+         ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
+         InstanceIdentifierBuilder<IpPortMap> idBuilder = InstanceIdentifier.builder(IntextIpPortMap.class)
+                 .child(IpPortMapping.class, new IpPortMappingKey(segmentId)).child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType))
+                 .child(IpPortMap.class, new IpPortMapKey(internalIpPort));
+         InstanceIdentifier<IpPortMap> id = idBuilder.build();
+         // remove from ipportmap DS
+         LOG.debug("NAPT Service : Removing ipportmap from datastore : {}", id);
+         MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, id);
+    }
+
+     protected void removeFromIpMapDS(long segmentId, String internalIp) {
+         InstanceIdentifierBuilder<IpMap> idBuilder = InstanceIdentifier.builder(IntextIpMap.class)
+                 .child(IpMapping.class, new IpMappingKey(segmentId))
+                 .child(IpMap.class, new IpMapKey(internalIp));
+         InstanceIdentifier<IpMap> id = idBuilder.build();
+         // remove from ipmap DS
+         LOG.debug("NAPT Service : Removing ipmap from datastore : {}", id);
+         MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, id);
+     }
+
+    private void removeIpMappingForRouterID(long segmentId) {
+        InstanceIdentifierBuilder<IpMapping> idBuilder = InstanceIdentifier.builder(IntextIpMap.class)
+                .child(IpMapping.class, new IpMappingKey(segmentId));
+        InstanceIdentifier<IpMapping> id = idBuilder.build();
+        // remove from ipmap DS
+        LOG.debug("NAPT Service : Removing ipmap from datastore : {}", id);
+        MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, id);
+    }
+
+     protected void removePortFromPool(String internalIpPort, String externalIp) {
+         LOG.debug("NAPT Service : removePortFromPool method called");
+         ReleaseIdInput idInput = new ReleaseIdInputBuilder().
+                                        setPoolName(externalIp)
+                                        .setIdKey(internalIpPort).build();
+         try {
+             Future<RpcResult<Void>> result = idManager.releaseId(idInput);
+             RpcResult<Void> rpcResult = result.get();
+             if(!rpcResult.isSuccessful()) {
+                 LOG.error("NAPT Service : idmanager failed to remove port from pool {}", rpcResult.getErrors());
+             }
+             LOG.debug("NAPT Service : Removed port from pool for InternalIpPort {} with externalIp {}",internalIpPort,externalIp);
+         } catch (InterruptedException | ExecutionException e) {
+             LOG.error("NAPT Service : idmanager failed with Exception {} when removing entry in pool with key {}, ", e, internalIpPort);
+         }
+     }
+}
\ No newline at end of file
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptPacketInHandler.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptPacketInHandler.java
new file mode 100644 (file)
index 0000000..b663f50
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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 org.opendaylight.controller.liblldp.NetUtils;
+import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
+import org.opendaylight.vpnservice.mdsalutil.NWUtil;
+import org.opendaylight.vpnservice.mdsalutil.packet.Ethernet;
+import org.opendaylight.vpnservice.mdsalutil.packet.IPv4;
+import org.opendaylight.vpnservice.mdsalutil.packet.TCP;
+import org.opendaylight.vpnservice.mdsalutil.packet.UDP;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.util.HashSet;
+import com.google.common.primitives.Ints;
+
+public class NaptPacketInHandler implements PacketProcessingListener {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NaptPacketInHandler.class);
+    private final static HashSet<String> incomingPacketMap = new HashSet<>();
+    private final EventDispatcher naptEventdispatcher;
+
+    public NaptPacketInHandler(EventDispatcher eventDispatcher) {
+        this.naptEventdispatcher = eventDispatcher;
+    }
+
+    @Override
+    public void onPacketReceived(PacketReceived packetReceived) {
+        String internalIPAddress = "";
+        int portNumber = 0;
+        long routerId = 0L;
+        NAPTEntryEvent.Operation operation = NAPTEntryEvent.Operation.ADD;
+        NAPTEntryEvent.Protocol protocol;
+
+        Short tableId = packetReceived.getTableId().getValue();
+
+        if (LOG.isTraceEnabled()) {
+            LOG.trace("packet: {}, tableId {}", packetReceived, tableId);
+        }
+
+        if (tableId == NatConstants.OUTBOUND_NAPT_TABLE) {
+            LOG.debug("NAT Service : NAPTPacketInHandler Packet for Outbound NAPT Table");
+            byte[] inPayload = packetReceived.getPayload();
+            Ethernet ethPkt = new Ethernet();
+            if (inPayload != null) {
+                try {
+                    ethPkt.deserialize(inPayload, 0, inPayload.length * NetUtils.NumBitsInAByte);
+                } catch (Exception e) {
+                    LOG.warn("Failed to decode Packet", e);
+                    return;
+                }
+                if (ethPkt.getPayload() instanceof IPv4) {
+                    IPv4 ipPkt = (IPv4) ethPkt.getPayload();
+                    byte[] ipSrc = Ints.toByteArray(ipPkt.getSourceAddress());
+
+                    internalIPAddress = NatUtil.toStringIpAddress(ipSrc, LOG);
+                    LOG.trace("Retrieved internalIPAddress {}", internalIPAddress);
+                    if (ipPkt.getPayload() instanceof TCP) {
+                        TCP tcpPkt = (TCP) ipPkt.getPayload();
+                        portNumber = tcpPkt.getSourcePort();
+                        protocol = NAPTEntryEvent.Protocol.TCP;
+                        LOG.trace("Retrieved TCP portNumber {}", portNumber);
+                    } else if (ipPkt.getPayload() instanceof UDP) {
+                        UDP udpPkt = (UDP) ipPkt.getPayload();
+                        portNumber = udpPkt.getSourcePort();
+                        protocol = NAPTEntryEvent.Protocol.UDP;
+                        LOG.trace("Retrieved UDP portNumber {}", portNumber);
+                    } else {
+                        LOG.error("Incoming Packet is neither TCP or UDP packet");
+                        return;
+                    }
+                } else {
+                    LOG.error("Incoming Packet is not IPv4 packet");
+                    return;
+                }
+
+                if(internalIPAddress != null) {
+                    String sourceIPPortKey = internalIPAddress + ":" + portNumber;
+                    LOG.debug("NAT Service : sourceIPPortKey {} mapping maintained in the map", sourceIPPortKey);
+                    if (!incomingPacketMap.contains(sourceIPPortKey)) {
+                        incomingPacketMap.add(internalIPAddress + portNumber);
+
+                        BigInteger metadata = packetReceived.getMatch().getMetadata().getMetadata();
+                        routerId = (metadata.and(MetaDataUtil.METADATA_MASK_VRFID)).longValue();
+                        if( routerId <= 0) {
+                            LOG.error("Nat Service : Router ID is invalid");
+                            return;
+                        }
+                        //send to Event Queue
+                        NAPTEntryEvent naptEntryEvent = new NAPTEntryEvent(internalIPAddress,portNumber,routerId,
+                                operation,protocol);
+                        naptEventdispatcher.addNaptEvent(naptEntryEvent);
+                    } else {
+                        LOG.trace("Ignore the packet, already processed");
+                    }
+                }else {
+                    LOG.error("Nullpointer exception in retrieving internalIPAddress");
+                }
+            }
+        }else {
+            LOG.trace("Packet is not from the Outbound NAPT table");
+        }
+    }
+
+    public void removeIncomingPacketMap(String sourceIPPortKey) {
+        incomingPacketMap.remove(sourceIPPortKey);
+        LOG.debug("NAT Service : sourceIPPortKey {} mapping is removed from map", sourceIPPortKey);
+    }
+}
\ No newline at end of file
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptSwitchHA.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NaptSwitchHA.java
new file mode 100644 (file)
index 0000000..66fadc5
--- /dev/null
@@ -0,0 +1,661 @@
+/*
+ * 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 com.google.common.base.Optional;
+import org.opendaylight.bgpmanager.api.IBgpManager;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.vpnservice.mdsalutil.*;
+import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+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.action.types.rev131112.action.action.OutputActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+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.fib.rpc.rev160121.FibRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetTunnelInterfaceNameInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetTunnelInterfaceNameOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.*;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.Routers;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.RoutersKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMapping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMappingKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.IpPortMapping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.IpPortMappingKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal;
+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.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.VpnRpcService;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+public class NaptSwitchHA {
+    private static final Logger LOG = LoggerFactory.getLogger(NaptSwitchHA.class);
+    private final DataBroker dataBroker;
+    private IMdsalApiManager mdsalManager;
+    private ItmRpcService itmManager;
+    private OdlInterfaceRpcService interfaceManager;
+    private IdManagerService idManager;
+    private NAPTSwitchSelector naptSwitchSelector;
+    private ExternalRoutersListener externalRouterListener;
+    private IBgpManager bgpManager;
+    private VpnRpcService vpnService;
+    private FibRpcService fibService;
+
+    public NaptSwitchHA(DataBroker broker,NAPTSwitchSelector selector){
+        dataBroker = broker;
+        naptSwitchSelector = selector;
+    }
+
+    public void setItmManager(ItmRpcService itmManager) {
+        this.itmManager = itmManager;
+    }
+
+    public void setMdsalManager(IMdsalApiManager mdsalManager) {
+        this.mdsalManager = mdsalManager;
+    }
+
+    public void setInterfaceManager(OdlInterfaceRpcService interfaceManager) {
+        this.interfaceManager = interfaceManager;
+    }
+
+    public void setIdManager(IdManagerService idManager) {
+        this.idManager = idManager;
+    }
+
+    void setExternalRoutersListener(ExternalRoutersListener externalRoutersListener) {
+        this.externalRouterListener = externalRoutersListener;
+    }
+
+    public void setBgpManager(IBgpManager bgpManager) {
+        this.bgpManager = bgpManager;
+    }
+
+    public void setVpnService(VpnRpcService vpnService) {
+        this.vpnService = vpnService;
+    }
+
+    public void setFibService(FibRpcService fibService) {
+        this.fibService = fibService;
+    }
+
+    /* This method checks the switch that gone down is a NaptSwitch for a router.
+       If it is a NaptSwitch
+          1) selects new NAPT switch
+          2) installs nat flows in new NAPT switch
+          table 21(FIB)->26(PSNAT)->group(resubmit/napttunnel)->36(Terminating)->46(outbound)->47(resubmit)->21
+          3) modify the group and miss entry flow in other vSwitches pointing to newNaptSwitch
+          4) Remove nat flows in oldNaptSwitch
+     */
+    public void handleNaptSwitchDown(BigInteger dpnId){
+
+        LOG.debug("handleNaptSwitchDown method is called with dpnId {}",dpnId);
+        BigInteger naptSwitch;
+        try {
+            NaptSwitches naptSwitches = NatUtil.getNaptSwitch(dataBroker);
+            if (naptSwitches == null || naptSwitches.getRouterToNaptSwitch() == null || naptSwitches.getRouterToNaptSwitch().isEmpty()) {
+                LOG.debug("NaptSwitchDown: NaptSwitch is not allocated for none of the routers");
+                return;
+            }
+            for (RouterToNaptSwitch routerToNaptSwitch : naptSwitches.getRouterToNaptSwitch()) {
+                String routerName = routerToNaptSwitch.getRouterName();
+                naptSwitch = routerToNaptSwitch.getPrimarySwitchId();
+                boolean naptStatus = isNaptSwitchDown(routerName,dpnId,naptSwitch);
+                if (!naptStatus) {
+                    LOG.debug("NaptSwitchDown: Switch with DpnId {} is not naptSwitch for router {}",
+                            dpnId, routerName);
+                } else {
+                    removeSnatFlowsInOldNaptSwitch(routerName,naptSwitch);
+                    return;
+                }
+            }
+        } catch (Exception ex) {
+            LOG.error("Exception in handleNaptSwitchDown method {}",ex);
+        }
+    }
+
+    private void removeSnatFlowsInOldNaptSwitch(String routerName, BigInteger naptSwitch) {
+        //remove SNAT flows in old NAPT SWITCH
+        Long routerId = NatUtil.getVpnId(dataBroker, routerName);
+        if (routerId == NatConstants.INVALID_ID) {
+            LOG.error("Invalid routerId returned for routerName {}",routerName);
+            return;
+        }
+        BigInteger cookieSnatFlow = NatUtil.getCookieSnatFlow(routerId);
+
+        //Build and remove flows in outbound NAPT table
+        try {
+            FlowEntity outboundNaptFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.OUTBOUND_NAPT_TABLE, cookieSnatFlow);
+            mdsalManager.removeFlow(outboundNaptFlowEntity);
+            LOG.info("Removed all flows for router {} in the table {} for oldNaptswitch {}"
+                    ,routerName, NatConstants.OUTBOUND_NAPT_TABLE, naptSwitch);
+        } catch (Exception ex) {
+            LOG.info("Failed to remove all flows for router {} in the table {} for oldNaptswitch {}"
+                    ,routerName, NatConstants.OUTBOUND_NAPT_TABLE, naptSwitch);
+        }
+
+        //Build and remove flows in inbound NAPT table
+        try {
+            FlowEntity inboundNaptFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.INBOUND_NAPT_TABLE,
+                    cookieSnatFlow);
+            mdsalManager.removeFlow(inboundNaptFlowEntity);
+            LOG.info("Removed all flows for router {} in the table {} for oldNaptswitch {}"
+                    ,routerName, NatConstants.INBOUND_NAPT_TABLE, naptSwitch);
+        } catch (Exception ex) {
+            LOG.info("Failed to remove all flows for router {} in the table {} for oldNaptswitch {}"
+                    ,routerName, NatConstants.INBOUND_NAPT_TABLE, naptSwitch);
+        }
+
+        //Remove the Terminating Service table entry which forwards the packet to Outbound NAPT Table
+        String tsFlowRef = externalRouterListener.getFlowRefTs(naptSwitch, NatConstants.TERMINATING_SERVICE_TABLE, routerId);
+        FlowEntity tsNatFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.TERMINATING_SERVICE_TABLE, tsFlowRef);
+
+        LOG.info("Remove the flow in table {} for the active switch with the DPN ID {} and router ID {}"
+                ,NatConstants.TERMINATING_SERVICE_TABLE, naptSwitch, routerId);
+        mdsalManager.removeFlow(tsNatFlowEntity);
+
+        //Remove the Outbound flow entry which forwards the packet to Outbound NAPT Table
+        String outboundNatFlowRef = externalRouterListener.getFlowRefOutbound(naptSwitch, NatConstants.OUTBOUND_NAPT_TABLE, routerId);
+        FlowEntity outboundNatFlowEntity = NatUtil.buildFlowEntity(naptSwitch,
+                NatConstants.OUTBOUND_NAPT_TABLE, outboundNatFlowRef);
+        LOG.info("Remove the flow in the for the active switch with the DPN ID {} and router ID {}"
+                ,NatConstants.OUTBOUND_NAPT_TABLE, naptSwitch, routerId);
+        mdsalManager.removeFlow(outboundNatFlowEntity);
+
+        //Remove the NAPT_PFIB_TABLE(47) flow entry forwards the packet to Fib Table
+        String naptPFibflowRef = externalRouterListener.getFlowRefTs(naptSwitch, NatConstants.NAPT_PFIB_TABLE, routerId);
+        FlowEntity naptPFibFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.NAPT_PFIB_TABLE,naptPFibflowRef);
+        LOG.info("Remove the flow in the for the active switch with the DPN ID {} and router ID {}",
+                NatConstants.NAPT_PFIB_TABLE, naptSwitch, routerId);
+        mdsalManager.removeFlow(naptPFibFlowEntity);
+
+        //Remove Fib entries and 36-> 44
+        Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
+        if (networkId == null) {
+            LOG.debug("network is not associated to router {}", routerId);
+        }
+        Optional<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
+                NatUtil.buildRouterIdentifier(routerName));
+        if(routerData.isPresent()){
+            List<String> externalIps = routerData.get().getExternalIps();
+            if (externalIps != null) {
+                externalRouterListener.advToBgpAndRemoveFibAndTsFlows(naptSwitch, routerId, networkId, externalIps);
+                LOG.debug("Successfully removed fib entries in naptswitch {} for router {} with external IP {}", naptSwitch,
+                        routerId, externalIps);
+            } else {
+                LOG.debug("ExternalIps not found for router {} with networkId {}",routerName,networkId);
+            }
+        }
+    }
+
+    public boolean isNaptSwitchDown(String routerName, BigInteger dpnId , BigInteger naptSwitch) {
+        if (!naptSwitch.equals(dpnId)) {
+            LOG.debug("DpnId {} is not a naptSwitch {} for Router {}",dpnId, naptSwitch, routerName);
+            return false;
+        }
+        LOG.debug("NaptSwitch {} is down for Router {}", naptSwitch, routerName);
+        //elect a new NaptSwitch
+        naptSwitch = naptSwitchSelector.selectNewNAPTSwitch(routerName);
+        if (naptSwitch.equals("0")) {
+            LOG.info("No napt switch is elected since all the switches for router {} are down",routerName);
+            return true;
+        }
+        //checking elected switch health status
+        if (!getSwitchStatus(naptSwitch)) {
+            LOG.error("Newly elected Napt switch {} for router {} is down", naptSwitch, routerName);
+            return true;
+        }
+        LOG.debug("New NaptSwitch {} is up for Router {} and can proceed for flow installation",naptSwitch, routerName);
+        Long routerId = NatUtil.getVpnId(dataBroker, routerName);
+        if (routerId == NatConstants.INVALID_ID) {
+            LOG.error("Invalid routerId returned for routerName {}", routerName);
+            return true;
+        }
+        //update napt model for new napt switch
+        boolean naptUpdated = updateNaptSwitch(routerName, naptSwitch);
+        if (naptUpdated) {
+            //update group of naptswitch point to table36/ordinary switch point to naptswitchtunnelport
+            updateNaptSwitchBucketStatus(routerName, naptSwitch);
+        } else {
+            LOG.error("Failed to update naptSwitch model for newNaptSwitch {} for router {}",naptSwitch, routerName);
+        }
+        //36 -> 46 ..Install flow going to 46 from table36
+        externalRouterListener.installTerminatingServiceTblEntry(naptSwitch, routerName);
+
+        //Install default flows punting to controller in table 46(OutBoundNapt table)
+        externalRouterListener.installOutboundMissEntry(routerName, naptSwitch);
+
+        //Table 47 point to table 21 for inbound traffic
+        LOG.debug("installNaptPfibEntry for dpnId {} and routerId {}", naptSwitch, routerId);
+        externalRouterListener.installNaptPfibEntry(naptSwitch, routerId);
+
+        //Table 47 point to table 21 for outbound traffic
+        String vpnName = getVpnName(routerId);
+        if(vpnName != null) {
+            long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
+            if(vpnId > 0) {
+                LOG.debug("installNaptPfibEntry for dpnId {} and vpnId {}", naptSwitch, vpnId);
+                externalRouterListener.installNaptPfibEntry(naptSwitch, vpnId);
+            } else {
+                LOG.debug("Associated vpnId not found for router {}",routerId);
+            }
+        } else {
+            LOG.debug("Associated vpnName not found for router {}",routerId);
+        }
+
+        //Install Fib entries for ExternalIps & program 36 -> 44
+
+        Optional<IpMapping> ipMappingOptional = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
+                getIpMappingBuilder(routerId));
+        if (vpnName != null) {
+            if (ipMappingOptional.isPresent()) {
+                List<IpMap> ipMaps = ipMappingOptional.get().getIpMap();
+                for (IpMap ipMap : ipMaps) {
+                    String externalIp = ipMap.getExternalIp();
+                    LOG.debug("advToBgpAndInstallFibAndTsFlows for naptswitch {}, vpnName {} and externalIp {}",
+                            naptSwitch, vpnName, externalIp);
+                    externalRouterListener.advToBgpAndInstallFibAndTsFlows(naptSwitch, NatConstants.INBOUND_NAPT_TABLE,
+                            vpnName, routerId, externalIp, vpnService, fibService, bgpManager, dataBroker, LOG);
+                    LOG.debug("Successfully added fib entries in naptswitch {} for router {} with external IP {}", naptSwitch,
+                            routerId, externalIp);
+                }
+            }
+        } else {
+            LOG.debug("Vpn is not associated to the network of router {}",routerName);
+        }
+
+        boolean flowInstalledStatus = handleFlowsInNewNaptSwitch(routerId, dpnId, naptSwitch);
+        if (flowInstalledStatus) {
+            LOG.debug("Installed all activesession flows in newNaptSwitch {} for routerName {}", routerName);
+        } else {
+            LOG.error("Failed to install flows in newNaptSwitch {} for routerId {}", naptSwitch, routerId);
+        }
+        return true;
+    }
+
+    private InstanceIdentifier<IpMapping> getIpMappingBuilder(Long routerId) {
+        InstanceIdentifier<IpMapping> idBuilder = InstanceIdentifier.builder(IntextIpMap.class)
+                .child(IpMapping.class, new IpMappingKey(routerId)).build();
+        return idBuilder;
+    }
+
+    private String getVpnName(long routerId) {
+        Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
+        if(networkId == null) {
+            LOG.error("networkId is null for the router ID {}", routerId);
+        } else {
+            final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
+            if (vpnName != null) {
+                LOG.debug("retreived vpnname {} associated with ext nw {} in router {}",
+                        vpnName,networkId,routerId);
+                return vpnName;
+            } else {
+                LOG.error("No VPN associated with ext nw {} belonging to routerId {}",
+                        networkId, routerId);
+            }
+        }
+        return null;
+    }
+
+    public void updateNaptSwitchBucketStatus(String routerName, BigInteger naptSwitch) {
+        LOG.debug("updateNaptSwitchBucketStatus method is called");
+
+        List<BigInteger> dpnList = getDpnListForRouter(routerName);
+        for (BigInteger dpn : dpnList) {
+            if (dpn.equals(naptSwitch)) {
+                LOG.debug("Updating SNAT_TABLE missentry for DpnId {} which is naptSwitch for router {}",dpn,routerName);
+                List<BucketInfo> bucketInfoList = handleGroupInPrimarySwitch();
+                modifySnatGroupEntry(naptSwitch, bucketInfoList, routerName);
+            } else {
+                LOG.debug("Updating SNAT_TABLE missentry for DpnId {} which is not naptSwitch for router {}"
+                        , dpn, routerName);
+                List<BucketInfo> bucketInfoList = handleGroupInNeighborSwitches(dpn, routerName, naptSwitch);
+                if (bucketInfoList == null) {
+                    LOG.debug("bucketInfo is not populated for orinaryswitch {} whose naptSwitch {} with router {} ",
+                            dpn,routerName,naptSwitch);
+                    return;
+                }
+                modifySnatGroupEntry(naptSwitch, bucketInfoList, routerName);
+            }
+        }
+    }
+
+    private boolean handleFlowsInNewNaptSwitch(Long routerId,BigInteger oldNaptSwitch, BigInteger newNaptSwitch) {
+
+        LOG.debug("Proceeding to install flows in newNaptSwitch {} for routerId {}", routerId);
+        IpPortMapping ipPortMapping = getIpPortMapping(routerId);
+        if (ipPortMapping == null || ipPortMapping.getIntextIpProtocolType() == null || ipPortMapping.getIntextIpProtocolType().isEmpty()) {
+            LOG.debug("No Internal Ip Port mapping associated to router {}, no flows need to be installed in" +
+                    "newNaptSwitch ", routerId, newNaptSwitch);
+            return true;
+        }
+        //getvpnId
+        Long vpnId = null;
+        try {
+            vpnId = getVpnIdForRouter(routerId);
+        }catch (Exception ex) {
+            LOG.error("Failed to retreive vpnID for router {} : {}", routerId,ex);
+            return false;
+        }
+        for (IntextIpProtocolType protocolType : ipPortMapping.getIntextIpProtocolType()) {
+            if (protocolType.getIpPortMap() == null || protocolType.getIpPortMap().isEmpty()) {
+                LOG.debug("No {} session associated to router {}", protocolType.getProtocol(), routerId);
+                return true;
+            }
+            for (IpPortMap intIpPortMap : protocolType.getIpPortMap()) {
+                String internalIpAddress = intIpPortMap.getIpPortInternal().split(":")[0];
+                String intportnum = intIpPortMap.getIpPortInternal().split(":")[1];
+
+                //Get the external IP address and the port from the model
+                NAPTEntryEvent.Protocol proto = protocolType.getProtocol().toString().equals(ProtocolTypes.TCP.toString())
+                        ? NAPTEntryEvent.Protocol.TCP : NAPTEntryEvent.Protocol.UDP;
+                IpPortExternal ipPortExternal = NatUtil.getExternalIpPortMap(dataBroker, routerId,
+                        internalIpAddress, intportnum, proto);
+                if (ipPortExternal == null) {
+                    LOG.debug("External Ipport mapping is not found for internalIp {} with port {}", internalIpAddress, intportnum);
+                    continue;
+                }
+                String externalIpAddress = ipPortExternal.getIpAddress();
+                Integer extportNumber = ipPortExternal.getPortNum();
+                LOG.debug("ExternalIPport {}:{} mapping for internal ipport {}:{}",externalIpAddress,extportNumber,
+                        internalIpAddress,intportnum);
+
+                SessionAddress sourceAddress = new SessionAddress(internalIpAddress,Integer.valueOf(intportnum));
+                SessionAddress externalAddress = new SessionAddress(externalIpAddress,extportNumber);
+
+                //checking naptSwitch status before installing flows
+                if(getSwitchStatus(newNaptSwitch)) {
+                    //Install the flow in newNaptSwitch Outbound NAPT table.
+                    try {
+                        NaptEventHandler.buildAndInstallNatFlows(newNaptSwitch, NatConstants.OUTBOUND_NAPT_TABLE,
+                                vpnId,  routerId, sourceAddress, externalAddress, proto);
+                    } catch (Exception ex) {
+                        LOG.error("Failed to add flow in OUTBOUND_NAPT_TABLE for routerid {} dpnId {} ipport {}:{} proto {}" +
+                                "extIpport {}:{}", routerId, newNaptSwitch, internalIpAddress
+                                , intportnum, proto, externalAddress, extportNumber);
+                        return false;
+                    }
+                    LOG.debug("Succesfully installed a flow in SecondarySwitch {} Outbound NAPT table for router {} " +
+                            "ipport {}:{} proto {} extIpport {}:{}", newNaptSwitch,routerId, internalIpAddress
+                            , intportnum, proto, externalAddress, extportNumber);
+                    //Install the flow in newNaptSwitch Inbound NAPT table.
+                    try {
+                        NaptEventHandler.buildAndInstallNatFlows(newNaptSwitch, NatConstants.INBOUND_NAPT_TABLE,
+                                vpnId, routerId, externalAddress, sourceAddress, proto);
+                    } catch (Exception ex) {
+                        LOG.error("Failed to add flow in INBOUND_NAPT_TABLE for routerid {} dpnId {} extIpport{}:{} proto {} ipport {}:{}",
+                                routerId, newNaptSwitch, externalAddress, extportNumber,
+                                proto, internalIpAddress, intportnum);
+                        return false;
+                    }
+                    LOG.debug("Succesfully installed a flow in SecondarySwitch {} Inbound NAPT table for router {} " +
+                            "ipport {}:{} proto {} extIpport {}:{}", newNaptSwitch,routerId, internalIpAddress
+                            , intportnum, proto, externalAddress, extportNumber);
+
+                } else {
+                    LOG.error("NewNaptSwitch {} gone down while installing flows from oldNaptswitch {}",
+                            newNaptSwitch,oldNaptSwitch);
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private Long getVpnIdForRouter(Long routerId) {
+        try {
+            //getvpnId
+            Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
+            if (networkId == null) {
+                LOG.debug("network is not associated to router {}", routerId);
+            } else {
+                Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
+                if (vpnUuid == null) {
+                    LOG.debug("vpn is not associated for network {} in router {}", networkId, routerId);
+                } else {
+                    Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
+                    if (vpnId != null) {
+                        LOG.debug("retrieved vpnId {} for router {}",vpnId,routerId);
+                        return vpnId;
+                    } else {
+                        LOG.debug("retrieved invalid vpn Id");
+                    }
+                }
+            }
+        } catch (Exception ex){
+            LOG.debug("Exception while retreiving vpnId for router {} - {}", routerId, ex);
+        }
+        return  null;
+    }
+
+    private List<BigInteger> getDpnListForRouter(String routerName) {
+        List<BigInteger> dpnList = new ArrayList<BigInteger>();
+        List<VpnToDpnList> vpnDpnList = NatUtil.getVpnToDpnList(dataBroker, routerName);
+        for (VpnToDpnList vpnToDpn : vpnDpnList) {
+            dpnList.add(vpnToDpn.getDpnId());
+        }
+        return dpnList;
+    }
+
+    public boolean getSwitchStatus(BigInteger switchId){
+        NodeId nodeId = new NodeId("openflow:" + switchId);
+        LOG.debug("Querying switch with dpnId {} is up/down", nodeId);
+        InstanceIdentifier<Node> nodeInstanceId = InstanceIdentifier.builder(Nodes.class)
+                .child(Node.class, new NodeKey(nodeId)).build();
+        Optional<Node> nodeOptional = NatUtil.read(dataBroker,LogicalDatastoreType.OPERATIONAL,nodeInstanceId);
+        if (nodeOptional.isPresent()) {
+            LOG.debug("Switch {} is up", nodeId);
+            return true;
+        }
+        LOG.debug("Switch {} is down", nodeId);
+        return false;
+    }
+
+    public List<BucketInfo> handleGroupInPrimarySwitch() {
+        List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
+        List<ActionInfo> listActionInfoPrimary = new ArrayList<ActionInfo>();
+        listActionInfoPrimary.add(new ActionInfo(ActionType.nx_resubmit,
+                new String[]{String.valueOf(NatConstants.TERMINATING_SERVICE_TABLE)}));
+        BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
+        listBucketInfo.add(bucketPrimary);
+        return listBucketInfo;
+    }
+
+    public List<BucketInfo> handleGroupInNeighborSwitches(BigInteger dpnId, String routerName, BigInteger naptSwitch) {
+        List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
+        String ifNamePrimary;
+        Long routerId = NatUtil.getVpnId(dataBroker, routerName);
+        if (routerId == NatConstants.INVALID_ID) {
+            LOG.error("Invalid routerId returned for routerName {}",routerName);
+            return listBucketInfo;
+        }
+        ifNamePrimary = getTunnelInterfaceName(dpnId, naptSwitch);
+        if (ifNamePrimary != null) {
+            LOG.debug("TunnelInterface {} between ordinary switch {} and naptSwitch {}",ifNamePrimary,dpnId,naptSwitch);
+            List<ActionInfo> listActionInfoPrimary = getEgressActionsForInterface(ifNamePrimary, routerId);
+            BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
+            listBucketInfo.add(bucketPrimary);
+        } else {
+            LOG.debug("No TunnelInterface between ordinary switch {} and naptSwitch {}",dpnId,naptSwitch);
+        }
+        return listBucketInfo;
+    }
+
+    protected void installSnatGroupEntry(BigInteger dpnId, List<BucketInfo> bucketInfo, String routerName) {
+        GroupEntity groupEntity = null;
+        try {
+            long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
+            LOG.debug("install SnatMissEntry for groupId {} for dpnId {} for router {}", groupId, dpnId,routerName);
+            groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName,
+                    GroupTypes.GroupAll, bucketInfo);
+            mdsalManager.installGroup(groupEntity);
+            LOG.debug("installed the SNAT to NAPT GroupEntity:{}", groupEntity);
+        } catch (Exception ex) {
+            LOG.error("Failed to install group for groupEntity {} : {}",groupEntity,ex);
+        }
+    }
+
+    private void modifySnatGroupEntry(BigInteger dpnId, List<BucketInfo> bucketInfo, String routerName) {
+        installSnatGroupEntry(dpnId,bucketInfo,routerName);
+        LOG.debug("modified SnatMissEntry for dpnId {} of router {}",dpnId,routerName);
+    }
+
+    protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
+        try {
+            Future<RpcResult<GetTunnelInterfaceNameOutput>> result = itmManager.getTunnelInterfaceName(
+                    new GetTunnelInterfaceNameInputBuilder().setSourceDpid(srcDpId).setDestinationDpid(dstDpId).build());
+            RpcResult<GetTunnelInterfaceNameOutput> rpcResult = result.get();
+            if(!rpcResult.isSuccessful()) {
+                LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
+            } else {
+                return rpcResult.getResult().getInterfaceName();
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and  {} : {}",
+                    srcDpId, dstDpId, e);
+        }
+
+        return null;
+    }
+
+    protected List<ActionInfo> getEgressActionsForInterface(String ifName, long routerId) {
+        LOG.debug("getEgressActionsForInterface called for interface {}", ifName);
+        List<ActionInfo> listActionInfo = new ArrayList<ActionInfo>();
+        try {
+            Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
+                    interfaceManager.getEgressActionsForInterface(
+                            new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).setTunnelKey(routerId).build());
+            RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
+            if(!rpcResult.isSuccessful()) {
+                LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}"
+                        , ifName, rpcResult.getErrors());
+            } else {
+                List<Action> actions =
+                        rpcResult.getResult().getAction();
+                for (Action action : actions) {
+                    org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action actionClass = action.getAction();
+                    if (actionClass instanceof OutputActionCase) {
+                        listActionInfo.add(new ActionInfo(ActionType.output,
+                                new String[] {((OutputActionCase)actionClass).getOutputAction()
+                                        .getOutputNodeConnector().getValue()}));
+                    } else if (actionClass instanceof PushVlanActionCase) {
+                        listActionInfo.add(new ActionInfo(ActionType.push_vlan, new String[] {}));
+                    } else if (actionClass instanceof SetFieldCase) {
+                        if (((SetFieldCase)actionClass).getSetField().getVlanMatch() != null) {
+                            int vlanVid = ((SetFieldCase)actionClass).getSetField().getVlanMatch()
+                                    .getVlanId().getVlanId().getValue();
+                            listActionInfo.add(new ActionInfo(ActionType.set_field_vlan_vid,
+                                    new String[] { Long.toString(vlanVid) }));
+                        }
+                    }
+                }
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.warn("Exception when egress actions for interface {}", ifName, e);
+        }
+        return listActionInfo;
+    }
+
+    private IpPortMapping getIpPortMapping(Long routerId) {
+        Optional<IpPortMapping> ipPortMapData = NatUtil.read(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
+                buildIpToPortMapIdentifier(routerId));
+        if (ipPortMapData.isPresent()) {
+            return ipPortMapData.get();
+        }
+        return null;
+    }
+
+    public boolean updateNaptSwitch(String routerName, BigInteger naptSwitchId) {
+        RouterToNaptSwitch naptSwitch = new RouterToNaptSwitchBuilder().setKey(new RouterToNaptSwitchKey(routerName))
+                .setPrimarySwitchId(naptSwitchId).build();
+        try {
+            MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
+                    NatUtil.buildNaptSwitchRouterIdentifier(routerName), naptSwitch);
+        } catch (Exception ex) {
+            LOG.error("Failed to write naptSwitch {} for router {} in ds",
+                    naptSwitchId,routerName);
+            return false;
+        }
+        LOG.debug("Successfully updated naptSwitch {} for router {} in ds",
+                naptSwitchId,routerName);
+        return true;
+    }
+
+    private InstanceIdentifier<IpPortMapping> buildIpToPortMapIdentifier(Long routerId) {
+        InstanceIdentifier<IpPortMapping> ipPortMapId = InstanceIdentifier.builder(IntextIpPortMap.class).child
+                (IpPortMapping.class, new IpPortMappingKey(routerId)).build();
+        return ipPortMapId;
+    }
+
+    public FlowEntity buildSnatFlowEntity(BigInteger dpId, String routerName, long groupId, int addordel) {
+
+        FlowEntity flowEntity = null;
+        long routerId = NatUtil.getVpnId(dataBroker, routerName);
+        if (routerId == NatConstants.INVALID_ID) {
+            LOG.error("Invalid routerId returned for routerName {}",routerName);
+            return flowEntity;
+        }
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[]{ 0x0800L }));
+        matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+                BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+        String flowRef = getFlowRefSnat(dpId, NatConstants.PSNAT_TABLE, routerName);
+
+        if (addordel == NatConstants.ADD_FLOW) {
+            List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+            List<ActionInfo> actionsInfo = new ArrayList<ActionInfo>();
+
+            ActionInfo actionSetField = new ActionInfo(ActionType.set_field_tunnel_id, new BigInteger[] {
+                    BigInteger.valueOf(routerId)}) ;
+            actionsInfo.add(actionSetField);
+            LOG.debug("Setting the tunnel to the list of action infos {}", actionsInfo);
+            actionsInfo.add(new ActionInfo(ActionType.group, new String[] {String.valueOf(groupId)}));
+            instructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfo));
+
+            flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
+                    NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                    NatConstants.COOKIE_SNAT_TABLE, matches, instructions);
+        } else {
+            flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
+                    NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                    NatConstants.COOKIE_SNAT_TABLE, matches, null);
+        }
+        return flowEntity;
+    }
+
+    private String getFlowRefSnat(BigInteger dpnId, short tableId, String routerID) {
+        return new StringBuilder().append(NatConstants.SNAT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR).
+                append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString();
+    }
+}
\ No newline at end of file
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatConstants.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatConstants.java
new file mode 100644 (file)
index 0000000..bf4a8e2
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+
+public class NatConstants {
+    public static final short INBOUND_NAPT_TABLE = 44;
+    public static final short OUTBOUND_NAPT_TABLE = 46;
+    public static final short NAPT_PFIB_TABLE = 47;
+    public static final short TERMINATING_SERVICE_TABLE = 36;
+    public static final short DEFAULT_NAPT_FLOW_PRIORITY = 10;
+    public static final String NAPT_FLOW_NAME = "SNAT";
+    public static BigInteger COOKIE_NAPT_BASE = new BigInteger("8000000", 16);
+    public static final String NAPT_FLOWID_PREFIX = "SNAT.";
+    public static final String FLOWID_SEPARATOR = ".";
+    public static final int DEFAULT_NAPT_IDLE_TIMEOUT = 300;
+    public static int EVENT_QUEUE_LENGTH = 1000000;
+    public static final short PDNAT_TABLE = 25;
+    public static final short DNAT_TABLE = 27;
+    public static final short SNAT_TABLE = 28;
+    public static final short PSNAT_TABLE = 26;
+    public static final short L3_FIB_TABLE = 21;
+    public static final String FLOWID_PREFIX = "L3.";
+    public static final int DEFAULT_DNAT_FLOW_PRIORITY = 10;
+    public static final BigInteger COOKIE_DNAT_TABLE = new BigInteger("8000004", 16);
+    public static final long INVALID_ID = -1;
+    public static final BigInteger COOKIE_OUTBOUND_NAPT_TABLE = new BigInteger("8000008", 16);
+    public static final short DEFAULT_SNAT_FLOW_PRIORITY = 10;
+    public static final short DEFAULT_PSNAT_FLOW_PRIORITY = 5;
+    public static final String SNAT_FLOW_NAME = "SNAT";
+    public static final String SNAT_FLOWID_PREFIX = "SNAT.";
+    public static final BigInteger COOKIE_SNAT_TABLE = new BigInteger("8000006", 16);
+    public static final String SNAT_IDPOOL_NAME = "snatGroupIdPool";
+    public static final long SNAT_ID_LOW_VALUE = 200000L;
+    public static final long SNAT_ID_HIGH_VALUE = 225000L;
+    public static final int DEFAULT_TS_FLOW_PRIORITY = 10;
+    public static final BigInteger COOKIE_TS_TABLE = new BigInteger("8000002", 16);
+    public static final short DEFAULT_PREFIX = 32;
+
+    // Flow Actions
+    public static final int ADD_FLOW = 0;
+    public static final int DEL_FLOW = 1;
+
+}
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatNodeEventListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatNodeEventListener.java
new file mode 100644 (file)
index 0000000..79c8f15
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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 org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+
+public class NatNodeEventListener extends AbstractDataChangeListener<Node> implements AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(NatNodeEventListener.class);
+    private ListenerRegistration<DataChangeListener> listenerRegistration;
+    private NaptSwitchHA naptSwitchHA;
+
+    public NatNodeEventListener(final DataBroker db,final  NaptSwitchHA napt) {
+        super(Node.class);
+        naptSwitchHA = napt;
+        registerListener(db);
+    }
+
+    private void registerListener(final DataBroker db) {
+        try {
+            listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
+                    getWildCardPath(), NatNodeEventListener.this, AsyncDataBroker.DataChangeScope.SUBTREE);
+        } catch (final Exception e) {
+            LOG.error("NatNodeEventListener: DataChange listener registration fail!", e);
+            throw new IllegalStateException("NatNodeEventListener: registration Listener failed.", e);
+        }
+    }
+
+    private InstanceIdentifier<Node> getWildCardPath() {
+        return InstanceIdentifier.create(Nodes.class).child(Node.class);
+    }
+
+    @Override
+    protected void remove(InstanceIdentifier<Node> identifier, Node del) {
+        LOG.debug("NatNodeEventListener: Node removed received");
+        NodeId nodeId = del.getId();
+        String[] node =  nodeId.getValue().split(":");
+        if(node.length < 2) {
+            LOG.warn("Unexpected nodeId {}", nodeId.getValue());
+            return;
+        }
+        BigInteger dpnId = new BigInteger(node[1]);
+        LOG.debug("NodeId removed is {}",dpnId);
+        naptSwitchHA.handleNaptSwitchDown(dpnId);
+    }
+
+    @Override
+    protected void update(InstanceIdentifier<Node> identifier, Node original, Node update) {
+        LOG.trace("NatNodeEventListener: Node update received");
+    }
+
+    @Override
+    protected void add(InstanceIdentifier<Node> identifier, Node add) {
+        LOG.debug("NatNodeEventListener: Node added received");
+        NodeId nodeId = add.getId();
+        String[] node =  nodeId.getValue().split(":");
+        if(node.length < 2) {
+            LOG.warn("Unexpected nodeId {}", nodeId.getValue());
+            return;
+        }
+        BigInteger dpnId = new BigInteger(node[1]);
+        LOG.debug("NodeId added is {}",dpnId);
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (listenerRegistration != null) {
+            try {
+                listenerRegistration.close();
+            } catch (final Exception e) {
+                LOG.error("Error when cleaning up DataChangeListener.", e);
+            }
+            listenerRegistration = null;
+        }
+        LOG.info("NatNodeEventListener Closed");
+    }
+}
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatServiceProvider.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatServiceProvider.java
new file mode 100644 (file)
index 0000000..1af2237
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * 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 org.opendaylight.bgpmanager.api.IBgpManager;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
+import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.FibRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.NeutronvpnService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.VpnRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+public class NatServiceProvider implements BindingAwareProvider, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(NatServiceProvider.class);
+    private IMdsalApiManager mdsalManager;
+    private RpcProviderRegistry rpcProviderRegistry;
+    private OdlInterfaceRpcService interfaceManager;
+    private NotificationService notificationService;
+    private ItmRpcService itmManager;
+    private FloatingIPListener floatingIpListener;
+    private ExternalNetworkListener extNwListener;
+    private NaptManager naptManager;
+    private NaptEventHandler naptEventHandler;
+    private BlockingQueue<NAPTEntryEvent> naptEventQueue;
+    private ExternalNetworksChangeListener externalNetworksChangeListener;
+    private ExternalRoutersListener externalRouterListener;
+    private NaptPacketInHandler naptPacketInHandler;
+    private EventDispatcher eventDispatcher;
+    private IBgpManager bgpManager;
+    private NaptFlowRemovedEventHandler naptFlowRemovedEventHandler;
+    private InterfaceStateEventListener interfaceStateEventListener;
+    private NatNodeEventListener natNodeEventListener;
+    private NAPTSwitchSelector naptSwitchSelector;
+    private RouterPortsListener routerPortsListener;
+
+    public NatServiceProvider(RpcProviderRegistry rpcProviderRegistry) {
+        this.rpcProviderRegistry = rpcProviderRegistry;
+    }
+
+    public void setNotificationService(NotificationService notificationService) {
+        this.notificationService = notificationService;
+    }
+
+    public void setMdsalManager(IMdsalApiManager mdsalManager) {
+        this.mdsalManager = mdsalManager;
+    }
+
+    public void setBgpManager(IBgpManager bgpManager) {
+        LOG.debug("BGP Manager reference initialized");
+        this.bgpManager = bgpManager;
+    }
+
+    @Override
+    public void close() throws Exception {
+        floatingIpListener.close();
+        extNwListener.close();
+        externalNetworksChangeListener.close();
+        externalRouterListener.close();
+        routerPortsListener.close();
+    }
+
+    @Override
+    public void onSessionInitiated(ProviderContext session) {
+        LOG.info("NAT Manager Provider Session Initiated");
+        try {
+            //Get the DataBroker, PacketProcessingService, IdManagerService and the OdlInterfaceRpcService instances
+            final  DataBroker dataBroker = session.getSALService(DataBroker.class);
+            PacketProcessingService pktProcessingService = session.getRpcService(PacketProcessingService.class);
+            IdManagerService idManager = rpcProviderRegistry.getRpcService(IdManagerService.class);
+            OdlInterfaceRpcService interfaceService = rpcProviderRegistry.getRpcService(OdlInterfaceRpcService.class);
+            NeutronvpnService neutronvpnService = rpcProviderRegistry.getRpcService(NeutronvpnService.class);
+            itmManager = rpcProviderRegistry.getRpcService(ItmRpcService.class);
+
+            //Instantiate FloatingIPListener and set the MdsalManager and OdlInterfaceRpcService in it.
+            floatingIpListener = new FloatingIPListener(dataBroker);
+            floatingIpListener.setInterfaceManager(interfaceService);
+            floatingIpListener.setMdsalManager(mdsalManager);
+
+            //Instantiate ExternalNetworkListener and set the MdsalManager in it.
+            extNwListener = new ExternalNetworkListener(dataBroker);
+            extNwListener.setMdsalManager(mdsalManager);
+
+            //Instantiate NaptManager and set the IdManagerService in it.
+            naptManager = new NaptManager(dataBroker);
+            naptManager.setIdManager(idManager);
+
+            //Instantiate NaptEventHandler and start it as a Thread.
+            naptEventHandler = new NaptEventHandler(dataBroker);
+            naptEventHandler.setMdsalManager(mdsalManager);
+            naptEventHandler.setNaptManager(naptManager);
+            naptEventQueue = new ArrayBlockingQueue<>(NatConstants.EVENT_QUEUE_LENGTH);
+            eventDispatcher = new EventDispatcher(naptEventQueue, naptEventHandler);
+            new Thread(eventDispatcher).start();
+
+            //Instantiate NaptPacketInHandler and register it in the notification service.
+            naptPacketInHandler = new NaptPacketInHandler(eventDispatcher);
+            notificationService.registerNotificationListener(naptPacketInHandler);
+
+            //Floating ip Handler
+            VpnRpcService vpnService = rpcProviderRegistry.getRpcService(VpnRpcService.class);
+            FibRpcService fibService = rpcProviderRegistry.getRpcService(FibRpcService.class);
+            VpnFloatingIpHandler handler = new VpnFloatingIpHandler(vpnService, bgpManager, fibService);
+            handler.setBroker(dataBroker);
+            handler.setMdsalManager(mdsalManager);
+            handler.setListener(floatingIpListener);
+            floatingIpListener.setFloatingIpHandler(handler);
+
+            //Instantiate NaptSwitchSelector and set the dataBroker in it.
+            naptSwitchSelector = new NAPTSwitchSelector( dataBroker );
+
+            //Instantiate ExternalRouterListener and set the dataBroker in it.
+            externalRouterListener = new ExternalRoutersListener( dataBroker );
+            externalRouterListener.registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker );
+            externalRouterListener.setMdsalManager(mdsalManager);
+            externalRouterListener.setItmManager(itmManager);
+            externalRouterListener.setInterfaceManager(interfaceService);
+            externalRouterListener.setIdManager(idManager);
+            externalRouterListener.setNaptManager(naptManager);
+            externalRouterListener.setBgpManager(bgpManager);
+            externalRouterListener.setFibService(fibService);
+            externalRouterListener.setVpnService(vpnService);
+            externalRouterListener.setNaptSwitchSelector(naptSwitchSelector);
+
+            //Instantiate ExternalNetworksChangeListener and set the dataBroker in it.
+            externalNetworksChangeListener = new ExternalNetworksChangeListener( dataBroker );
+            externalNetworksChangeListener.registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
+            externalNetworksChangeListener.setMdsalManager(mdsalManager);
+            externalNetworksChangeListener.setInterfaceManager(interfaceService);
+            externalNetworksChangeListener.setFloatingIpListener(floatingIpListener);
+            externalNetworksChangeListener.setBgpManager(bgpManager);
+            externalNetworksChangeListener.setFibService(fibService);
+            externalNetworksChangeListener.setVpnService(vpnService);
+            externalNetworksChangeListener.setExternalRoutersListener(externalRouterListener);
+            externalNetworksChangeListener.setNaptManager(naptManager);
+            externalNetworksChangeListener.setExternalRoutersListener(externalRouterListener);
+
+            //Instantiate NaptFlowRemovedHandler and register it in the notification service.
+            naptFlowRemovedEventHandler = new NaptFlowRemovedEventHandler(eventDispatcher, dataBroker, naptPacketInHandler, mdsalManager, naptManager);
+            notificationService.registerNotificationListener(naptFlowRemovedEventHandler);
+
+            //Instantiate interfaceStateEventListener and set the MdsalManager in it.
+            interfaceStateEventListener = new InterfaceStateEventListener(dataBroker);
+            interfaceStateEventListener.setMdsalManager(mdsalManager);
+            interfaceStateEventListener.setFloatingIpListener(floatingIpListener);
+            interfaceStateEventListener.setNeutronVpnService(neutronvpnService);
+            interfaceStateEventListener.setNaptManager(naptManager);
+
+            SNATDefaultRouteProgrammer defaultRouteProgrammer = new SNATDefaultRouteProgrammer(mdsalManager);
+            DpnInVpnListener dpnInVpnListener = new DpnInVpnListener(dataBroker);
+            dpnInVpnListener.setDefaultProgrammer(defaultRouteProgrammer);
+            notificationService.registerNotificationListener(dpnInVpnListener);
+
+            externalRouterListener.setDefaultProgrammer(defaultRouteProgrammer);
+
+            NaptSwitchHA naptSwitchHA = new NaptSwitchHA(dataBroker,naptSwitchSelector);
+            naptSwitchHA.setIdManager(idManager);
+            naptSwitchHA.setInterfaceManager(interfaceService);
+            naptSwitchHA.setMdsalManager(mdsalManager);
+            naptSwitchHA.setItmManager(itmManager);
+            naptSwitchHA.setBgpManager(bgpManager);
+            naptSwitchHA.setFibService(fibService);
+            naptSwitchHA.setVpnService(vpnService);
+            naptSwitchHA.setExternalRoutersListener(externalRouterListener);
+
+            natNodeEventListener = new NatNodeEventListener(dataBroker,naptSwitchHA);
+
+            dpnInVpnListener.setNaptSwitchHA(naptSwitchHA);
+            dpnInVpnListener.setMdsalManager(mdsalManager);
+            dpnInVpnListener.setIdManager(idManager);
+
+            routerPortsListener = new RouterPortsListener(dataBroker);
+        } catch (Exception e) {
+            LOG.error("Error initializing NAT Manager service", e);
+        }
+    }
+}
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatUtil.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/NatUtil.java
new file mode 100644 (file)
index 0000000..166bb72
--- /dev/null
@@ -0,0 +1,700 @@
+/*
+ * 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.List;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.vpnservice.mdsalutil.NwConstants;
+import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
+import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
+import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceOpData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceToVpnId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.*;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.Routers;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.RoutersKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolTypeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMapKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.router.id.name.RouterIds;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.router.id.name.RouterIdsKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.IntipPortMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.IntipPortMapKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPort;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPortKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoTypeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.FloatingIpInfo;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.VpnMaps;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.vpnmaps.VpnMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.vpnmaps.VpnMapKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+
+import com.google.common.base.Optional;
+import org.opendaylight.bgpmanager.api.IBgpManager;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.Networks;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.NetworksKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPorts;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPortsKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.Ports;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.PortsKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ExternalNetworks;
+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.RouterToNaptSwitchKey;
+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.itm.op.rev150701.DpnEndpoints;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.op.rev150701.dpn.endpoints.DPNTEPsInfo;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.op.rev150701.dpn.endpoints.DPNTEPsInfoKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.op.rev150701.dpn.endpoints.dpn.teps.info.TunnelEndPoints;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.ports.IpMapping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.ports.IpMappingKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.IntextIpPortMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.IpPortMapping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.IpPortMappingKey;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+
+
+public class NatUtil {
+
+    private static String OF_URI_SEPARATOR = ":";
+    private static final Logger LOG = LoggerFactory.getLogger(NatUtil.class);
+
+    /*
+        getCookieSnatFlow() computes and returns a unique cookie value for the NAT flows using the router ID as the reference value.
+     */
+    public static BigInteger getCookieSnatFlow(long routerId) {
+        return NatConstants.COOKIE_NAPT_BASE.add(new BigInteger("0110000", 16)).add(
+                BigInteger.valueOf(routerId));
+    }
+
+    /*
+        getCookieNaptFlow() computes and returns a unique cookie value for the NAPT flows using the router ID as the reference value.
+    */
+    public static BigInteger getCookieNaptFlow(long routerId) {
+        return NatConstants.COOKIE_NAPT_BASE.add(new BigInteger("0111000", 16)).add(
+                BigInteger.valueOf(routerId));
+    }
+
+    /*
+        getVpnId() returns the VPN ID from the VPN name
+     */
+    public static long getVpnId(DataBroker broker, String vpnName) {
+
+        InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> id
+                = getVpnInstanceToVpnIdIdentifier(vpnName);
+        Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> vpnInstance
+                = read(broker, LogicalDatastoreType.CONFIGURATION, id);
+
+        long vpnId = NatConstants.INVALID_ID;
+        if(vpnInstance.isPresent()) {
+            vpnId = vpnInstance.get().getVpnId();
+        }
+        return vpnId;
+    }
+       
+    static InstanceIdentifier<RouterPorts> getRouterPortsId(String routerId) {
+        return InstanceIdentifier.builder(FloatingIpInfo.class).child(RouterPorts.class, new RouterPortsKey(routerId)).build();
+    }
+    
+    static InstanceIdentifier<Ports> getPortsIdentifier(String routerId, String portName) {
+        return InstanceIdentifier.builder(FloatingIpInfo.class).child(RouterPorts.class, new RouterPortsKey(routerId))
+                                                               .child(Ports.class, new PortsKey(portName)).build();
+    }
+
+    static InstanceIdentifier<IpMapping> getIpMappingIdentifier(String routerId, String portName, String internalIp) {
+        return InstanceIdentifier.builder(FloatingIpInfo.class).child(RouterPorts.class, new RouterPortsKey(routerId))
+                                                               .child(Ports.class, new PortsKey(portName))
+                                                               .child(IpMapping.class, new IpMappingKey(internalIp)).build();
+    }
+
+    /*
+        getVpnInstanceToVpnIdIdentifier() returns the VPN instance from the below model using the VPN name as the key.
+            list vpn-instance {
+                key "vpn-instance-name"
+                leaf vpn-instance-name {
+                    type string;
+                }
+                leaf vpn-id {
+                    type uint32;
+                }
+                leaf vrf-id {
+                    type string;
+                }
+            }
+    */
+
+    static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance>
+    getVpnInstanceToVpnIdIdentifier(String vpnName) {
+        return InstanceIdentifier.builder(VpnInstanceToVpnId.class)
+                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance.class,
+                        new org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstanceKey(vpnName)).build();
+    }
+
+     /*
+        getFlowRef() returns a string identfier for the SNAT flows using the router ID as the reference.
+     */
+    public static String getFlowRef(BigInteger dpnId, short tableId, String routerID) {
+        return new StringBuffer().append(NatConstants.NAPT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR).
+                append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString();
+    }
+
+    public static String getNaptFlowRef(BigInteger dpnId, short tableId, String routerID, String ip, int port) {
+        return new StringBuffer().append(NatConstants.NAPT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR).
+                append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).append(NatConstants.FLOWID_SEPARATOR).append(ip).
+                append(NatConstants.FLOWID_SEPARATOR).append(port).toString();
+    }
+
+    /*
+        getNetworkIdFromRouterId() returns the network-id from the below model using the router-id as the key
+               container ext-routers {
+                   list routers {
+                       key router-name;
+                       leaf router-name { type string; }
+                       leaf network-id { type yang:uuid; }
+                       leaf enable-snat { type boolean; }
+                       leaf-list external-ips {
+                            type string; //format - ipaddress\prefixlength
+                       }
+                       leaf-list subnet-ids { type yang:uuid; }
+                   }
+               }
+
+    */
+    static Uuid getNetworkIdFromRouterId(DataBroker broker, long routerId) {
+        String routerName = getRouterName(broker, routerId);
+        InstanceIdentifier id = buildRouterIdentifier(routerName);
+        Optional<Routers> routerData = read(broker, LogicalDatastoreType.CONFIGURATION, id);
+        if (routerData.isPresent()) {
+            return routerData.get().getNetworkId();
+        }
+        return null;
+    }
+
+    static InstanceIdentifier<Routers> buildRouterIdentifier(String routerId) {
+        InstanceIdentifier<Routers> routerInstanceIndentifier = InstanceIdentifier.builder(ExtRouters.class).child
+                (Routers.class, new RoutersKey(routerId)).build();
+        return routerInstanceIndentifier;
+    }
+    /*
+     * getEnableSnatFromRouterId() returns IsSnatEnabled true is routerID is present in external n/w otherwise returns false
+     */
+    static boolean isSnatEnabledForRouterId(DataBroker broker, String routerId){
+        InstanceIdentifier id = buildRouterIdentifier(routerId);
+        Optional<Routers> routerData = read(broker, LogicalDatastoreType.CONFIGURATION, id);
+        if (routerData.isPresent()) {
+            return routerData.get().isEnableSnat();
+        }
+        return false;
+    }
+    /*
+        getVpnIdfromNetworkId() returns the vpnid from the below model using the network ID as the key.
+            container external-networks {
+                list networks  {
+                    key id;
+                    leaf id {
+                        type yang:uuid;
+                    }
+                    leaf vpnid { type yang:uuid; }
+                    leaf-list router-ids { type yang:uuid; }
+                    leaf-list subnet-ids{ type yang:uuid; }
+                }
+            }
+    */
+    public static Uuid getVpnIdfromNetworkId(DataBroker broker, Uuid networkId) {
+        InstanceIdentifier id = buildNetworkIdentifier(networkId);
+        Optional<Networks> networkData = read(broker, LogicalDatastoreType.CONFIGURATION, id);
+        if (networkData.isPresent()) {
+            return networkData.get().getVpnid();
+        }
+        return null;
+    }
+
+    private static InstanceIdentifier<Networks> buildNetworkIdentifier(Uuid networkId) {
+        InstanceIdentifier<Networks> network = InstanceIdentifier.builder(ExternalNetworks.class).child
+                (Networks.class, new NetworksKey(networkId)).build();
+        return network;
+    }
+
+
+
+    /*
+        getNaptSwitchesDpnIdsfromRouterId() returns the primary-switch-id and the secondary-switch-id in a array using the router-id; as the key.
+            container napt-switches {
+                list router-to-napt-switch {
+                    key router-id;
+                    leaf router-id { type uint32; }
+                    leaf primary-switch-id { type uint64; }
+                    leaf secondary-switch-id { type uint64; }
+                }
+            }
+    */
+    public static BigInteger getPrimaryNaptfromRouterId(DataBroker broker, Long routerId) {
+        // convert routerId to Name
+        String routerName = getRouterName(broker, routerId);
+        InstanceIdentifier id = buildNaptSwitchIdentifier(routerName);
+        Optional<RouterToNaptSwitch> routerToNaptSwitchData = read(broker, LogicalDatastoreType.OPERATIONAL, id);
+        if (routerToNaptSwitchData.isPresent()) {
+            RouterToNaptSwitch routerToNaptSwitchInstance = routerToNaptSwitchData.get();
+            return routerToNaptSwitchInstance.getPrimarySwitchId();
+        }
+        return null;
+    }
+
+    private static InstanceIdentifier<RouterToNaptSwitch> buildNaptSwitchIdentifier(String routerId) {
+        InstanceIdentifier<RouterToNaptSwitch> rtrNaptSw = InstanceIdentifier.builder(NaptSwitches.class).child
+                (RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerId)).build();
+        return rtrNaptSw;
+    }
+
+    public static String getRouterName(DataBroker broker, Long routerId) {
+        InstanceIdentifier id = buildRouterIdentifier(routerId);
+        Optional<RouterIds> routerIdsData = read(broker, LogicalDatastoreType.CONFIGURATION, id);
+        if (routerIdsData.isPresent()) {
+            RouterIds routerIdsInstance = routerIdsData.get();
+            return routerIdsInstance.getRouterName();
+        }
+        return null;
+    }
+
+    private static InstanceIdentifier<RouterIds> buildRouterIdentifier(Long routerId) {
+        InstanceIdentifier<RouterIds> routerIds = InstanceIdentifier.builder(RouterIdName.class).child
+                (RouterIds.class, new RouterIdsKey(routerId)).build();
+        return routerIds;
+    }
+
+    public static <T extends DataObject> Optional<T> read(DataBroker broker, LogicalDatastoreType datastoreType,
+                                                   InstanceIdentifier<T> path) {
+
+        ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
+
+        Optional<T> result = Optional.absent();
+        try {
+            result = tx.read(datastoreType, path).get();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+        return result;
+    }
+
+    static InstanceIdentifier<VpnInstanceOpDataEntry> getVpnInstanceOpDataIdentifier(String vrfId) {
+        return InstanceIdentifier.builder(VpnInstanceOpData.class)
+                .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(vrfId)).build();
+    }
+
+    public static long readVpnId(DataBroker broker, String vpnName) {
+
+        InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> id
+                = getVpnInstanceToVpnIdIdentifier(vpnName);
+        Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> vpnInstance
+                = read(broker, LogicalDatastoreType.CONFIGURATION, id);
+
+        long vpnId = NatConstants.INVALID_ID;
+        if(vpnInstance.isPresent()) {
+            vpnId = vpnInstance.get().getVpnId();
+        }
+        return vpnId;
+    }
+
+    public static FlowEntity buildFlowEntity(BigInteger dpnId, short tableId, BigInteger cookie) {
+        FlowEntity flowEntity = new FlowEntity(dpnId);
+        flowEntity.setTableId(tableId);
+        flowEntity.setCookie(cookie);
+        return flowEntity;
+    }
+
+    public static long getIpAddress(byte[] rawIpAddress) {
+        return (((rawIpAddress[0] & 0xFF) << (3 * 8)) + ((rawIpAddress[1] & 0xFF) << (2 * 8))
+                + ((rawIpAddress[2] & 0xFF) << (1 * 8)) + (rawIpAddress[3] & 0xFF)) & 0xffffffffL;
+    }
+
+    public static String getFlowRef(BigInteger dpnId, short tableId, InetAddress destPrefix) {
+        return new StringBuilder(64).append(NatConstants.FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
+                .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
+                .append(destPrefix.getHostAddress()).toString();
+    }
+
+    public static String getEndpointIpAddressForDPN(DataBroker broker, BigInteger dpnId) {
+        String nextHopIp = null;
+        InstanceIdentifier<DPNTEPsInfo> tunnelInfoId =
+            InstanceIdentifier.builder(DpnEndpoints.class).child(DPNTEPsInfo.class, new DPNTEPsInfoKey(dpnId)).build();
+        Optional<DPNTEPsInfo> tunnelInfo = read(broker, LogicalDatastoreType.CONFIGURATION, tunnelInfoId);
+        if (tunnelInfo.isPresent()) {
+          List<TunnelEndPoints> nexthopIpList = tunnelInfo.get().getTunnelEndPoints();
+          if (nexthopIpList != null && !nexthopIpList.isEmpty()) {
+            nextHopIp = nexthopIpList.get(0).getIpAddress().getIpv4Address().getValue();
+          }
+        }
+        return nextHopIp;
+      }
+
+    /*
+        getVpnRd returns the rd (route distinguisher) which is the VRF ID from the below model using the vpnName
+            list vpn-instance {
+                key "vpn-instance-name"
+                leaf vpn-instance-name {
+                    type string;
+                }
+                leaf vpn-id {
+                    type uint32;
+                }
+                leaf vrf-id {
+                    type string;
+                }
+            }
+    */
+    public static String getVpnRd(DataBroker broker, String vpnName) {
+
+        InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> id
+                = getVpnInstanceToVpnIdIdentifier(vpnName);
+        Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> vpnInstance
+                = read(broker, LogicalDatastoreType.CONFIGURATION, id);
+
+        String rd = null;
+        if(vpnInstance.isPresent()) {
+            rd = vpnInstance.get().getVrfId();
+        }
+        return rd;
+    }
+
+    /*  getExternalIPPortMap() returns the internal IP and the port for the querried router ID, external IP and the port.
+        container intext-ip-port-map {
+        config true;
+        list ip-port-mapping {
+            key router-id;
+            leaf router-id { type uint32; }
+            list intext-ip-protocol-type {
+                key protocol;
+                leaf protocol { type protocol-types; }
+                list ip-port-map {
+                    key ip-port-internal;
+                    description "internal to external ip-port mapping";
+                    leaf ip-port-internal { type string; }
+                    container ip-port-external {
+                       uses ip-port-entity;
+                    }
+                }
+            }
+         }
+       }
+    */
+    public static IpPortExternal getExternalIpPortMap(DataBroker broker, Long routerId, String internalIpAddress, String internalPort, NAPTEntryEvent.Protocol protocol) {
+        ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
+        InstanceIdentifier ipPortMapId = buildIpToPortMapIdentifier(routerId, internalIpAddress, internalPort, protocolType);
+        Optional<IpPortMap> ipPortMapData = read(broker, LogicalDatastoreType.CONFIGURATION, ipPortMapId);
+        if (ipPortMapData.isPresent()) {
+            IpPortMap ipPortMapInstance = ipPortMapData.get();
+            return ipPortMapInstance.getIpPortExternal();
+        }
+        return null;
+    }
+
+    private static InstanceIdentifier<IpPortMap> buildIpToPortMapIdentifier(Long routerId, String internalIpAddress, String internalPort , ProtocolTypes protocolType) {
+        InstanceIdentifier<IpPortMap> ipPortMapId = InstanceIdentifier.builder(IntextIpPortMap.class).child
+                (IpPortMapping.class, new IpPortMappingKey(routerId)).child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType))
+                .child(IpPortMap.class, new IpPortMapKey(internalIpAddress + ":" + internalPort)).build();
+        return ipPortMapId;
+    }
+
+    public static FlowEntity buildFlowEntity(BigInteger dpnId, short tableId, String flowId, int priority, String flowName,
+                                             BigInteger cookie, List<MatchInfo> listMatchInfo) {
+
+        FlowEntity flowEntity = new FlowEntity(dpnId);
+        flowEntity.setTableId(tableId);
+        flowEntity.setFlowId(flowId);
+        flowEntity.setPriority(priority);
+        flowEntity.setFlowName(flowName);
+        flowEntity.setCookie(cookie);
+        flowEntity.setMatchInfoList(listMatchInfo);
+        return flowEntity;
+    }
+
+    static boolean isVpnInterfaceConfigured(DataBroker broker, String interfaceName)
+    {
+        InstanceIdentifier<VpnInterface> interfaceId = getVpnInterfaceIdentifier(interfaceName);
+        Optional<VpnInterface> configuredVpnInterface = read(broker, LogicalDatastoreType.CONFIGURATION, interfaceId);
+
+        if (configuredVpnInterface.isPresent()) {
+            return true;
+        }
+        return false;
+    }
+
+    static InstanceIdentifier<VpnInterface> getVpnInterfaceIdentifier(String vpnInterfaceName) {
+        return InstanceIdentifier.builder(VpnInterfaces.class)
+                .child(VpnInterface.class, new VpnInterfaceKey(vpnInterfaceName)).build();
+    }
+
+    static VpnInterface getConfiguredVpnInterface(DataBroker broker, String interfaceName) {
+        InstanceIdentifier<VpnInterface> interfaceId = getVpnInterfaceIdentifier(interfaceName);
+        Optional<VpnInterface> configuredVpnInterface = read(broker, LogicalDatastoreType.CONFIGURATION, interfaceId);
+
+        if (configuredVpnInterface.isPresent()) {
+            return configuredVpnInterface.get();
+        }
+        return null;
+    }
+
+    public static String getDpnFromNodeConnectorId(NodeConnectorId portId) {
+        /*
+         * NodeConnectorId is of form 'openflow:dpnid:portnum'
+         */
+        String[] split = portId.getValue().split(OF_URI_SEPARATOR);
+        return split[1];
+    }
+
+    public static BigInteger getDpIdFromInterface(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface ifState) {
+        String lowerLayerIf = ifState.getLowerLayerIf().get(0);
+        NodeConnectorId nodeConnectorId = new NodeConnectorId(lowerLayerIf);
+        return new BigInteger(getDpnFromNodeConnectorId(nodeConnectorId));
+    }
+
+
+    /*
+    container vpnMaps {
+        list vpnMap {
+            key vpn-id;
+            leaf vpn-id {
+                type    yang:uuid;
+                description "vpn-id";
+            }
+            leaf name {
+                type  string;
+                description "vpn name";
+            }
+            leaf tenant-id {
+                type    yang:uuid;
+                description "The UUID of the tenant that will own the subnet.";
+            }
+
+            leaf router-id {
+              type    yang:uuid;
+              description "UUID of router ";
+            }
+            leaf-list network_ids {
+              type    yang:uuid;
+              description "UUID representing the network ";
+            }
+        }
+    }
+    Method returns router Id associated to a VPN
+     */
+
+    public static String getRouterIdfromVpnId(DataBroker broker,String vpnName){
+        InstanceIdentifier<VpnMap> vpnMapIdentifier = InstanceIdentifier.builder(VpnMaps.class)
+                .child(VpnMap.class, new VpnMapKey(new Uuid(vpnName))).build();
+        Optional<VpnMap> optionalVpnMap = read(broker, LogicalDatastoreType.CONFIGURATION,
+                vpnMapIdentifier);
+        if (optionalVpnMap.isPresent()) {
+            return optionalVpnMap.get().getRouterId().getValue();
+        }
+        return null;
+    }
+
+
+    public static List<VpnToDpnList> getVpnToDpnList(DataBroker dataBroker, String vrfId )
+    {
+        List<VpnToDpnList> vpnDpnList = null;
+
+        InstanceIdentifier<VpnInstanceOpDataEntry> id = InstanceIdentifier
+                .builder(VpnInstanceOpData.class)
+                .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(vrfId))
+                .build();
+
+        Optional<VpnInstanceOpDataEntry> vpnInstanceOpData = read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
+
+        if(vpnInstanceOpData.isPresent())
+        {
+            VpnInstanceOpDataEntry vpnInstanceOpDataEntry = vpnInstanceOpData.get();
+            vpnDpnList = vpnInstanceOpDataEntry.getVpnToDpnList();
+        }
+
+        return vpnDpnList;
+    }
+
+    public static String getAssociatedVPN(DataBroker dataBroker, Uuid networkId, Logger log) {
+        Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
+        if(vpnUuid == null ){
+            log.error("No VPN instance associated with ext network {}", networkId);
+            return null;
+        }
+        return vpnUuid.getValue();
+    }
+
+    public static void addPrefixToBGP(IBgpManager bgpManager, String rd, String prefix, String nextHopIp, long label, Logger log) {
+        try {
+            bgpManager.addPrefix(rd, prefix, nextHopIp, (int)label);
+        } catch(Exception e) {
+            log.error("Add prefix failed", e);
+        }
+    }
+
+    static InstanceIdentifier<Ports> buildPortToIpMapIdentifier(String routerId, String portName) {
+        InstanceIdentifier<Ports> ipPortMapId = InstanceIdentifier.builder(FloatingIpInfo.class).child
+                (RouterPorts.class, new RouterPortsKey(routerId)).child(Ports.class, new PortsKey(portName)).build();
+        return ipPortMapId;
+    }
+
+    static InstanceIdentifier<RouterPorts> buildRouterPortsIdentifier(String routerId) {
+        InstanceIdentifier<RouterPorts> routerInstanceIndentifier = InstanceIdentifier.builder(FloatingIpInfo.class).child
+                (RouterPorts.class, new RouterPortsKey(routerId)).build();
+        return routerInstanceIndentifier;
+    }
+
+    /* container snatint-ip-port-map {
+        list intip-port-map {
+            key router-id;
+            leaf router-id { type uint32; }
+            list ip-port {
+                key internal-ip;
+                leaf internal-ip { type string; }
+                list int-ip-proto-type {
+                    key protocol;
+                    leaf protocol { type protocol-types; }
+                    leaf-list ports { type uint16; }
+                }
+            }
+        }
+    }
+    Method returns InternalIp port List
+    */
+
+    public static List<Integer> getInternalIpPortListInfo(DataBroker dataBroker,Long routerId, String internalIpAddress, ProtocolTypes protocolType){
+        Optional<IntIpProtoType> optionalIpProtoType = read(dataBroker, LogicalDatastoreType.CONFIGURATION, buildSnatIntIpPortIdentifier(routerId, internalIpAddress, protocolType));
+        if (optionalIpProtoType.isPresent()) {
+            return optionalIpProtoType.get().getPorts();
+        }
+        return null;
+    }
+
+    public static InstanceIdentifier<IntIpProtoType> buildSnatIntIpPortIdentifier(Long routerId, String internalIpAddress, ProtocolTypes protocolType) {
+        InstanceIdentifier<IntIpProtoType> intIpProtocolTypeId = InstanceIdentifier.builder(SnatintIpPortMap.class).child
+                (IntipPortMap.class, new IntipPortMapKey(routerId)).child(IpPort.class, new IpPortKey(internalIpAddress)).child
+                (IntIpProtoType.class, new IntIpProtoTypeKey(protocolType)).build();
+        return intIpProtocolTypeId;
+    }
+
+    public static ProtocolTypes getProtocolType(NAPTEntryEvent.Protocol protocol) {
+        ProtocolTypes protocolType = ProtocolTypes.TCP.toString().equals(protocol.toString()) ? ProtocolTypes.TCP : ProtocolTypes.UDP;
+        return protocolType;
+    }
+
+    public static NaptSwitches getNaptSwitch(DataBroker broker) {
+        Optional<NaptSwitches> switchesOptional = read(broker, LogicalDatastoreType.OPERATIONAL, getNaptSwitchesIdentifier());
+        if(switchesOptional.isPresent()) {
+            return switchesOptional.get();
+        }
+        return null;
+    }
+
+    public static InstanceIdentifier<NaptSwitches> getNaptSwitchesIdentifier() {
+        return InstanceIdentifier.create(NaptSwitches.class);
+    }
+
+    public static InstanceIdentifier<RouterToNaptSwitch> buildNaptSwitchRouterIdentifier(String routerId) {
+        return InstanceIdentifier.create(NaptSwitches.class).child(RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerId));
+    }
+
+    public static String toStringIpAddress(byte[] ipAddress, Logger log)
+    {
+      String ip = "";
+      if (ipAddress == null) {
+        return ip;
+      }
+
+      try {
+        ip = InetAddress.getByAddress(ipAddress).getHostAddress();
+      } catch(UnknownHostException e) {
+        log.error("NAT Service : Caught exception during toStringIpAddress()");
+      }
+
+      return ip;
+    }
+
+    public static String getGroupIdKey(String routerName){
+        String groupIdKey = new String("snatmiss." + routerName);
+        return groupIdKey;
+    }
+
+    public static long createGroupId(String groupIdKey,IdManagerService idManager) {
+        AllocateIdInput getIdInput = new AllocateIdInputBuilder()
+                .setPoolName(NatConstants.SNAT_IDPOOL_NAME).setIdKey(groupIdKey)
+                .build();
+        try {
+            Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
+            RpcResult<AllocateIdOutput> rpcResult = result.get();
+            return rpcResult.getResult().getIdValue();
+        } catch (NullPointerException | InterruptedException | ExecutionException e) {
+            LOG.trace("", e);
+        }
+        return 0;
+    }
+
+    public static void removePrefixFromBGP(IBgpManager bgpManager, String rd, String prefix, Logger log) {
+        try {
+            bgpManager.deletePrefix(rd, prefix);
+        } catch(Exception e) {
+            log.error("Delete prefix failed", e);
+        }
+    }
+
+    public static FlowEntity buildFlowEntity(BigInteger dpnId, short tableId, BigInteger cookie, String flowId) {
+        FlowEntity flowEntity = new FlowEntity(dpnId);
+        flowEntity.setTableId(tableId);
+        flowEntity.setCookie(cookie);
+        flowEntity.setFlowId(flowId);
+        return flowEntity;
+    }
+
+    public static FlowEntity buildFlowEntity(BigInteger dpnId, short tableId, String flowId) {
+        FlowEntity flowEntity = new FlowEntity(dpnId);
+        flowEntity.setTableId(tableId);
+        flowEntity.setFlowId(flowId);
+        return flowEntity;
+    }
+
+    public static IpPortMapping getIportMapping(DataBroker broker, long routerId) {
+        Optional<IpPortMapping> getIportMappingData = read(broker, LogicalDatastoreType.CONFIGURATION, getIportMappingIdentifier(routerId));
+        if(getIportMappingData.isPresent()) {
+            return getIportMappingData.get();
+        }
+        return null;
+    }
+
+    public static InstanceIdentifier<IpPortMapping> getIportMappingIdentifier(long routerId) {
+        return InstanceIdentifier.builder(IntextIpPortMap.class).child(IpPortMapping.class, new IpPortMappingKey(routerId)).build();
+    }
+}
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/RouterPortsListener.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/RouterPortsListener.java
new file mode 100644 (file)
index 0000000..b5b8d52
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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 org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.FloatingIpInfo;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPorts;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPortsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.RouterPortsKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.PortsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.floating.ip.info.router.ports.PortsKey;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+
+public class RouterPortsListener extends AbstractDataChangeListener<RouterPorts> implements AutoCloseable{
+    private static final Logger LOG = LoggerFactory.getLogger(RouterPortsListener.class);
+    private ListenerRegistration<DataChangeListener> listenerRegistration;
+    private final DataBroker broker;
+
+
+    public RouterPortsListener (final DataBroker db) {
+        super(RouterPorts.class);
+        broker = db;
+        registerListener(db);
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (listenerRegistration != null) {
+            try {
+                listenerRegistration.close();
+            } catch (final Exception e) {
+                LOG.error("Error when cleaning up DataChangeListener.", e);
+            }
+            listenerRegistration = null;
+        }
+        LOG.info("Router ports Listener Closed");
+    }
+
+    private void registerListener(final DataBroker db) {
+        try {
+            listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
+                    getWildCardPath(), RouterPortsListener.this, AsyncDataBroker.DataChangeScope.SUBTREE);
+        } catch (final Exception e) {
+            LOG.error("RouterPorts DataChange listener registration fail!", e);
+            throw new IllegalStateException("RouterPorts Listener registration Listener failed.", e);
+        }
+    }
+
+    private InstanceIdentifier<RouterPorts> getWildCardPath() {
+        return InstanceIdentifier.create(FloatingIpInfo.class).child(RouterPorts.class);
+    }
+
+
+    @Override
+    protected void add(final InstanceIdentifier<RouterPorts> identifier, final RouterPorts routerPorts) {
+        LOG.trace("Add router ports method - key: " + identifier + ", value=" + routerPorts );
+        Optional<RouterPorts> optRouterPorts = NatUtil.read(broker, LogicalDatastoreType.OPERATIONAL, identifier);
+        if(optRouterPorts.isPresent()) {
+            RouterPorts ports = optRouterPorts.get();
+            String routerName = ports.getRouterId();
+            MDSALUtil.syncUpdate(broker, LogicalDatastoreType.OPERATIONAL, identifier, 
+                new RouterPortsBuilder().setKey(new RouterPortsKey(routerName)).setRouterId(routerName)
+                    .setExternalNetworkId(routerPorts.getExternalNetworkId()).build());
+        } else {
+            String routerName = routerPorts.getRouterId();
+            MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, identifier, 
+                new RouterPortsBuilder().setKey(new RouterPortsKey(routerName)).setRouterId(routerName)
+                        .setExternalNetworkId(routerPorts.getExternalNetworkId()).build());
+        }
+    }
+
+    @Override
+    protected void remove(InstanceIdentifier<RouterPorts> identifier, RouterPorts routerPorts) {
+        LOG.trace("Remove router ports method - key: " + identifier + ", value=" + routerPorts );
+        //MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, identifier);
+
+    }
+
+    @Override
+    protected void update(InstanceIdentifier<RouterPorts> identifier, RouterPorts original, RouterPorts update) {
+        LOG.trace("Update router ports method - key: " + identifier + ", original=" + original + ", update=" + update );
+    }
+}
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/SNATDefaultRouteProgrammer.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/SNATDefaultRouteProgrammer.java
new file mode 100644 (file)
index 0000000..6c79bb1
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
+import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
+import org.opendaylight.vpnservice.mdsalutil.InstructionType;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
+import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
+import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
+import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SNATDefaultRouteProgrammer {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SNATDefaultRouteProgrammer.class);
+    private IMdsalApiManager mdsalManager;
+
+    public SNATDefaultRouteProgrammer(IMdsalApiManager mdsalManager) {
+        this.mdsalManager = mdsalManager;
+    }
+
+    private FlowEntity buildDefNATFlowEntity(BigInteger dpId, long vpnId) {
+
+        InetAddress defaultIP = null;
+
+        try {
+            defaultIP = InetAddress.getByName("0.0.0.0");
+
+        } catch (UnknownHostException e) {
+            LOG.error("UnknowHostException in buildDefNATFlowEntity. Failed  to build FIB Table Flow for Default Route to NAT table ");
+            return null;
+        }
+
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x0800L }));
+
+        //add match for default route "0.0.0.0/0"
+//        matches.add(new MatchInfo(MatchFieldType.ipv4_dst, new long[] {
+//                NatUtil.getIpAddress(defaultIP.getAddress()), 0 }));
+
+        //add match for vrfid
+        matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
+                BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
+
+        List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+        instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.PSNAT_TABLE }));
+
+        String flowRef = getFlowRefFib(dpId, NatConstants.L3_FIB_TABLE, vpnId);
+
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.L3_FIB_TABLE, flowRef,
+                NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                NatConstants.COOKIE_DNAT_TABLE, matches, instructions);
+
+        return flowEntity;
+
+
+    }
+
+    private String getFlowRefFib(BigInteger dpnId, short tableId, long routerID) {
+        return new StringBuilder().append(NatConstants.NAPT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR).
+                append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString();
+    }
+
+    void installDefNATRouteInDPN(BigInteger dpnId, long vpnId) {
+        FlowEntity flowEntity = buildDefNATFlowEntity(dpnId, vpnId);
+        if(flowEntity == null) {
+            LOG.error("Flow entity received is NULL. Cannot proceed with installation of Default NAT flow");
+            return;
+        }
+        mdsalManager.installFlow(flowEntity);
+    }
+
+    void removeDefNATRouteInDPN(BigInteger dpnId, long vpnId) {
+        FlowEntity flowEntity = buildDefNATFlowEntity(dpnId, vpnId);
+        if(flowEntity == null) {
+            LOG.error("Flow entity received is NULL. Cannot proceed with installation of Default NAT flow");
+            return;
+        }
+        mdsalManager.removeFlow(flowEntity);
+    }
+
+}
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/SessionAddress.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/SessionAddress.java
new file mode 100644 (file)
index 0000000..eee5d4d
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+public class SessionAddress {
+
+    private String ipAddress;
+    private int portNumber;
+
+    public SessionAddress(String ipAddress, int portNumber) {
+        this.ipAddress = ipAddress;
+        this.portNumber = portNumber;
+    }
+    public String getIpAddress() {
+        return ipAddress;
+    }
+    public int getPortNumber() {
+        return portNumber;
+    }
+
+}
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/VpnFloatingIpHandler.java b/natservice/natservice-impl/src/main/java/org/opendaylight/vpnservice/natservice/internal/VpnFloatingIpHandler.java
new file mode 100644 (file)
index 0000000..8974ebb
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * 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.concurrent.Future;
+import java.util.List;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.bgpmanager.api.IBgpManager;
+import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
+import org.opendaylight.vpnservice.mdsalutil.ActionType;
+import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
+import org.opendaylight.vpnservice.mdsalutil.InstructionType;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
+import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
+import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
+import org.opendaylight.vpnservice.mdsalutil.NwConstants;
+import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+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.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.CreateFibEntryInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.CreateFibEntryInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.RemoveFibEntryInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.FibRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.VpnRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.RemoveVpnLabelInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.RemoveVpnLabelInputBuilder;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public class VpnFloatingIpHandler implements FloatingIPHandler {
+    private static final Logger LOG = LoggerFactory.getLogger(VpnFloatingIpHandler.class);
+    private VpnRpcService vpnService;
+    private FibRpcService fibService;
+    private IBgpManager bgpManager;
+    private DataBroker dataBroker;
+    private IMdsalApiManager mdsalManager;
+    private FloatingIPListener listener;
+
+    static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
+    static final String FLOWID_PREFIX = "NAT.";
+    static final BigInteger COOKIE_VM_LFIB_TABLE = new BigInteger("8000002", 16);
+
+    public VpnFloatingIpHandler(VpnRpcService vpnService, IBgpManager bgpManager, FibRpcService fibService) {
+        this.vpnService = vpnService;
+        this.fibService = fibService;
+        this.bgpManager = bgpManager;
+    }
+
+    void setListener(FloatingIPListener listener) {
+        this.listener = listener;
+    }
+
+    void setBroker(DataBroker broker) {
+        dataBroker = broker;
+    }
+
+    void setMdsalManager(IMdsalApiManager mdsalManager) {
+        this.mdsalManager = mdsalManager;
+    }
+
+    @Override
+    public void onAddFloatingIp(final BigInteger dpnId, final String routerId,
+                                Uuid networkId, final String interfaceName, final String externalIp, final String internalIp) {
+        final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
+        if(vpnName == null) {
+            LOG.info("No VPN associated with ext nw {} to handle add floating ip configuration {} in router {}",
+                    networkId, externalIp, routerId);
+            return;
+        }
+
+        GenerateVpnLabelInput labelInput = new GenerateVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
+        Future<RpcResult<GenerateVpnLabelOutput>> labelFuture = vpnService.generateVpnLabel(labelInput);
+
+        ListenableFuture<RpcResult<Void>> future = Futures.transform(JdkFutureAdapters.listenInPoolThread(labelFuture), new AsyncFunction<RpcResult<GenerateVpnLabelOutput>, RpcResult<Void>>() {
+
+            @Override
+            public ListenableFuture<RpcResult<Void>> apply(RpcResult<GenerateVpnLabelOutput> result) throws Exception {
+                if(result.isSuccessful()) {
+                    GenerateVpnLabelOutput output = result.getResult();
+                    long label = output.getLabel();
+                    LOG.debug("Generated label {} for prefix {}", label, externalIp);
+                    listener.updateOperationalDS(routerId, interfaceName, (int)label, internalIp, externalIp);
+
+                    //Inform BGP
+                    String rd = NatUtil.getVpnRd(dataBroker, vpnName);
+                    String nextHopIp = NatUtil.getEndpointIpAddressForDPN(dataBroker, dpnId);
+                    LOG.debug("Nexthop ip for prefix {} is {}", externalIp, nextHopIp);
+                    NatUtil.addPrefixToBGP(bgpManager, rd, externalIp + "/32", nextHopIp, label, LOG);
+
+                    List<Instruction> instructions = new ArrayList<Instruction>();
+                    List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+                    actionsInfos.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NatConstants.PDNAT_TABLE) }));
+                    instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos).buildInstruction(0));
+                    makeTunnelTableEntry(dpnId, label, instructions);
+                    makeLFibTableEntry(dpnId, label, instructions);
+
+                    //Install custom FIB routes
+                    List<Instruction> customInstructions = new ArrayList<>();
+                    customInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.PDNAT_TABLE }).buildInstruction(0));
+                    CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setInstruction(customInstructions)
+                            .setIpAddress(externalIp + "/32").setServiceId(label).setInstruction(customInstructions).build();
+                    //Future<RpcResult<java.lang.Void>> createFibEntry(CreateFibEntryInput input);
+                    Future<RpcResult<Void>> future = fibService.createFibEntry(input);
+                    return JdkFutureAdapters.listenInPoolThread(future);
+                } else {
+                    String errMsg = String.format("Could not retrieve the label for prefix %s in VPN %s, %s", externalIp, vpnName, result.getErrors());
+                    LOG.error(errMsg);
+                    return Futures.immediateFailedFuture(new RuntimeException(errMsg));
+                }
+            }
+        });
+
+        Futures.addCallback(future, new FutureCallback<RpcResult<Void>>() {
+
+            @Override
+            public void onFailure(Throwable error) {
+                LOG.error("Error in generate label or fib install process", error);
+            }
+
+            @Override
+            public void onSuccess(RpcResult<Void> result) {
+                if(result.isSuccessful()) {
+                    LOG.info("Successfully installed custom FIB routes for prefix {}", externalIp);
+                } else {
+                    LOG.error("Error in rpc call to create custom Fib entries for prefix {} in DPN {}, {}", externalIp, dpnId, result.getErrors());
+                }
+            }
+        });
+    }
+
+    @Override
+    public void onRemoveFloatingIp(final BigInteger dpnId, String routerId, Uuid networkId, final String externalIp,
+                                   String internalIp, final long label) {
+        final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
+        if(vpnName == null) {
+            LOG.info("No VPN associated with ext nw {} to handle remove floating ip configuration {} in router {}",
+                    networkId, externalIp, routerId);
+            return;
+        }
+        //Remove Prefix from BGP
+        String rd = NatUtil.getVpnRd(dataBroker, vpnName);
+        removePrefixFromBGP(rd, externalIp + "/32");
+
+        //Remove custom FIB routes
+        //Future<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
+        RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp + "/32").setServiceId(label).build();
+        Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
+
+        ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+
+            @Override
+            public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
+                //Release label
+                if(result.isSuccessful()) {
+                    removeTunnelTableEntry(dpnId, label);
+                    removeLFibTableEntry(dpnId, label);
+                    RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
+                    Future<RpcResult<Void>> labelFuture = vpnService.removeVpnLabel(labelInput);
+                    return JdkFutureAdapters.listenInPoolThread(labelFuture);
+                } else {
+                    String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors());
+                    LOG.error(errMsg);
+                    return Futures.immediateFailedFuture(new RuntimeException(errMsg));
+                }
+            }
+        });
+
+        Futures.addCallback(labelFuture, new FutureCallback<RpcResult<Void>>() {
+
+            @Override
+            public void onFailure(Throwable error) {
+                LOG.error("Error in removing the label or custom fib entries", error);
+            }
+
+            @Override
+            public void onSuccess(RpcResult<Void> result) {
+                if(result.isSuccessful()) {
+                    LOG.debug("Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName);
+                } else {
+                    LOG.error("Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors());
+                }
+            }
+        });
+    }
+
+    private void removePrefixFromBGP(String rd, String prefix) {
+        try {
+            bgpManager.deletePrefix(rd, prefix);
+        } catch(Exception e) {
+            LOG.error("Delete prefix failed", e);
+        }
+    }
+
+    void cleanupFibEntries(final BigInteger dpnId, final String vpnName, final String externalIp, final long label ) {
+        //Remove Prefix from BGP
+        String rd = NatUtil.getVpnRd(dataBroker, vpnName);
+        removePrefixFromBGP(rd, externalIp + "/32");
+
+        //Remove custom FIB routes
+
+        //Future<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
+        RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp).setServiceId(label).build();
+        Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
+
+        ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), 
+            new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+
+            @Override
+            public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
+                //Release label
+                if(result.isSuccessful()) {
+                    removeTunnelTableEntry(dpnId, label);
+                    removeLFibTableEntry(dpnId, label);
+                    RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
+                    Future<RpcResult<Void>> labelFuture = vpnService.removeVpnLabel(labelInput);
+                    return JdkFutureAdapters.listenInPoolThread(labelFuture);
+                } else {
+                    String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors());
+                    LOG.error(errMsg);
+                    return Futures.immediateFailedFuture(new RuntimeException(errMsg));
+                }
+            }
+        });
+
+        Futures.addCallback(labelFuture, new FutureCallback<RpcResult<Void>>() {
+
+            @Override
+            public void onFailure(Throwable error) {
+                LOG.error("Error in removing the label or custom fib entries", error);
+            }
+
+            @Override
+            public void onSuccess(RpcResult<Void> result) {
+                if(result.isSuccessful()) {
+                    LOG.debug("Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName);
+                } else {
+                    LOG.error("Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors());
+                }
+            }
+        });
+    }
+
+    private String getFlowRef(BigInteger dpnId, short tableId, long id, String ipAddress) {
+        return new StringBuilder(64).append(FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
+                .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
+                .append(id).append(NwConstants.FLOWID_SEPARATOR).append(ipAddress).toString();
+    }
+
+    private void removeTunnelTableEntry(BigInteger dpnId, long serviceId) {
+        LOG.info("remove terminatingServiceActions called with DpnId = {} and label = {}", dpnId , serviceId);
+        List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+        // Matching metadata
+        mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
+        Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
+                getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""),
+                5, String.format("%s:%d","TST Flow Entry ",serviceId), 0, 0,
+                COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)), mkMatches, null);
+        mdsalManager.removeFlow(dpnId, flowEntity);
+        LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully {}",dpnId, serviceId);
+    }
+
+    private void makeTunnelTableEntry(BigInteger dpnId, long serviceId, List<Instruction> customInstructions) {
+        List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
+
+        LOG.info("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}", dpnId , serviceId);
+
+        mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
+
+        Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
+                getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""), 5, String.format("%s:%d","TST Flow Entry ",serviceId),
+                0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)),mkMatches, customInstructions);
+
+        mdsalManager.installFlow(dpnId, terminatingServiceTableFlowEntity);
+    }
+
+    private void makeLFibTableEntry(BigInteger dpId, long serviceId, List<Instruction> customInstructions) {
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x8847L }));
+        matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
+
+        List<Instruction> instructions = new ArrayList<Instruction>();
+        List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+        actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
+        Instruction writeInstruction = new InstructionInfo(InstructionType.write_actions, actionsInfos).buildInstruction(0);
+        instructions.add(writeInstruction);
+        instructions.addAll(customInstructions);
+
+        // Install the flow entry in L3_LFIB_TABLE
+        String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, serviceId, "");
+
+        Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
+                10, flowRef, 0, 0,
+                COOKIE_VM_LFIB_TABLE, matches, instructions);
+
+        mdsalManager.installFlow(dpId, flowEntity);
+
+        LOG.debug("LFIB Entry for dpID {} : label : {} modified successfully {}",dpId, serviceId );
+    }
+
+    private void removeLFibTableEntry(BigInteger dpnId, long serviceId) {
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                                  new long[] { 0x8847L }));
+        matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
+
+        String flowRef = getFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, serviceId, "");
+
+        LOG.debug("removing LFib entry with flow ref {}", flowRef);
+
+        Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
+                                               10, flowRef, 0, 0,
+                                               COOKIE_VM_LFIB_TABLE, matches, null);
+
+        mdsalManager.removeFlow(dpnId, flowEntity);
+
+        LOG.debug("LFIB Entry for dpID : {} label : {} removed successfully {}",dpnId, serviceId);
+    }
+
+}
+
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/natservice/impl/rev160111/NATServiceModule.java b/natservice/natservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/natservice/impl/rev160111/NATServiceModule.java
new file mode 100644 (file)
index 0000000..6d34659
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.natservice.impl.rev160111;
+
+import org.opendaylight.vpnservice.natservice.internal.NatServiceProvider;
+
+public class NATServiceModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.natservice.impl.rev160111.AbstractNATServiceModule {
+    public NATServiceModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public NATServiceModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.natservice.impl.rev160111.NATServiceModule oldModule, java.lang.AutoCloseable oldInstance) {
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void customValidation() {
+        // add custom validation form module attributes here.
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        NatServiceProvider provider = new NatServiceProvider(getRpcRegistryDependency());
+        provider.setNotificationService(getNotificationServiceDependency());
+        provider.setMdsalManager(getMdsalutilDependency());
+        provider.setBgpManager(getBgpmanagerDependency());
+        getBrokerDependency().registerProvider(provider);
+        return provider;
+    }
+}
diff --git a/natservice/natservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/natservice/impl/rev160111/NATServiceModuleFactory.java b/natservice/natservice-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/natservice/impl/rev160111/NATServiceModuleFactory.java
new file mode 100644 (file)
index 0000000..d2c9332
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: natmanager-impl yang module local name: natservice-impl
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Jan 20 15:47:25 IST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.natservice.impl.rev160111;
+public class NATServiceModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.natservice.impl.rev160111.AbstractNATServiceModuleFactory {
+
+}
diff --git a/natservice/natservice-impl/src/main/yang/natservice-impl.yang b/natservice/natservice-impl/src/main/yang/natservice-impl.yang
new file mode 100644 (file)
index 0000000..4c71f98
--- /dev/null
@@ -0,0 +1,70 @@
+module natservice-impl {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:natservice:impl";
+    prefix "natservice-impl";
+
+    import config { prefix config; revision-date 2013-04-05; }
+    import opendaylight-md-sal-binding { prefix md-sal-binding; revision-date 2013-10-28;}
+    import opendaylight-sal-binding-broker-impl { prefix md-sal-binding-impl; revision-date 2013-10-28; }
+    import odl-mdsalutil { prefix odl-mdsal; revision-date 2015-04-10;}
+    import bgpmanager-api { prefix bgpmgr-api; revision-date 2015-04-20;}
+
+    description
+        "Service definition for NAT Service module";
+
+    revision "2016-01-11" {
+        description
+            "Initial revision";
+    }
+
+    identity natservice-impl {
+        base config:module-type;
+        config:java-name-prefix NATService;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case natservice-impl {
+            when "/config:modules/config:module/config:type = 'natservice-impl'";
+            container broker {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity md-sal-binding:binding-broker-osgi-registry;
+                    }
+                }
+            }
+            container rpc-registry {
+                 uses config:service-ref {
+                      refine type {
+                         mandatory true;
+                         config:required-identity md-sal-binding:binding-rpc-registry;
+                      }
+                 }
+            }
+            container bgpmanager {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity bgpmgr-api:bgpmanager-api;
+                    }
+                }
+            }
+            container notification-service {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity md-sal-binding-impl:binding-new-notification-service;
+                    }
+                }
+            }
+            container mdsalutil {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity odl-mdsal:odl-mdsalutil;
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/natservice/natservice-impl/src/test/java/org/opendaylight/vpnservice/natservice/internal/test/ExternalNetworksChangeListenerTest.java b/natservice/natservice-impl/src/test/java/org/opendaylight/vpnservice/natservice/internal/test/ExternalNetworksChangeListenerTest.java
new file mode 100644 (file)
index 0000000..a284db1
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.mock;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
+import org.opendaylight.vpnservice.mdsalutil.ActionType;
+import org.opendaylight.vpnservice.mdsalutil.BucketInfo;
+import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
+import org.opendaylight.vpnservice.mdsalutil.GroupEntity;
+import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
+import org.opendaylight.vpnservice.mdsalutil.InstructionType;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
+import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
+import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.vpnservice.natservice.internal.ExternalNetworksChangeListener;
+import org.opendaylight.vpnservice.natservice.internal.NatUtil;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(MDSALUtil.class)
+public class ExternalNetworksChangeListenerTest {
+
+    @Mock DataBroker dataBroker;
+    @Mock ListenerRegistration<DataChangeListener> dataChangeListenerRegistration;
+    @Mock IMdsalApiManager mdsalManager;
+    @Mock FlowEntity flowMock;
+    @Mock GroupEntity groupMock;
+    InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.Networks> id = null;
+    org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.external.networks.Networks networks = null;
+    private ExternalNetworksChangeListener extNetworks;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(dataBroker.registerDataChangeListener(
+                any(LogicalDatastoreType.class),
+                any(InstanceIdentifier.class),
+                any(DataChangeListener.class),
+                any(DataChangeScope.class)))
+                .thenReturn(dataChangeListenerRegistration);
+        extNetworks = new ExternalNetworksChangeListener(dataBroker);
+
+        PowerMockito.mockStatic(MDSALUtil.class);
+    }
+
+
+    @Test
+    public void testSnatFlowEntity() {
+        FlowEntity flowMock = mock(FlowEntity.class);
+        final short SNAT_TABLE = 40;
+        final int DEFAULT_SNAT_FLOW_PRIORITY = 0;
+        final String FLOWID_SEPARATOR = ".";
+        String SNAT_FLOWID_PREFIX = "SNAT.";
+
+
+        BigInteger dpnId = new BigInteger("100");
+        String routerName = new String("200");
+        long routerId = 200;
+        long groupId = 300;
+        List<BucketInfo> bucketInfo = new ArrayList<BucketInfo>();
+        List<ActionInfo> listActionInfoPrimary = new ArrayList<ActionInfo>();
+        listActionInfoPrimary.add(new ActionInfo(ActionType.output,
+                new String[] {"3"}));
+        BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
+        List<ActionInfo> listActionInfoSecondary = new ArrayList<ActionInfo>();
+        listActionInfoSecondary.add(new ActionInfo(ActionType.output,
+                new String[] {"4"}));
+        BucketInfo bucketSecondary = new BucketInfo(listActionInfoPrimary);
+        bucketInfo.add(0, bucketPrimary);
+        bucketInfo.add(1, bucketSecondary);
+
+        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+        matches.add(new MatchInfo(MatchFieldType.eth_type,
+                new long[] { 0x0800L }));
+
+        List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
+        List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
+        actionsInfos.add(new ActionInfo(ActionType.group, new String[] {String.valueOf(groupId)}));
+        instructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfos));
+
+
+        String flowRef =  new StringBuffer().append(SNAT_FLOWID_PREFIX).append(dpnId).append(FLOWID_SEPARATOR).
+                append(SNAT_TABLE).append(FLOWID_SEPARATOR).append(routerId).toString();
+
+        BigInteger cookieSnat = NatUtil.getCookieSnatFlow(routerId);
+        try {
+            PowerMockito.when(MDSALUtil.class, "buildFlowEntity", dpnId, SNAT_TABLE, flowRef,
+                    DEFAULT_SNAT_FLOW_PRIORITY, flowRef, 0, 0,
+                    cookieSnat, matches, instructions ).thenReturn(flowMock);
+        } catch (Exception e) {
+            // Test failed anyways
+            assertEquals("true", "false");
+        }
+        /* TODO : Fix this to mock it properly when it reads DS
+        extNetworks.buildSnatFlowEntity(dpnId, routerName, groupId);
+        PowerMockito.verifyStatic(); */
+
+    }
+
+}
diff --git a/natservice/natservice-impl/src/test/java/org/opendaylight/vpnservice/natservice/internal/test/NaptManagerTest.java b/natservice/natservice-impl/src/test/java/org/opendaylight/vpnservice/natservice/internal/test/NaptManagerTest.java
new file mode 100644 (file)
index 0000000..12194b3
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * 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.test;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.OngoingStubbing;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.eq;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
+import org.opendaylight.vpnservice.natservice.internal.IPAddress;
+import org.opendaylight.vpnservice.natservice.internal.NaptManager;
+import org.opendaylight.vpnservice.natservice.internal.SessionAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.IntextIpMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.IntextIpPortMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMapping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMappingKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMapBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMapKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.IpPortMapping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.IpPortMappingKey;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+import com.google.common.util.concurrent.Futures;
+
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(MDSALUtil.class)
+public class NaptManagerTest {
+
+    @Mock IdManagerService idMgr;
+    @Mock DataBroker dataBroker;
+    @Mock ListenerRegistration<DataChangeListener> dataChangeListenerRegistration;
+    InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap> ipmapId = null;
+    org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap ipmap = null;
+
+    private NaptManager naptManager;
+
+    @Before
+    public void init() {
+        MockitoAnnotations.initMocks(this);
+        when(dataBroker.registerDataChangeListener(
+                any(LogicalDatastoreType.class),
+                any(InstanceIdentifier.class),
+                any(DataChangeListener.class),
+                any(DataChangeScope.class)))
+                .thenReturn(dataChangeListenerRegistration);
+        naptManager = new NaptManager(dataBroker);
+        when(idMgr.createIdPool(any(CreateIdPoolInput.class)))
+                  .thenReturn(Futures.immediateFuture(RpcResultBuilder.<Void>success().build()));
+        naptManager.setIdManager(idMgr);
+
+        PowerMockito.mockStatic(MDSALUtil.class);
+
+    }
+
+
+    @Test
+    public void testRegisterMappingIpIP() {
+
+        // TODO : Issue with Mockito.any() usage, so for now run registerMapping testcases as seperate Tests. This needs to be fixed properly.
+        ipmapId = InstanceIdentifier.builder(
+                IntextIpMap.class).child(IpMapping.class, new IpMappingKey(5L)).child(IpMap.class, new IpMapKey("10.0.0.1")).build();
+        ipmap = new IpMapBuilder().setKey(new IpMapKey("10.0.0.1")).setInternalIp("10.0.0.1").setExternalIp("192.17.13.1").build();
+        try {
+            PowerMockito.doNothing().when(MDSALUtil.class, "syncWrite", dataBroker, LogicalDatastoreType.OPERATIONAL, ipmapId, ipmap);
+        } catch (Exception e) {
+            // Test failed anyways
+            assertEquals("true", "false");
+        }
+        IPAddress internal = new IPAddress("10.0.0.1",0);
+        IPAddress external = new IPAddress("192.17.13.1", 0);
+        naptManager.registerMapping(5, internal, external);
+        PowerMockito.verifyStatic();
+
+    }
+
+    @Test
+    public void testRegisterMappingIpSubnet() {
+
+        ipmapId = InstanceIdentifier.builder(
+                IntextIpMap.class).child(IpMapping.class, new IpMappingKey(5L)).child(IpMap.class, new IpMapKey("10.0.0.1")).build();
+        ipmap = new IpMapBuilder().setKey(new IpMapKey("10.0.0.1")).setInternalIp("10.0.0.1").setExternalIp("192.17.13.1/24").build();
+        try {
+            PowerMockito.doNothing().when(MDSALUtil.class, "syncWrite", dataBroker, LogicalDatastoreType.OPERATIONAL, ipmapId, ipmap);
+        } catch (Exception e) {
+            // Test failed anyways
+            assertEquals("true", "false");
+        }
+        IPAddress internal = new IPAddress("10.0.0.1",0);
+        IPAddress external = new IPAddress("192.17.13.1", 24);
+        naptManager.registerMapping(5, internal, external);
+        PowerMockito.verifyStatic();
+    }
+
+    @Test
+    public void testRegisterMappingSubnetIp() {
+
+        ipmapId = InstanceIdentifier.builder(
+                IntextIpMap.class).child(IpMapping.class, new IpMappingKey(6L)).child(IpMap.class, new IpMapKey("10.0.2.1/16")).build();
+        ipmap = new IpMapBuilder().setKey(new IpMapKey("10.0.0.1")).setInternalIp("10.0.0.1").setExternalIp("192.19.15.3").build();
+        try {
+            PowerMockito.doNothing().when(MDSALUtil.class, "syncWrite", dataBroker, LogicalDatastoreType.OPERATIONAL, ipmapId, ipmap);
+        } catch (Exception e) {
+            // Test failed anyways
+            assertEquals("true", "false");
+        }
+        IPAddress internal = new IPAddress("10.0.2.1",16);
+        IPAddress external = new IPAddress("192.19.15.3", 0);
+        naptManager.registerMapping(6, internal, external);
+        PowerMockito.verifyStatic();
+     }
+
+    @Test
+    public void testRegisterMappingSubnetSubnet() {
+
+        ipmapId = InstanceIdentifier.builder(
+                IntextIpMap.class).child(IpMapping.class, new IpMappingKey(6L)).child(IpMap.class, new IpMapKey("10.2.0.1/24")).build();
+        ipmap = new IpMapBuilder().setKey(new IpMapKey("10.2.0.1/24")).setInternalIp("10.2.0.1/24").setExternalIp("192.21.16.1/16").build();
+        try {
+            PowerMockito.doNothing().when(MDSALUtil.class, "syncWrite", dataBroker, LogicalDatastoreType.OPERATIONAL, ipmapId, ipmap);
+        } catch (Exception e) {
+            // Test failed anyways
+            assertEquals("true", "false");
+        }
+        IPAddress internal = new IPAddress("10.2.0.1",24);
+        IPAddress external = new IPAddress("192.21.16.1", 16);
+        naptManager.registerMapping(6, internal, external);
+        PowerMockito.verifyStatic();
+    }
+
+
+    @Test
+    public void testgetExternalAddressMapping() {
+        // TODO : This needs to be modified to make it work
+        // Testcase to test when no entry exists in ip-pot-map
+        /*SessionAddress internalIpPort = new SessionAddress("10.0.0.1", 2);
+        InstanceIdentifierBuilder<IpPortMapping> idBuilder =
+                InstanceIdentifier.builder(IntextIpPortMap.class).child(IpPortMapping.class, new IpPortMappingKey(5L));
+        InstanceIdentifier<IpPortMapping> id = idBuilder.build();
+        try {
+             PowerMockito.when(MDSALUtil.class, "read", dataBroker, LogicalDatastoreType.CONFIGURATION, id).thenReturn(null);
+        } catch (Exception e) {
+            // Test failed anyways
+            assertEquals("true", "false");
+        }
+        naptManager.getExternalAddressMapping(5, internalIpPort);
+        PowerMockito.verifyStatic(); */  
+    }
+
+    @Test
+    public void testReleaseAddressMapping() {
+        // TODO : Below needs to be modified to make it work
+      /*  InstanceIdentifierBuilder<IpMapping> idBuilder =
+                InstanceIdentifier.builder(IntextIpMap.class).child(IpMapping.class, new IpMappingKey(5L));
+        InstanceIdentifier<IpMapping> id = idBuilder.build();
+        try {
+            PowerMockito.doNothing().when(MDSALUtil.class, "read", dataBroker, LogicalDatastoreType.OPERATIONAL, id);
+        } catch (Exception e) {
+            // Test failed anyways
+            assertEquals("true", "false");
+        }
+        IPAddress internal = new IPAddress("10.0.0.1",0);
+        IPAddress external = new IPAddress("192.17.13.1", 0);
+        naptManager.registerMapping(5, internal, external);
+        SessionAddress internalSession = new SessionAddress("10.0.0.1", 0);
+        naptManager.releaseAddressMapping(5L, internalSession);*/
+    }
+
+
+}
diff --git a/natservice/pom.xml b/natservice/pom.xml
new file mode 100644 (file)
index 0000000..adba57f
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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 INTERNAL
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <parent>
+    <groupId>org.opendaylight.odlparent</groupId>
+    <artifactId>odlparent</artifactId>
+    <version>1.7.0-SNAPSHOT</version>
+    <relativePath/>
+  </parent>
+
+  <groupId>org.opendaylight.vpnservice</groupId>
+  <artifactId>natservice-aggregator</artifactId>
+  <version>0.3.0-SNAPSHOT</version>
+  <name>natservice</name>
+  <packaging>pom</packaging>
+  <modelVersion>4.0.0</modelVersion>
+  <prerequisites>
+    <maven>3.1.1</maven>
+  </prerequisites>
+  <modules>
+    <module>natservice-api</module>
+    <module>natservice-impl</module>
+  </modules>
+  <!-- DO NOT install or deploy the repo root pom as it's only needed to initiate a build -->
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-install-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/pom.xml b/pom.xml
index e29557e99b9cce51a66757b8a4c7e0bf4d871739..baa7a3198d053dd16a212415bad2361abe43f01f 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -31,6 +31,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
     <module>neutronvpn</module>
     <module>dhcpservice</module>
     <module>itm</module>
+    <module>natservice</module>
     <module>distribution/karaf</module>
     <module>features</module>
     <module>vpnservice-artifacts</module>
index 6857e98ec55b1a2c73624d70f5523b4e06cfda7c..384bfb2ec8118d04b5ad6ce8444ad0e397905317 100644 (file)
@@ -122,6 +122,12 @@ module odl-l3vpn {
                       type string;
                   }
                }
+               list ip-addresses {
+                  key ip-address;
+                  leaf ip-address {
+                      type string;
+                  }
+               }
            }
         }
     }
@@ -234,4 +240,22 @@ module odl-l3vpn {
         }
     }
 
+    grouping dpn-in-vpn-event {
+            leaf dpn-id { type uint64; }
+            leaf vpn-name { type string; }
+            leaf rd { type string; }
+        }
+
+        notification add-dpn-event {
+            container add-event-data {
+               uses dpn-in-vpn-event;
+            }
+        }
+
+        notification remove-dpn-event {
+            container remove-event-data {
+               uses dpn-in-vpn-event;
+            }
+        }
+
 }
\ No newline at end of file
diff --git a/vpnmanager/vpnmanager-api/src/main/yang/vpn-rpc.yang b/vpnmanager/vpnmanager-api/src/main/yang/vpn-rpc.yang
new file mode 100644 (file)
index 0000000..ec78fc1
--- /dev/null
@@ -0,0 +1,39 @@
+module vpn-rpc {\r
+    namespace "urn:opendaylight:vpnservice:vpn:rpc";\r
+    prefix "vpn-rpc";\r
+\r
+    revision "2016-02-01" {\r
+        description "VPN Service RPC Module";\r
+    }\r
+\r
+    /* RPCs */\r
+\r
+    rpc generate-vpn-label {\r
+        description "to generate label for the given ip prefix from the associated VPN";\r
+        input {\r
+            leaf vpn-name {\r
+                type string;\r
+            }\r
+            leaf ip-prefix {\r
+                type string;\r
+            }\r
+        }\r
+        output {\r
+            leaf label {\r
+                type uint32;\r
+            }\r
+        }\r
+    }\r
+\r
+    rpc remove-vpn-label {\r
+        description "to remove label for the given ip prefix from the associated VPN";\r
+        input {\r
+            leaf vpn-name {\r
+                type string;\r
+            }\r
+            leaf ip-prefix {\r
+                type string;\r
+            }\r
+        }\r
+    }\r
+}
\ No newline at end of file
index 62d6db72e2992a5d59c6c744d6d6a73470e1dfc2..a764d396855feffe0701c9b44afce7b4954f52ef 100644 (file)
@@ -21,7 +21,8 @@ public class VpnConstants {
     public static final BigInteger COOKIE_VM_INGRESS_TABLE = new BigInteger("8000001", 16);
     public static final BigInteger COOKIE_L3_BASE = new BigInteger("8000000", 16);
     public static final String FLOWID_PREFIX = "L3.";
-    public static final long WAIT_TIME_IN_MILLISECONDS = 5000;
+    public static final long MIN_WAIT_TIME_IN_MILLISECONDS = 10000;
+    public static final long MAX_WAIT_TIME_IN_MILLISECONDS = 90000;
     public static final int ELAN_GID_MIN = 200000;
     public static final short ELAN_SMAC_TABLE = 50;
     public static final short FIB_TABLE = 21;
index 2a9a96138aa8c6a3fb5689531fa7fe1f81452fa8..ac639b42fe44d2de47c958d063a980958ef4178f 100644 (file)
@@ -499,7 +499,7 @@ public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface
                 vpnIntfMap.put(interfaceName, notifyTask);
                 synchronized (notifyTask) {
                     try {
-                        notifyTask.wait(VpnConstants.WAIT_TIME_IN_MILLISECONDS);
+                        notifyTask.wait(VpnConstants.MIN_WAIT_TIME_IN_MILLISECONDS);
                     } catch (InterruptedException e) {
                     }
                 }
@@ -514,15 +514,18 @@ public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface
         Optional<Adjacencies> adjacencies = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, path);
 
         String rd = VpnUtil.getVpnRd(broker, intf.getVpnInstanceName());
+        LOG.trace("removeAdjacenciesFromVpn: For interface {} RD recovered for vpn {} as rd {}", intf.getName(),
+                intf.getVpnInstanceName(), rd);
         if (adjacencies.isPresent()) {
             List<Adjacency> nextHops = adjacencies.get().getAdjacency();
 
             if (!nextHops.isEmpty()) {
                 LOG.trace("NextHops are " + nextHops);
                 for (Adjacency nextHop : nextHops) {
-                    VpnUtil.releaseId(idManager, VpnConstants.VPN_IDPOOL_NAME,
+                    // Commenting the release of ID here as it will be released by FIB
+                   /* VpnUtil.releaseId(idManager, VpnConstants.VPN_IDPOOL_NAME,
                                       VpnUtil.getNextHopLabelKey(rd, nextHop.getIpAddress()));
-                    /*VpnUtil.delete(broker, LogicalDatastoreType.OPERATIONAL,
+                    VpnUtil.delete(broker, LogicalDatastoreType.OPERATIONAL,
                                    VpnUtil.getPrefixToInterfaceIdentifier(
                                        VpnUtil.getVpnId(broker, intf.getVpnInstanceName()),
                                        nextHop.getIpAddress()),
@@ -734,8 +737,9 @@ public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface
                     while (adjIt.hasNext()) {
                         Adjacency adjElem = adjIt.next();
                         if (adjElem.getIpAddress().equals(adj.getIpAddress())) {
-                            VpnUtil.releaseId(idManager, VpnConstants.VPN_IDPOOL_NAME,
-                                    VpnUtil.getNextHopLabelKey(rd, adj.getIpAddress()));
+                            // Commenting the release of ID here as it will be released by FIB
+                           /* VpnUtil.releaseId(idManager, VpnConstants.VPN_IDPOOL_NAME,
+                                    VpnUtil.getNextHopLabelKey(rd, adj.getIpAddress()));*/
                             adjIt.remove();
 
                             Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(adjacencies);
@@ -801,42 +805,56 @@ public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface
         protected void remove(InstanceIdentifier<VpnInterface> identifier, VpnInterface del) {
             final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
             String interfaceName = key.getName();
-
-            //increment the vpn interface count in Vpn Instance Op Data
-            Long ifCnt = 0L;
-            String rd = getRouteDistinguisher(del.getVpnInstanceName());
-            if(rd == null || rd.isEmpty()) rd = del.getVpnInstanceName();
-            VpnInstanceOpDataEntry vpnInstOp = VpnUtil.getVpnInstanceOpData(broker, rd);
-            if(vpnInstOp != null && vpnInstOp.getVpnInterfaceCount() != null) {
-                ifCnt = vpnInstOp.getVpnInterfaceCount();
-            }
-
-            LOG.trace("VpnInterfaceOpListener remove: interface name {} rd {} interface count in Vpn Op Instance {}", interfaceName, rd, ifCnt);
-
-            if(ifCnt != 0) {
-                VpnUtil.asyncUpdate(broker, LogicalDatastoreType.OPERATIONAL,
-                        VpnUtil.getVpnInstanceOpDataIdentifier(rd),
-                        VpnUtil.updateIntfCntInVpnInstOpData(ifCnt - 1, rd), VpnUtil.DEFAULT_CALLBACK);
-            }
-
-            // Vpn Interface removed => No more adjacencies from it.
-            // Hence clean up interface from vpn-dpn-interface list.
-            Adjacency adjacency = del.getAugmentation(Adjacencies.class).getAdjacency().get(0);
-            Optional<Prefixes> prefixToInterface = Optional.absent();
-            prefixToInterface = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
-                         VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
-                                                VpnUtil.getIpPrefix(adjacency.getIpAddress())));
-            if (!prefixToInterface.isPresent()) {
-                prefixToInterface = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
-                                                 VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
-                                                         VpnUtil.getIpPrefix(adjacency.getNextHopIp())));
-            }
-            if (prefixToInterface.isPresent()) {
-                VpnUtil.delete(broker, LogicalDatastoreType.OPERATIONAL,
-                               VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
-                                                 prefixToInterface.get().getIpAddress()),
-                               VpnUtil.DEFAULT_CALLBACK);
-                updateDpnDbs(prefixToInterface.get().getDpnId(), del.getVpnInstanceName(), interfaceName, false);
+            String vpnName = del.getVpnInstanceName();
+
+            LOG.trace("VpnInterfaceOpListener removed: interface name {} vpnName {}", interfaceName, vpnName);
+            //decrement the vpn interface count in Vpn Instance Op Data
+            InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance>
+                    id = VpnUtil.getVpnInstanceToVpnIdIdentifier(vpnName);
+            Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> vpnInstance
+                    = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
+
+            if (vpnInstance.isPresent()) {
+                String rd = null;
+                rd = vpnInstance.get().getVrfId();
+                //String rd = getRouteDistinguisher(del.getVpnInstanceName());
+
+                VpnInstanceOpDataEntry vpnInstOp = VpnUtil.getVpnInstanceOpData(broker, rd);
+                LOG.trace("VpnInterfaceOpListener removed: interface name {} rd {} vpnName {} in Vpn Op Instance {}",
+                        interfaceName, rd, vpnName, vpnInstOp);
+
+                if (vpnInstOp != null) {
+                    // Vpn Interface removed => No more adjacencies from it.
+                    // Hence clean up interface from vpn-dpn-interface list.
+                    Adjacency adjacency = del.getAugmentation(Adjacencies.class).getAdjacency().get(0);
+                    Optional<Prefixes> prefixToInterface = Optional.absent();
+                    prefixToInterface = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
+                            VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
+                                    VpnUtil.getIpPrefix(adjacency.getIpAddress())));
+                    if (!prefixToInterface.isPresent()) {
+                        prefixToInterface = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
+                                VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
+                                        VpnUtil.getIpPrefix(adjacency.getNextHopIp())));
+                    }
+                    if (prefixToInterface.isPresent()) {
+                        VpnUtil.delete(broker, LogicalDatastoreType.OPERATIONAL,
+                                VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
+                                        prefixToInterface.get().getIpAddress()),
+                                VpnUtil.DEFAULT_CALLBACK);
+                        updateDpnDbs(prefixToInterface.get().getDpnId(), del.getVpnInstanceName(), interfaceName, false);
+                    }
+                    Long ifCnt = 0L;
+                    ifCnt = vpnInstOp.getVpnInterfaceCount();
+                    LOG.trace("VpnInterfaceOpListener removed: interface name {} rd {} vpnName {} Intf count {}",
+                            interfaceName, rd, vpnName, ifCnt);
+                    if ((ifCnt != null) && (ifCnt > 0)) {
+                        VpnUtil.asyncUpdate(broker, LogicalDatastoreType.OPERATIONAL,
+                                VpnUtil.getVpnInstanceOpDataIdentifier(rd),
+                                VpnUtil.updateIntfCntInVpnInstOpData(ifCnt - 1, rd), VpnUtil.DEFAULT_CALLBACK);
+                    }
+                }
+            } else {
+                LOG.error("rd not retrievable as vpninstancetovpnid for vpn {} is absent, trying rd as ", vpnName, vpnName);
             }
             notifyTaskIfRequired(interfaceName);
         }
index 88c73c2de8b7d68a6522dd2517175a62e712b8b7..e5d7a44aa0c8c8af842e64e8a2204912adc671ff 100644 (file)
@@ -98,7 +98,7 @@ public class VpnManager extends AbstractDataChangeListener<VpnInstance> implemen
                     getWildCardPath(), VpnManager.this, DataChangeScope.SUBTREE);
             fibListenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
                     getFibEntryListenerPath(), fibListener, DataChangeScope.BASE);
-            opListenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
+            opListenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
                     getVpnInstanceOpListenerPath(), vpnInstOpListener, DataChangeScope.SUBTREE);
 
         } catch (final Exception e) {
@@ -115,16 +115,20 @@ public class VpnManager extends AbstractDataChangeListener<VpnInstance> implemen
         this.vpnInterfaceManager = vpnInterfaceManager;
     }
 
-    private void waitForOpDataRemoval(String id) {
+    private void waitForOpRemoval(String id, long timeout) {
         //wait till DCN for update on VPN Instance Op Data signals that vpn interfaces linked to this vpn instance is zero
         Runnable notifyTask = new VpnNotifyTask();
         synchronized (id.intern()) {
-            vpnOpMap.put(id, notifyTask);
-            synchronized (notifyTask) {
-                try {
-                    notifyTask.wait(VpnConstants.WAIT_TIME_IN_MILLISECONDS);
-                } catch (InterruptedException e) {
+            try {
+                vpnOpMap.put(id, notifyTask);
+                synchronized (notifyTask) {
+                    try {
+                        notifyTask.wait(timeout);
+                    } catch (InterruptedException e) {
+                    }
                 }
+            } finally {
+                vpnOpMap.remove(id);
             }
         }
 
@@ -132,55 +136,96 @@ public class VpnManager extends AbstractDataChangeListener<VpnInstance> implemen
 
     @Override
     protected void remove(InstanceIdentifier<VpnInstance> identifier, VpnInstance del) {
-        LOG.trace("Remove VPN event - Key: {}, value: {}", identifier, del);
+        LOG.trace("Remove VPN event key: {}, value: {}", identifier, del);
         String vpnName = del.getVpnInstanceName();
+        String rd = del.getIpv4Family().getRouteDistinguisher();
+        long vpnId = VpnUtil.getVpnId(broker, vpnName);
 
-        //Clean up vpn Interface
-        InstanceIdentifier<VpnInterfaces> vpnInterfacesId = InstanceIdentifier.builder(VpnInterfaces.class).build();
-        Optional<VpnInterfaces> optionalVpnInterfaces = read(LogicalDatastoreType.OPERATIONAL, vpnInterfacesId);
+        //TODO(vpnteam): Entire code would need refactoring to listen only on the parent object - VPNInstance
+        Optional<VpnInstanceOpDataEntry> vpnOpValue = null;
+        if ((rd != null) && (!rd.isEmpty())) {
+            vpnOpValue = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
+                    VpnUtil.getVpnInstanceOpDataIdentifier(rd));
+        } else {
+            vpnOpValue = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
+                    VpnUtil.getVpnInstanceOpDataIdentifier(vpnName));
+        }
 
-        if(optionalVpnInterfaces.isPresent()) {
-            List<VpnInterface> vpnInterfaces = optionalVpnInterfaces.get().getVpnInterface();
-            for(VpnInterface vpnInterface : vpnInterfaces) {
-                if(vpnInterface.getVpnInstanceName().equals(vpnName)) {
-                    LOG.debug("VpnInterface {} will be removed from VPN {}", vpnInterface.getName(), vpnName);
-                    vpnInterfaceManager.remove(
-                            VpnUtil.getVpnInterfaceIdentifier(vpnInterface.getName()), vpnInterface);
+        if ((vpnOpValue != null) && (vpnOpValue.isPresent())) {
+            VpnInstanceOpDataEntry vpnOpEntry = null;
+            long timeout = VpnConstants.MIN_WAIT_TIME_IN_MILLISECONDS;
+            Long intfCount = 0L;
+
+            vpnOpEntry = vpnOpValue.get();
+            intfCount = vpnOpEntry.getVpnInterfaceCount();
+            if (intfCount != null && intfCount > 0) {
+                // Minimum wait time of 10 seconds for one VPN Interface clearance (inclusive of full trace on)
+                timeout = intfCount * 10000;
+                // Maximum wait time of 90 seconds for all VPN Interfaces clearance (inclusive of full trace on)
+                if (timeout > VpnConstants.MAX_WAIT_TIME_IN_MILLISECONDS) {
+                    timeout = VpnConstants.MAX_WAIT_TIME_IN_MILLISECONDS;
                 }
+                LOG.trace("VPNInstance removal count of interface at {} for for rd {}, vpnname {}",
+                        intfCount, rd, vpnName);
             }
-        }
-        InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance>
-            vpnIdentifier = VpnUtil.getVpnInstanceToVpnIdIdentifier(vpnName);
-        delete(LogicalDatastoreType.CONFIGURATION, vpnIdentifier);
-
-        VpnUtil.releaseId(idManager, VpnConstants.VPN_IDPOOL_NAME, vpnName);
-        String rd = del.getIpv4Family().getRouteDistinguisher();
+            LOG.trace("VPNInstance removal thread waiting for {} seconds for rd {}, vpnname {}",
+                    (timeout/1000), rd, vpnName);
 
-        if (rd !=null) {
+            if ((rd != null)  && (!rd.isEmpty())) {
+                waitForOpRemoval(rd, timeout);
+            } else {
+                waitForOpRemoval(vpnName, timeout);
+            }
 
+            LOG.trace("Returned out of waiting for  Op Data removal for rd {}, vpnname {}", rd, vpnName);
+        }
+        // Clean up VpnInstanceToVpnId from Config DS
+        VpnUtil.removeVpnInstanceToVpnId(broker, vpnName);
+        LOG.trace("Removed vpnIdentifier for  rd{} vpnname {}", rd, vpnName);
+        if (rd != null) {
             try {
                 bgpManager.deleteVrf(rd);
             } catch (Exception e) {
-                LOG.error("Exception when removing VRF from BGP", e);
+                LOG.error("Exception when removing VRF from BGP for RD {} in VPN {} exception " + e, rd, vpnName);
             }
-            waitForOpDataRemoval(rd);
-            delete(LogicalDatastoreType.OPERATIONAL, VpnUtil.getVpnInstanceOpDataIdentifier(rd));
+
+            // Clean up VPNExtraRoutes Operational DS
+            VpnUtil.removeVpnExtraRouteForVpn(broker, rd);
+
+            // Clean up VPNInstanceOpDataEntry
+            VpnUtil.removeVpnOpInstance(broker, rd);
         } else {
-            waitForOpDataRemoval(vpnName);
-            delete(LogicalDatastoreType.OPERATIONAL, VpnUtil.getVpnInstanceOpDataIdentifier(vpnName));
+            // Clean up FIB Entries Config DS
+            VpnUtil.removeVrfTableForVpn(broker, vpnName);
+
+            // Clean up VPNExtraRoutes Operational DS
+            VpnUtil.removeVpnExtraRouteForVpn(broker, vpnName);
+
+            // Clean up VPNInstanceOpDataEntry
+            VpnUtil.removeVpnOpInstance(broker, vpnName);
         }
+
+        // Clean up PrefixToInterface Operational DS
+        VpnUtil.removePrefixToInterfaceForVpnId(broker, vpnId);
+
+        // Clean up L3NextHop Operational DS
+        VpnUtil.removeL3nexthopForVpnId(broker, vpnId);
+
+        // Release the ID used for this VPN back to IdManager
+
+        VpnUtil.releaseId(idManager, VpnConstants.VPN_IDPOOL_NAME, vpnName);
     }
 
     @Override
     protected void update(InstanceIdentifier<VpnInstance> identifier,
             VpnInstance original, VpnInstance update) {
-        LOG.trace("Update event - Key: {}, value: {}", identifier, update);
+        LOG.trace("Update VPN event key: {}, value: {}", identifier, update);
     }
 
     @Override
     protected void add(InstanceIdentifier<VpnInstance> identifier,
             VpnInstance value) {
-        LOG.trace("VPN Instance key: {}, value: {}", identifier, value);
+        LOG.trace("Add VPN event key: {}, value: {}", identifier, value);
         VpnAfConfig config = value.getIpv4Family();
         String rd = config.getRouteDistinguisher();
 
@@ -196,14 +241,20 @@ public class VpnManager extends AbstractDataChangeListener<VpnInstance> implemen
 
 
         if(rd == null) {
+            VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder();
+            builder.setVrfId(value.getVpnInstanceName()).setVpnId(vpnId);
+            builder.setVpnInterfaceCount(0L);
             syncWrite(LogicalDatastoreType.OPERATIONAL,
                     VpnUtil.getVpnInstanceOpDataIdentifier(value.getVpnInstanceName()),
-                    VpnUtil.getVpnInstanceOpDataBuilder(value.getVpnInstanceName(), vpnId), DEFAULT_CALLBACK);
+                    builder.build(), DEFAULT_CALLBACK);
 
         } else {
+            VpnInstanceOpDataEntryBuilder builder = new VpnInstanceOpDataEntryBuilder();
+            builder.setVrfId(rd).setVpnId(vpnId);
+            builder.setVpnInterfaceCount(0L);
             syncWrite(LogicalDatastoreType.OPERATIONAL,
                        VpnUtil.getVpnInstanceOpDataIdentifier(rd),
-                       VpnUtil.getVpnInstanceOpDataBuilder(rd, vpnId), DEFAULT_CALLBACK);
+                       builder.build(), DEFAULT_CALLBACK);
 
             List<VpnTarget> vpnTargetList = config.getVpnTargets().getVpnTarget();
 
@@ -230,7 +281,6 @@ public class VpnManager extends AbstractDataChangeListener<VpnInstance> implemen
             }
         }
         //Try to add up vpn Interfaces if already in Operational Datastore
-        LOG.trace("Trying to add the vpn interfaces  -1.");
         InstanceIdentifier<VpnInterfaces> vpnInterfacesId = InstanceIdentifier.builder(VpnInterfaces.class).build();
         Optional<VpnInterfaces> optionalVpnInterfaces = read(LogicalDatastoreType.CONFIGURATION, vpnInterfacesId);
 
@@ -428,6 +478,7 @@ public class VpnManager extends AbstractDataChangeListener<VpnInstance> implemen
         private void notifyTaskIfRequired(String vpnName) {
             Runnable notifyTask = vpnOpMap.remove(vpnName);
             if (notifyTask == null) {
+                LOG.trace("VpnInstanceOpListener update: No Notify Task queued for vpnName {}", vpnName);
                 return;
             }
             executorService.execute(notifyTask);
index 9e3ba8f0d91b588c5502048b98e265bd6f08056a..8e22e7bd96c2409c10300423cd2566af3f6879e3 100644 (file)
@@ -107,7 +107,7 @@ public class VpnSubnetRouteHandler implements NeutronvpnListener {
                 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder().setKey(new SubnetOpDataEntryKey(subnetId));
                 subOpBuilder.setSubnetId(subnetId);
                 subOpBuilder.setSubnetCidr(subnetIp);
-                String rd = VpnUtil.getVpnRd(broker, vpnName);
+                String rd = VpnUtil.getVpnRdFromVpnInstanceConfig(broker, vpnName);
                 if (rd == null) {
                     logger.error("onSubnetAddedToVpn: The VPN Instance name " + notification.getVpnName() + " does not have RD ");
                     return;
index 76ad366faad1b3a4b98d607cd9c5ec9a17a87f8e..7cb826cc876610d9a91855fd44d99c94713e621b 100644 (file)
@@ -28,6 +28,7 @@ import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFaile
 import org.opendaylight.vpnservice.mdsalutil.NwConstants;
 import org.opendaylight.vpnservice.mdsalutil.packet.ARP;
 import org.opendaylight.vpnservice.mdsalutil.packet.Ethernet;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
@@ -61,6 +62,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.E
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesList;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesListKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.FibEntries;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTables;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTablesKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdPools;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.id.pools.IdPool;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.id.pools.IdPoolKey;
@@ -73,6 +77,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.meta.rev151007.IfIndexesInterfaceMap;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.meta.rev151007._if.indexes._interface.map.IfIndexInterface;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.meta.rev151007._if.indexes._interface.map.IfIndexInterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.L3nexthop;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.VpnNexthops;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.VpnNexthopsKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.NeutronPortData;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.neutron.port.data.PortFixedipToPortName;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.neutron.port.data.PortFixedipToPortNameKey;
@@ -230,6 +237,19 @@ public class VpnUtil {
         return rd;
     }
 
+    static String getVpnRdFromVpnInstanceConfig(DataBroker broker, String vpnName) {
+        InstanceIdentifier<VpnInstance> id = InstanceIdentifier.builder(VpnInstances.class)
+                .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
+        Optional<VpnInstance> vpnInstance = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
+        String rd = null;
+        if(vpnInstance.isPresent()) {
+            VpnInstance instance = vpnInstance.get();
+            VpnAfConfig config = instance.getIpv4Family();
+            rd = config.getRouteDistinguisher();
+        }
+        return rd;
+    }
+
     static org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance
            getVpnInstanceToVpnId(String vpnName, long vpnId, String rd) {
         return new org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstanceBuilder()
@@ -457,4 +477,66 @@ public class VpnUtil {
         return false;
     }
 
+    public static void removePrefixToInterfaceForVpnId(DataBroker broker, long vpnId) {
+        try {
+            // Clean up PrefixToInterface Operational DS
+            delete(broker, LogicalDatastoreType.OPERATIONAL,
+                    InstanceIdentifier.builder(PrefixToInterface.class).child(VpnIds.class, new VpnIdsKey(vpnId)).build(),
+                    DEFAULT_CALLBACK);
+        } catch (Exception e) {
+            LOG.error("Exception during cleanup of PrefixToInterface for VPN ID {}", vpnId, e);
+        }
+    }
+
+    public static void removeVpnExtraRouteForVpn(DataBroker broker, String vpnName) {
+        try {
+            // Clean up VPNExtraRoutes Operational DS
+            delete(broker, LogicalDatastoreType.OPERATIONAL,
+                    InstanceIdentifier.builder(VpnToExtraroute.class).child(Vpn.class, new VpnKey(vpnName)).build(),
+                    DEFAULT_CALLBACK);
+        } catch (Exception e) {
+            LOG.error("Exception during cleanup of VPNToExtraRoute for VPN {}", vpnName, e);
+        }
+    }
+
+    public static void removeVpnOpInstance(DataBroker broker, String vpnName) {
+        try {
+            // Clean up VPNInstanceOpDataEntry
+            delete(broker, LogicalDatastoreType.OPERATIONAL, getVpnInstanceOpDataIdentifier(vpnName),
+                    DEFAULT_CALLBACK);
+        } catch (Exception e) {
+            LOG.error("Exception during cleanup of VPNInstanceOpDataEntry for VPN {}", vpnName, e);
+        }
+    }
+
+    public static void removeVpnInstanceToVpnId(DataBroker broker, String vpnName) {
+        try {
+            delete(broker, LogicalDatastoreType.CONFIGURATION, getVpnInstanceToVpnIdIdentifier(vpnName),
+                    DEFAULT_CALLBACK);
+        } catch (Exception e) {
+            LOG.error("Exception during clean up of VpnInstanceToVpnId for VPN {}", vpnName, e);
+        }
+    }
+
+    public static void removeVrfTableForVpn(DataBroker broker, String vpnName) {
+        // Clean up FIB Entries Config DS
+        try {
+            delete(broker, LogicalDatastoreType.CONFIGURATION,
+                    InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(vpnName)).build(),
+                    DEFAULT_CALLBACK);
+        } catch (Exception e) {
+            LOG.error("Exception during clean up of VrfTable from FIB for VPN {}", vpnName, e);
+        }
+    }
+
+    public static void removeL3nexthopForVpnId(DataBroker broker, long vpnId) {
+        try {
+            // Clean up L3NextHop Operational DS
+            delete(broker, LogicalDatastoreType.OPERATIONAL,
+                    InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class, new VpnNexthopsKey(vpnId)).build(),
+                    DEFAULT_CALLBACK);
+        } catch (Exception e) {
+            LOG.error("Exception during cleanup of L3NextHop for VPN ID {}", vpnId, e);
+        }
+    }
 }
\ No newline at end of file
index bf933fef79df2beeca9bf1825f0aae1c81e70ab0..3d9217014e9c69f8f56ed2b0b9f90f2c4362c093 100644 (file)
@@ -26,6 +26,10 @@ import org.opendaylight.vpnservice.VpnInterfaceManager;
 import org.opendaylight.vpnservice.VpnSubnetRouteHandler;
 import org.opendaylight.vpnservice.interfacemgr.globals.IfmConstants;
 import org.opendaylight.vpnservice.utilities.InterfaceUtils;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.vpn.instance.Ipv4Family;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.vpn.instance
+        .Ipv4FamilyBuilder;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddressBuilder;
@@ -109,6 +113,7 @@ public class VpnSubnetRouteHandlerTest {
     SubnetToDpn subnetToDpn = null;
     RdToElanOpEntry rdToElanOpEntry = null;
     String subnetIp = "10.1.1.24";
+    String routeDistinguisher = "100:1";
     String nexthopIp = null;
     String poolName = null;
     String interfaceName = "VPN";
@@ -131,9 +136,11 @@ public class VpnSubnetRouteHandlerTest {
     DPNTEPsInfo dpntePsInfo = null;
     TunnelEndPoints tunlEndPts = null;
     IpAddress ipAddress = null;
+    Ipv4Family ipv4Family = null;
     String idKey = null;
     AllocateIdOutput allocateIdOutput = null;
     AllocateIdInput allocateIdInput = null;
+    org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance vpnInstnce;
 
     InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
             .state.Interface> ifStateId = InterfaceUtils.buildStateInterfaceId(portKey);
@@ -153,6 +160,11 @@ public class VpnSubnetRouteHandlerTest {
             child(RdToElanOpEntry.class, new RdToElanOpEntryKey(interfaceName, subnetIp)).build();
     InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id
             .VpnInstance> instVpnInstance = getVpnInstanceToVpnIdIdentifier(interfaceName);
+    InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.
+            VpnInstance> vpnInstanceIdentifier = InstanceIdentifier.builder(VpnInstances.class).child(org.opendaylight
+            .yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance.class,
+            new org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances
+                    .VpnInstanceKey(interfaceName)).build();
 
     @Mock DataBroker dataBroker;
     @Mock ListenerRegistration<DataChangeListener> dataChangeListenerRegistration;
@@ -174,6 +186,8 @@ public class VpnSubnetRouteHandlerTest {
     Optional<RdToElanOpEntry> optionalRd;
     Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance>
             optionalVpnInstnce;
+    Optional<org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance>
+            vpnInstanceOptional;
 
     @Before
     public void setUp() throws Exception {
@@ -198,6 +212,7 @@ public class VpnSubnetRouteHandlerTest {
         optionalSubnetMap = Optional.of(subnetmap);
         optionalRd = Optional.of(rdToElanOpEntry);
         optionalVpnInstnce = Optional.of(vpnInstance);
+        vpnInstanceOptional = Optional.of(vpnInstnce);
 
         doReturn(Futures.immediateCheckedFuture(optionalRd)).when(mockReadTx).read(LogicalDatastoreType.OPERATIONAL,
                 rdIdentifier);
@@ -219,13 +234,15 @@ public class VpnSubnetRouteHandlerTest {
                 .CONFIGURATION, subMapid);
         doReturn(Futures.immediateCheckedFuture(optionalVpnInstnce)).when(mockReadTx).read(LogicalDatastoreType
                 .CONFIGURATION, instVpnInstance);
+        doReturn(Futures.immediateCheckedFuture(vpnInstanceOptional)).when(mockReadTx).read(LogicalDatastoreType
+                .CONFIGURATION,vpnInstanceIdentifier);
         doReturn(idOutputOptional).when(idManager).allocateId(allocateIdInput);
     }
 
     private void setupMocks() {
 
         nexthopIp = "10.1.1.25";
-        idKey = "VPN.10.1.1.24";
+        idKey = "100:1.10.1.1.24";
         poolName = "vpnservices";
         elanTag = Long.valueOf(2);
         longId = Long.valueOf("100");
@@ -271,7 +288,7 @@ public class VpnSubnetRouteHandlerTest {
                 .setSubnetIp(subnetIp).setVpnName(interfaceName).setElanTag(elanTag).build();
         subnetOp = new SubnetOpDataEntryBuilder().setElanTag(elanTag).setNhDpnId(dpId).setSubnetCidr(subnetIp)
                 .setSubnetId(subnetId).setKey(new SubnetOpDataEntryKey(subnetId)).setVpnName(interfaceName)
-                .setVrfId(interfaceName).setSubnetToDpn(subToDpn).setRouteAdvState(TaskState.Done).build();
+                .setVrfId(routeDistinguisher).setSubnetToDpn(subToDpn).setRouteAdvState(TaskState.Done).build();
         vpnInstance = new VpnInstanceBuilder().setVpnId(elanTag).setVpnInstanceName(interfaceName).setVrfId
                 (interfaceName).setKey(new VpnInstanceKey(interfaceName)).build();
         subnetmap = new SubnetmapBuilder().setSubnetIp(subnetIp).setId(subnetId).setNetworkId(portId).setKey(new
@@ -286,6 +303,11 @@ public class VpnSubnetRouteHandlerTest {
         rdToElanOpEntry = new RdToElanOpEntryBuilder().setElanTag(elanTag).setRd(interfaceName).setVpnName
                 (interfaceName).setNextHopIp(nexthopIp)
                 .setKey(new RdToElanOpEntryKey(interfaceName, subnetIp)).setSubnetIp(subnetIp).build();
+        ipv4Family = new Ipv4FamilyBuilder().setRouteDistinguisher(routeDistinguisher).build();
+        vpnInstnce = new org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances
+                .VpnInstanceBuilder().setKey(new org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn
+                .rev140815.vpn.instances.VpnInstanceKey(interfaceName)).setVpnInstanceName(interfaceName)
+                .setIpv4Family(ipv4Family).build();
         doReturn(mockReadTx).when(dataBroker).newReadOnlyTransaction();
         doReturn(mockWriteTx).when(dataBroker).newWriteOnlyTransaction();
         doReturn(Futures.immediateCheckedFuture(null)).when(mockWriteTx).submit();