NAT failure due to Napt Switch-Over not happened
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / NatInterfaceStateChangeListener.java
1 /*
2  * Copyright (c) 2016 - 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netvirt.natservice.internal;
9
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
12
13 import com.google.common.base.Optional;
14 import com.google.common.collect.HashBasedTable;
15 import com.google.common.collect.Table;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import java.math.BigInteger;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.concurrent.Callable;
21 import java.util.concurrent.ExecutionException;
22 import java.util.concurrent.Future;
23
24 import javax.annotation.PostConstruct;
25 import javax.inject.Inject;
26 import javax.inject.Singleton;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
30 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
31 import org.opendaylight.genius.infra.Datastore.Operational;
32 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
33 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
34 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
35 import org.opendaylight.genius.mdsalutil.FlowEntity;
36 import org.opendaylight.genius.mdsalutil.NwConstants;
37 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
38 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
39 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
40 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.vpn._interface.VpnInstanceNames;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.router.interfaces.RouterInterface;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.NaptSwitches;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProtocolTypes;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
53 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;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitch;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPort;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoType;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortInputBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortOutput;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService;
61 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
62 import org.opendaylight.yangtools.yang.common.RpcResult;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66 @Singleton
67 public class NatInterfaceStateChangeListener
68     extends AsyncDataTreeChangeListenerBase<Interface, NatInterfaceStateChangeListener> {
69
70     private static final Logger LOG = LoggerFactory.getLogger(NatInterfaceStateChangeListener.class);
71     private static final String NAT_DS = "NATDS";
72     private final DataBroker dataBroker;
73     private final ManagedNewTransactionRunner txRunner;
74     private final OdlInterfaceRpcService odlInterfaceRpcService;
75     private final JobCoordinator coordinator;
76     private final FloatingIPListener floatingIPListener;
77     private final NeutronvpnService neutronVpnService;
78     private final IMdsalApiManager mdsalManager;
79     private final NaptManager naptManager;
80     Table<Interface.OperStatus, Interface.OperStatus, IntfTransitionState> stateTable = HashBasedTable.create();
81
82     enum IntfTransitionState {
83         STATE_UP,
84         STATE_DOWN,
85         STATE_IGNORE
86     }
87
88     private void initialize() {
89         //  Interface State Transition Table
90         //               Up                Down            Unknown
91         // ---------------------------------------------------------------
92         /* Up       { STATE_IGNORE,   STATE_DOWN,     STATE_DOWN }, */
93         /* Down     { STATE_UP,       STATE_IGNORE,   STATE_IGNORE }, */
94         /* Unknown  { STATE_UP,       STATE_DOWN,     STATE_IGNORE }, */
95         stateTable.put(Interface.OperStatus.Up, Interface.OperStatus.Down, IntfTransitionState.STATE_DOWN);
96         stateTable.put(Interface.OperStatus.Down, Interface.OperStatus.Up, IntfTransitionState.STATE_UP);
97         stateTable.put(Interface.OperStatus.Unknown, Interface.OperStatus.Up, IntfTransitionState.STATE_UP);
98         stateTable.put(Interface.OperStatus.Unknown, Interface.OperStatus.Down, IntfTransitionState.STATE_DOWN);
99         stateTable.put(Interface.OperStatus.Up, Interface.OperStatus.Unknown, IntfTransitionState.STATE_DOWN);
100     }
101
102     @Inject
103     public NatInterfaceStateChangeListener(final DataBroker dataBroker,
104             final OdlInterfaceRpcService odlInterfaceRpcService, final JobCoordinator coordinator,
105             final FloatingIPListener floatingIPListener,final NeutronvpnService neutronvpnService,
106             final IMdsalApiManager mdsalManager, final NaptManager naptManager) {
107         super(Interface.class, NatInterfaceStateChangeListener.class);
108         this.dataBroker = dataBroker;
109         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
110         this.odlInterfaceRpcService = odlInterfaceRpcService;
111         this.coordinator = coordinator;
112         this.floatingIPListener = floatingIPListener;
113         this.neutronVpnService = neutronvpnService;
114         this.mdsalManager = mdsalManager;
115         this.naptManager = naptManager;
116         initialize();
117     }
118
119     @Override
120     @PostConstruct
121     public void init() {
122         LOG.info("{} init", getClass().getSimpleName());
123         registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
124     }
125
126     @Override
127     protected InstanceIdentifier<Interface> getWildCardPath() {
128         return InstanceIdentifier.create(InterfacesState.class).child(Interface.class);
129     }
130
131     @Override
132     protected NatInterfaceStateChangeListener getDataTreeChangeListener() {
133         return NatInterfaceStateChangeListener.this;
134     }
135
136     @Override
137     // TODO Clean up the exception handling
138     @SuppressWarnings("checkstyle:IllegalCatch")
139     protected void add(InstanceIdentifier<Interface> identifier, Interface intrf) {
140         LOG.trace("add : Interface {} up event received", intrf);
141         if (!L2vlan.class.equals(intrf.getType())) {
142             LOG.debug("add : Interface {} is not Vlan Interface.Ignoring", intrf.getName());
143             return;
144         }
145         String interfaceName = intrf.getName();
146         BigInteger intfDpnId;
147         try {
148             intfDpnId = NatUtil.getDpIdFromInterface(intrf);
149         } catch (Exception e) {
150             LOG.error("add : Exception occured while retriving dpnid for interface {}", intrf.getName(), e);
151             return;
152         }
153         if (BigInteger.ZERO.equals(intfDpnId)) {
154             LOG.warn("add : Could not retrieve dp id for interface {} ", interfaceName);
155             return;
156         }
157         // We service only VM interfaces. We do not service Tunnel Interfaces here.
158         // Tunnel events are directly serviced by TunnelInterfacesStateListener present as part of
159         // VpnInterfaceManager
160         RouterInterface routerInterface = NatUtil.getConfiguredRouterInterface(dataBroker, interfaceName);
161         if (routerInterface != null) {
162             String routerName = routerInterface.getRouterName();
163             NatInterfaceStateAddWorker natIfaceStateAddWorker = new NatInterfaceStateAddWorker(interfaceName,
164                     intfDpnId, routerName);
165             coordinator.enqueueJob(NAT_DS + "-" + intrf.getName(), natIfaceStateAddWorker);
166
167             NatFlowAddWorker natFlowAddWorker = new NatFlowAddWorker(interfaceName, routerName);
168             coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natFlowAddWorker, NatConstants.NAT_DJC_MAX_RETRIES);
169         } else {
170             LOG.info("add : Router-Interface Mapping not found for Interface : {}", interfaceName);
171         }
172     }
173
174     @Override
175     // TODO Clean up the exception handling
176     @SuppressWarnings("checkstyle:IllegalCatch")
177     protected void remove(InstanceIdentifier<Interface> identifier, Interface intrf) {
178         LOG.trace("remove : Interface {} removed event received", intrf);
179         if (!L2vlan.class.equals(intrf.getType())) {
180             LOG.debug("remove : Interface {} is not Vlan Interface.Ignoring", intrf.getName());
181             return;
182         }
183         String interfaceName = intrf.getName();
184         BigInteger intfDpnId = BigInteger.ZERO;
185         try {
186             intfDpnId = NatUtil.getDpIdFromInterface(intrf);
187         } catch (Exception e) {
188             LOG.error("remove : Exception occured while retriving dpnid for interface {}",  intrf.getName(), e);
189             InstanceIdentifier<VpnInterface> id = NatUtil.getVpnInterfaceIdentifier(interfaceName);
190             Optional<VpnInterface> cfgVpnInterface =
191                     SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(
192                             dataBroker, LogicalDatastoreType.CONFIGURATION, id);
193             if (!cfgVpnInterface.isPresent()) {
194                 LOG.warn("remove : Interface {} is not a VPN Interface, ignoring.", interfaceName);
195                 return;
196             }
197             for (VpnInstanceNames vpnInterfaceVpnInstance : cfgVpnInterface.get().getVpnInstanceNames()) {
198                 String vpnName  = vpnInterfaceVpnInstance.getVpnName();
199                 InstanceIdentifier<VpnInterfaceOpDataEntry> idOper = NatUtil
200                       .getVpnInterfaceOpDataEntryIdentifier(interfaceName, vpnName);
201                 Optional<VpnInterfaceOpDataEntry> optVpnInterface =
202                       SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(
203                             dataBroker, LogicalDatastoreType.OPERATIONAL, idOper);
204                 if (optVpnInterface.isPresent()) {
205                     intfDpnId = optVpnInterface.get().getDpnId();
206                     break;
207                 }
208             }
209         }
210         if (intfDpnId.equals(BigInteger.ZERO)) {
211             LOG.warn("remove : Could not retrieve dpnid for interface {} ", interfaceName);
212             return;
213         }
214         RouterInterface routerInterface = NatUtil.getConfiguredRouterInterface(dataBroker, interfaceName);
215         if (routerInterface != null) {
216             String routerName = routerInterface.getRouterName();
217             NatInterfaceStateRemoveWorker natIfaceStateRemoveWorker = new NatInterfaceStateRemoveWorker(interfaceName,
218                     intfDpnId, routerName);
219             coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natIfaceStateRemoveWorker);
220
221             NatFlowRemoveWorker natFlowRemoveWorker = new NatFlowRemoveWorker(intrf, intfDpnId, routerName);
222             coordinator.enqueueJob(NAT_DS + "-" + interfaceName, natFlowRemoveWorker,
223                     NatConstants.NAT_DJC_MAX_RETRIES);
224         } else {
225             LOG.info("remove : Router-Interface Mapping not found for Interface : {}", interfaceName);
226         }
227     }
228
229     @Override
230     // TODO Clean up the exception handling
231     @SuppressWarnings("checkstyle:IllegalCatch")
232     protected void update(InstanceIdentifier<Interface> identifier, Interface original, Interface update) {
233         LOG.trace("update : Operation Interface update event - Old: {}, New: {}", original, update);
234         if (!L2vlan.class.equals(update.getType())) {
235             LOG.debug("update : Interface {} is not Vlan Interface.Ignoring", update.getName());
236             return;
237         }
238         BigInteger intfDpnId = BigInteger.ZERO;
239         String interfaceName = update.getName();
240         try {
241             intfDpnId = NatUtil.getDpIdFromInterface(update);
242         } catch (Exception e) {
243             LOG.error("update : Exception occured while retriving dpnid for interface {}",  update.getName(), e);
244             InstanceIdentifier<VpnInterface> id = NatUtil.getVpnInterfaceIdentifier(interfaceName);
245             Optional<VpnInterface> cfgVpnInterface =
246                     SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(
247                             dataBroker, LogicalDatastoreType.CONFIGURATION, id);
248             if (!cfgVpnInterface.isPresent()) {
249                 LOG.warn("update : Interface {} is not a VPN Interface, ignoring.", interfaceName);
250                 return;
251             }
252             for (VpnInstanceNames vpnInterfaceVpnInstance : cfgVpnInterface.get().getVpnInstanceNames()) {
253                 String vpnName  = vpnInterfaceVpnInstance.getVpnName();
254                 InstanceIdentifier<VpnInterfaceOpDataEntry> idOper = NatUtil
255                       .getVpnInterfaceOpDataEntryIdentifier(interfaceName, vpnName);
256                 Optional<VpnInterfaceOpDataEntry> optVpnInterface =
257                       SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(
258                             dataBroker, LogicalDatastoreType.OPERATIONAL, idOper);
259                 if (optVpnInterface.isPresent()) {
260                     intfDpnId = optVpnInterface.get().getDpnId();
261                     break;
262                 }
263             }
264         }
265         if (intfDpnId.equals(BigInteger.ZERO)) {
266             LOG.warn("remove : Could not retrieve dpnid for interface {} ", interfaceName);
267             return;
268         }
269         RouterInterface routerInterface = NatUtil.getConfiguredRouterInterface(dataBroker, interfaceName);
270         if (routerInterface != null) {
271             String routerName = routerInterface.getRouterName();
272             NatInterfaceStateUpdateWorker natIfaceStateupdateWorker = new NatInterfaceStateUpdateWorker(original,
273                     update, intfDpnId, routerName);
274             coordinator.enqueueJob(NAT_DS + "-" + update.getName(), natIfaceStateupdateWorker);
275             NatFlowUpdateWorker natFlowUpdateWorker = new NatFlowUpdateWorker(original, update, routerName);
276             coordinator.enqueueJob(NAT_DS + "-" + update.getName(), natFlowUpdateWorker,
277                     NatConstants.NAT_DJC_MAX_RETRIES);
278         } else {
279             LOG.info("update : Router-Interface Mapping not found for Interface : {}", interfaceName);
280         }
281     }
282
283     void handleRouterInterfacesUpEvent(String routerName, String interfaceName, BigInteger dpId,
284             TypedReadWriteTransaction<Operational> operTx) throws ExecutionException, InterruptedException {
285         LOG.debug("handleRouterInterfacesUpEvent : Handling UP event for router interface {} in Router {} on Dpn {}",
286                 interfaceName, routerName, dpId);
287         NatUtil.addToNeutronRouterDpnsMap(routerName, interfaceName, dpId, operTx);
288         NatUtil.addToDpnRoutersMap(routerName, interfaceName, dpId, operTx);
289     }
290
291     void handleRouterInterfacesDownEvent(String routerName, String interfaceName, BigInteger dpnId,
292                                          TypedReadWriteTransaction<Operational> operTx)
293         throws ExecutionException, InterruptedException {
294         LOG.debug("handleRouterInterfacesDownEvent : Handling DOWN event for router Interface {} in Router {}",
295                 interfaceName, routerName);
296         NatUtil.removeFromNeutronRouterDpnsMap(routerName, dpnId, operTx);
297         NatUtil.removeFromDpnRoutersMap(dataBroker, routerName, interfaceName, dpnId, odlInterfaceRpcService,
298                 operTx);
299     }
300
301     private IntfTransitionState getTransitionState(Interface.OperStatus original , Interface.OperStatus updated) {
302         IntfTransitionState transitionState = stateTable.get(original, updated);
303
304         if (transitionState == null) {
305             return IntfTransitionState.STATE_IGNORE;
306         }
307         return transitionState;
308     }
309
310     private class NatInterfaceStateAddWorker implements Callable<List<ListenableFuture<Void>>> {
311         private String interfaceName;
312         private String routerName;
313         private BigInteger intfDpnId;
314
315         NatInterfaceStateAddWorker(String interfaceName, BigInteger intfDpnId, String routerName) {
316             this.interfaceName = interfaceName;
317             this.routerName = routerName;
318             this.intfDpnId = intfDpnId;
319         }
320
321         @Override
322         @SuppressWarnings("checkstyle:IllegalCatch")
323         public List<ListenableFuture<Void>> call() {
324             List<ListenableFuture<Void>> futures = new ArrayList<>();
325             try {
326                 LOG.trace("call : Received interface {} PORT UP OR ADD event ", interfaceName);
327                 String dpnLock = NatConstants.NAT_DJC_PREFIX + intfDpnId;
328                 synchronized (dpnLock.intern()) {
329                     futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx ->
330                         handleRouterInterfacesUpEvent(routerName, interfaceName, intfDpnId, tx)));
331                 }
332             } catch (Exception e) {
333                 LOG.error("call : Exception caught in Interface {} Operational State Up event",
334                         interfaceName, e);
335             }
336             return futures;
337         }
338     }
339
340     private class NatInterfaceStateRemoveWorker implements Callable<List<ListenableFuture<Void>>> {
341         private String interfaceName;
342         private String routerName;
343         private BigInteger intfDpnId;
344
345         NatInterfaceStateRemoveWorker(String interfaceName, BigInteger intfDpnId, String routerName) {
346             this.interfaceName = interfaceName;
347             this.routerName = routerName;
348             this.intfDpnId = intfDpnId;
349         }
350
351         @Override
352         @SuppressWarnings("checkstyle:IllegalCatch")
353         public List<ListenableFuture<Void>> call() {
354             List<ListenableFuture<Void>> futures = new ArrayList<>();
355             try {
356                 LOG.trace("call : Received interface {} PORT DOWN or REMOVE event", interfaceName);
357                 String dpnLock = NatConstants.NAT_DJC_PREFIX + intfDpnId;
358                 synchronized (dpnLock.intern()) {
359                     futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx ->
360                         handleRouterInterfacesDownEvent(routerName, interfaceName, intfDpnId, tx)));
361                 }
362             } catch (Exception e) {
363                 LOG.error("call : Exception observed in handling deletion of VPN Interface {}.", interfaceName, e);
364             }
365             return futures;
366         }
367     }
368
369     private class NatInterfaceStateUpdateWorker implements Callable<List<ListenableFuture<Void>>> {
370         private Interface original;
371         private Interface update;
372         private BigInteger intfDpnId;
373         private String routerName;
374
375         NatInterfaceStateUpdateWorker(Interface original, Interface update, BigInteger intfDpnId, String routerName) {
376             this.original = original;
377             this.update = update;
378             this.intfDpnId = intfDpnId;
379             this.routerName = routerName;
380         }
381
382         @Override
383         @SuppressWarnings("checkstyle:IllegalCatch")
384         public List<ListenableFuture<Void>> call() {
385             List<ListenableFuture<Void>> futures = new ArrayList<>();
386             try {
387                 final String interfaceName = update.getName();
388                 LOG.trace("call : Received interface {} state change event", interfaceName);
389                 LOG.debug("call : DPN ID {} for the interface {} ", intfDpnId, interfaceName);
390                 String dpnLock = NatConstants.NAT_DJC_PREFIX + intfDpnId;
391                 synchronized (dpnLock.intern()) {
392                     IntfTransitionState state = getTransitionState(original.getOperStatus(), update.getOperStatus());
393                     if (state.equals(IntfTransitionState.STATE_IGNORE)) {
394                         LOG.info("NAT Service: Interface {} state original {} updated {} not handled",
395                                 interfaceName, original.getOperStatus(), update.getOperStatus());
396                         return futures;
397                     }
398                     futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
399                         if (state.equals(IntfTransitionState.STATE_DOWN)) {
400                             LOG.debug("call : DPN {} connnected to the interface {} has gone down."
401                                     + "Hence clearing the dpn-vpninterfaces-list entry from the"
402                                     + " neutron-router-dpns model in the ODL:L3VPN", intfDpnId, interfaceName);
403                             // If the interface state is unknown, it means that the corresponding DPN has gone down.
404                             // So remove the dpn-vpninterfaces-list from the neutron-router-dpns model.
405                             NatUtil.removeFromNeutronRouterDpnsMap(routerName, interfaceName,
406                                     intfDpnId, tx);
407                         } else if (state.equals(IntfTransitionState.STATE_UP)) {
408                             LOG.debug("call : DPN {} connnected to the interface {} has come up. Hence adding"
409                                     + " the dpn-vpninterfaces-list entry from the neutron-router-dpns model"
410                                     + " in the ODL:L3VPN", intfDpnId, interfaceName);
411                             handleRouterInterfacesUpEvent(routerName, interfaceName, intfDpnId, tx);
412                         }
413                     }));
414                 }
415             } catch (Exception e) {
416                 LOG.error("call : Exception observed in handling updation of VPN Interface {}.", update.getName(), e);
417             }
418             return futures;
419         }
420     }
421
422     private void processInterfaceAdded(String portName, String routerId, List<ListenableFuture<Void>> futures) {
423         LOG.trace("processInterfaceAdded : Processing Interface Add Event for interface {}", portName);
424         List<InternalToExternalPortMap> intExtPortMapList = getIntExtPortMapListForPortName(portName, routerId);
425         if (intExtPortMapList == null || intExtPortMapList.isEmpty()) {
426             LOG.debug("processInterfaceAdded : Ip Mapping list is empty/null for portname {}", portName);
427             return;
428         }
429         InstanceIdentifier<RouterPorts> portIid = NatUtil.buildRouterPortsIdentifier(routerId);
430         ListenableFuture<Void> future = txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
431             for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
432                 floatingIPListener.createNATFlowEntries(portName, intExtPortMap, portIid, routerId, tx);
433             }
434         });
435         futures.add(future);
436         try {
437             future.get();
438         } catch (InterruptedException | ExecutionException e) {
439             LOG.error("Error processing interface addition", e);
440         }
441     }
442
443     private List<InternalToExternalPortMap> getIntExtPortMapListForPortName(String portName, String routerId) {
444         InstanceIdentifier<Ports> portToIpMapIdentifier = NatUtil.buildPortToIpMapIdentifier(routerId, portName);
445         Optional<Ports> port =
446                 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
447                         LogicalDatastoreType.CONFIGURATION, portToIpMapIdentifier);
448         if (!port.isPresent()) {
449             LOG.info("getIntExtPortMapListForPortName : Unable to read router port entry for router ID {} "
450                     + "and port name {}", routerId, portName);
451             return null;
452         }
453         return port.get().getInternalToExternalPortMap();
454     }
455
456     private BigInteger getNaptSwitchforRouter(DataBroker broker, String routerName) {
457         InstanceIdentifier<RouterToNaptSwitch> rtrNaptSw = InstanceIdentifier.builder(NaptSwitches.class)
458             .child(RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerName)).build();
459         Optional<RouterToNaptSwitch> routerToNaptSwitchData =
460                 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
461                         LogicalDatastoreType.CONFIGURATION, rtrNaptSw);
462         if (routerToNaptSwitchData.isPresent()) {
463             RouterToNaptSwitch routerToNaptSwitchInstance = routerToNaptSwitchData.get();
464             return routerToNaptSwitchInstance.getPrimarySwitchId();
465         }
466         return null;
467     }
468
469     private void removeNatFlow(BigInteger dpnId, short tableId, Long routerId, String ipAddress, int ipPort) {
470
471         String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(routerId), ipAddress, ipPort);
472         FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, tableId, switchFlowRef);
473
474         mdsalManager.removeFlow(snatFlowEntity);
475         LOG.debug("removeNatFlow : Removed the flow in table {} for the switch with the DPN ID {} for "
476             + "router {} ip {} port {}", tableId, dpnId, routerId, ipAddress, ipPort);
477     }
478
479     private List<String> getFixedIpsForPort(String interfname) {
480         LOG.debug("getFixedIpsForPort : getFixedIpsForPort method is called for interface {}", interfname);
481         try {
482             Future<RpcResult<GetFixedIPsForNeutronPortOutput>> result =
483                 neutronVpnService.getFixedIPsForNeutronPort(new GetFixedIPsForNeutronPortInputBuilder()
484                     .setPortId(new Uuid(interfname)).build());
485
486             RpcResult<GetFixedIPsForNeutronPortOutput> rpcResult = result.get();
487             if (!rpcResult.isSuccessful()) {
488                 LOG.error("getFixedIpsForPort : RPC Call to GetFixedIPsForNeutronPortOutput returned with Errors {}",
489                     rpcResult.getErrors());
490             } else {
491                 return rpcResult.getResult().getFixedIPs();
492             }
493         } catch (InterruptedException | ExecutionException | NullPointerException ex) {
494             LOG.error("getFixedIpsForPort : Exception while receiving fixedIps for port {}", interfname, ex);
495         }
496         return null;
497     }
498
499     private void processInterfaceRemoved(String portName, BigInteger dpnId, String routerId,
500             List<ListenableFuture<Void>> futures) {
501         LOG.trace("processInterfaceRemoved : Processing Interface Removed Event for interface {} on DPN ID {}",
502                 portName, dpnId);
503         List<InternalToExternalPortMap> intExtPortMapList = getIntExtPortMapListForPortName(portName, routerId);
504         if (intExtPortMapList == null || intExtPortMapList.isEmpty()) {
505             LOG.debug("processInterfaceRemoved : Ip Mapping list is empty/null for portName {}", portName);
506             return;
507         }
508         InstanceIdentifier<RouterPorts> portIid = NatUtil.buildRouterPortsIdentifier(routerId);
509         ListenableFuture<Void> future = txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
510             for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
511                 LOG.trace("processInterfaceRemoved : Removing DNAT Flow entries for dpnId {} ", dpnId);
512                 floatingIPListener.removeNATFlowEntries(portName, intExtPortMap, portIid, routerId, dpnId, tx);
513             }
514         });
515         futures.add(future);
516         try {
517             future.get();
518         } catch (InterruptedException | ExecutionException e) {
519             LOG.error("Error processing interface removal", e);
520         }
521     }
522
523     // TODO Clean up the exception handling
524     @SuppressWarnings("checkstyle:IllegalCatch")
525     private void removeSnatEntriesForPort(String interfaceName, String routerName) {
526         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
527         if (routerId == NatConstants.INVALID_ID) {
528             LOG.error("removeSnatEntriesForPort : routerId not found for routername {}", routerName);
529             return;
530         }
531         BigInteger naptSwitch = getNaptSwitchforRouter(dataBroker, routerName);
532         if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
533             LOG.error("removeSnatEntriesForPort : NaptSwitch is not elected for router {} with Id {}",
534                     routerName, routerId);
535             return;
536         }
537         //getInternalIp for port
538         List<String> fixedIps = getFixedIpsForPort(interfaceName);
539         if (fixedIps == null) {
540             LOG.warn("removeSnatEntriesForPort : Internal Ips not found for InterfaceName {} in router {} with id {}",
541                 interfaceName, routerName, routerId);
542             return;
543         }
544
545         for (String internalIp : fixedIps) {
546             LOG.debug("removeSnatEntriesForPort : Internal Ip retrieved for interface {} is {} in router with Id {}",
547                 interfaceName, internalIp, routerId);
548             IpPort ipPort = NatUtil.getInternalIpPortInfo(dataBroker, routerId, internalIp);
549             if (ipPort == null) {
550                 LOG.debug("removeSnatEntriesForPort : no snatint-ip-port-map found for ip:{}", internalIp);
551                 continue;
552             }
553
554             for (IntIpProtoType protoType: ipPort.getIntIpProtoType()) {
555                 ProtocolTypes protocol = protoType.getProtocol();
556                 for (Integer portnum : protoType.getPorts()) {
557                     //build and remove the flow in outbound table
558                     try {
559                         removeNatFlow(naptSwitch, NwConstants.OUTBOUND_NAPT_TABLE, routerId, internalIp, portnum);
560                     } catch (Exception ex) {
561                         LOG.error("removeSnatEntriesForPort : Failed to remove snat flow for internalIP {} with "
562                                 + "Port {} protocol {} for routerId {} in OUTBOUNDTABLE of NaptSwitch {}",
563                             internalIp, portnum, protocol, routerId, naptSwitch, ex);
564                     }
565                     //Get the external IP address and the port from the model
566                     NAPTEntryEvent.Protocol proto = protocol.toString().equals(ProtocolTypes.TCP.toString())
567                         ? NAPTEntryEvent.Protocol.TCP : NAPTEntryEvent.Protocol.UDP;
568                     IpPortExternal ipPortExternal = NatUtil.getExternalIpPortMap(dataBroker, routerId,
569                         internalIp, String.valueOf(portnum), proto);
570                     if (ipPortExternal == null) {
571                         LOG.error("removeSnatEntriesForPort : Mapping for internalIp {} with port {} is not found in "
572                             + "router with Id {}", internalIp, portnum, routerId);
573                         return;
574                     }
575                     String externalIpAddress = ipPortExternal.getIpAddress();
576                     Integer portNumber = ipPortExternal.getPortNum();
577
578                     //build and remove the flow in inboundtable
579                     try {
580                         removeNatFlow(naptSwitch, NwConstants.INBOUND_NAPT_TABLE, routerId,
581                             externalIpAddress, portNumber);
582                     } catch (Exception ex) {
583                         LOG.error("removeSnatEntriesForPort : Failed to remove snat flow internalIP {} with "
584                                 + "Port {} protocol {} for routerId {} in INBOUNDTABLE of naptSwitch {}",
585                             externalIpAddress, portNumber, protocol, routerId, naptSwitch, ex);
586                     }
587
588                     String internalIpPort = internalIp + ":" + portnum;
589                     // delete the entry from IntExtIpPortMap DS
590                     try {
591                         naptManager.removeFromIpPortMapDS(routerId, internalIpPort, proto);
592                         naptManager.removePortFromPool(internalIpPort, externalIpAddress);
593                     } catch (Exception ex) {
594                         LOG.error("removeSnatEntriesForPort : releaseIpExtPortMapping failed, Removal of "
595                             + "ipportmap {} for router {} failed", internalIpPort, routerId, ex);
596                     }
597                 }
598             }
599             // delete the entry from SnatIntIpPortMap DS
600             LOG.debug("removeSnatEntriesForPort : Removing InternalIp:{} on router {}", internalIp, routerId);
601             naptManager.removeFromSnatIpPortDS(routerId, internalIp);
602         }
603     }
604
605     private class NatFlowAddWorker implements Callable<List<ListenableFuture<Void>>> {
606         private String interfaceName;
607         private String routerName;
608
609         NatFlowAddWorker(String interfaceName,String routerName) {
610             this.interfaceName = interfaceName;
611             this.routerName = routerName;
612         }
613
614         @Override
615         @SuppressWarnings("checkstyle:IllegalCatch")
616         public List<ListenableFuture<Void>> call() {
617             final List<ListenableFuture<Void>> futures = new ArrayList<>();
618             LOG.trace("call : Interface {} up event received", interfaceName);
619             try {
620                 LOG.trace("call : Port added event received for interface {} ", interfaceName);
621                 processInterfaceAdded(interfaceName, routerName, futures);
622             } catch (Exception ex) {
623                 LOG.error("call : Exception caught in Interface {} Operational State Up event",
624                         interfaceName, ex);
625             }
626             return futures;
627         }
628     }
629
630     private class NatFlowUpdateWorker implements Callable<List<ListenableFuture<Void>>> {
631         private Interface original;
632         private Interface update;
633         private String routerName;
634
635         NatFlowUpdateWorker(Interface original, Interface update, String routerName) {
636             this.original = original;
637             this.update = update;
638             this.routerName = routerName;
639         }
640
641         @Override
642         @SuppressWarnings("checkstyle:IllegalCatch")
643         public List<ListenableFuture<Void>> call() {
644             final List<ListenableFuture<Void>> futures = new ArrayList<>();
645             String interfaceName = update.getName();
646             IntfTransitionState state = getTransitionState(original.getOperStatus(), update.getOperStatus());
647             if (state.equals(IntfTransitionState.STATE_IGNORE)) {
648                 LOG.info("NAT Service: Interface {} state original {} updated {} not handled",
649                         interfaceName, original.getOperStatus(), update.getOperStatus());
650                 return futures;
651             }
652             if (state.equals(IntfTransitionState.STATE_UP)) {
653                 LOG.debug("call : Port UP event received for interface {} ", interfaceName);
654             } else if (state.equals(IntfTransitionState.STATE_DOWN)) {
655                 LOG.debug("call : Port DOWN event received for interface {} ", interfaceName);
656                 try {
657                     removeSnatEntriesForPort(interfaceName, routerName);
658                 } catch (Exception ex) {
659                     LOG.error("call : Exception caught in Interface {} OperationalStateDown", interfaceName, ex);
660                 }
661             }
662             return futures;
663         }
664     }
665
666     private class NatFlowRemoveWorker implements Callable<List<ListenableFuture<Void>>> {
667         private Interface delintrf;
668         private String routerName;
669         private BigInteger intfDpnId;
670
671         NatFlowRemoveWorker(Interface delintrf, BigInteger intfDpnId, String routerName) {
672             this.delintrf = delintrf;
673             this.routerName = routerName;
674             this.intfDpnId = intfDpnId;
675         }
676
677         @Override
678         @SuppressWarnings("checkstyle:IllegalCatch")
679         public List<ListenableFuture<Void>> call() {
680             final List<ListenableFuture<Void>> futures = new ArrayList<>();
681             final String interfaceName = delintrf.getName();
682             LOG.trace("call : Interface {} removed event received", delintrf);
683             try {
684                 LOG.trace("call : Port removed event received for interface {} ", interfaceName);
685                 processInterfaceRemoved(interfaceName, intfDpnId, routerName, futures);
686                 removeSnatEntriesForPort(interfaceName, routerName);
687             } catch (Exception e) {
688                 LOG.error("call : Exception caught in Interface {} OperationalStateRemove", interfaceName, e);
689             }
690             return futures;
691         }
692     }
693 }