Stale table=44 flows remains on VM deletion
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / NaptSwitchHA.java
1 /*
2  * Copyright © 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 com.google.common.base.Optional;
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Map.Entry;
18 import java.util.Set;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.Future;
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
28 import org.opendaylight.genius.infra.Datastore.Configuration;
29 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
30 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
31 import org.opendaylight.genius.mdsalutil.ActionInfo;
32 import org.opendaylight.genius.mdsalutil.BucketInfo;
33 import org.opendaylight.genius.mdsalutil.FlowEntity;
34 import org.opendaylight.genius.mdsalutil.GroupEntity;
35 import org.opendaylight.genius.mdsalutil.InstructionInfo;
36 import org.opendaylight.genius.mdsalutil.MDSALUtil;
37 import org.opendaylight.genius.mdsalutil.MatchInfo;
38 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
39 import org.opendaylight.genius.mdsalutil.NwConstants;
40 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
41 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
42 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
43 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
44 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
45 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
46 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
47 import org.opendaylight.netvirt.elanmanager.api.IElanService;
48 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
49 import org.opendaylight.netvirt.natservice.api.SnatServiceManager;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryKey;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig.NatMode;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalNetworks;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProtocolTypes;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.Networks;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.NetworksKey;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.IpPortMapping;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap;
76 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;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitch;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchBuilder;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
80 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
81 import org.opendaylight.yangtools.yang.common.RpcResult;
82 import org.slf4j.Logger;
83 import org.slf4j.LoggerFactory;
84
85 @Singleton
86 public class NaptSwitchHA {
87     private static final Logger LOG = LoggerFactory.getLogger(NaptSwitchHA.class);
88     private final DataBroker dataBroker;
89     private final IMdsalApiManager mdsalManager;
90     private final ItmRpcService itmManager;
91     private final OdlInterfaceRpcService odlInterfaceRpcService;
92     private final IdManagerService idManager;
93     private final NAPTSwitchSelector naptSwitchSelector;
94     private final ExternalRoutersListener externalRouterListener;
95     private final NaptEventHandler naptEventHandler;
96     private final IFibManager fibManager;
97     private final IElanService elanManager;
98     private final EvpnNaptSwitchHA evpnNaptSwitchHA;
99     private final SnatServiceManager natServiceManager;
100     private final NatMode natMode;
101     private final IInterfaceManager interfaceManager;
102     private final NatOverVxlanUtil natOverVxlanUtil;
103
104     private volatile Collection<String> externalIpsCache;
105
106     @Inject
107     public NaptSwitchHA(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
108                         final ExternalRoutersListener externalRouterListener,
109                         final ItmRpcService itmManager,
110                         final OdlInterfaceRpcService odlInterfaceRpcService,
111                         final IdManagerService idManager,
112                         final NAPTSwitchSelector naptSwitchSelector,
113                         final IFibManager fibManager,
114                         final EvpnNaptSwitchHA evpnNaptSwitchHA,
115                         final IElanService elanManager,
116                         final SnatServiceManager natServiceManager,
117                         final NatserviceConfig config,
118                         final NaptEventHandler naptEventHandler,
119                         final IInterfaceManager interfaceManager,
120                         final NatOverVxlanUtil natOverVxlanUtil) {
121         this.dataBroker = dataBroker;
122         this.mdsalManager = mdsalManager;
123         this.externalRouterListener = externalRouterListener;
124         this.itmManager = itmManager;
125         this.odlInterfaceRpcService = odlInterfaceRpcService;
126         this.idManager = idManager;
127         this.naptSwitchSelector = naptSwitchSelector;
128         this.naptEventHandler = naptEventHandler;
129         this.fibManager = fibManager;
130         this.evpnNaptSwitchHA = evpnNaptSwitchHA;
131         this.elanManager = elanManager;
132         this.natServiceManager = natServiceManager;
133         this.interfaceManager = interfaceManager;
134         if (config != null) {
135             this.natMode = config.getNatMode();
136         } else {
137             this.natMode = NatMode.Controller;
138         }
139         this.natOverVxlanUtil = natOverVxlanUtil;
140     }
141
142     protected void removeSnatFlowsInOldNaptSwitch(String routerName, Long routerId, BigInteger naptSwitch,
143                                                   @Nullable Map<String, Long> externalIpmap,
144                                                   TypedReadWriteTransaction<Configuration> confTx)
145             throws ExecutionException, InterruptedException {
146
147         //remove SNAT flows in old NAPT SWITCH
148         Uuid networkId = NatUtil.getNetworkIdFromRouterName(dataBroker, routerName);
149         String vpnName = getExtNetworkVpnName(routerName, networkId);
150         if (vpnName == null) {
151             LOG.error("removeSnatFlowsInOldNaptSwitch : Vpn is not associated to externalN/w of router {}",
152                 routerName);
153             return;
154         }
155         ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker, routerName, networkId);
156         if (extNwProvType == null) {
157             LOG.error("removeSnatFlowsInOldNaptSwitch : Unable to retrieve the External Network Provider Type "
158                 + "for Router {}", routerName);
159             return;
160         }
161         if (extNwProvType == ProviderTypes.VXLAN) {
162             evpnNaptSwitchHA.evpnRemoveSnatFlowsInOldNaptSwitch(routerName, routerId, vpnName, naptSwitch, confTx);
163         } else {
164             //Remove the Terminating Service table entry which forwards the packet to Outbound NAPT Table
165             long tunnelId = NatUtil.getTunnelIdForNonNaptToNaptFlow(dataBroker, natOverVxlanUtil, elanManager,
166                     idManager, routerId, routerName);
167             String tsFlowRef = externalRouterListener.getFlowRefTs(naptSwitch, NwConstants.INTERNAL_TUNNEL_TABLE,
168                 tunnelId);
169             FlowEntity tsNatFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NwConstants.INTERNAL_TUNNEL_TABLE,
170                 tsFlowRef);
171
172             LOG.info("removeSnatFlowsInOldNaptSwitch : Remove the flow in table {} for the old napt switch "
173                 + "with the DPN ID {} and router ID {}", NwConstants.INTERNAL_TUNNEL_TABLE, naptSwitch, routerId);
174             mdsalManager.removeFlow(confTx, tsNatFlowEntity);
175         }
176         if (NatUtil.isOpenStackVniSemanticsEnforcedForGreAndVxlan(elanManager, extNwProvType)) {
177             //Remove the flow table 25->44 If there is no FIP Match on table 25 (PDNAT_TABLE)
178             NatUtil.removePreDnatToSnatTableEntry(confTx, mdsalManager, naptSwitch);
179         }
180         //Remove the Outbound flow entry which forwards the packet to Outbound NAPT Table
181         LOG.info("Remove the flow in table {} for the old napt switch with the DPN ID {} and router ID {}",
182             NwConstants.OUTBOUND_NAPT_TABLE, naptSwitch, routerId);
183
184         String outboundTcpNatFlowRef = externalRouterListener.getFlowRefOutbound(naptSwitch,
185             NwConstants.OUTBOUND_NAPT_TABLE, routerId, NwConstants.IP_PROT_TCP);
186         FlowEntity outboundTcpNatFlowEntity = NatUtil.buildFlowEntity(naptSwitch,
187             NwConstants.OUTBOUND_NAPT_TABLE, outboundTcpNatFlowRef);
188         mdsalManager.removeFlow(confTx, outboundTcpNatFlowEntity);
189
190         String outboundUdpNatFlowRef = externalRouterListener.getFlowRefOutbound(naptSwitch,
191             NwConstants.OUTBOUND_NAPT_TABLE, routerId, NwConstants.IP_PROT_UDP);
192         FlowEntity outboundUdpNatFlowEntity = NatUtil.buildFlowEntity(naptSwitch,
193             NwConstants.OUTBOUND_NAPT_TABLE, outboundUdpNatFlowRef);
194         mdsalManager.removeFlow(confTx, outboundUdpNatFlowEntity);
195
196         String icmpDropFlowRef = externalRouterListener.getFlowRefOutbound(naptSwitch,
197             NwConstants.OUTBOUND_NAPT_TABLE, routerId, NwConstants.IP_PROT_ICMP);
198         FlowEntity icmpDropFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NwConstants.OUTBOUND_NAPT_TABLE,
199             icmpDropFlowRef);
200         mdsalManager.removeFlow(confTx, icmpDropFlowEntity);
201
202         //Remove the NAPT PFIB TABLE (47->21) which forwards the incoming packet to FIB Table matching on the
203         // External Subnet Vpn Id.
204         Collection<Uuid> externalSubnetIdsForRouter = NatUtil.getExternalSubnetIdsForRouter(dataBroker,
205             routerName);
206         for (Uuid externalSubnetId : externalSubnetIdsForRouter) {
207             long subnetVpnId = NatUtil.getVpnId(dataBroker, externalSubnetId.getValue());
208             if (subnetVpnId != -1 && !NatUtil.checkForRoutersWithSameExtSubnetAndNaptSwitch(
209                 dataBroker, externalSubnetId, routerName, naptSwitch)) {
210                 String natPfibSubnetFlowRef = externalRouterListener.getFlowRefTs(naptSwitch,
211                     NwConstants.NAPT_PFIB_TABLE, subnetVpnId);
212                 FlowEntity natPfibFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NwConstants.NAPT_PFIB_TABLE,
213                     natPfibSubnetFlowRef);
214                 mdsalManager.removeFlow(confTx, natPfibFlowEntity);
215                 LOG.debug("removeSnatFlowsInOldNaptSwitch : Removed the flow in table {} with external subnet "
216                           + "Vpn Id {} as metadata on Napt Switch {}", NwConstants.NAPT_PFIB_TABLE,
217                     subnetVpnId, naptSwitch);
218             }
219         }
220
221         // Remove the NAPT_PFIB_TABLE(47) flow entry forwards the packet to Fib Table for inbound traffic
222         // matching on the router ID.
223         String naptPFibflowRef =
224             externalRouterListener.getFlowRefTs(naptSwitch, NwConstants.NAPT_PFIB_TABLE, routerId);
225         FlowEntity naptPFibFlowEntity =
226             NatUtil.buildFlowEntity(naptSwitch, NwConstants.NAPT_PFIB_TABLE, naptPFibflowRef);
227         LOG.info("removeSnatFlowsInOldNaptSwitch : Remove the flow in table {} for the old napt switch "
228             + "with the DPN ID {} and router ID {}", NwConstants.NAPT_PFIB_TABLE, naptSwitch, routerId);
229         mdsalManager.removeFlow(confTx, naptPFibFlowEntity);
230
231         // Remove the NAPT_PFIB_TABLE(47) flow entry forwards the packet to Fib Table for outbound traffic
232         // matching on the vpn ID.
233         boolean switchSharedByRouters = false;
234         Uuid extNetworkId = NatUtil.getNetworkIdFromRouterName(dataBroker, routerName);
235         if (extNetworkId != null && !NatUtil.checkForRoutersWithSameExtNetAndNaptSwitch(
236             dataBroker, networkId, routerName, naptSwitch)) {
237             List<String> routerNamesAssociated = getRouterIdsForExtNetwork(extNetworkId);
238             for (String routerNameAssociated : routerNamesAssociated) {
239                 if (!routerNameAssociated.equals(routerName)) {
240                     Long routerIdAssociated = NatUtil.getVpnId(dataBroker, routerNameAssociated);
241                     BigInteger naptDpn = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerNameAssociated);
242                     if (naptDpn != null && naptDpn.equals(naptSwitch)) {
243                         LOG.debug("removeSnatFlowsInOldNaptSwitch : Napt switch {} is also acting as primary "
244                             + "for router {}", naptSwitch, routerIdAssociated);
245                         switchSharedByRouters = true;
246                         break;
247                     }
248                 }
249             }
250             if (!switchSharedByRouters) {
251                 Long vpnId = getVpnIdForRouter(routerId, extNetworkId);
252                 if (vpnId != NatConstants.INVALID_ID) {
253                     String naptFibflowRef =
254                         externalRouterListener.getFlowRefTs(naptSwitch, NwConstants.NAPT_PFIB_TABLE, vpnId);
255                     FlowEntity naptFibFlowEntity =
256                         NatUtil.buildFlowEntity(naptSwitch, NwConstants.NAPT_PFIB_TABLE, naptFibflowRef);
257                     LOG.info("removeSnatFlowsInOldNaptSwitch : Remove the flow in table {} for the old napt switch"
258                         + " with the DPN ID {} and vpnId {}", NwConstants.NAPT_PFIB_TABLE, naptSwitch, vpnId);
259                     mdsalManager.removeFlow(confTx, naptFibFlowEntity);
260                 } else {
261                     LOG.error("removeSnatFlowsInOldNaptSwitch : Invalid vpnId retrieved for routerId {}",
262                         routerId);
263                     return;
264                 }
265             }
266         }
267
268         //Remove Fib entries,tables 20->44 ,36-> 44
269         String gwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routerName);
270         if (externalIpmap != null && !externalIpmap.isEmpty()) {
271             for (Entry<String, Long> entry : externalIpmap.entrySet()) {
272                 String externalIp = entry.getKey();
273                 Long label = entry.getValue();
274                 externalRouterListener.delFibTsAndReverseTraffic(naptSwitch, routerName, routerId, externalIp, vpnName,
275                     extNetworkId, label, gwMacAddress, true, confTx);
276                 LOG.debug("removeSnatFlowsInOldNaptSwitch : Successfully removed fib entries in old naptswitch {} "
277                     + "for router {} and externalIps {} label {}", naptSwitch, routerId, externalIp, label);
278             }
279         } else {
280             List<String> externalIps = NatUtil.getExternalIpsForRouter(dataBroker, routerName);
281             if (networkId != null) {
282                 externalRouterListener.clearFibTsAndReverseTraffic(naptSwitch, routerId, networkId,
283                     externalIps, null, gwMacAddress, confTx);
284                 LOG.debug(
285                     "removeSnatFlowsInOldNaptSwitch : Successfully removed fib entries in old naptswitch {} for "
286                         + "router {} with networkId {} and externalIps {}", naptSwitch, routerId, networkId,
287                     externalIps);
288             } else {
289                 LOG.debug("removeSnatFlowsInOldNaptSwitch : External network not associated to router {}",
290                     routerId);
291             }
292             externalRouterListener.removeNaptFibExternalOutputFlows(routerId, naptSwitch, extNetworkId,
293                 externalIps, confTx);
294         }
295
296         //For the router ID get the internal IP , internal port and the corresponding external IP and external Port.
297         IpPortMapping ipPortMapping = NatUtil.getIportMapping(dataBroker, routerId);
298         if (ipPortMapping == null || ipPortMapping.getIntextIpProtocolType() == null
299             || ipPortMapping.getIntextIpProtocolType().isEmpty()) {
300             LOG.warn("removeSnatFlowsInOldNaptSwitch : No Internal Ip Port mapping associated to router {}, "
301                 + "no flows need to be removed in oldNaptSwitch {}", routerId, naptSwitch);
302             return;
303         }
304         BigInteger cookieSnatFlow = NatUtil.getCookieNaptFlow(routerId);
305         List<IntextIpProtocolType> intextIpProtocolTypes = ipPortMapping.getIntextIpProtocolType();
306         for (IntextIpProtocolType intextIpProtocolType : intextIpProtocolTypes) {
307             if (intextIpProtocolType.getIpPortMap() == null || intextIpProtocolType.getIpPortMap().isEmpty()) {
308                 LOG.debug("removeSnatFlowsInOldNaptSwitch : No {} session associated to router {},"
309                         + "no flows need to be removed in oldNaptSwitch {}",
310                     intextIpProtocolType.getProtocol(), routerId, naptSwitch);
311                 continue;
312             }
313             String protocol = intextIpProtocolType.getProtocol().name();
314             List<IpPortMap> ipPortMaps = intextIpProtocolType.getIpPortMap();
315             for (IpPortMap ipPortMap : ipPortMaps) {
316                 String ipPortInternal = ipPortMap.getIpPortInternal();
317                 String[] ipPortParts = ipPortInternal.split(":");
318                 if (ipPortParts.length != 2) {
319                     LOG.error("removeSnatFlowsInOldNaptSwitch : Unable to retrieve the Internal IP and port");
320                     continue;
321                 }
322                 String internalIp = ipPortParts[0];
323                 String internalPort = ipPortParts[1];
324
325                 //Build and remove flow in outbound NAPT table
326                 String switchFlowRef =
327                     NatUtil.getNaptFlowRef(naptSwitch, NwConstants.OUTBOUND_NAPT_TABLE, String.valueOf(routerId),
328                         internalIp, Integer.parseInt(internalPort), protocol);
329                 FlowEntity outboundNaptFlowEntity =
330                     NatUtil.buildFlowEntity(naptSwitch, NwConstants.OUTBOUND_NAPT_TABLE,
331                         cookieSnatFlow, switchFlowRef);
332
333                 LOG.info("removeSnatFlowsInOldNaptSwitch : Remove the flow in table {} for old napt switch "
334                     + "with the DPN ID {} and router ID {}", NwConstants.OUTBOUND_NAPT_TABLE, naptSwitch, routerId);
335                 mdsalManager.removeFlow(confTx, outboundNaptFlowEntity);
336
337                 //Build and remove flow in  inbound NAPT table
338                 switchFlowRef =
339                     NatUtil.getNaptFlowRef(naptSwitch, NwConstants.INBOUND_NAPT_TABLE, String.valueOf(routerId),
340                         internalIp, Integer.parseInt(internalPort), protocol);
341                 FlowEntity inboundNaptFlowEntity =
342                     NatUtil.buildFlowEntity(naptSwitch, NwConstants.INBOUND_NAPT_TABLE,
343                         cookieSnatFlow, switchFlowRef);
344
345                 LOG.info(
346                     "removeSnatFlowsInOldNaptSwitch : Remove the flow in table {} for old napt switch with the "
347                         + "DPN ID {} and router ID {}", NwConstants.INBOUND_NAPT_TABLE, naptSwitch, routerId);
348                 mdsalManager.removeFlow(confTx, inboundNaptFlowEntity);
349             }
350         }
351     }
352
353     @NonNull
354     private List<String> getRouterIdsForExtNetwork(Uuid extNetworkId) {
355         List<String> routerUuidsAsString = new ArrayList<>();
356         InstanceIdentifier<Networks> extNetwork = InstanceIdentifier.builder(ExternalNetworks.class)
357             .child(Networks.class, new NetworksKey(extNetworkId)).build();
358         Optional<Networks> extNetworkData =
359                 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
360                         LogicalDatastoreType.CONFIGURATION, extNetwork);
361         if (extNetworkData.isPresent()) {
362             List<Uuid> routerUuids = extNetworkData.get().getRouterIds();
363             if (routerUuids != null) {
364                 for (Uuid routerUuid : routerUuids) {
365                     routerUuidsAsString.add(routerUuid.getValue());
366                 }
367             }
368         }
369         return routerUuidsAsString;
370     }
371
372     public boolean isNaptSwitchDown(String routerName, Long routerId, BigInteger dpnId, BigInteger naptSwitch,
373                                     Long routerVpnId, Collection<String> externalIpCache,
374                                     TypedReadWriteTransaction<Configuration> confTx)
375             throws ExecutionException, InterruptedException {
376         return isNaptSwitchDown(routerName, routerId, dpnId, naptSwitch, routerVpnId, externalIpCache, true,
377                 confTx);
378     }
379
380     // TODO Clean up the exception handling
381     @SuppressWarnings("checkstyle:IllegalCatch")
382     public boolean isNaptSwitchDown(String routerName, Long routerId, BigInteger dpnId, BigInteger naptSwitch,
383                                     Long routerVpnId, Collection<String> externalIpCache, boolean isClearBgpRts,
384                                     TypedReadWriteTransaction<Configuration> confTx)
385             throws ExecutionException, InterruptedException {
386         externalIpsCache = externalIpCache;
387         if (!naptSwitch.equals(dpnId)) {
388             LOG.debug("isNaptSwitchDown : DpnId {} is not a naptSwitch {} for Router {}",
389                     dpnId, naptSwitch, routerName);
390             return false;
391         }
392         LOG.debug("NaptSwitch {} is down for Router {}", naptSwitch, routerName);
393         if (routerId == NatConstants.INVALID_ID) {
394             LOG.error("isNaptSwitchDown : Invalid routerId returned for routerName {}", routerName);
395             return true;
396         }
397         Uuid networkId = NatUtil.getNetworkIdFromRouterName(dataBroker, routerName);
398         String vpnName = getExtNetworkVpnName(routerName, networkId);
399         //elect a new NaptSwitch
400         naptSwitch = naptSwitchSelector.selectNewNAPTSwitch(routerName);
401         if (natMode == NatMode.Conntrack) {
402             Routers extRouters = NatUtil.getRoutersFromConfigDS(dataBroker, routerName);
403             natServiceManager.notify(confTx, extRouters, null, dpnId, dpnId,
404                     SnatServiceManager.Action.CNT_ROUTER_ALL_SWITCH_DISBL);
405             if (extRouters.isEnableSnat()) {
406                 natServiceManager.notify(confTx, extRouters, null, dpnId, dpnId,
407                         SnatServiceManager.Action.SNAT_ALL_SWITCH_DISBL);
408             }
409             natServiceManager.notify(confTx, extRouters, null, naptSwitch, naptSwitch,
410                     SnatServiceManager.Action.CNT_ROUTER_ALL_SWITCH_ENBL);
411             if (extRouters.isEnableSnat()) {
412                 natServiceManager.notify(confTx, extRouters, null, naptSwitch, naptSwitch,
413                         SnatServiceManager.Action.SNAT_ALL_SWITCH_ENBL);
414             }
415         } else {
416             if (naptSwitch.equals(BigInteger.ZERO)) {
417                 LOG.warn("isNaptSwitchDown : No napt switch is elected since all the switches for router {}"
418                         + " are down. SNAT IS NOT SUPPORTED FOR ROUTER {}", routerName, routerName);
419                 boolean naptUpdatedStatus = updateNaptSwitch(routerName, naptSwitch);
420                 if (!naptUpdatedStatus) {
421                     LOG.debug("isNaptSwitchDown : Failed to update naptSwitch {} for router {} in ds",
422                             naptSwitch, routerName);
423                 }
424                 //clearBgpRoutes
425                 if (externalIpsCache != null) {
426                     if (vpnName != null) {
427                         //List<String> externalIps = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
428                         //if (externalIps != null) {
429                         if (isClearBgpRts) {
430                             LOG.debug("isNaptSwitchDown : Clearing both FIB entries and the BGP routes");
431                             for (String externalIp : externalIpsCache) {
432                                 externalRouterListener.clearBgpRoutes(externalIp, vpnName);
433                             }
434                         } else {
435                             LOG.debug("isNaptSwitchDown : Clearing the FIB entries but not the BGP routes");
436                             String rd = NatUtil.getVpnRd(dataBroker, vpnName);
437                             for (String externalIp : externalIpsCache) {
438                                 LOG.debug("isNaptSwitchDown : Removing Fib entry rd {} prefix {}", rd, externalIp);
439                                 fibManager.removeFibEntry(rd, externalIp, null);
440                             }
441                         }
442                     } else {
443                         LOG.debug("isNaptSwitchDown : vpn is not associated to extn/w for router {}", routerName);
444                     }
445                 } else {
446                     LOG.debug("isNaptSwitchDown : No ExternalIps found for subnets under router {}, "
447                             + "no bgp routes need to be cleared", routerName);
448                 }
449                 return true;
450             }
451             //checking elected switch health status
452             if (!NatUtil.getSwitchStatus(dataBroker, naptSwitch)) {
453                 LOG.error("isNaptSwitchDown : Newly elected Napt switch {} for router {} is down",
454                         naptSwitch, routerName);
455                 return true;
456             }
457             LOG.debug("isNaptSwitchDown : New NaptSwitch {} is up for Router {} and can proceed for flow installation",
458                     naptSwitch, routerName);
459             //update napt model for new napt switch
460             boolean naptUpdated = updateNaptSwitch(routerName, naptSwitch);
461             if (naptUpdated) {
462                 //update group of ordinary switch point to naptSwitch tunnel port
463                 updateNaptSwitchBucketStatus(routerName, routerId, naptSwitch);
464             } else {
465                 LOG.error("isNaptSwitchDown : Failed to update naptSwitch model for newNaptSwitch {} for router {}",
466                         naptSwitch, routerName);
467             }
468
469             //update table26 forward packets to table46(outbound napt table)
470             FlowEntity flowEntity =
471                     buildSnatFlowEntityForNaptSwitch(naptSwitch, routerName, routerVpnId, NatConstants.ADD_FLOW);
472             if (flowEntity == null) {
473                 LOG.error("isNaptSwitchDown : Failed to populate flowentity for router {} in naptSwitch {}",
474                         routerName, naptSwitch);
475             } else {
476                 LOG.debug("isNaptSwitchDown : Successfully installed flow in naptSwitch {} for router {}",
477                         naptSwitch, routerName);
478                 mdsalManager.addFlow(confTx, flowEntity);
479             }
480
481             installSnatFlows(routerName, routerId, naptSwitch, routerVpnId, confTx);
482
483             boolean flowInstalledStatus = handleNatFlowsInNewNaptSwitch(routerName, routerId, dpnId, naptSwitch,
484                     routerVpnId, networkId);
485             if (flowInstalledStatus) {
486                 LOG.debug("isNaptSwitchDown :Installed all active session flows in newNaptSwitch {} for routerName {}",
487                         naptSwitch, routerName);
488             } else {
489                 LOG.error("isNaptSwitchDown : Failed to install flows in newNaptSwitch {} for routerId {}",
490                         naptSwitch, routerId);
491             }
492
493             //remove group in new naptswitch, coz this switch acted previously as ordinary switch
494             long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
495             try {
496                 LOG.info("isNaptSwitchDown : Removing NAPT Group in new naptSwitch {}", naptSwitch);
497                 mdsalManager.removeGroup(confTx, naptSwitch, groupId);
498             } catch (Exception ex) {
499                 LOG.error("isNaptSwitchDown : Failed to remove group in new naptSwitch {}", naptSwitch, ex);
500             }
501         }
502         return true;
503     }
504
505     @Nullable
506     private String getExtNetworkVpnName(String routerName, Uuid networkId) {
507         if (networkId == null) {
508             LOG.error("getExtNetworkVpnName : networkId is null for the router ID {}", routerName);
509         } else {
510             final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId);
511             if (vpnName != null) {
512                 LOG.debug("getExtNetworkVpnName : retrieved vpn name {} associated with ext nw {} in router {}",
513                     vpnName, networkId, routerName);
514                 return vpnName;
515             } else {
516                 LOG.error("getExtNetworkVpnName : No VPN associated with ext nw {} belonging to routerId {}",
517                     networkId, routerName);
518             }
519         }
520         LOG.error("getExtNetworkVpnName : External Network VPN not found for router : {}", routerName);
521         return null;
522     }
523
524     public void updateNaptSwitchBucketStatus(String routerName, long routerId, BigInteger naptSwitch) {
525         LOG.debug("updateNaptSwitchBucketStatus : called");
526
527         List<BigInteger> dpnList = naptSwitchSelector.getDpnsForVpn(routerName);
528         //List<BigInteger> dpnList = getDpnListForRouter(routerName);
529         if (dpnList.isEmpty()) {
530             LOG.warn("updateNaptSwitchBucketStatus : No switches found for router {}", routerName);
531             return;
532         }
533         for (BigInteger dpn : dpnList) {
534             if (!dpn.equals(naptSwitch)) {
535                 LOG.debug("updateNaptSwitchBucketStatus : Updating SNAT_TABLE missentry for DpnId {} "
536                         + "which is not naptSwitch for router {}", dpn, routerName);
537                 List<BucketInfo> bucketInfoList = handleGroupInNeighborSwitches(dpn, routerName, routerId, naptSwitch);
538                 modifySnatGroupEntry(dpn, bucketInfoList, routerName);
539             }
540         }
541     }
542
543     // TODO Clean up the exception handling
544     @SuppressWarnings("checkstyle:IllegalCatch")
545     private boolean handleNatFlowsInNewNaptSwitch(String routerName, Long routerId, BigInteger oldNaptSwitch,
546                                                     BigInteger newNaptSwitch, Long routerVpnId, Uuid networkId) {
547         LOG.debug("handleNatFlowsInNewNaptSwitch : Proceeding to install flows in newNaptSwitch {} for routerId {}",
548                 newNaptSwitch, routerId);
549         IpPortMapping ipPortMapping = NatUtil.getIportMapping(dataBroker, routerId);
550         if (ipPortMapping == null || ipPortMapping.getIntextIpProtocolType() == null
551             || ipPortMapping.getIntextIpProtocolType().isEmpty()) {
552             LOG.debug("handleNatFlowsInNewNaptSwitch : No Internal Ip Port mapping associated to router {},"
553                     + "no flows need to be installed in newNaptSwitch {}", routerId, newNaptSwitch);
554             return true;
555         }
556         //getvpnId
557         Long vpnId = getVpnIdForRouter(routerId, networkId);
558         if (vpnId == NatConstants.INVALID_ID) {
559             LOG.error("handleNatFlowsInNewNaptSwitch : Invalid vpnId for routerId {}", routerId);
560             return false;
561         }
562         Long bgpVpnId;
563         if (routerId.equals(routerVpnId)) {
564             bgpVpnId = NatConstants.INVALID_ID;
565         } else {
566             bgpVpnId = routerVpnId;
567         }
568         LOG.debug("handleNatFlowsInNewNaptSwitch : retrieved bgpVpnId {} for router {}", bgpVpnId, routerId);
569         // Get the External Gateway MAC Address
570         String extGwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routerName);
571         if (extGwMacAddress != null) {
572             LOG.debug("handleNatFlowsInNewNaptSwitch :External Gateway MAC address {} found for External Router ID {}",
573                     extGwMacAddress, routerId);
574         } else {
575             LOG.error("handleNatFlowsInNewNaptSwitch : No External Gateway MAC address found for External Router ID {}",
576                     routerId);
577             return false;
578         }
579         for (IntextIpProtocolType protocolType : ipPortMapping.getIntextIpProtocolType()) {
580             if (protocolType.getIpPortMap() == null || protocolType.getIpPortMap().isEmpty()) {
581                 LOG.debug("handleNatFlowsInNewNaptSwitch : No {} session associated to router {}",
582                         protocolType.getProtocol(), routerId);
583                 return true;
584             }
585             for (IpPortMap intIpPortMap : protocolType.getIpPortMap()) {
586                 String internalIpAddress = intIpPortMap.getIpPortInternal().split(":")[0];
587                 String intportnum = intIpPortMap.getIpPortInternal().split(":")[1];
588                 LOG.debug("handleNatFlowsInNewNaptSwitch : Found Internal IP Address {} and Port Number {}",
589                     internalIpAddress, intportnum);
590                 //Get the external IP address and the port from the model
591                 NAPTEntryEvent.Protocol proto =
592                     protocolType.getProtocol().toString().equals(ProtocolTypes.TCP.toString())
593                     ? NAPTEntryEvent.Protocol.TCP : NAPTEntryEvent.Protocol.UDP;
594                 IpPortExternal ipPortExternal = NatUtil.getExternalIpPortMap(dataBroker, routerId,
595                     internalIpAddress, intportnum, proto);
596                 if (ipPortExternal == null) {
597                     LOG.debug("handleNatFlowsInNewNaptSwitch : External Ipport mapping is not found for internalIp {} "
598                             + "with port {}", internalIpAddress, intportnum);
599                     continue;
600                 }
601                 String externalIpAddress = ipPortExternal.getIpAddress();
602                 Integer extportNumber = ipPortExternal.getPortNum();
603                 LOG.debug("handleNatFlowsInNewNaptSwitch : ExternalIPport {}:{} mapping for internal ipport {}:{}",
604                         externalIpAddress, extportNumber, internalIpAddress, intportnum);
605
606                 SessionAddress sourceAddress = new SessionAddress(internalIpAddress, Integer.parseInt(intportnum));
607                 SessionAddress externalAddress = new SessionAddress(externalIpAddress, extportNumber);
608
609                 //checking naptSwitch status before installing flows
610                 if (NatUtil.getSwitchStatus(dataBroker, newNaptSwitch)) {
611                     //Install the flow in newNaptSwitch Inbound NAPT table.
612                     try {
613                         naptEventHandler.buildAndInstallNatFlows(newNaptSwitch, NwConstants.INBOUND_NAPT_TABLE,
614                             vpnId, routerId, bgpVpnId, externalAddress, sourceAddress, proto, extGwMacAddress);
615                     } catch (RuntimeException ex) {
616                         LOG.error("handleNatFlowsInNewNaptSwitch : Failed to add flow in INBOUND_NAPT_TABLE for "
617                                 + "routerid {} dpnId {} extIpport{}:{} proto {} ipport {}:{} BgpVpnId {}",
618                             routerId, newNaptSwitch, externalAddress, extportNumber, proto,
619                             internalIpAddress, intportnum, bgpVpnId);
620                         return false;
621                     }
622                     LOG.debug("handleNatFlowsInNewNaptSwitch : Successfully installed a flow in Primary switch {} "
623                             + "Inbound NAPT table for router {} ipport {}:{} proto {} extIpport {}:{} BgpVpnId {}",
624                         newNaptSwitch, routerId, internalIpAddress,
625                         intportnum, proto, externalAddress, extportNumber, bgpVpnId);
626                     //Install the flow in newNaptSwitch Outbound NAPT table.
627                     try {
628                         naptEventHandler.buildAndInstallNatFlows(newNaptSwitch, NwConstants.OUTBOUND_NAPT_TABLE,
629                             vpnId, routerId, bgpVpnId, sourceAddress, externalAddress, proto, extGwMacAddress);
630                     } catch (RuntimeException ex) {
631                         LOG.error("handleNatFlowsInNewNaptSwitch : Failed to add flow in OUTBOUND_NAPT_TABLE for "
632                                 + "routerid {} dpnId {} ipport {}:{} proto {} extIpport {}:{} BgpVpnId {}",
633                             routerId, newNaptSwitch, internalIpAddress,
634                             intportnum, proto, externalAddress, extportNumber, bgpVpnId, ex);
635                         return false;
636                     }
637                     LOG.debug("handleNatFlowsInNewNaptSwitch : Successfully installed a flow in Primary switch {} "
638                             + "Outbound NAPT table for router {} ipport {}:{} proto {} extIpport {}:{} BgpVpnId {}",
639                         newNaptSwitch, routerId, internalIpAddress,
640                         intportnum, proto, externalAddress, extportNumber, bgpVpnId);
641                 } else {
642                     LOG.error("handleNatFlowsInNewNaptSwitch : NewNaptSwitch {} gone down while installing flows "
643                             + "from oldNaptswitch {}", newNaptSwitch, oldNaptSwitch);
644                     return false;
645                 }
646             }
647         }
648         return true;
649     }
650
651     // TODO Clean up the exception handling
652     @SuppressWarnings("checkstyle:IllegalCatch")
653     private Long getVpnIdForRouter(Long routerId, Uuid networkId) {
654         try {
655             //getvpnId
656             if (networkId == null) {
657                 LOG.debug("getVpnIdForRouter : network is not associated to router {}", routerId);
658             } else {
659                 Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
660                 if (vpnUuid == null) {
661                     LOG.debug("getVpnIdForRouter : vpn is not associated for network {} in router {}",
662                             networkId, routerId);
663                 } else {
664                     Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
665                     if (vpnId > 0) {
666                         LOG.debug("getVpnIdForRouter : retrieved vpnId {} for router {}", vpnId, routerId);
667                         return vpnId;
668                     } else {
669                         LOG.debug("getVpnIdForRouter : retrieved invalid vpn Id");
670                     }
671                 }
672             }
673         } catch (Exception ex) {
674             LOG.error("getVpnIdForRouter : Exception while retrieving vpnId for router {}", routerId, ex);
675         }
676         return NatConstants.INVALID_ID;
677     }
678
679     @NonNull
680     public List<BucketInfo> handleGroupInNeighborSwitches(BigInteger dpnId, String routerName, long routerId,
681             BigInteger naptSwitch) {
682         List<BucketInfo> listBucketInfo = new ArrayList<>();
683         String ifNamePrimary;
684         if (routerId == NatConstants.INVALID_ID) {
685             LOG.error("handleGroupInNeighborSwitches : Invalid routerId returned for routerName {}", routerName);
686             return listBucketInfo;
687         }
688         ifNamePrimary = getTunnelInterfaceName(dpnId, naptSwitch);
689         if (ifNamePrimary != null) {
690             LOG.debug("handleGroupInNeighborSwitches : TunnelInterface {} between ordinary switch {} and naptSwitch {}",
691                 ifNamePrimary, dpnId, naptSwitch);
692             List<ActionInfo> listActionInfoPrimary =
693                 NatUtil.getEgressActionsForInterface(odlInterfaceRpcService, itmManager, interfaceManager,
694                         ifNamePrimary, routerId, true);
695             BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
696             listBucketInfo.add(bucketPrimary);
697         } else {
698             LOG.debug("handleGroupInNeighborSwitches : No TunnelInterface between ordinary switch {} and naptSwitch {}",
699                     dpnId, naptSwitch);
700         }
701         return listBucketInfo;
702     }
703
704     // TODO Clean up the exception handling
705     @SuppressWarnings("checkstyle:IllegalCatch")
706     protected void installSnatGroupEntry(BigInteger dpnId, List<BucketInfo> bucketInfo, String routerName) {
707         GroupEntity groupEntity = null;
708         try {
709             long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
710             LOG.debug("installSnatGroupEntry : install SnatMissEntry for groupId {} for dpnId {} for router {}",
711                     groupId, dpnId, routerName);
712             groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName,
713                 GroupTypes.GroupAll, bucketInfo);
714             mdsalManager.syncInstallGroup(groupEntity);
715             LOG.debug("installSnatGroupEntry : installed the SNAT to NAPT GroupEntity:{}", groupEntity);
716         } catch (Exception ex) {
717             LOG.error("installSnatGroupEntry : Failed to install group for groupEntity {}", groupEntity, ex);
718         }
719     }
720
721     private void modifySnatGroupEntry(BigInteger dpnId, List<BucketInfo> bucketInfo, String routerName) {
722         installSnatGroupEntry(dpnId, bucketInfo, routerName);
723         LOG.debug("modifySnatGroupEntry : modified SnatMissEntry for dpnId {} of router {}", dpnId, routerName);
724     }
725
726     @Nullable
727     protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
728         Class<? extends TunnelTypeBase> tunType = TunnelTypeVxlan.class;
729         RpcResult<GetTunnelInterfaceNameOutput> rpcResult;
730
731         try {
732             Future<RpcResult<GetTunnelInterfaceNameOutput>> result = itmManager.getTunnelInterfaceName(
733                 new GetTunnelInterfaceNameInputBuilder().setSourceDpid(srcDpId).setDestinationDpid(dstDpId)
734                     .setTunnelType(tunType).build());
735             rpcResult = result.get();
736             if (!rpcResult.isSuccessful()) {
737                 tunType = TunnelTypeGre.class;
738                 result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
739                     .setSourceDpid(srcDpId)
740                     .setDestinationDpid(dstDpId)
741                     .setTunnelType(tunType)
742                     .build());
743                 rpcResult = result.get();
744                 if (!rpcResult.isSuccessful()) {
745                     LOG.warn("getTunnelInterfaceName : RPC Call to getTunnelInterfaceId returned with Errors {}",
746                             rpcResult.getErrors());
747                 } else {
748                     return rpcResult.getResult().getInterfaceName();
749                 }
750                 LOG.warn("getTunnelInterfaceName : RPC Call to getTunnelInterfaceId returned with Errors {}",
751                         rpcResult.getErrors());
752             } else {
753                 return rpcResult.getResult().getInterfaceName();
754             }
755         } catch (InterruptedException | ExecutionException e) {
756             LOG.error("getTunnelInterfaceName :Exception when getting tunnel interface Id for tunnel between {} and {}",
757                 srcDpId, dstDpId, e);
758         }
759         LOG.error("getTunnelInterfaceName : Tunnel missing between dpn {}:{}", srcDpId, dstDpId);
760         return null;
761     }
762
763     // TODO Clean up the exception handling
764     @SuppressWarnings("checkstyle:IllegalCatch")
765     public boolean updateNaptSwitch(String routerName, BigInteger naptSwitchId) {
766         RouterToNaptSwitch naptSwitch = new RouterToNaptSwitchBuilder().withKey(new RouterToNaptSwitchKey(routerName))
767             .setPrimarySwitchId(naptSwitchId).build();
768         try {
769             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
770                 NatUtil.buildNaptSwitchRouterIdentifier(routerName), naptSwitch);
771         } catch (Exception ex) {
772             LOG.error("updateNaptSwitch : Failed to write naptSwitch {} for router {} in ds",
773                 naptSwitchId, routerName);
774             return false;
775         }
776         LOG.debug("updateNaptSwitch : Successfully updated naptSwitch {} for router {} in ds",
777             naptSwitchId, routerName);
778         return true;
779     }
780
781     public FlowEntity buildSnatFlowEntity(BigInteger dpId, String routerName, long groupId,
782                                           long routerVpnId, int addordel) {
783         FlowEntity flowEntity;
784         List<MatchInfo> matches = new ArrayList<>();
785         matches.add(MatchEthernetType.IPV4);
786         matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(routerVpnId), MetaDataUtil.METADATA_MASK_VRFID));
787
788         String flowRef = getFlowRefSnat(dpId, NwConstants.PSNAT_TABLE, routerName);
789
790         if (addordel == NatConstants.ADD_FLOW) {
791             List<ActionInfo> actionsInfo = new ArrayList<>();
792             long tunnelId = NatUtil.getTunnelIdForNonNaptToNaptFlow(dataBroker, natOverVxlanUtil, elanManager,
793                     idManager, routerVpnId, routerName);
794             actionsInfo.add(new ActionSetFieldTunnelId(BigInteger.valueOf(tunnelId)));
795             LOG.debug("buildSnatFlowEntity : Setting the tunnel to the list of action infos {}", actionsInfo);
796             actionsInfo.add(new ActionGroup(groupId));
797             List<InstructionInfo> instructions = new ArrayList<>();
798             instructions.add(new InstructionApplyActions(actionsInfo));
799
800             flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
801                     NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
802                     NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
803         } else {
804             flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
805                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
806                 NwConstants.COOKIE_SNAT_TABLE, matches, null);
807         }
808         return flowEntity;
809     }
810
811     public FlowEntity buildSnatFlowEntityForNaptSwitch(BigInteger dpId, String routerName,
812                                                        long routerVpnId, int addordel) {
813         FlowEntity flowEntity;
814         List<MatchInfo> matches = new ArrayList<>();
815         matches.add(MatchEthernetType.IPV4);
816         matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(routerVpnId), MetaDataUtil.METADATA_MASK_VRFID));
817
818         String flowRef = getFlowRefSnat(dpId, NwConstants.PSNAT_TABLE, routerName);
819
820         if (addordel == NatConstants.ADD_FLOW) {
821             List<InstructionInfo> instructions = new ArrayList<>();
822
823             instructions.add(new InstructionGotoTable(NwConstants.OUTBOUND_NAPT_TABLE));
824
825             flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
826                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
827                 NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
828         } else {
829             flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
830                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
831                 NwConstants.COOKIE_SNAT_TABLE, matches, null);
832         }
833         return flowEntity;
834     }
835
836     private String getFlowRefSnat(BigInteger dpnId, short tableId, String routerID) {
837         return NatConstants.SNAT_FLOWID_PREFIX + dpnId + NatConstants.FLOWID_SEPARATOR + tableId + NatConstants
838                 .FLOWID_SEPARATOR + routerID;
839     }
840
841     protected void installSnatFlows(String routerName, Long routerId, BigInteger naptSwitch, Long routerVpnId,
842                                     TypedReadWriteTransaction<Configuration> confTx) {
843
844         if (routerId.equals(routerVpnId)) {
845             LOG.debug("installSnatFlows : Installing flows for router with internalvpnId");
846             //36 -> 46 ..Install flow forwarding packet to table46 from table36
847             LOG.debug("installSnatFlows : installTerminatingServiceTblEntry in naptswitch with dpnId {} for "
848                 + "routerName {} with routerId {}", naptSwitch, routerName, routerId);
849             externalRouterListener.installTerminatingServiceTblEntry(naptSwitch, routerName, routerId, confTx);
850
851             //Install default flows punting to controller in table 46(OutBoundNapt table)
852             LOG.debug("installSnatFlows : installOutboundMissEntry in naptswitch with dpnId {} for "
853                 + "routerName {} with routerId {}", naptSwitch, routerName, routerId);
854             externalRouterListener.createOutboundTblEntry(naptSwitch, routerId, confTx);
855
856             //Table 47 point to table 21 for inbound traffic
857             LOG.debug("installSnatFlows : installNaptPfibEntry in naptswitch with dpnId {} for router {}",
858                 naptSwitch, routerId);
859             externalRouterListener.installNaptPfibEntry(naptSwitch, routerId, confTx);
860
861             //Table 47 point to group
862             LOG.debug("installSnatFlows : installNaptPfibExternalOutputFlow in naptswitch with dpnId {} for router {}",
863                 naptSwitch, routerId);
864             externalRouterListener.installNaptPfibExternalOutputFlow(routerName, routerId, naptSwitch, confTx);
865         } else {
866             Uuid extNetworkUuid = NatUtil.getNetworkIdFromRouterName(dataBroker, routerName);
867             if (extNetworkUuid == null) {
868                 LOG.error("onRouterAssociatedToVpn : Unable to retrieve external network Uuid for router {}",
869                         routerName);
870                 return;
871             }
872             ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker, routerName,
873                     extNetworkUuid);
874             if (extNwProvType == null) {
875                 LOG.error("onRouterAssociatedToVpn : External Network Provider Type missing");
876                 return;
877             }
878             //36 -> 46 ..Install flow forwarding packet to table46 from table36
879             LOG.debug("installSnatFlows : installTerminatingServiceTblEntry in naptswitch with dpnId {} for "
880                 + "routerName {} with BgpVpnId {}", naptSwitch, routerName, routerVpnId);
881             externalRouterListener
882                 .installTerminatingServiceTblEntryWithUpdatedVpnId(naptSwitch, routerName, routerId,
883                         routerVpnId, confTx, extNwProvType);
884
885             //Install default flows punting to controller in table 46(OutBoundNapt table)
886             LOG.debug("installSnatFlows : installOutboundMissEntry in naptswitch with dpnId {} for "
887                 + "routerName {} with BgpVpnId {}", naptSwitch, routerName, routerVpnId);
888             externalRouterListener.createOutboundTblEntryWithBgpVpn(naptSwitch, routerId, routerVpnId, confTx);
889
890             //Table 47 point to table 21 for inbound traffic
891             LOG.debug("installSnatFlows : installNaptPfibEntry in naptswitch with dpnId {} for router {} "
892                     + "with BgpVpnId {}", naptSwitch, routerId, routerVpnId);
893             externalRouterListener.installNaptPfibEntryWithBgpVpn(naptSwitch, routerId, routerVpnId, confTx);
894         }
895
896         Uuid networkId = NatUtil.getNetworkIdFromRouterName(dataBroker, routerName);
897         String vpnName = getExtNetworkVpnName(routerName, networkId);
898         if (vpnName != null) {
899             //NAPT PFIB point to FIB table for outbound traffic
900             long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
901             boolean shouldInstallNaptPfibWithExtNetworkVpnId = true;
902             Collection<Uuid> externalSubnetIds = NatUtil.getExternalSubnetIdsForRouter(dataBroker, routerName);
903             if (!externalSubnetIds.isEmpty()) {
904                 //NAPT PFIB point to FIB table for outbound traffic - using external subnetID as vpnID.
905                 for (Uuid externalSubnetId : externalSubnetIds) {
906                     long externalSubnetVpnId = NatUtil.getExternalSubnetVpnId(dataBroker, externalSubnetId);
907                     if (externalSubnetVpnId != NatConstants.INVALID_ID) {
908                         shouldInstallNaptPfibWithExtNetworkVpnId = false;
909                         LOG.debug("installSnatFlows : installNaptPfibEntry fin naptswitch with dpnId {} for "
910                                 + "BgpVpnId {}", naptSwitch, externalSubnetVpnId);
911                         externalRouterListener.installNaptPfibEntry(naptSwitch, externalSubnetVpnId, confTx);
912                     }
913                 }
914             }
915             if (vpnId != NatConstants.INVALID_ID && shouldInstallNaptPfibWithExtNetworkVpnId) {
916                 //NAPT PFIB table point to FIB table for outbound traffic - using external networkID as vpnID.
917                 LOG.debug("installSnatFlows : installNaptPfibEntry fin naptswitch with dpnId {} for "
918                     + "BgpVpnId {}", naptSwitch, vpnId);
919                 externalRouterListener.installNaptPfibEntry(naptSwitch, vpnId, confTx);
920             } else if (vpnId != NatConstants.INVALID_ID) {
921                 LOG.debug("installSnatFlows : Associated BgpvpnId not found for router {}", routerId);
922             }
923
924             //Install Fib entries for ExternalIps & program 36 -> 44
925             Collection<String> externalIps = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
926             String rd = NatUtil.getVpnRd(dataBroker, vpnName);
927             for (String externalIp : externalIps) {
928                 removeFibEntry(rd, externalIp);
929                 LOG.debug("installSnatFlows : advToBgpAndInstallFibAndTsFlows in naptswitch id {} "
930                     + "with vpnName {} and externalIp {}", naptSwitch, vpnName, externalIp);
931                 externalRouterListener.advToBgpAndInstallFibAndTsFlows(naptSwitch, NwConstants.INBOUND_NAPT_TABLE,
932                     vpnName, routerId, routerName, externalIp, networkId, null /* external-router */, confTx);
933                 LOG.debug("installSnatFlows : Successfully added fib entries in naptswitch {} for "
934                     + "router {} with external IP {}", naptSwitch, routerId, externalIp);
935             }
936         } else {
937             LOG.debug("installSnatFlows : Associated vpnName not found for router {}", routerId);
938         }
939     }
940
941     protected void bestEffortDeletion(long routerId, String routerName, Map<String, Long> externalIpLabel,
942                                       TypedReadWriteTransaction<Configuration> confTx)
943             throws ExecutionException, InterruptedException {
944         Collection<String> newExternalIps = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
945         if (externalIpsCache != null) {
946             Set<String> removedExternalIps = new HashSet<>(externalIpsCache);
947             removedExternalIps.removeAll(newExternalIps);
948             if (removedExternalIps.isEmpty()) {
949                 LOG.info("bestEffortDeletion : No external Ip needed to be removed in bestEffortDeletion "
950                         + "method for router {}", routerName);
951                 return;
952             }
953             Uuid networkId = NatUtil.getNetworkIdFromRouterName(dataBroker, routerName);
954             String vpnName = getExtNetworkVpnName(routerName, networkId);
955             if (vpnName == null) {
956                 LOG.error("bestEffortDeletion : Vpn is not associated to externalN/w of router {}", routerName);
957                 return;
958             }
959             BigInteger naptSwitch = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
960             if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
961                 LOG.error("bestEffortDeletion : No naptSwitch is selected for router {}", routerName);
962                 return;
963             }
964             ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker,routerName, networkId);
965             if (extNwProvType == null) {
966                 return;
967             }
968             String gwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routerName);
969             if (gwMacAddress != null) {
970                 LOG.debug("bestEffortDeletion : External Gateway MAC address {} found for External Router ID {}",
971                         gwMacAddress, routerId);
972             } else {
973                 LOG.error("bestEffortDeletion : No External Gateway MAC address found for External Router ID {}",
974                         routerId);
975                 return;
976             }
977             if (extNwProvType == ProviderTypes.VXLAN) {
978                 for (String externalIp : removedExternalIps) {
979                     externalRouterListener.clearBgpRoutes(externalIp, vpnName);
980                     externalRouterListener.delFibTsAndReverseTraffic(naptSwitch, routerName, routerId, externalIp,
981                         vpnName, networkId, NatConstants.DEFAULT_LABEL_VALUE, gwMacAddress, true, confTx);
982                     LOG.debug("bestEffortDeletion : Successfully removed fib entry for externalIp {} for routerId {} "
983                                     + "on NAPT switch {} ", externalIp, routerId, naptSwitch);
984                 }
985             } else {
986                 if (externalIpLabel == null || externalIpLabel.isEmpty()) {
987                     LOG.error("bestEffortDeletion : ExternalIpLabel map is empty for router {}", routerName);
988                     return;
989                 }
990                 Long label;
991                 for (String externalIp : removedExternalIps) {
992                     if (externalIpLabel.containsKey(externalIp)) {
993                         label = externalIpLabel.get(externalIp);
994                         LOG.debug("bestEffortDeletion : Label {} for ExternalIp {} for router {}",
995                                 label, externalIp, routerName);
996                     } else {
997                         LOG.debug("bestEffortDeletion : Label for ExternalIp {} is not found for router {}",
998                                 externalIp, routerName);
999                         continue;
1000                     }
1001                     externalRouterListener.clearBgpRoutes(externalIp, vpnName);
1002                     externalRouterListener.delFibTsAndReverseTraffic(naptSwitch, routerName, routerId, externalIp,
1003                             vpnName, networkId, label, gwMacAddress, true, confTx);
1004                     LOG.debug("bestEffortDeletion : Successfully removed fib entries in switch {} for router {} "
1005                             + "and externalIps {}", naptSwitch, routerId, externalIp);
1006                 }
1007             }
1008         } else {
1009             LOG.debug("bestEffortDeletion : No external IP found for router {}", routerId);
1010         }
1011     }
1012
1013     private void removeFibEntry(String rd, String prefix) {
1014         InstanceIdentifier.InstanceIdentifierBuilder<VrfEntry> idBuilder =
1015             InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
1016                 .child(VrfEntry.class, new VrfEntryKey(prefix));
1017         InstanceIdentifier<VrfEntry> vrfEntryId = idBuilder.build();
1018         Optional<VrfEntry> ent = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
1019         if (ent.isPresent()) {
1020             LOG.debug("removeFibEntry : Removing Fib entry rd {} prefix {}", rd, prefix);
1021             fibManager.removeFibEntry(rd, prefix, null);
1022         }
1023     }
1024
1025     protected void subnetRegisterMapping(Routers routerEntry, Long segmentId) {
1026         externalRouterListener.subnetRegisterMapping(routerEntry, segmentId);
1027     }
1028 }