FIP support for Octavia VIPs 81/75281/16
authorJosh <jhershbe@redhat.com>
Fri, 17 Aug 2018 22:20:29 +0000 (15:20 -0700)
committerSam Hague <shague@redhat.com>
Sun, 23 Sep 2018 12:02:29 +0000 (12:02 +0000)
This patch resolves the issue documented in the Jira
issue referenced below.

The basic solution is to use the OF packet-in event to
learn the dpn where the NAT flows need to be programmed.

Overview of code changes:
1. Refactor NatInterfaceStateChangeListener into two classes:
 - NatInterfaceStateChangeListener which just receives events
 - NatSouthboundEventHandlers which contains the logic invoked
   for when an interface state changes (or a garp is received)
2. Implementation of NatArpNotificationHandler which receives
   the garps and invokes the correct methods in
   NatSouthboundEventHandlers
3. neutron-vip-state yang model which is used together with
   VipStateTracker (DataObjectCache) to manage state of the
   discovered VIPs. This is required in cases where the
   associated Ocatavia Amphora VM changes to a different
   compute node. In this case the existing flows must be
   removed from the odl compute node.
4. Tweak VIP learning code to accept neutron ports that are
   owned by "Octavia", previously the code assumed no neutron
   port ever needed to be learned.

Jira: NETVIRT-1402

Change-Id: I7867124f3cbbe88d1ce6d075e51e2b11f941aec2
Signed-off-by: Josh <jhershbe@redhat.com>
natservice/api/src/main/yang/odl-nat.yang
natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/FloatingIPListener.java
natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/NatArpNotificationHandler.java [new file with mode: 0644]
natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/NatInterfaceStateChangeListener.java
natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/NatSouthboundEventHandlers.java [new file with mode: 0644]
natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/VipStateTracker.java [new file with mode: 0644]
natservice/impl/src/main/resources/org/opendaylight/blueprint/natservice.xml
vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/iplearn/AbstractIpLearnNotificationHandler.java
vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/iplearn/ipv4/ArpNotificationHandler.java
vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/iplearn/ipv6/Ipv6NaNotificationHandler.java

index 2496f79216a4b59c07e396d2b4d8a7e878ca3d69..d06444c5027e94d6b02d23f727df484f7c03aa84 100644 (file)
@@ -208,4 +208,20 @@ module odl-nat {
             }
         }
     }
+
+    container neutron-vip-states {
+        config false;
+        list vip-state {
+            key ip;
+            leaf ip {
+                type string;
+            }
+            leaf dpn-id {
+                type uint64;
+            }
+            leaf ifc-name {
+                type string;
+            }
+        }
+    }
 }
index 2909fac1b7413350db153abcc90d0ab92d68507b..6420bddd8bd6181f093848a60385068263566d9e 100644 (file)
@@ -392,7 +392,7 @@ public class FloatingIPListener extends AsyncDataTreeChangeListenerBase<Internal
         InstanceIdentifier<RouterPorts> portIid = identifier.firstIdentifierOf(RouterPorts.class);
         coordinator.enqueueJob(NatConstants.NAT_DJC_PREFIX + mapping.key(), () -> Collections.singletonList(
                 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
-                    tx -> createNATFlowEntries(interfaceName, mapping, portIid, routerId, tx))),
+                    tx -> createNATFlowEntries(interfaceName, mapping, portIid, routerId, null, tx))),
                 NatConstants.NAT_DJC_MAX_RETRIES);
     }
 
