Merge "LLDP monitor interval update fixes"
[vpnservice.git] / natservice / natservice-impl / src / main / java / org / opendaylight / vpnservice / natservice / internal / NaptSwitchHA.java
1 /*
2  * Copyright (c) 2016 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.vpnservice.natservice.internal;
9
10 import com.google.common.base.Optional;
11 import org.opendaylight.bgpmanager.api.IBgpManager;
12 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
13 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
14 import org.opendaylight.vpnservice.mdsalutil.*;
15 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
16 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.FibRpcService;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceInputBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceOutput;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetTunnelInterfaceNameInputBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetTunnelInterfaceNameOutput;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.*;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.Routers;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.RoutersKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMapping;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.IpMappingKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.IpPortMapping;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.IpPortMappingKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitch;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitchBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.VpnRpcService;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.opendaylight.yangtools.yang.common.RpcResult;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 import java.math.BigInteger;
56 import java.util.ArrayList;
57 import java.util.List;
58 import java.util.concurrent.ExecutionException;
59 import java.util.concurrent.Future;
60
61 public class NaptSwitchHA {
62     private static final Logger LOG = LoggerFactory.getLogger(NaptSwitchHA.class);
63     private final DataBroker dataBroker;
64     private IMdsalApiManager mdsalManager;
65     private ItmRpcService itmManager;
66     private OdlInterfaceRpcService interfaceManager;
67     private IdManagerService idManager;
68     private NAPTSwitchSelector naptSwitchSelector;
69     private ExternalRoutersListener externalRouterListener;
70     private IBgpManager bgpManager;
71     private VpnRpcService vpnService;
72     private FibRpcService fibService;
73
74     public NaptSwitchHA(DataBroker broker,NAPTSwitchSelector selector){
75         dataBroker = broker;
76         naptSwitchSelector = selector;
77     }
78
79     public void setItmManager(ItmRpcService itmManager) {
80         this.itmManager = itmManager;
81     }
82
83     public void setMdsalManager(IMdsalApiManager mdsalManager) {
84         this.mdsalManager = mdsalManager;
85     }
86
87     public void setInterfaceManager(OdlInterfaceRpcService interfaceManager) {
88         this.interfaceManager = interfaceManager;
89     }
90
91     public void setIdManager(IdManagerService idManager) {
92         this.idManager = idManager;
93     }
94
95     void setExternalRoutersListener(ExternalRoutersListener externalRoutersListener) {
96         this.externalRouterListener = externalRoutersListener;
97     }
98
99     public void setBgpManager(IBgpManager bgpManager) {
100         this.bgpManager = bgpManager;
101     }
102
103     public void setVpnService(VpnRpcService vpnService) {
104         this.vpnService = vpnService;
105     }
106
107     public void setFibService(FibRpcService fibService) {
108         this.fibService = fibService;
109     }
110
111     /* This method checks the switch that gone down is a NaptSwitch for a router.
112        If it is a NaptSwitch
113           1) selects new NAPT switch
114           2) installs nat flows in new NAPT switch
115           table 21(FIB)->26(PSNAT)->group(resubmit/napttunnel)->36(Terminating)->46(outbound)->47(resubmit)->21
116           3) modify the group and miss entry flow in other vSwitches pointing to newNaptSwitch
117           4) Remove nat flows in oldNaptSwitch
118      */
119     public void handleNaptSwitchDown(BigInteger dpnId){
120
121         LOG.debug("handleNaptSwitchDown method is called with dpnId {}",dpnId);
122         BigInteger naptSwitch;
123         try {
124             NaptSwitches naptSwitches = NatUtil.getNaptSwitch(dataBroker);
125             if (naptSwitches == null || naptSwitches.getRouterToNaptSwitch() == null || naptSwitches.getRouterToNaptSwitch().isEmpty()) {
126                 LOG.debug("NaptSwitchDown: NaptSwitch is not allocated for none of the routers");
127                 return;
128             }
129             for (RouterToNaptSwitch routerToNaptSwitch : naptSwitches.getRouterToNaptSwitch()) {
130                 String routerName = routerToNaptSwitch.getRouterName();
131                 naptSwitch = routerToNaptSwitch.getPrimarySwitchId();
132                 boolean naptStatus = isNaptSwitchDown(routerName,dpnId,naptSwitch);
133                 if (!naptStatus) {
134                     LOG.debug("NaptSwitchDown: Switch with DpnId {} is not naptSwitch for router {}",
135                             dpnId, routerName);
136                 } else {
137                     removeSnatFlowsInOldNaptSwitch(routerName,naptSwitch);
138                     return;
139                 }
140             }
141         } catch (Exception ex) {
142             LOG.error("Exception in handleNaptSwitchDown method {}",ex);
143         }
144     }
145
146     private void removeSnatFlowsInOldNaptSwitch(String routerName, BigInteger naptSwitch) {
147         //remove SNAT flows in old NAPT SWITCH
148         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
149         if (routerId == NatConstants.INVALID_ID) {
150             LOG.error("Invalid routerId returned for routerName {}",routerName);
151             return;
152         }
153         BigInteger cookieSnatFlow = NatUtil.getCookieSnatFlow(routerId);
154
155         //Build and remove flows in outbound NAPT table
156         try {
157             FlowEntity outboundNaptFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.OUTBOUND_NAPT_TABLE, cookieSnatFlow);
158             mdsalManager.removeFlow(outboundNaptFlowEntity);
159             LOG.info("Removed all flows for router {} in the table {} for oldNaptswitch {}"
160                     ,routerName, NatConstants.OUTBOUND_NAPT_TABLE, naptSwitch);
161         } catch (Exception ex) {
162             LOG.info("Failed to remove all flows for router {} in the table {} for oldNaptswitch {}"
163                     ,routerName, NatConstants.OUTBOUND_NAPT_TABLE, naptSwitch);
164         }
165
166         //Build and remove flows in inbound NAPT table
167         try {
168             FlowEntity inboundNaptFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.INBOUND_NAPT_TABLE,
169                     cookieSnatFlow);
170             mdsalManager.removeFlow(inboundNaptFlowEntity);
171             LOG.info("Removed all flows for router {} in the table {} for oldNaptswitch {}"
172                     ,routerName, NatConstants.INBOUND_NAPT_TABLE, naptSwitch);
173         } catch (Exception ex) {
174             LOG.info("Failed to remove all flows for router {} in the table {} for oldNaptswitch {}"
175                     ,routerName, NatConstants.INBOUND_NAPT_TABLE, naptSwitch);
176         }
177
178         //Remove the Terminating Service table entry which forwards the packet to Outbound NAPT Table
179         String tsFlowRef = externalRouterListener.getFlowRefTs(naptSwitch, NatConstants.TERMINATING_SERVICE_TABLE, routerId);
180         FlowEntity tsNatFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.TERMINATING_SERVICE_TABLE, tsFlowRef);
181
182         LOG.info("Remove the flow in table {} for the active switch with the DPN ID {} and router ID {}"
183                 ,NatConstants.TERMINATING_SERVICE_TABLE, naptSwitch, routerId);
184         mdsalManager.removeFlow(tsNatFlowEntity);
185
186         //Remove the Outbound flow entry which forwards the packet to Outbound NAPT Table
187         String outboundNatFlowRef = externalRouterListener.getFlowRefOutbound(naptSwitch, NatConstants.OUTBOUND_NAPT_TABLE, routerId);
188         FlowEntity outboundNatFlowEntity = NatUtil.buildFlowEntity(naptSwitch,
189                 NatConstants.OUTBOUND_NAPT_TABLE, outboundNatFlowRef);
190         LOG.info("Remove the flow in the for the active switch with the DPN ID {} and router ID {}"
191                 ,NatConstants.OUTBOUND_NAPT_TABLE, naptSwitch, routerId);
192         mdsalManager.removeFlow(outboundNatFlowEntity);
193
194         //Remove the NAPT_PFIB_TABLE(47) flow entry forwards the packet to Fib Table
195         String naptPFibflowRef = externalRouterListener.getFlowRefTs(naptSwitch, NatConstants.NAPT_PFIB_TABLE, routerId);
196         FlowEntity naptPFibFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.NAPT_PFIB_TABLE,naptPFibflowRef);
197         LOG.info("Remove the flow in the for the active switch with the DPN ID {} and router ID {}",
198                 NatConstants.NAPT_PFIB_TABLE, naptSwitch, routerId);
199         mdsalManager.removeFlow(naptPFibFlowEntity);
200
201         //Remove Fib entries and 36-> 44
202         Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
203         if (networkId == null) {
204             LOG.debug("network is not associated to router {}", routerId);
205         }
206         Optional<Routers> routerData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
207                 NatUtil.buildRouterIdentifier(routerName));
208         if(routerData.isPresent()){
209             List<String> externalIps = routerData.get().getExternalIps();
210             if (externalIps != null) {
211                 externalRouterListener.advToBgpAndRemoveFibAndTsFlows(naptSwitch, routerId, networkId, externalIps);
212                 LOG.debug("Successfully removed fib entries in naptswitch {} for router {} with external IP {}", naptSwitch,
213                         routerId, externalIps);
214             } else {
215                 LOG.debug("ExternalIps not found for router {} with networkId {}",routerName,networkId);
216             }
217         }
218     }
219
220     public boolean isNaptSwitchDown(String routerName, BigInteger dpnId , BigInteger naptSwitch) {
221         if (!naptSwitch.equals(dpnId)) {
222             LOG.debug("DpnId {} is not a naptSwitch {} for Router {}",dpnId, naptSwitch, routerName);
223             return false;
224         }
225         LOG.debug("NaptSwitch {} is down for Router {}", naptSwitch, routerName);
226         //elect a new NaptSwitch
227         naptSwitch = naptSwitchSelector.selectNewNAPTSwitch(routerName);
228         if (naptSwitch.equals("0")) {
229             LOG.info("No napt switch is elected since all the switches for router {} are down",routerName);
230             return true;
231         }
232         //checking elected switch health status
233         if (!getSwitchStatus(naptSwitch)) {
234             LOG.error("Newly elected Napt switch {} for router {} is down", naptSwitch, routerName);
235             return true;
236         }
237         LOG.debug("New NaptSwitch {} is up for Router {} and can proceed for flow installation",naptSwitch, routerName);
238         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
239         if (routerId == NatConstants.INVALID_ID) {
240             LOG.error("Invalid routerId returned for routerName {}", routerName);
241             return true;
242         }
243         //update napt model for new napt switch
244         boolean naptUpdated = updateNaptSwitch(routerName, naptSwitch);
245         if (naptUpdated) {
246             //update group of naptswitch point to table36/ordinary switch point to naptswitchtunnelport
247             updateNaptSwitchBucketStatus(routerName, naptSwitch);
248         } else {
249             LOG.error("Failed to update naptSwitch model for newNaptSwitch {} for router {}",naptSwitch, routerName);
250         }
251         //36 -> 46 ..Install flow going to 46 from table36
252         externalRouterListener.installTerminatingServiceTblEntry(naptSwitch, routerName);
253
254         //Install default flows punting to controller in table 46(OutBoundNapt table)
255         externalRouterListener.installOutboundMissEntry(routerName, naptSwitch);
256
257         //Table 47 point to table 21 for inbound traffic
258         LOG.debug("installNaptPfibEntry for dpnId {} and routerId {}", naptSwitch, routerId);
259         externalRouterListener.installNaptPfibEntry(naptSwitch, routerId);
260
261         //Table 47 point to table 21 for outbound traffic
262         String vpnName = getVpnName(routerId);
263         if(vpnName != null) {
264             long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
265             if(vpnId > 0) {
266                 LOG.debug("installNaptPfibEntry for dpnId {} and vpnId {}", naptSwitch, vpnId);
267                 externalRouterListener.installNaptPfibEntry(naptSwitch, vpnId);
268             } else {
269                 LOG.debug("Associated vpnId not found for router {}",routerId);
270             }
271         } else {
272             LOG.debug("Associated vpnName not found for router {}",routerId);
273         }
274
275         //Install Fib entries for ExternalIps & program 36 -> 44
276
277         Optional<IpMapping> ipMappingOptional = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
278                 getIpMappingBuilder(routerId));
279         if (vpnName != null) {
280             if (ipMappingOptional.isPresent()) {
281                 List<IpMap> ipMaps = ipMappingOptional.get().getIpMap();
282                 for (IpMap ipMap : ipMaps) {
283                     String externalIp = ipMap.getExternalIp();
284                     LOG.debug("advToBgpAndInstallFibAndTsFlows for naptswitch {}, vpnName {} and externalIp {}",
285                             naptSwitch, vpnName, externalIp);
286                     externalRouterListener.advToBgpAndInstallFibAndTsFlows(naptSwitch, NatConstants.INBOUND_NAPT_TABLE,
287                             vpnName, routerId, externalIp, vpnService, fibService, bgpManager, dataBroker, LOG);
288                     LOG.debug("Successfully added fib entries in naptswitch {} for router {} with external IP {}", naptSwitch,
289                             routerId, externalIp);
290                 }
291             }
292         } else {
293             LOG.debug("Vpn is not associated to the network of router {}",routerName);
294         }
295
296         boolean flowInstalledStatus = handleFlowsInNewNaptSwitch(routerId, dpnId, naptSwitch);
297         if (flowInstalledStatus) {
298             LOG.debug("Installed all activesession flows in newNaptSwitch {} for routerName {}", routerName);
299         } else {
300             LOG.error("Failed to install flows in newNaptSwitch {} for routerId {}", naptSwitch, routerId);
301         }
302         return true;
303     }
304
305     private InstanceIdentifier<IpMapping> getIpMappingBuilder(Long routerId) {
306         InstanceIdentifier<IpMapping> idBuilder = InstanceIdentifier.builder(IntextIpMap.class)
307                 .child(IpMapping.class, new IpMappingKey(routerId)).build();
308         return idBuilder;
309     }
310
311     private String getVpnName(long routerId) {
312         Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
313         if(networkId == null) {
314             LOG.error("networkId is null for the router ID {}", routerId);
315         } else {
316             final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
317             if (vpnName != null) {
318                 LOG.debug("retreived vpnname {} associated with ext nw {} in router {}",
319                         vpnName,networkId,routerId);
320                 return vpnName;
321             } else {
322                 LOG.error("No VPN associated with ext nw {} belonging to routerId {}",
323                         networkId, routerId);
324             }
325         }
326         return null;
327     }
328
329     public void updateNaptSwitchBucketStatus(String routerName, BigInteger naptSwitch) {
330         LOG.debug("updateNaptSwitchBucketStatus method is called");
331
332         List<BigInteger> dpnList = getDpnListForRouter(routerName);
333         for (BigInteger dpn : dpnList) {
334             if (dpn.equals(naptSwitch)) {
335                 LOG.debug("Updating SNAT_TABLE missentry for DpnId {} which is naptSwitch for router {}",dpn,routerName);
336                 List<BucketInfo> bucketInfoList = handleGroupInPrimarySwitch();
337                 modifySnatGroupEntry(naptSwitch, bucketInfoList, routerName);
338             } else {
339                 LOG.debug("Updating SNAT_TABLE missentry for DpnId {} which is not naptSwitch for router {}"
340                         , dpn, routerName);
341                 List<BucketInfo> bucketInfoList = handleGroupInNeighborSwitches(dpn, routerName, naptSwitch);
342                 if (bucketInfoList == null) {
343                     LOG.debug("bucketInfo is not populated for orinaryswitch {} whose naptSwitch {} with router {} ",
344                             dpn,routerName,naptSwitch);
345                     return;
346                 }
347                 modifySnatGroupEntry(naptSwitch, bucketInfoList, routerName);
348             }
349         }
350     }
351
352     private boolean handleFlowsInNewNaptSwitch(Long routerId,BigInteger oldNaptSwitch, BigInteger newNaptSwitch) {
353
354         LOG.debug("Proceeding to install flows in newNaptSwitch {} for routerId {}", routerId);
355         IpPortMapping ipPortMapping = getIpPortMapping(routerId);
356         if (ipPortMapping == null || ipPortMapping.getIntextIpProtocolType() == null || ipPortMapping.getIntextIpProtocolType().isEmpty()) {
357             LOG.debug("No Internal Ip Port mapping associated to router {}, no flows need to be installed in" +
358                     "newNaptSwitch ", routerId, newNaptSwitch);
359             return true;
360         }
361         //getvpnId
362         Long vpnId = null;
363         try {
364             vpnId = getVpnIdForRouter(routerId);
365         }catch (Exception ex) {
366             LOG.error("Failed to retreive vpnID for router {} : {}", routerId,ex);
367             return false;
368         }
369         for (IntextIpProtocolType protocolType : ipPortMapping.getIntextIpProtocolType()) {
370             if (protocolType.getIpPortMap() == null || protocolType.getIpPortMap().isEmpty()) {
371                 LOG.debug("No {} session associated to router {}", protocolType.getProtocol(), routerId);
372                 return true;
373             }
374             for (IpPortMap intIpPortMap : protocolType.getIpPortMap()) {
375                 String internalIpAddress = intIpPortMap.getIpPortInternal().split(":")[0];
376                 String intportnum = intIpPortMap.getIpPortInternal().split(":")[1];
377
378                 //Get the external IP address and the port from the model
379                 NAPTEntryEvent.Protocol proto = protocolType.getProtocol().toString().equals(ProtocolTypes.TCP.toString())
380                         ? NAPTEntryEvent.Protocol.TCP : NAPTEntryEvent.Protocol.UDP;
381                 IpPortExternal ipPortExternal = NatUtil.getExternalIpPortMap(dataBroker, routerId,
382                         internalIpAddress, intportnum, proto);
383                 if (ipPortExternal == null) {
384                     LOG.debug("External Ipport mapping is not found for internalIp {} with port {}", internalIpAddress, intportnum);
385                     continue;
386                 }
387                 String externalIpAddress = ipPortExternal.getIpAddress();
388                 Integer extportNumber = ipPortExternal.getPortNum();
389                 LOG.debug("ExternalIPport {}:{} mapping for internal ipport {}:{}",externalIpAddress,extportNumber,
390                         internalIpAddress,intportnum);
391
392                 SessionAddress sourceAddress = new SessionAddress(internalIpAddress,Integer.valueOf(intportnum));
393                 SessionAddress externalAddress = new SessionAddress(externalIpAddress,extportNumber);
394
395                 //checking naptSwitch status before installing flows
396                 if(getSwitchStatus(newNaptSwitch)) {
397                     //Install the flow in newNaptSwitch Outbound NAPT table.
398                     try {
399                         NaptEventHandler.buildAndInstallNatFlows(newNaptSwitch, NatConstants.OUTBOUND_NAPT_TABLE,
400                                 vpnId,  routerId, sourceAddress, externalAddress, proto);
401                     } catch (Exception ex) {
402                         LOG.error("Failed to add flow in OUTBOUND_NAPT_TABLE for routerid {} dpnId {} ipport {}:{} proto {}" +
403                                 "extIpport {}:{}", routerId, newNaptSwitch, internalIpAddress
404                                 , intportnum, proto, externalAddress, extportNumber);
405                         return false;
406                     }
407                     LOG.debug("Succesfully installed a flow in SecondarySwitch {} Outbound NAPT table for router {} " +
408                             "ipport {}:{} proto {} extIpport {}:{}", newNaptSwitch,routerId, internalIpAddress
409                             , intportnum, proto, externalAddress, extportNumber);
410                     //Install the flow in newNaptSwitch Inbound NAPT table.
411                     try {
412                         NaptEventHandler.buildAndInstallNatFlows(newNaptSwitch, NatConstants.INBOUND_NAPT_TABLE,
413                                 vpnId, routerId, externalAddress, sourceAddress, proto);
414                     } catch (Exception ex) {
415                         LOG.error("Failed to add flow in INBOUND_NAPT_TABLE for routerid {} dpnId {} extIpport{}:{} proto {} ipport {}:{}",
416                                 routerId, newNaptSwitch, externalAddress, extportNumber,
417                                 proto, internalIpAddress, intportnum);
418                         return false;
419                     }
420                     LOG.debug("Succesfully installed a flow in SecondarySwitch {} Inbound NAPT table for router {} " +
421                             "ipport {}:{} proto {} extIpport {}:{}", newNaptSwitch,routerId, internalIpAddress
422                             , intportnum, proto, externalAddress, extportNumber);
423
424                 } else {
425                     LOG.error("NewNaptSwitch {} gone down while installing flows from oldNaptswitch {}",
426                             newNaptSwitch,oldNaptSwitch);
427                     return false;
428                 }
429             }
430         }
431         return true;
432     }
433
434     private Long getVpnIdForRouter(Long routerId) {
435         try {
436             //getvpnId
437             Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
438             if (networkId == null) {
439                 LOG.debug("network is not associated to router {}", routerId);
440             } else {
441                 Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
442                 if (vpnUuid == null) {
443                     LOG.debug("vpn is not associated for network {} in router {}", networkId, routerId);
444                 } else {
445                     Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
446                     if (vpnId != null) {
447                         LOG.debug("retrieved vpnId {} for router {}",vpnId,routerId);
448                         return vpnId;
449                     } else {
450                         LOG.debug("retrieved invalid vpn Id");
451                     }
452                 }
453             }
454         } catch (Exception ex){
455             LOG.debug("Exception while retreiving vpnId for router {} - {}", routerId, ex);
456         }
457         return  null;
458     }
459
460     private List<BigInteger> getDpnListForRouter(String routerName) {
461         List<BigInteger> dpnList = new ArrayList<BigInteger>();
462         List<VpnToDpnList> vpnDpnList = NatUtil.getVpnToDpnList(dataBroker, routerName);
463         for (VpnToDpnList vpnToDpn : vpnDpnList) {
464             dpnList.add(vpnToDpn.getDpnId());
465         }
466         return dpnList;
467     }
468
469     public boolean getSwitchStatus(BigInteger switchId){
470         NodeId nodeId = new NodeId("openflow:" + switchId);
471         LOG.debug("Querying switch with dpnId {} is up/down", nodeId);
472         InstanceIdentifier<Node> nodeInstanceId = InstanceIdentifier.builder(Nodes.class)
473                 .child(Node.class, new NodeKey(nodeId)).build();
474         Optional<Node> nodeOptional = NatUtil.read(dataBroker,LogicalDatastoreType.OPERATIONAL,nodeInstanceId);
475         if (nodeOptional.isPresent()) {
476             LOG.debug("Switch {} is up", nodeId);
477             return true;
478         }
479         LOG.debug("Switch {} is down", nodeId);
480         return false;
481     }
482
483     public List<BucketInfo> handleGroupInPrimarySwitch() {
484         List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
485         List<ActionInfo> listActionInfoPrimary = new ArrayList<ActionInfo>();
486         listActionInfoPrimary.add(new ActionInfo(ActionType.nx_resubmit,
487                 new String[]{String.valueOf(NatConstants.TERMINATING_SERVICE_TABLE)}));
488         BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
489         listBucketInfo.add(bucketPrimary);
490         return listBucketInfo;
491     }
492
493     public List<BucketInfo> handleGroupInNeighborSwitches(BigInteger dpnId, String routerName, BigInteger naptSwitch) {
494         List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
495         String ifNamePrimary;
496         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
497         if (routerId == NatConstants.INVALID_ID) {
498             LOG.error("Invalid routerId returned for routerName {}",routerName);
499             return listBucketInfo;
500         }
501         ifNamePrimary = getTunnelInterfaceName(dpnId, naptSwitch);
502         if (ifNamePrimary != null) {
503             LOG.debug("TunnelInterface {} between ordinary switch {} and naptSwitch {}",ifNamePrimary,dpnId,naptSwitch);
504             List<ActionInfo> listActionInfoPrimary = getEgressActionsForInterface(ifNamePrimary, routerId);
505             BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
506             listBucketInfo.add(bucketPrimary);
507         } else {
508             LOG.debug("No TunnelInterface between ordinary switch {} and naptSwitch {}",dpnId,naptSwitch);
509         }
510         return listBucketInfo;
511     }
512
513     protected void installSnatGroupEntry(BigInteger dpnId, List<BucketInfo> bucketInfo, String routerName) {
514         GroupEntity groupEntity = null;
515         try {
516             long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
517             LOG.debug("install SnatMissEntry for groupId {} for dpnId {} for router {}", groupId, dpnId,routerName);
518             groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName,
519                     GroupTypes.GroupAll, bucketInfo);
520             mdsalManager.installGroup(groupEntity);
521             LOG.debug("installed the SNAT to NAPT GroupEntity:{}", groupEntity);
522         } catch (Exception ex) {
523             LOG.error("Failed to install group for groupEntity {} : {}",groupEntity,ex);
524         }
525     }
526
527     private void modifySnatGroupEntry(BigInteger dpnId, List<BucketInfo> bucketInfo, String routerName) {
528         installSnatGroupEntry(dpnId,bucketInfo,routerName);
529         LOG.debug("modified SnatMissEntry for dpnId {} of router {}",dpnId,routerName);
530     }
531
532     protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
533         try {
534             Future<RpcResult<GetTunnelInterfaceNameOutput>> result = itmManager.getTunnelInterfaceName(
535                     new GetTunnelInterfaceNameInputBuilder().setSourceDpid(srcDpId).setDestinationDpid(dstDpId).build());
536             RpcResult<GetTunnelInterfaceNameOutput> rpcResult = result.get();
537             if(!rpcResult.isSuccessful()) {
538                 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
539             } else {
540                 return rpcResult.getResult().getInterfaceName();
541             }
542         } catch (InterruptedException | ExecutionException e) {
543             LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and  {} : {}",
544                     srcDpId, dstDpId, e);
545         }
546
547         return null;
548     }
549
550     protected List<ActionInfo> getEgressActionsForInterface(String ifName, long routerId) {
551         LOG.debug("getEgressActionsForInterface called for interface {}", ifName);
552         List<ActionInfo> listActionInfo = new ArrayList<ActionInfo>();
553         try {
554             Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
555                     interfaceManager.getEgressActionsForInterface(
556                             new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).setTunnelKey(routerId).build());
557             RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
558             if(!rpcResult.isSuccessful()) {
559                 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}"
560                         , ifName, rpcResult.getErrors());
561             } else {
562                 List<Action> actions =
563                         rpcResult.getResult().getAction();
564                 for (Action action : actions) {
565                     org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action actionClass = action.getAction();
566                     if (actionClass instanceof OutputActionCase) {
567                         listActionInfo.add(new ActionInfo(ActionType.output,
568                                 new String[] {((OutputActionCase)actionClass).getOutputAction()
569                                         .getOutputNodeConnector().getValue()}));
570                     } else if (actionClass instanceof PushVlanActionCase) {
571                         listActionInfo.add(new ActionInfo(ActionType.push_vlan, new String[] {}));
572                     } else if (actionClass instanceof SetFieldCase) {
573                         if (((SetFieldCase)actionClass).getSetField().getVlanMatch() != null) {
574                             int vlanVid = ((SetFieldCase)actionClass).getSetField().getVlanMatch()
575                                     .getVlanId().getVlanId().getValue();
576                             listActionInfo.add(new ActionInfo(ActionType.set_field_vlan_vid,
577                                     new String[] { Long.toString(vlanVid) }));
578                         }
579                     }
580                 }
581             }
582         } catch (InterruptedException | ExecutionException e) {
583             LOG.warn("Exception when egress actions for interface {}", ifName, e);
584         }
585         return listActionInfo;
586     }
587
588     private IpPortMapping getIpPortMapping(Long routerId) {
589         Optional<IpPortMapping> ipPortMapData = NatUtil.read(this.dataBroker, LogicalDatastoreType.CONFIGURATION,
590                 buildIpToPortMapIdentifier(routerId));
591         if (ipPortMapData.isPresent()) {
592             return ipPortMapData.get();
593         }
594         return null;
595     }
596
597     public boolean updateNaptSwitch(String routerName, BigInteger naptSwitchId) {
598         RouterToNaptSwitch naptSwitch = new RouterToNaptSwitchBuilder().setKey(new RouterToNaptSwitchKey(routerName))
599                 .setPrimarySwitchId(naptSwitchId).build();
600         try {
601             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
602                     NatUtil.buildNaptSwitchRouterIdentifier(routerName), naptSwitch);
603         } catch (Exception ex) {
604             LOG.error("Failed to write naptSwitch {} for router {} in ds",
605                     naptSwitchId,routerName);
606             return false;
607         }
608         LOG.debug("Successfully updated naptSwitch {} for router {} in ds",
609                 naptSwitchId,routerName);
610         return true;
611     }
612
613     private InstanceIdentifier<IpPortMapping> buildIpToPortMapIdentifier(Long routerId) {
614         InstanceIdentifier<IpPortMapping> ipPortMapId = InstanceIdentifier.builder(IntextIpPortMap.class).child
615                 (IpPortMapping.class, new IpPortMappingKey(routerId)).build();
616         return ipPortMapId;
617     }
618
619     public FlowEntity buildSnatFlowEntity(BigInteger dpId, String routerName, long groupId, int addordel) {
620
621         FlowEntity flowEntity = null;
622         long routerId = NatUtil.getVpnId(dataBroker, routerName);
623         if (routerId == NatConstants.INVALID_ID) {
624             LOG.error("Invalid routerId returned for routerName {}",routerName);
625             return flowEntity;
626         }
627         List<MatchInfo> matches = new ArrayList<MatchInfo>();
628         matches.add(new MatchInfo(MatchFieldType.eth_type,
629                 new long[]{ 0x0800L }));
630         matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
631                 BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
632
633         String flowRef = getFlowRefSnat(dpId, NatConstants.PSNAT_TABLE, routerName);
634
635         if (addordel == NatConstants.ADD_FLOW) {
636             List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
637             List<ActionInfo> actionsInfo = new ArrayList<ActionInfo>();
638
639             ActionInfo actionSetField = new ActionInfo(ActionType.set_field_tunnel_id, new BigInteger[] {
640                     BigInteger.valueOf(routerId)}) ;
641             actionsInfo.add(actionSetField);
642             LOG.debug("Setting the tunnel to the list of action infos {}", actionsInfo);
643             actionsInfo.add(new ActionInfo(ActionType.group, new String[] {String.valueOf(groupId)}));
644             instructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfo));
645
646             flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
647                     NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
648                     NatConstants.COOKIE_SNAT_TABLE, matches, instructions);
649         } else {
650             flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
651                     NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
652                     NatConstants.COOKIE_SNAT_TABLE, matches, null);
653         }
654         return flowEntity;
655     }
656
657     private String getFlowRefSnat(BigInteger dpnId, short tableId, String routerID) {
658         return new StringBuilder().append(NatConstants.SNAT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR).
659                 append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString();
660     }
661 }