@@ -426,7 +426,7 @@ public class FloatingIPListener extends AsyncDataTreeChangeListenerBase<Internal
     }
 
     void createNATFlowEntries(String interfaceName, final InternalToExternalPortMap mapping,
-                              final InstanceIdentifier<RouterPorts> portIid, final String routerName,
+                              final InstanceIdentifier<RouterPorts> portIid, final String routerName, BigInteger dpnId,
             TypedReadWriteTransaction<Configuration> confTx) throws ExecutionException, InterruptedException {
         if (!validateIpMapping(mapping)) {
             LOG.error("createNATFlowEntries : Not a valid ip addresses in the mapping {}", mapping);
@@ -434,7 +434,9 @@ public class FloatingIPListener extends AsyncDataTreeChangeListenerBase<Internal
         }
 
         //Get the DPN on which this interface resides
-        BigInteger dpnId = NatUtil.getDpnForInterface(interfaceManager, interfaceName);
+        if (dpnId == null) {
+            dpnId = NatUtil.getDpnForInterface(interfaceManager, interfaceName);
+        }
 
         if (dpnId.equals(BigInteger.ZERO)) {
             LOG.warn("createNATFlowEntries : No DPN for interface {}. NAT flow entries for ip mapping {} will "
diff --git a/natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/NatArpNotificationHandler.java b/natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/NatArpNotificationHandler.java
new file mode 100644 (file)
index 0000000..90770d9
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2018 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.natservice.internal;
+
+import java.util.List;
+import java.util.Objects;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.netvirt.elanmanager.api.IElanService;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.ArpRequestReceived;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.ArpResponseReceived;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.MacChanged;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.elan._interface.StaticMacEntries;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.router.interfaces.RouterInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.neutron.vip.states.VipState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class NatArpNotificationHandler implements OdlArputilListener {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NatArpNotificationHandler.class);
+
+    private final DataBroker dataBroker;
+    private final IElanService elanService;
+    private final NatSouthboundEventHandlers southboundEventHandlers;
+    private final VipStateTracker vipStateTracker;
+
+    @Inject
+    public NatArpNotificationHandler(final DataBroker dataBroker,
+                                     final IElanService elanService,
+                                     final NatSouthboundEventHandlers southboundEventHandlers,
+                                     final VipStateTracker vipStateTracker) {
+        this.dataBroker = dataBroker;
+        this.elanService = elanService;
+        this.southboundEventHandlers = southboundEventHandlers;
+        this.vipStateTracker = vipStateTracker;
+    }
+
+    @Override
+    public void onArpResponseReceived(ArpResponseReceived notification) {
+
+    }
+
+    @Override
+    public void onMacChanged(MacChanged notification) {
+
+    }
+
+    @Override
+    public void onArpRequestReceived(ArpRequestReceived notification) {
+
+        LOG.debug("NatArpNotificationHandler received {}", notification);
+
+        IpAddress srcIp = notification.getSrcIpaddress();
+        if (srcIp == null || !Objects.equals(srcIp, notification.getDstIpaddress())) {
+            LOG.debug("NatArpNotificationHandler: ignoring ARP packet, not gratuitous {}", notification);
+            return;
+        }
+
+        //Since the point of all this is to learn VIPs those are by definition
+        //not the IP addresses assigned to a port by neutron configuration.
+        ElanInterface arpSenderIfc = elanService.getElanInterfaceByElanInterfaceName(notification.getInterface());
+        if (ipBelongsToElanInterface(arpSenderIfc, srcIp)) {
+            LOG.debug("NatArpNotificationHandler: ignoring GARP packet. No need to NAT a port's static IP. {}",
+                    notification);
+            return;
+        }
+
+        ElanInterface targetIfc = null;
+        for (String ifcName : elanService.getElanInterfaces(arpSenderIfc.getElanInstanceName())) {
+            ElanInterface elanInterface = elanService.getElanInterfaceByElanInterfaceName(ifcName);
+            if (ipBelongsToElanInterface(elanInterface, srcIp)) {
+                targetIfc = elanInterface;
+                break;
+            }
+        }
+
+        if (null == targetIfc) {
+            LOG.warn("NatArpNotificationHandler: GARP does not correspond to an interface in this elan {}",
+                     notification);
+            return;
+        }
+
+        RouterInterface routerInterface = NatUtil.getConfiguredRouterInterface(dataBroker, targetIfc.getName());
+        if (null == routerInterface) {
+            LOG.warn("NatArpNotificationHandler: Could not retrieve router ifc for {}", targetIfc);
+            return;
+        }
+
+        VipState newVipState = this.vipStateTracker.buildVipState(srcIp.getIpv4Address().getValue(),
+                                    notification.getSrcMac().getValue(), notification.getDpnId(), targetIfc.getName());
+        VipState cachedState = null;
+        try {
+            cachedState = this.vipStateTracker.get(newVipState.getIp()).orNull();
+        } catch (ReadFailedException e) {
+            LOG.warn("NatArpNotificationHandler failed to read vip state {}", notification, e);
+        }
+
+        if (null == cachedState) {
+            this.southboundEventHandlers.handleAdd(
+                                    targetIfc.getName(), notification.getDpnId(), routerInterface, newVipState);
+        } else if (!cachedState.getDpnId().equals(newVipState.getDpnId())) {
+            this.southboundEventHandlers.handleRemove(cachedState.getIfcName(),
+                    cachedState.getDpnId(), routerInterface);
+            this.southboundEventHandlers.handleAdd(
+                    targetIfc.getName(), notification.getDpnId(), routerInterface, newVipState);
+        }
+
+    }
+
+    private boolean ipBelongsToElanInterface(ElanInterface elanInterface, IpAddress ip) {
+        if (elanInterface == null) {
+            return false;
+        }
+
+        List<StaticMacEntries> staticMacEntries = elanInterface.getStaticMacEntries();
+        if (null == staticMacEntries) {
+            return false;
+        }
+
+        for (StaticMacEntries staticMacEntry :  staticMacEntries) {
+            if (Objects.equals(staticMacEntry.getIpPrefix(), ip)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
index bb985ac39377cfc323427622fdd6ed94b3a20c6b..49567a129cac74c5dccc4a6426a3ee284bf720a2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 - 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
+ * Copyright (c) 2016 - 2018 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,
@@ -7,19 +7,8 @@
  */
 package org.opendaylight.netvirt.natservice.internal;
 
-import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
-import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
-
 import com.google.common.base.Optional;
-import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.Table;
-import com.google.common.util.concurrent.ListenableFuture;
 import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
 
 import javax.annotation.PostConstruct;
 import javax.inject.Inject;
@@ -28,38 +17,14 @@ import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
-import org.opendaylight.genius.infra.Datastore.Operational;
-import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
-import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
-import org.opendaylight.genius.infra.TypedReadWriteTransaction;
-import org.opendaylight.genius.mdsalutil.FlowEntity;
-import org.opendaylight.genius.mdsalutil.NwConstants;
-import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
-import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
 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.vpn._interface.VpnInstanceNames;
 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.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.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.router.interfaces.RouterInterface;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.NaptSwitches;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProtocolTypes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.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.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitch;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPort;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoType;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -68,52 +33,15 @@ public class NatInterfaceStateChangeListener
     extends AsyncDataTreeChangeListenerBase<Interface, NatInterfaceStateChangeListener> {
 
     private static final Logger LOG = LoggerFactory.getLogger(NatInterfaceStateChangeListener.class);
-    private static final String NAT_DS = "NATDS";
     private final DataBroker dataBroker;
-    private final ManagedNewTransactionRunner txRunner;
-    private final OdlInterfaceRpcService odlInterfaceRpcService;
-    private final JobCoordinator coordinator;
-    private final FloatingIPListener floatingIPListener;
-    private final NeutronvpnService neutronVpnService;
-    private final IMdsalApiManager mdsalManager;
-    private final NaptManager naptManager;
-    Table<Interface.OperStatus, Interface.OperStatus, IntfTransitionState> stateTable = HashBasedTable.create();
-
-    enum IntfTransitionState {
-        STATE_UP,
-        STATE_DOWN,
-        STATE_IGNORE
-    }
-
-    private void initialize() {
-        //  Interface State Transition Table
-        //               Up                Down            Unknown
-        // ---------------------------------------------------------------
-        /* Up       { STATE_IGNORE,   STATE_DOWN,     STATE_DOWN }, */
-        /* Down     { STATE_UP,       STATE_IGNORE,   STATE_IGNORE }, */
-        /* Unknown  { STATE_UP,       STATE_DOWN,     STATE_IGNORE }, */
-        stateTable.put(Interface.OperStatus.Up, Interface.OperStatus.Down, IntfTransitionState.STATE_DOWN);
-        stateTable.put(Interface.OperStatus.Down, Interface.OperStatus.Up, IntfTransitionState.STATE_UP);
-        stateTable.put(Interface.OperStatus.Unknown, Interface.OperStatus.Up, IntfTransitionState.STATE_UP);
-        stateTable.put(Interface.OperStatus.Unknown, Interface.OperStatus.Down, IntfTransitionState.STATE_DOWN);
-        stateTable.put(Interface.OperStatus.Up, Interface.OperStatus.Unknown, IntfTransitionState.STATE_DOWN);
-    }
+    private final NatSouthboundEventHandlers southboundEventHandlers;
 
     @Inject
     public NatInterfaceStateChangeListener(final DataBroker dataBroker,
-            final OdlInterfaceRpcService odlInterfaceRpcService, final JobCoordinator coordinator,
-            final FloatingIPListener floatingIPListener,final NeutronvpnService neutronvpnService,
-            final IMdsalApiManager mdsalManager, final NaptManager naptManager) {
+                                           final NatSouthboundEventHandlers southboundEventHandlers) {
         super(Interface.class, NatInterfaceStateChangeListener.class);
         this.dataBroker = dataBroker;
-        this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
-        this.odlInterfaceRpcService = odlInterfaceRpcService;
-        this.coordinator = coordinator;
-        this.floatingIPListener = floatingIPListener;
-        this.neutronVpnService = neutronvpnService;
-        this.mdsalManager = mdsalManager;
-        this.naptManager = naptManager;
-        initialize();
+        this.southboundEventHandlers = southboundEventHandlers;
     }
 
     @Override
@@ -159,13 +87,7 @@ public class NatInterfaceStateChangeListener
         // VpnInterfaceManager
         RouterInterface routerInterface = NatUtil.getConfiguredRouterInterface(dataBroker, interfaceName);
         if (routerInterface != null) {
-            String routerName = routerInterface.getRouterName();
-            NatInterfaceStateAddWorker natIfaceStateAddWorker = new NatInterfaceStateAddWorker(interfaceName,
-                    intfDpnId, routerName);
-            coordinator.enqueueJob(NAT_DS + "-" + intrf.getName(), natIfaceStateAddWorker);
-
-            NatFlowAddWorker natFlowAddWorker = new NatFlowAddWorker(interfaceName, routerName);
-            coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natFlowAddWorker, NatConstants.NAT_DJC_MAX_RETRIES);
+            this.southboundEventHandlers.handleAdd(interfaceName, intfDpnId, routerInterface);
         } else {
             LOG.info("add : Router-Interface Mapping not found for Interface : {}", interfaceName);
         }
@@ -213,14 +135,7 @@ public class NatInterfaceStateChangeListener
         }
         RouterInterface routerInterface = NatUtil.getConfiguredRouterInterface(dataBroker, interfaceName);
         if (routerInterface != null) {
-            String routerName = routerInterface.getRouterName();
-            NatInterfaceStateRemoveWorker natIfaceStateRemoveWorker = new NatInterfaceStateRemoveWorker(interfaceName,
-                    intfDpnId, routerName);
-            coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natIfaceStateRemoveWorker);
-
-            NatFlowRemoveWorker natFlowRemoveWorker = new NatFlowRemoveWorker(intrf, intfDpnId, routerName);
-            coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natFlowRemoveWorker,
-                    NatConstants.NAT_DJC_MAX_RETRIES);
+            this.southboundEventHandlers.handleRemove(intrf.getName(), intfDpnId, routerInterface);
         } else {
             LOG.info("remove : Router-Interface Mapping not found for Interface : {}", interfaceName);
         }
@@ -268,426 +183,9 @@ public class NatInterfaceStateChangeListener
         }
         RouterInterface routerInterface = NatUtil.getConfiguredRouterInterface(dataBroker, interfaceName);
         if (routerInterface != null) {
-            String routerName = routerInterface.getRouterName();
-            NatInterfaceStateUpdateWorker natIfaceStateupdateWorker = new NatInterfaceStateUpdateWorker(original,
-                    update, intfDpnId, routerName);
-            coordinator.enqueueJob(NAT_DS + "-" + update.getName(), natIfaceStateupdateWorker);
-            NatFlowUpdateWorker natFlowUpdateWorker = new NatFlowUpdateWorker(original, update, routerName);
-            coordinator.enqueueJob(NAT_DS + "-" + update.getName(), natFlowUpdateWorker,
-                    NatConstants.NAT_DJC_MAX_RETRIES);
+            this.southboundEventHandlers.handleUpdate(original, update, intfDpnId, routerInterface);
         } else {
             LOG.info("update : Router-Interface Mapping not found for Interface : {}", interfaceName);
         }
     }
-
-    void handleRouterInterfacesUpEvent(String routerName, String interfaceName, BigInteger dpId,
-            TypedReadWriteTransaction<Operational> operTx) throws ExecutionException, InterruptedException {
-        LOG.debug("handleRouterInterfacesUpEvent : Handling UP event for router interface {} in Router {} on Dpn {}",
-                interfaceName, routerName, dpId);
-        NatUtil.addToNeutronRouterDpnsMap(routerName, interfaceName, dpId, operTx);
-        NatUtil.addToDpnRoutersMap(routerName, interfaceName, dpId, operTx);
-    }
-
-    void handleRouterInterfacesDownEvent(String routerName, String interfaceName, BigInteger dpnId,
-                                         TypedReadWriteTransaction<Operational> operTx)
-        throws ExecutionException, InterruptedException {
-        LOG.debug("handleRouterInterfacesDownEvent : Handling DOWN event for router Interface {} in Router {}",
-                interfaceName, routerName);
-        NatUtil.removeFromNeutronRouterDpnsMap(routerName, dpnId, operTx);
-        NatUtil.removeFromDpnRoutersMap(dataBroker, routerName, interfaceName, dpnId, odlInterfaceRpcService,
-                operTx);
-    }
-
-    private IntfTransitionState getTransitionState(Interface.OperStatus original , Interface.OperStatus updated) {
-        IntfTransitionState transitionState = stateTable.get(original, updated);
-
-        if (transitionState == null) {
-            return IntfTransitionState.STATE_IGNORE;
-        }
-        return transitionState;
-    }
-
-    private class NatInterfaceStateAddWorker implements Callable<List<ListenableFuture<Void>>> {
-        private String interfaceName;
-        private String routerName;
-        private BigInteger intfDpnId;
-
-        NatInterfaceStateAddWorker(String interfaceName, BigInteger intfDpnId, String routerName) {
-            this.interfaceName = interfaceName;
-            this.routerName = routerName;
-            this.intfDpnId = intfDpnId;
-        }
-
-        @Override
-        @SuppressWarnings("checkstyle:IllegalCatch")
-        public List<ListenableFuture<Void>> call() {
-            List<ListenableFuture<Void>> futures = new ArrayList<>();
-            try {
-                LOG.trace("call : Received interface {} PORT UP OR ADD event ", interfaceName);
-                String dpnLock = NatConstants.NAT_DJC_PREFIX + intfDpnId;
-                synchronized (dpnLock.intern()) {
-                    futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx ->
-                        handleRouterInterfacesUpEvent(routerName, interfaceName, intfDpnId, tx)));
-                }
-            } catch (Exception e) {
-                LOG.error("call : Exception caught in Interface {} Operational State Up event",
-                        interfaceName, e);
-            }
-            return futures;
-        }
-    }
-
-    private class NatInterfaceStateRemoveWorker implements Callable<List<ListenableFuture<Void>>> {
-        private String interfaceName;
-        private String routerName;
-        private BigInteger intfDpnId;
-
-        NatInterfaceStateRemoveWorker(String interfaceName, BigInteger intfDpnId, String routerName) {
-            this.interfaceName = interfaceName;
-            this.routerName = routerName;
-            this.intfDpnId = intfDpnId;
-        }
-
-        @Override
-        @SuppressWarnings("checkstyle:IllegalCatch")
-        public List<ListenableFuture<Void>> call() {
-            List<ListenableFuture<Void>> futures = new ArrayList<>();
-            try {
-                LOG.trace("call : Received interface {} PORT DOWN or REMOVE event", interfaceName);
-                String dpnLock = NatConstants.NAT_DJC_PREFIX + intfDpnId;
-                synchronized (dpnLock.intern()) {
-                    futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx ->
-                        handleRouterInterfacesDownEvent(routerName, interfaceName, intfDpnId, tx)));
-                }
-            } catch (Exception e) {
-                LOG.error("call : Exception observed in handling deletion of VPN Interface {}.", interfaceName, e);
-            }
-            return futures;
-        }
-    }
-
-    private class NatInterfaceStateUpdateWorker implements Callable<List<ListenableFuture<Void>>> {
-        private Interface original;
-        private Interface update;
-        private BigInteger intfDpnId;
-        private String routerName;
-
-        NatInterfaceStateUpdateWorker(Interface original, Interface update, BigInteger intfDpnId, String routerName) {
-            this.original = original;
-            this.update = update;
-            this.intfDpnId = intfDpnId;
-            this.routerName = routerName;
-        }
-
-        @Override
-        @SuppressWarnings("checkstyle:IllegalCatch")
-        public List<ListenableFuture<Void>> call() {
-            List<ListenableFuture<Void>> futures = new ArrayList<>();
-            try {
-                final String interfaceName = update.getName();
-                LOG.trace("call : Received interface {} state change event", interfaceName);
-                LOG.debug("call : DPN ID {} for the interface {} ", intfDpnId, interfaceName);
-                String dpnLock = NatConstants.NAT_DJC_PREFIX + intfDpnId;
-                synchronized (dpnLock.intern()) {
-                    IntfTransitionState state = getTransitionState(original.getOperStatus(), update.getOperStatus());
-                    if (state.equals(IntfTransitionState.STATE_IGNORE)) {
-                        LOG.info("NAT Service: Interface {} state original {} updated {} not handled",
-                                interfaceName, original.getOperStatus(), update.getOperStatus());
-                        return futures;
-                    }
-                    futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
-                        if (state.equals(IntfTransitionState.STATE_DOWN)) {
-                            LOG.debug("call : DPN {} connnected to the interface {} has gone down."
-                                    + "Hence clearing the dpn-vpninterfaces-list entry from the"
-                                    + " neutron-router-dpns model in the ODL:L3VPN", intfDpnId, interfaceName);
-                            // If the interface state is unknown, it means that the corresponding DPN has gone down.
-                            // So remove the dpn-vpninterfaces-list from the neutron-router-dpns model.
-                            NatUtil.removeFromNeutronRouterDpnsMap(routerName, interfaceName,
-                                    intfDpnId, tx);
-                        } else if (state.equals(IntfTransitionState.STATE_UP)) {
-                            LOG.debug("call : DPN {} connnected to the interface {} has come up. Hence adding"
-                                    + " the dpn-vpninterfaces-list entry from the neutron-router-dpns model"
-                                    + " in the ODL:L3VPN", intfDpnId, interfaceName);
-                            handleRouterInterfacesUpEvent(routerName, interfaceName, intfDpnId, tx);
-                        }
-                    }));
-                }
-            } catch (Exception e) {
-                LOG.error("call : Exception observed in handling updation of VPN Interface {}.", update.getName(), e);
-            }
-            return futures;
-        }
-    }
-
-    private void processInterfaceAdded(String portName, String routerId, List<ListenableFuture<Void>> futures) {
-        LOG.trace("processInterfaceAdded : Processing Interface Add Event for interface {}", portName);
-        List<InternalToExternalPortMap> intExtPortMapList = getIntExtPortMapListForPortName(portName, routerId);
-        if (intExtPortMapList == null || intExtPortMapList.isEmpty()) {
-            LOG.debug("processInterfaceAdded : Ip Mapping list is empty/null for portname {}", portName);
-            return;
-        }
-        InstanceIdentifier<RouterPorts> portIid = NatUtil.buildRouterPortsIdentifier(routerId);
-        ListenableFuture<Void> future = txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
-            for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
-                floatingIPListener.createNATFlowEntries(portName, intExtPortMap, portIid, routerId, tx);
-            }
-        });
-        futures.add(future);
-        try {
-            future.get();
-        } catch (InterruptedException | ExecutionException e) {
-            LOG.error("Error processing interface addition", e);
-        }
-    }
-
-    private List<InternalToExternalPortMap> getIntExtPortMapListForPortName(String portName, String routerId) {
-        InstanceIdentifier<Ports> portToIpMapIdentifier = NatUtil.buildPortToIpMapIdentifier(routerId, portName);
-        Optional<Ports> port =
-                SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
-                        LogicalDatastoreType.CONFIGURATION, portToIpMapIdentifier);
-        if (!port.isPresent()) {
-            LOG.info("getIntExtPortMapListForPortName : Unable to read router port entry for router ID {} "
-                    + "and port name {}", routerId, portName);
-            return null;
-        }
-        return port.get().getInternalToExternalPortMap();
-    }
-
-    private BigInteger getNaptSwitchforRouter(DataBroker broker, String routerName) {
-        InstanceIdentifier<RouterToNaptSwitch> rtrNaptSw = InstanceIdentifier.builder(NaptSwitches.class)
-            .child(RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerName)).build();
-        Optional<RouterToNaptSwitch> routerToNaptSwitchData =
-                SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
-                        LogicalDatastoreType.CONFIGURATION, 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("removeNatFlow : Removed the flow in table {} for the switch with the DPN ID {} for "
-            + "router {} ip {} port {}", tableId, dpnId, routerId, ipAddress, ipPort);
-    }
-
-    private List<String> getFixedIpsForPort(String interfname) {
-        LOG.debug("getFixedIpsForPort : 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.error("getFixedIpsForPort : RPC Call to GetFixedIPsForNeutronPortOutput returned with Errors {}",
-                    rpcResult.getErrors());
-            } else {
-                return rpcResult.getResult().getFixedIPs();
-            }
-        } catch (InterruptedException | ExecutionException | NullPointerException ex) {
-            LOG.error("getFixedIpsForPort : Exception while receiving fixedIps for port {}", interfname, ex);
-        }
-        return null;
-    }
-
-    private void processInterfaceRemoved(String portName, BigInteger dpnId, String routerId,
-            List<ListenableFuture<Void>> futures) {
-        LOG.trace("processInterfaceRemoved : Processing Interface Removed Event for interface {} on DPN ID {}",
-                portName, dpnId);
-        List<InternalToExternalPortMap> intExtPortMapList = getIntExtPortMapListForPortName(portName, routerId);
-        if (intExtPortMapList == null || intExtPortMapList.isEmpty()) {
-            LOG.debug("processInterfaceRemoved : Ip Mapping list is empty/null for portName {}", portName);
-            return;
-        }
-        InstanceIdentifier<RouterPorts> portIid = NatUtil.buildRouterPortsIdentifier(routerId);
-        ListenableFuture<Void> future = txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
-            for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
-                LOG.trace("processInterfaceRemoved : Removing DNAT Flow entries for dpnId {} ", dpnId);
-                floatingIPListener.removeNATFlowEntries(portName, intExtPortMap, portIid, routerId, dpnId, tx);
-            }
-        });
-        futures.add(future);
-        try {
-            future.get();
-        } catch (InterruptedException | ExecutionException e) {
-            LOG.error("Error processing interface removal", e);
-        }
-    }
-
-    // TODO Clean up the exception handling
-    @SuppressWarnings("checkstyle:IllegalCatch")
-    private void removeSnatEntriesForPort(String interfaceName, String routerName) {
-        Long routerId = NatUtil.getVpnId(dataBroker, routerName);
-        if (routerId == NatConstants.INVALID_ID) {
-            LOG.error("removeSnatEntriesForPort : routerId not found for routername {}", routerName);
-            return;
-        }
-        BigInteger naptSwitch = getNaptSwitchforRouter(dataBroker, routerName);
-        if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
-            LOG.error("removeSnatEntriesForPort : NaptSwitch is not elected for router {} with Id {}",
-                    routerName, routerId);
-            return;
-        }
-        //getInternalIp for port
-        List<String> fixedIps = getFixedIpsForPort(interfaceName);
-        if (fixedIps == null) {
-            LOG.warn("removeSnatEntriesForPort : Internal Ips not found for InterfaceName {} in router {} with id {}",
-                interfaceName, routerName, routerId);
-            return;
-        }
-
-        for (String internalIp : fixedIps) {
-            LOG.debug("removeSnatEntriesForPort : Internal Ip retrieved for interface {} is {} in router with Id {}",
-                interfaceName, internalIp, routerId);
-            IpPort ipPort = NatUtil.getInternalIpPortInfo(dataBroker, routerId, internalIp);
-            if (ipPort == null) {
-                LOG.debug("removeSnatEntriesForPort : no snatint-ip-port-map found for ip:{}", internalIp);
-                continue;
-            }
-
-            for (IntIpProtoType protoType: ipPort.getIntIpProtoType()) {
-                ProtocolTypes protocol = protoType.getProtocol();
-                for (Integer portnum : protoType.getPorts()) {
-                    //build and remove the flow in outbound table
-                    try {
-                        removeNatFlow(naptSwitch, NwConstants.OUTBOUND_NAPT_TABLE, routerId, internalIp, portnum);
-                    } catch (Exception ex) {
-                        LOG.error("removeSnatEntriesForPort : 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("removeSnatEntriesForPort : 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, NwConstants.INBOUND_NAPT_TABLE, routerId,
-                            externalIpAddress, portNumber);
-                    } catch (Exception ex) {
-                        LOG.error("removeSnatEntriesForPort : 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("removeSnatEntriesForPort : releaseIpExtPortMapping failed, Removal of "
-                            + "ipportmap {} for router {} failed", internalIpPort, routerId, ex);
-                    }
-                }
-            }
-            // delete the entry from SnatIntIpPortMap DS
-            LOG.debug("removeSnatEntriesForPort : Removing InternalIp:{} on router {}", internalIp, routerId);
-            naptManager.removeFromSnatIpPortDS(routerId, internalIp);
-        }
-    }
-
-    private class NatFlowAddWorker implements Callable<List<ListenableFuture<Void>>> {
-        private String interfaceName;
-        private String routerName;
-
-        NatFlowAddWorker(String interfaceName,String routerName) {
-            this.interfaceName = interfaceName;
-            this.routerName = routerName;
-        }
-
-        @Override
-        @SuppressWarnings("checkstyle:IllegalCatch")
-        public List<ListenableFuture<Void>> call() {
-            final List<ListenableFuture<Void>> futures = new ArrayList<>();
-            LOG.trace("call : Interface {} up event received", interfaceName);
-            try {
-                LOG.trace("call : Port added event received for interface {} ", interfaceName);
-                processInterfaceAdded(interfaceName, routerName, futures);
-            } catch (Exception ex) {
-                LOG.error("call : Exception caught in Interface {} Operational State Up event",
-                        interfaceName, ex);
-            }
-            return futures;
-        }
-    }
-
-    private class NatFlowUpdateWorker implements Callable<List<ListenableFuture<Void>>> {
-        private Interface original;
-        private Interface update;
-        private String routerName;
-
-        NatFlowUpdateWorker(Interface original, Interface update, String routerName) {
-            this.original = original;
-            this.update = update;
-            this.routerName = routerName;
-        }
-
-        @Override
-        @SuppressWarnings("checkstyle:IllegalCatch")
-        public List<ListenableFuture<Void>> call() {
-            final List<ListenableFuture<Void>> futures = new ArrayList<>();
-            String interfaceName = update.getName();
-            IntfTransitionState state = getTransitionState(original.getOperStatus(), update.getOperStatus());
-            if (state.equals(IntfTransitionState.STATE_IGNORE)) {
-                LOG.info("NAT Service: Interface {} state original {} updated {} not handled",
-                        interfaceName, original.getOperStatus(), update.getOperStatus());
-                return futures;
-            }
-            if (state.equals(IntfTransitionState.STATE_UP)) {
-                LOG.debug("call : Port UP event received for interface {} ", interfaceName);
-            } else if (state.equals(IntfTransitionState.STATE_DOWN)) {
-                LOG.debug("call : Port DOWN event received for interface {} ", interfaceName);
-                try {
-                    removeSnatEntriesForPort(interfaceName, routerName);
-                } catch (Exception ex) {
-                    LOG.error("call : Exception caught in Interface {} OperationalStateDown", interfaceName, ex);
-                }
-            }
-            return futures;
-        }
-    }
-
-    private class NatFlowRemoveWorker implements Callable<List<ListenableFuture<Void>>> {
-        private Interface delintrf;
-        private String routerName;
-        private BigInteger intfDpnId;
-
-        NatFlowRemoveWorker(Interface delintrf, BigInteger intfDpnId, String routerName) {
-            this.delintrf = delintrf;
-            this.routerName = routerName;
-            this.intfDpnId = intfDpnId;
-        }
-
-        @Override
-        @SuppressWarnings("checkstyle:IllegalCatch")
-        public List<ListenableFuture<Void>> call() {
-            final List<ListenableFuture<Void>> futures = new ArrayList<>();
-            final String interfaceName = delintrf.getName();
-            LOG.trace("call : Interface {} removed event received", delintrf);
-            try {
-                LOG.trace("call : Port removed event received for interface {} ", interfaceName);
-                processInterfaceRemoved(interfaceName, intfDpnId, routerName, futures);
-                removeSnatEntriesForPort(interfaceName, routerName);
-            } catch (Exception e) {
-                LOG.error("call : Exception caught in Interface {} OperationalStateRemove", interfaceName, e);
-            }
-            return futures;
-        }
-    }
 }
diff --git a/natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/NatSouthboundEventHandlers.java b/natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/NatSouthboundEventHandlers.java
new file mode 100644 (file)
index 0000000..caa14eb
--- /dev/null
@@ -0,0 +1,560 @@
+/*
+ * Copyright (c) 2016 - 2018 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.natservice.internal;
+
+import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
+import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
+import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
+import org.opendaylight.genius.infra.Datastore.Operational;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
+import org.opendaylight.genius.infra.TypedReadWriteTransaction;
+import org.opendaylight.genius.mdsalutil.FlowEntity;
+import org.opendaylight.genius.mdsalutil.NwConstants;
+import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
+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.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.router.interfaces.RouterInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.NaptSwitches;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProtocolTypes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.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.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.neutron.vip.states.VipState;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPort;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class NatSouthboundEventHandlers {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NatSouthboundEventHandlers.class);
+    private static final String NAT_DS = "NATDS";
+    private final DataBroker dataBroker;
+    private final ManagedNewTransactionRunner txRunner;
+    private final OdlInterfaceRpcService odlInterfaceRpcService;
+    private final JobCoordinator coordinator;
+    private final FloatingIPListener floatingIPListener;
+    private final NeutronvpnService neutronVpnService;
+    private final IMdsalApiManager mdsalManager;
+    private final NaptManager naptManager;
+    private final VipStateTracker vipStateTracker;
+    Table<Interface.OperStatus, Interface.OperStatus, IntfTransitionState> stateTable = HashBasedTable.create();
+
+    enum IntfTransitionState {
+        STATE_UP,
+        STATE_DOWN,
+        STATE_IGNORE
+    }
+
+    private void initialize() {
+        stateTable.put(Interface.OperStatus.Up, Interface.OperStatus.Down, IntfTransitionState.STATE_DOWN);
+        stateTable.put(Interface.OperStatus.Down, Interface.OperStatus.Up, IntfTransitionState.STATE_UP);
+        stateTable.put(Interface.OperStatus.Unknown, Interface.OperStatus.Up, IntfTransitionState.STATE_UP);
+        stateTable.put(Interface.OperStatus.Unknown, Interface.OperStatus.Down, IntfTransitionState.STATE_DOWN);
+        stateTable.put(Interface.OperStatus.Up, Interface.OperStatus.Unknown, IntfTransitionState.STATE_DOWN);
+    }
+
+    @Inject
+    public NatSouthboundEventHandlers(final DataBroker dataBroker,
+            final OdlInterfaceRpcService odlInterfaceRpcService, final JobCoordinator coordinator,
+            final FloatingIPListener floatingIPListener,final NeutronvpnService neutronvpnService,
+            final IMdsalApiManager mdsalManager, final NaptManager naptManager, final VipStateTracker vipStateTracker) {
+        this.dataBroker = dataBroker;
+        this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
+        this.odlInterfaceRpcService = odlInterfaceRpcService;
+        this.coordinator = coordinator;
+        this.floatingIPListener = floatingIPListener;
+        this.neutronVpnService = neutronvpnService;
+        this.mdsalManager = mdsalManager;
+        this.naptManager = naptManager;
+        this.vipStateTracker = vipStateTracker;
+        initialize();
+    }
+
+    public void handleAdd(String interfaceName, BigInteger intfDpnId, RouterInterface routerInterface) {
+        handleAdd(interfaceName, intfDpnId, routerInterface, null);
+    }
+
+    public void handleAdd(String interfaceName, BigInteger intfDpnId,
+                          RouterInterface routerInterface, VipState vipState) {
+        String routerName = routerInterface.getRouterName();
+        NatInterfaceStateAddWorker natIfaceStateAddWorker = new NatInterfaceStateAddWorker(interfaceName,
+                intfDpnId, routerName);
+        coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natIfaceStateAddWorker);
+
+        NatFlowAddWorker natFlowAddWorker = new NatFlowAddWorker(interfaceName, routerName, intfDpnId, vipState);
+        coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natFlowAddWorker, NatConstants.NAT_DJC_MAX_RETRIES);
+    }
+
+    public void handleRemove(String interfaceName, BigInteger intfDpnId, RouterInterface routerInterface) {
+        String routerName = routerInterface.getRouterName();
+        NatInterfaceStateRemoveWorker natIfaceStateRemoveWorker = new NatInterfaceStateRemoveWorker(interfaceName,
+                intfDpnId, routerName);
+        coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natIfaceStateRemoveWorker);
+
+        NatFlowRemoveWorker natFlowRemoveWorker = new NatFlowRemoveWorker(interfaceName, intfDpnId, routerName);
+        coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natFlowRemoveWorker,
+                NatConstants.NAT_DJC_MAX_RETRIES);
+    }
+
+    public void handleUpdate(Interface original, Interface update,
+                             BigInteger intfDpnId, RouterInterface routerInterface) {
+        String routerName = routerInterface.getRouterName();
+        NatInterfaceStateUpdateWorker natIfaceStateupdateWorker = new NatInterfaceStateUpdateWorker(original,
+                update, intfDpnId, routerName);
+        coordinator.enqueueJob(NAT_DS + "-" + update.getName(), natIfaceStateupdateWorker);
+        NatFlowUpdateWorker natFlowUpdateWorker = new NatFlowUpdateWorker(original, update, routerName);
+        coordinator.enqueueJob(NAT_DS + "-" + update.getName(), natFlowUpdateWorker,
+                NatConstants.NAT_DJC_MAX_RETRIES);
+    }
+
+    void handleRouterInterfacesUpEvent(String routerName, String interfaceName, BigInteger dpId,
+            TypedReadWriteTransaction<Operational> operTx) throws ExecutionException, InterruptedException {
+        LOG.debug("handleRouterInterfacesUpEvent : Handling UP event for router interface {} in Router {} on Dpn {}",
+                interfaceName, routerName, dpId);
+        NatUtil.addToNeutronRouterDpnsMap(routerName, interfaceName, dpId, operTx);
+        NatUtil.addToDpnRoutersMap(routerName, interfaceName, dpId, operTx);
+    }
+
+    void handleRouterInterfacesDownEvent(String routerName, String interfaceName, BigInteger dpnId,
+                                         TypedReadWriteTransaction<Operational> operTx)
+        throws ExecutionException, InterruptedException {
+        LOG.debug("handleRouterInterfacesDownEvent : Handling DOWN event for router Interface {} in Router {}",
+                interfaceName, routerName);
+        NatUtil.removeFromNeutronRouterDpnsMap(routerName, dpnId, operTx);
+        NatUtil.removeFromDpnRoutersMap(dataBroker, routerName, interfaceName, dpnId, odlInterfaceRpcService,
+                operTx);
+    }
+
+    private IntfTransitionState getTransitionState(Interface.OperStatus original , Interface.OperStatus updated) {
+        IntfTransitionState transitionState = stateTable.get(original, updated);
+
+        if (transitionState == null) {
+            return IntfTransitionState.STATE_IGNORE;
+        }
+        return transitionState;
+    }
+
+    private class NatInterfaceStateAddWorker implements Callable<List<ListenableFuture<Void>>> {
+        private String interfaceName;
+        private String routerName;
+        private BigInteger intfDpnId;
+
+        NatInterfaceStateAddWorker(String interfaceName, BigInteger intfDpnId, String routerName) {
+            this.interfaceName = interfaceName;
+            this.routerName = routerName;
+            this.intfDpnId = intfDpnId;
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:IllegalCatch")
+        public List<ListenableFuture<Void>> call() {
+            List<ListenableFuture<Void>> futures = new ArrayList<>();
+            try {
+                LOG.trace("call : Received interface {} PORT UP OR ADD event ", interfaceName);
+                String dpnLock = NatConstants.NAT_DJC_PREFIX + intfDpnId;
+                synchronized (dpnLock.intern()) {
+                    futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx ->
+                        handleRouterInterfacesUpEvent(routerName, interfaceName, intfDpnId, tx)));
+                }
+            } catch (Exception e) {
+                LOG.error("call : Exception caught in Interface {} Operational State Up event",
+                        interfaceName, e);
+            }
+            return futures;
+        }
+    }
+
+    private class NatInterfaceStateRemoveWorker implements Callable<List<ListenableFuture<Void>>> {
+        private String interfaceName;
+        private String routerName;
+        private BigInteger intfDpnId;
+
+        NatInterfaceStateRemoveWorker(String interfaceName, BigInteger intfDpnId, String routerName) {
+            this.interfaceName = interfaceName;
+            this.routerName = routerName;
+            this.intfDpnId = intfDpnId;
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:IllegalCatch")
+        public List<ListenableFuture<Void>> call() {
+            List<ListenableFuture<Void>> futures = new ArrayList<>();
+            try {
+                LOG.trace("call : Received interface {} PORT DOWN or REMOVE event", interfaceName);
+                String dpnLock = NatConstants.NAT_DJC_PREFIX + intfDpnId;
+                synchronized (dpnLock.intern()) {
+                    futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx ->
+                        handleRouterInterfacesDownEvent(routerName, interfaceName, intfDpnId, tx)));
+                }
+            } catch (Exception e) {
+                LOG.error("call : Exception observed in handling deletion of VPN Interface {}.", interfaceName, e);
+            }
+            return futures;
+        }
+    }
+
+    private class NatInterfaceStateUpdateWorker implements Callable<List<ListenableFuture<Void>>> {
+        private Interface original;
+        private Interface update;
+        private BigInteger intfDpnId;
+        private String routerName;
+
+        NatInterfaceStateUpdateWorker(Interface original, Interface update, BigInteger intfDpnId, String routerName) {
+            this.original = original;
+            this.update = update;
+            this.intfDpnId = intfDpnId;
+            this.routerName = routerName;
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:IllegalCatch")
+        public List<ListenableFuture<Void>> call() {
+            List<ListenableFuture<Void>> futures = new ArrayList<>();
+            try {
+                final String interfaceName = update.getName();
+                LOG.trace("call : Received interface {} state change event", interfaceName);
+                LOG.debug("call : DPN ID {} for the interface {} ", intfDpnId, interfaceName);
+                String dpnLock = NatConstants.NAT_DJC_PREFIX + intfDpnId;
+                synchronized (dpnLock.intern()) {
+                    IntfTransitionState state = getTransitionState(original.getOperStatus(), update.getOperStatus());
+                    if (state.equals(IntfTransitionState.STATE_IGNORE)) {
+                        LOG.info("NAT Service: Interface {} state original {} updated {} not handled",
+                                interfaceName, original.getOperStatus(), update.getOperStatus());
+                        return futures;
+                    }
+                    futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
+                        if (state.equals(IntfTransitionState.STATE_DOWN)) {
+                            LOG.debug("call : DPN {} connnected to the interface {} has gone down."
+                                    + "Hence clearing the dpn-vpninterfaces-list entry from the"
+                                    + " neutron-router-dpns model in the ODL:L3VPN", intfDpnId, interfaceName);
+                            // If the interface state is unknown, it means that the corresponding DPN has gone down.
+                            // So remove the dpn-vpninterfaces-list from the neutron-router-dpns model.
+                            NatUtil.removeFromNeutronRouterDpnsMap(routerName, interfaceName,
+                                    intfDpnId, tx);
+                        } else if (state.equals(IntfTransitionState.STATE_UP)) {
+                            LOG.debug("call : DPN {} connnected to the interface {} has come up. Hence adding"
+                                    + " the dpn-vpninterfaces-list entry from the neutron-router-dpns model"
+                                    + " in the ODL:L3VPN", intfDpnId, interfaceName);
+                            handleRouterInterfacesUpEvent(routerName, interfaceName, intfDpnId, tx);
+                        }
+                    }));
+                }
+            } catch (Exception e) {
+                LOG.error("call : Exception observed in handling updation of VPN Interface {}.", update.getName(), e);
+            }
+            return futures;
+        }
+    }
+
+    private void processInterfaceAdded(String portName, String routerId, BigInteger dpnId, VipState vipState) {
+        LOG.trace("processInterfaceAdded : Processing Interface Add Event for interface {}", portName);
+        List<InternalToExternalPortMap> intExtPortMapList = getIntExtPortMapListForPortName(portName, routerId);
+        if (intExtPortMapList == null || intExtPortMapList.isEmpty()) {
+            LOG.debug("processInterfaceAdded : Ip Mapping list is empty/null for portname {}", portName);
+            return;
+        }
+        InstanceIdentifier<RouterPorts> portIid = NatUtil.buildRouterPortsIdentifier(routerId);
+        FluentFuture<Void> future = txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
+            for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
+                floatingIPListener.createNATFlowEntries(portName, intExtPortMap, portIid, routerId, dpnId, tx);
+            }
+        });
+        future.transform((ignored) -> {
+            if (vipState != null) {
+                return this.vipStateTracker.writeVipState(vipState);
+            }
+            return null;
+        }, MoreExecutors.directExecutor());
+    }
+
+    private List<InternalToExternalPortMap> getIntExtPortMapListForPortName(String portName, String routerId) {
+        InstanceIdentifier<Ports> portToIpMapIdentifier = NatUtil.buildPortToIpMapIdentifier(routerId, portName);
+        Optional<Ports> port =
+                SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
+                        LogicalDatastoreType.CONFIGURATION, portToIpMapIdentifier);
+        if (!port.isPresent()) {
+            LOG.info("getIntExtPortMapListForPortName : Unable to read router port entry for router ID {} "
+                    + "and port name {}", routerId, portName);
+            return null;
+        }
+        return port.get().getInternalToExternalPortMap();
+    }
+
+    private BigInteger getNaptSwitchforRouter(DataBroker broker, String routerName) {
+        InstanceIdentifier<RouterToNaptSwitch> rtrNaptSw = InstanceIdentifier.builder(NaptSwitches.class)
+            .child(RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerName)).build();
+        Optional<RouterToNaptSwitch> routerToNaptSwitchData =
+                SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
+                        LogicalDatastoreType.CONFIGURATION, 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("removeNatFlow : Removed the flow in table {} for the switch with the DPN ID {} for "
+            + "router {} ip {} port {}", tableId, dpnId, routerId, ipAddress, ipPort);
+    }
+
+    private List<String> getFixedIpsForPort(String interfname) {
+        LOG.debug("getFixedIpsForPort : 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.error("getFixedIpsForPort : RPC Call to GetFixedIPsForNeutronPortOutput returned with Errors {}",
+                    rpcResult.getErrors());
+            } else {
+                return rpcResult.getResult().getFixedIPs();
+            }
+        } catch (InterruptedException | ExecutionException | NullPointerException ex) {
+            LOG.error("getFixedIpsForPort : Exception while receiving fixedIps for port {}", interfname, ex);
+        }
+        return null;
+    }
+
+    private void processInterfaceRemoved(String portName, BigInteger dpnId, String routerId,
+            List<ListenableFuture<Void>> futures) {
+        LOG.trace("processInterfaceRemoved : Processing Interface Removed Event for interface {} on DPN ID {}",
+                portName, dpnId);
+        List<InternalToExternalPortMap> intExtPortMapList = getIntExtPortMapListForPortName(portName, routerId);
+        if (intExtPortMapList == null || intExtPortMapList.isEmpty()) {
+            LOG.debug("processInterfaceRemoved : Ip Mapping list is empty/null for portName {}", portName);
+            return;
+        }
+        InstanceIdentifier<RouterPorts> portIid = NatUtil.buildRouterPortsIdentifier(routerId);
+        ListenableFuture<Void> future = txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
+            for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
+                LOG.trace("processInterfaceRemoved : Removing DNAT Flow entries for dpnId {} ", dpnId);
+                floatingIPListener.removeNATFlowEntries(portName, intExtPortMap, portIid, routerId, dpnId, tx);
+            }
+        });
+        futures.add(future);
+        try {
+            future.get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Error processing interface removal", e);
+        }
+    }
+
+    // TODO Clean up the exception handling
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    private void removeSnatEntriesForPort(String interfaceName, String routerName) {
+        Long routerId = NatUtil.getVpnId(dataBroker, routerName);
+        if (routerId == NatConstants.INVALID_ID) {
+            LOG.error("removeSnatEntriesForPort : routerId not found for routername {}", routerName);
+            return;
+        }
+        BigInteger naptSwitch = getNaptSwitchforRouter(dataBroker, routerName);
+        if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
+            LOG.error("removeSnatEntriesForPort : NaptSwitch is not elected for router {} with Id {}",
+                    routerName, routerId);
+            return;
+        }
+        //getInternalIp for port
+        List<String> fixedIps = getFixedIpsForPort(interfaceName);
+        if (fixedIps == null) {
+            LOG.warn("removeSnatEntriesForPort : Internal Ips not found for InterfaceName {} in router {} with id {}",
+                interfaceName, routerName, routerId);
+            return;
+        }
+
+        for (String internalIp : fixedIps) {
+            LOG.debug("removeSnatEntriesForPort : Internal Ip retrieved for interface {} is {} in router with Id {}",
+                interfaceName, internalIp, routerId);
+            IpPort ipPort = NatUtil.getInternalIpPortInfo(dataBroker, routerId, internalIp);
+            if (ipPort == null) {
+                LOG.debug("removeSnatEntriesForPort : no snatint-ip-port-map found for ip:{}", internalIp);
+                continue;
+            }
+
+            for (IntIpProtoType protoType: ipPort.getIntIpProtoType()) {
+                ProtocolTypes protocol = protoType.getProtocol();
+                for (Integer portnum : protoType.getPorts()) {
+                    //build and remove the flow in outbound table
+                    try {
+                        removeNatFlow(naptSwitch, NwConstants.OUTBOUND_NAPT_TABLE, routerId, internalIp, portnum);
+                    } catch (Exception ex) {
+                        LOG.error("removeSnatEntriesForPort : 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("removeSnatEntriesForPort : 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, NwConstants.INBOUND_NAPT_TABLE, routerId,
+                            externalIpAddress, portNumber);
+                    } catch (Exception ex) {
+                        LOG.error("removeSnatEntriesForPort : 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("removeSnatEntriesForPort : releaseIpExtPortMapping failed, Removal of "
+                            + "ipportmap {} for router {} failed", internalIpPort, routerId, ex);
+                    }
+                }
+            }
+            // delete the entry from SnatIntIpPortMap DS
+            LOG.debug("removeSnatEntriesForPort : Removing InternalIp:{} on router {}", internalIp, routerId);
+            naptManager.removeFromSnatIpPortDS(routerId, internalIp);
+        }
+    }
+
+    private class NatFlowAddWorker implements Callable<List<ListenableFuture<Void>>> {
+        private String interfaceName;
+        private String routerName;
+        private BigInteger dpnId;
+        private VipState vipState;
+
+        NatFlowAddWorker(String interfaceName,String routerName, BigInteger dpnId, VipState vipState) {
+            this.interfaceName = interfaceName;
+            this.routerName = routerName;
+            this.dpnId = dpnId;
+            this.vipState = vipState;
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:IllegalCatch")
+        public List<ListenableFuture<Void>> call() {
+            final List<ListenableFuture<Void>> futures = new ArrayList<>();
+            LOG.trace("call : Interface {} up event received", interfaceName);
+            try {
+                LOG.trace("call : Port added event received for interface {} ", interfaceName);
+                processInterfaceAdded(interfaceName, routerName, dpnId, vipState);
+            } catch (Exception ex) {
+                LOG.error("call : Exception caught in Interface {} Operational State Up event",
+                        interfaceName, ex);
+            }
+            return futures;
+        }
+    }
+
+    private class NatFlowUpdateWorker implements Callable<List<ListenableFuture<Void>>> {
+        private Interface original;
+        private Interface update;
+        private String routerName;
+
+        NatFlowUpdateWorker(Interface original, Interface update, String routerName) {
+            this.original = original;
+            this.update = update;
+            this.routerName = routerName;
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:IllegalCatch")
+        public List<ListenableFuture<Void>> call() {
+            final List<ListenableFuture<Void>> futures = new ArrayList<>();
+            String interfaceName = update.getName();
+            IntfTransitionState state = getTransitionState(original.getOperStatus(), update.getOperStatus());
+            if (state.equals(IntfTransitionState.STATE_IGNORE)) {
+                LOG.info("NAT Service: Interface {} state original {} updated {} not handled",
+                        interfaceName, original.getOperStatus(), update.getOperStatus());
+                return futures;
+            }
+            if (state.equals(IntfTransitionState.STATE_UP)) {
+                LOG.debug("call : Port UP event received for interface {} ", interfaceName);
+            } else if (state.equals(IntfTransitionState.STATE_DOWN)) {
+                LOG.debug("call : Port DOWN event received for interface {} ", interfaceName);
+                try {
+                    removeSnatEntriesForPort(interfaceName, routerName);
+                } catch (Exception ex) {
+                    LOG.error("call : Exception caught in Interface {} OperationalStateDown", interfaceName, ex);
+                }
+            }
+            return futures;
+        }
+    }
+
+    private class NatFlowRemoveWorker implements Callable<List<ListenableFuture<Void>>> {
+        private String interfaceName;
+        private String routerName;
+        private BigInteger intfDpnId;
+
+        NatFlowRemoveWorker(String interfaceName, BigInteger intfDpnId, String routerName) {
+            this.interfaceName = interfaceName;
+            this.routerName = routerName;
+            this.intfDpnId = intfDpnId;
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:IllegalCatch")
+        public List<ListenableFuture<Void>> call() {
+            final List<ListenableFuture<Void>> futures = new ArrayList<>();
+            LOG.trace("call : Interface {} removed event received", interfaceName);
+            try {
+                LOG.trace("call : Port removed event received for interface {} ", interfaceName);
+                processInterfaceRemoved(interfaceName, intfDpnId, routerName, futures);
+                removeSnatEntriesForPort(interfaceName, routerName);
+            } catch (Exception e) {
+                LOG.error("call : Exception caught in Interface {} OperationalStateRemove", interfaceName, e);
+            }
+            return futures;
+        }
+    }
+}
diff --git a/natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/VipStateTracker.java b/natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/VipStateTracker.java
new file mode 100644 (file)
index 0000000..b1c29ad
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.natservice.internal;
+
+import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
+
+import com.google.common.util.concurrent.FluentFuture;
+import java.math.BigInteger;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
+import org.opendaylight.genius.mdsalutil.cache.DataObjectCache;
+import org.opendaylight.infrautils.caches.CacheProvider;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.NeutronVipStates;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.neutron.vip.states.VipState;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.neutron.vip.states.VipStateBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.neutron.vip.states.VipStateKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@Singleton
+public class VipStateTracker extends DataObjectCache<String, VipState> {
+
+    private ManagedNewTransactionRunner txRunner = null;
+
+    @Inject
+    public VipStateTracker(DataBroker dataBroker, CacheProvider cacheProvider) {
+        super(VipState.class,
+            dataBroker,
+            LogicalDatastoreType.OPERATIONAL,
+            InstanceIdentifier.builder(NeutronVipStates.class).child(VipState.class).build(),
+            cacheProvider,
+            (iid, vipState) -> vipState.key().getIp(),
+            ip -> InstanceIdentifier.builder(NeutronVipStates.class) .child(VipState.class, new VipStateKey(ip)).build()
+            );
+        this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
+    }
+
+    public VipState buildVipState(String ip, String mac, BigInteger dpnId, String ifcName) {
+        return new VipStateBuilder().setIp(ip).setDpnId(dpnId).setIfcName(ifcName).build();
+    }
+
+    public FluentFuture<Void> writeVipState(VipState vipState) {
+        return txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, tx -> {
+            tx.put(InstanceIdentifier.builder(NeutronVipStates.class)
+                            .child(VipState.class, vipState.key()).build(),
+                    vipState, true);
+        });
+    }
+}
index e5ba2c416ea4381ce49b360fad0276fc84c0e34f..0dae2be35fae7f278a009236bdd750736af0dc2f 100644 (file)
@@ -32,6 +32,8 @@
              interface="org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar"/>
   <reference id="metricProvider"
              interface="org.opendaylight.infrautils.metrics.MetricProvider" />
+  <reference id="cacheProvider"
+             interface="org.opendaylight.infrautils.caches.CacheProvider"/>
 
   <odl:rpc-service id="idManagerService"
                    interface="org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService" />
@@ -69,4 +71,7 @@
   <service ref="natRpcServiceImpl"
            interface="org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rpc.rev170209.OdlNatRpcService"/>
 
+  <service ref="natArpNotificationHandler" odl:type="default"
+           interface="org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilListener" />
+  <odl:notification-listener ref="natArpNotificationHandler" />
 </blueprint>
index ab1b8d6bfd3f655bb85294e20070c2586f7dbd4b..a5a563edbabedde280880c0a9dbfb93078596d9c 100644 (file)
@@ -21,6 +21,7 @@ import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
 import org.opendaylight.genius.mdsalutil.NWUtil;
 import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice;
+import org.opendaylight.netvirt.neutronvpn.interfaces.INeutronVpnManager;
 import org.opendaylight.netvirt.vpnmanager.VpnUtil;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
@@ -34,6 +35,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.lea
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.config.rev161130.VpnConfig;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -49,14 +51,17 @@ public abstract class AbstractIpLearnNotificationHandler {
     protected final IInterfaceManager interfaceManager;
     protected final VpnConfig config;
     protected final VpnUtil vpnUtil;
+    protected final INeutronVpnManager neutronVpnManager;
 
     public AbstractIpLearnNotificationHandler(DataBroker dataBroker, IdManagerService idManager,
-            IInterfaceManager interfaceManager, VpnConfig vpnConfig, VpnUtil vpnUtil) {
+                                              IInterfaceManager interfaceManager, VpnConfig vpnConfig,
+                                              VpnUtil vpnUtil, INeutronVpnManager neutronVpnManager) {
         this.dataBroker = dataBroker;
         this.idManager = idManager;
         this.interfaceManager = interfaceManager;
         this.config = vpnConfig;
         this.vpnUtil = vpnUtil;
+        this.neutronVpnManager = neutronVpnManager;
 
         long duration = config.getIpLearnTimeout() * 10;
         long cacheSize = config.getMigrateIpCacheSize().longValue();
@@ -91,55 +96,71 @@ public abstract class AbstractIpLearnNotificationHandler {
 
     protected void processIpLearning(String srcInterface, IpAddress srcIP, MacAddress srcMac, BigInteger metadata,
             IpAddress dstIP, Uuid srcIpSubnetId) {
-        if (metadata != null && !Objects.equals(metadata, BigInteger.ZERO)) {
-            Optional<List<String>> vpnList = vpnUtil.getVpnHandlingIpv4AssociatedWithInterface(srcInterface);
-            if (vpnList.isPresent()) {
-                String srcIpToQuery = srcIP.stringValue();
-                String destIpToQuery = dstIP.stringValue();
-                for (String vpnName : vpnList.get()) {
-                    LOG.info("Received ARP/NA for sender MAC {} and sender IP {} via interface {}",
-                              srcMac.getValue(), srcIpToQuery, srcInterface);
-                    VpnPortipToPort vpnPortipToPort =
-                            vpnUtil.getNeutronPortFromVpnPortFixedIp(vpnName, srcIpToQuery);
-                    if (vpnPortipToPort != null) {
-                        /* This is a well known neutron port and so should be ignored
-                         * from being discovered
-                         */
-                        continue;
-                    }
-                    if (srcIpSubnetId != null) {
-                        Subnetmap snMap = vpnUtil.getSubnetmapFromItsUuid(srcIpSubnetId);
-                        if (snMap != null && snMap.getVpnId() == null) {
-                            /* If the subnet is not part of vpn then it should be ignored
-                             * from being discovered. This use case will come for dual stack
-                             * network. i.e V6 or V4 subnet only part of VPN.
-                             */
-                            continue;
-                        }
-                    }
-                    LearntVpnVipToPort learntVpnVipToPort = vpnUtil.getLearntVpnVipToPort(vpnName, srcIpToQuery);
-                    if (learntVpnVipToPort != null) {
-                        String oldPortName = learntVpnVipToPort.getPortName();
-                        String oldMac = learntVpnVipToPort.getMacAddress();
-                        if (!oldMac.equalsIgnoreCase(srcMac.getValue())) {
-                            //MAC has changed for requested IP
-                            LOG.info("ARP/NA Source IP/MAC data modified for IP {} with MAC {} and Port {}",
-                                    srcIpToQuery, srcMac, srcInterface);
-                            synchronized ((vpnName + srcIpToQuery).intern()) {
-                                vpnUtil.createLearntVpnVipToPortEvent(vpnName, srcIpToQuery, destIpToQuery,
-                                        oldPortName, oldMac, LearntVpnVipToPortEventAction.Delete, null);
-                                putVpnIpToMigrateIpCache(vpnName, srcIpToQuery, srcMac);
-                            }
-                        }
-                    } else if (!isIpInMigrateCache(vpnName, srcIpToQuery)) {
-                        learnMacFromIncomingPacket(vpnName, srcInterface, srcIP, srcMac, dstIP);
-                    }
+
+        if (metadata == null || Objects.equals(metadata, BigInteger.ZERO)) {
+            return;
+        }
+
+        Optional<List<String>> vpnList = vpnUtil.getVpnHandlingIpv4AssociatedWithInterface(srcInterface);
+        if (!vpnList.isPresent()) {
+            LOG.info("IP LEARN NO_RESOLVE: VPN  not configured. Ignoring responding to ARP/NA requests from this"
+                    + " Interface {}.", srcInterface);
+            return;
+        }
+
+        String srcIpToQuery = srcIP.stringValue();
+        String destIpToQuery = dstIP.stringValue();
+        for (String vpnName : vpnList.get()) {
+            LOG.info("Received ARP/NA for sender MAC {} and sender IP {} via interface {}",
+                      srcMac.getValue(), srcIpToQuery, srcInterface);
+            VpnPortipToPort vpnPortipToPort =
+                    vpnUtil.getNeutronPortFromVpnPortFixedIp(vpnName, srcIpToQuery);
+            if (vpnPortipToPort != null) {
+                /* This is a well known neutron port and so should be ignored
+                 * from being discovered...unless it is an Octavia VIP
+                 */
+                String portName = vpnPortipToPort.getPortName();
+                Port neutronPort = neutronVpnManager.getNeutronPort(portName);
+
+                if (neutronPort == null) {
+                    LOG.warn("{} should have been a neutron port but could not retrieve it. Aborting processing",
+                             portName);
+                    continue;
                 }
-            } else {
-                LOG.info("IP LEARN NO_RESOLVE: VPN  not configured. Ignoring responding to ARP/NA requests from this"
-                        + " Interface {}.", srcInterface);
-                return;
 
+                if (!"Octavia".equals(neutronPort.getDeviceOwner())) {
+                    LOG.debug("Neutron port {} is not an Octavia port, ignoring", portName);
+                    continue;
+                }
+            }
+
+            if (srcIpSubnetId != null) {
+                Subnetmap snMap = vpnUtil.getSubnetmapFromItsUuid(srcIpSubnetId);
+                if (snMap != null && snMap.getVpnId() == null) {
+                    /* If the subnet is not part of vpn then it should be ignored
+                     * from being discovered. This use case will come for dual stack
+                     * network. i.e V6 or V4 subnet only part of VPN.
+                     */
+                    continue;
+                }
+            }
+
+            LearntVpnVipToPort learntVpnVipToPort = vpnUtil.getLearntVpnVipToPort(vpnName, srcIpToQuery);
+            if (learntVpnVipToPort != null) {
+                String oldPortName = learntVpnVipToPort.getPortName();
+                String oldMac = learntVpnVipToPort.getMacAddress();
+                if (!oldMac.equalsIgnoreCase(srcMac.getValue())) {
+                    //MAC has changed for requested IP
+                    LOG.info("ARP/NA Source IP/MAC data modified for IP {} with MAC {} and Port {}",
+                            srcIpToQuery, srcMac, srcInterface);
+                    synchronized ((vpnName + srcIpToQuery).intern()) {
+                        vpnUtil.createLearntVpnVipToPortEvent(vpnName, srcIpToQuery, destIpToQuery,
+                                oldPortName, oldMac, LearntVpnVipToPortEventAction.Delete, null);
+                        putVpnIpToMigrateIpCache(vpnName, srcIpToQuery, srcMac);
+                    }
+                }
+            } else if (!isIpInMigrateCache(vpnName, srcIpToQuery)) {
+                learnMacFromIncomingPacket(vpnName, srcInterface, srcIP, srcMac, dstIP);
             }
         }
     }
index 610b116926b047c4505b039aad0e22dad94da99f..adba9dd4de97252df49f523f5a15c4818dffc401 100644 (file)
@@ -12,6 +12,7 @@ import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
+import org.opendaylight.netvirt.neutronvpn.interfaces.INeutronVpnManager;
 import org.opendaylight.netvirt.vpnmanager.VpnUtil;
 import org.opendaylight.netvirt.vpnmanager.iplearn.AbstractIpLearnNotificationHandler;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
@@ -32,8 +33,9 @@ public class ArpNotificationHandler extends AbstractIpLearnNotificationHandler i
 
     @Inject
     public ArpNotificationHandler(DataBroker dataBroker, IdManagerService idManager,
-                                  IInterfaceManager interfaceManager, VpnConfig vpnConfig, VpnUtil vpnUtil) {
-        super(dataBroker, idManager, interfaceManager, vpnConfig, vpnUtil);
+                                  IInterfaceManager interfaceManager, VpnConfig vpnConfig,
+                                  VpnUtil vpnUtil, INeutronVpnManager neutronVpnManager) {
+        super(dataBroker, idManager, interfaceManager, vpnConfig, vpnUtil, neutronVpnManager);
     }
 
     @Override
index cbc67ec47fa72ee128ee831b29e80f04d716dfe1..61170631f276704161d8cbf21b04657709b596cb 100644 (file)
@@ -13,6 +13,7 @@ import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
+import org.opendaylight.netvirt.neutronvpn.interfaces.INeutronVpnManager;
 import org.opendaylight.netvirt.vpnmanager.VpnUtil;
 import org.opendaylight.netvirt.vpnmanager.iplearn.AbstractIpLearnNotificationHandler;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
@@ -31,8 +32,9 @@ public class Ipv6NaNotificationHandler extends AbstractIpLearnNotificationHandle
 
     @Inject
     public Ipv6NaNotificationHandler(DataBroker dataBroker, IdManagerService idManager,
-                                     IInterfaceManager interfaceManager, VpnConfig vpnConfig, VpnUtil vpnUtil) {
-        super(dataBroker, idManager, interfaceManager, vpnConfig, vpnUtil);
+                                     IInterfaceManager interfaceManager, VpnConfig vpnConfig,
+                                     VpnUtil vpnUtil, INeutronVpnManager neutronVpnManager) {
+        super(dataBroker, idManager, interfaceManager, vpnConfig, vpnUtil, neutronVpnManager);
     }
 
     @Override