Merge "LLDP monitor interval update fixes"
[vpnservice.git] / natservice / natservice-impl / src / main / java / org / opendaylight / vpnservice / natservice / internal / ExternalRoutersListener.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
9 package org.opendaylight.vpnservice.natservice.internal;
10
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.concurrent.ExecutionException;
15 import java.util.concurrent.Future;
16
17 import org.opendaylight.bgpmanager.api.IBgpManager;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.FibRpcService;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.VpnRpcService;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.IpPortMapping;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap;
24 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;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelOutput;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelInput;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.GenerateVpnLabelInputBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.CreateFibEntryInput;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.CreateFibEntryInputBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.RemoveVpnLabelInput;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fib.rpc.rev160121.RemoveFibEntryInput;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.vpn.rpc.rev160201.RemoveVpnLabelInputBuilder;
34 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
35 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
36 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
37 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
38 import org.opendaylight.vpnservice.datastoreutils.AsyncDataTreeChangeListenerBase;
39 import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
40 import org.opendaylight.vpnservice.mdsalutil.ActionType;
41 import org.opendaylight.vpnservice.mdsalutil.BucketInfo;
42 import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
43 import org.opendaylight.vpnservice.mdsalutil.GroupEntity;
44 import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
45 import org.opendaylight.vpnservice.mdsalutil.InstructionType;
46 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
47 import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
48 import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
49 import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
50 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
51 import org.opendaylight.vpnservice.mdsalutil.NwConstants;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInput;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdInputBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.AllocateIdOutput;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInput;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.CreateIdPoolInputBuilder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.idmanager.rev150403.IdManagerService;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceInputBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceOutput;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetTunnelInterfaceNameInputBuilder;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.GetTunnelInterfaceNameOutput;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ExtRouters;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.RouterIdName;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.Routers;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.ext.routers.RoutersKey;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMap;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMapBuilder;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.intext.ip.map.ip.mapping.IpMapKey;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.napt.switches.RouterToNaptSwitch;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.router.id.name.RouterIds;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.router.id.name.RouterIdsBuilder;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.natservice.rev160111.router.id.name.RouterIdsKey;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.Subnetmaps;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.subnetmaps.Subnetmap;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
88 import org.opendaylight.yangtools.concepts.ListenerRegistration;
89 import org.opendaylight.yangtools.yang.binding.DataObject;
90 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
91 import org.opendaylight.yangtools.yang.common.RpcResult;
92 import org.slf4j.Logger;
93 import org.slf4j.LoggerFactory;
94
95 import com.google.common.base.Optional;
96 import com.google.common.util.concurrent.AsyncFunction;
97 import com.google.common.util.concurrent.FutureCallback;
98 import com.google.common.util.concurrent.Futures;
99 import com.google.common.util.concurrent.JdkFutureAdapters;
100 import com.google.common.util.concurrent.ListenableFuture;
101
102 /**
103  * Created by EYUGSAR on 2/20/2016.
104  */
105
106 public class ExternalRoutersListener extends AsyncDataTreeChangeListenerBase<Routers, ExternalRoutersListener>{
107
108     private static final Logger LOG = LoggerFactory.getLogger( ExternalRoutersListener.class);
109     private static long label;
110     private ListenerRegistration<DataChangeListener> listenerRegistration;
111     private final DataBroker dataBroker;
112     private IMdsalApiManager mdsalManager;
113     private ItmRpcService itmManager;
114     private OdlInterfaceRpcService interfaceManager;
115     private IdManagerService idManager;
116     private NaptManager naptManager;
117     private NAPTSwitchSelector naptSwitchSelector;
118     private IBgpManager bgpManager;
119     private VpnRpcService vpnService;
120     private FibRpcService fibService;
121     private SNATDefaultRouteProgrammer defaultRouteProgrammer;
122     private static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
123     static final BigInteger COOKIE_VM_LFIB_TABLE = new BigInteger("8000022", 16);
124
125     public void setMdsalManager(IMdsalApiManager mdsalManager) {
126         this.mdsalManager = mdsalManager;
127     }
128
129     public void setItmManager(ItmRpcService itmManager) {
130         this.itmManager = itmManager;
131     }
132
133     public void setIdManager(IdManagerService idManager) {
134         this.idManager = idManager;
135         createGroupIdPool();
136     }
137
138     void setDefaultProgrammer(SNATDefaultRouteProgrammer defaultRouteProgrammer) {
139         this.defaultRouteProgrammer = defaultRouteProgrammer;
140     }
141
142
143     public void setInterfaceManager(OdlInterfaceRpcService interfaceManager) {
144         this.interfaceManager = interfaceManager;
145     }
146
147     public void setNaptManager(NaptManager naptManager) {
148         this.naptManager = naptManager;
149     }
150
151     public void setNaptSwitchSelector(NAPTSwitchSelector naptSwitchSelector) {
152         this.naptSwitchSelector = naptSwitchSelector;
153     }
154
155     public void setBgpManager(IBgpManager bgpManager) {
156         this.bgpManager = bgpManager;
157     }
158
159     public void setVpnService(VpnRpcService vpnService) {
160         this.vpnService = vpnService;
161     }
162
163     public void setFibService(FibRpcService fibService) {
164         this.fibService = fibService;
165     }
166
167     public ExternalRoutersListener(DataBroker dataBroker )
168     {
169         super( Routers.class, ExternalRoutersListener.class );
170         this.dataBroker = dataBroker;
171     }
172
173     @Override
174     protected void add(InstanceIdentifier<Routers> identifier, Routers routers) {
175
176         LOG.info( "Add external router event for {}", routers.getRouterName() );
177
178         LOG.info("Installing NAT default route on all dpns part of router {}", routers.getRouterName());
179         addOrDelDefFibRouteToSNAT(routers.getRouterName(), true);
180
181         if( !routers.isEnableSnat()) {
182             LOG.info( "SNAT is disabled for external router {} ", routers.getRouterName());
183             return;
184         }
185
186         // Populate the router-id-name container
187         String routerName = routers.getRouterName();
188         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
189         RouterIds rtrs = new RouterIdsBuilder().setKey(new RouterIdsKey(routerId)).setRouterId(routerId).setRouterName(routerName).build();
190         MDSALUtil.syncWrite( dataBroker, LogicalDatastoreType.CONFIGURATION, getRoutersIdentifier(routerId), rtrs);
191
192         handleEnableSnat(routerName);
193     }
194
195     public void handleEnableSnat(String routerName){
196         LOG.info("Handling SNAT for router {}", routerName);
197
198         // Allocate Primary Napt Switch for this router
199         BigInteger primarySwitchId = naptSwitchSelector.selectNewNAPTSwitch(routerName);
200
201         LOG.debug("NAT Service : About to create and install outbound miss entry in Primary Switch {} for router {}", primarySwitchId, routerName);
202         // write metadata and punt
203         installOutboundMissEntry(routerName, primarySwitchId);
204         // Now install entries in SNAT tables to point to Primary for each router
205         List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
206         for(BigInteger dpnId : switches) {
207               // Handle switches and NAPT switches separately
208               if( dpnId != primarySwitchId ) {
209                    LOG.debug("NAT Service : Handle Ordinary switch");
210                    handleSwitches(dpnId, routerName, primarySwitchId);
211               } else {
212                    LOG.debug("NAT Service : Handle NAPT switch");
213                    handlePrimaryNaptSwitch(dpnId, routerName, primarySwitchId);
214               }
215         }
216
217         // call registerMapping Api
218         long segmentId = NatUtil.getVpnId(dataBroker, routerName);
219         LOG.debug("NAT Service : Preparing to call registerMapping for routerName {} and Id {}", routerName, segmentId);
220
221         List<Uuid> subnetList = null;
222         List<String> externalIps = null;
223
224         InstanceIdentifier<Routers> id = InstanceIdentifier
225                 .builder(ExtRouters.class)
226                 .child(Routers.class, new RoutersKey(routerName))
227                 .build();
228
229         Optional<Routers> extRouters = read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
230
231         if(extRouters.isPresent())
232         {
233             LOG.debug("NAT Service : Fetching values from extRouters model");
234             Routers routerEntry= extRouters.get();
235             subnetList = routerEntry.getSubnetIds();
236             externalIps = routerEntry.getExternalIps();
237             int counter = 0;
238             int extIpCounter = externalIps.size();
239             LOG.debug("NAT Service : counter values before looping counter {} and extIpCounter {}", counter, extIpCounter);
240             for(Uuid subnet : subnetList) {
241                   LOG.debug("NAT Service : Looping internal subnets for subnet {}", subnet);
242                   InstanceIdentifier<Subnetmap> subnetmapId = InstanceIdentifier
243                          .builder(Subnetmaps.class)
244                          .child(Subnetmap.class, new SubnetmapKey(subnet))
245                          .build();
246                   Optional<Subnetmap> sn = read(dataBroker, LogicalDatastoreType.CONFIGURATION, subnetmapId);
247                   if(sn.isPresent()){
248                       // subnets
249                       Subnetmap subnetmapEntry = sn.get();
250                       String subnetString = subnetmapEntry.getSubnetIp();
251                       String[] subnetSplit = subnetString.split("/");
252                       String subnetIp = subnetSplit[0];
253                       String subnetPrefix = "0";
254                       if(subnetSplit.length ==  2) {
255                           subnetPrefix = subnetSplit[1];
256                       }
257                       IPAddress subnetAddr = new IPAddress(subnetIp, Integer.parseInt(subnetPrefix));
258                       LOG.debug("NAT Service : subnetAddr is {} and subnetPrefix is {}", subnetAddr.getIpAddress(), subnetAddr.getPrefixLength());
259                       //externalIps
260                       LOG.debug("NAT Service : counter values counter {} and extIpCounter {}", counter, extIpCounter);
261                       if(extIpCounter != 0) {
262                             if(counter < extIpCounter) {
263                                    String[] IpSplit = externalIps.get(counter).split("/");
264                                    String externalIp = IpSplit[0];
265                                    String extPrefix = Short.toString(NatConstants.DEFAULT_PREFIX);
266                                    if(IpSplit.length==2) {
267                                        extPrefix = IpSplit[1];
268                                    }
269                                    IPAddress externalIpAddr = new IPAddress(externalIp, Integer.parseInt(extPrefix));
270                                    LOG.debug("NAT Service : externalIp is {} and extPrefix  is {}", externalIpAddr.getIpAddress(), externalIpAddr.getPrefixLength());
271                                    naptManager.registerMapping(segmentId, subnetAddr, externalIpAddr);
272                                    LOG.debug("NAT Service : Called registerMapping for subnetIp {}, prefix {}, externalIp {}. prefix {}", subnetIp, subnetPrefix,
273                                             externalIp, extPrefix);
274
275                                    String externalIpAddrPrefix = externalIpAddr.getIpAddress() + "/" + externalIpAddr.getPrefixLength();
276                                    LOG.debug("NAT Service : Calling handleSnatReverseTraffic for primarySwitchId {}, routerName {} and externalIpAddPrefix {}", primarySwitchId, routerName, externalIpAddrPrefix);
277                                    handleSnatReverseTraffic(primarySwitchId, segmentId, externalIpAddrPrefix);
278
279                             } else {
280                                    counter = 0;    //Reset the counter which runs on externalIps for round-robbin effect
281                                    LOG.debug("NAT Service : Counter on externalIps got reset");
282                                    String[] IpSplit = externalIps.get(counter).split("/");
283                                    String externalIp = IpSplit[0];
284                                    String extPrefix = Short.toString(NatConstants.DEFAULT_PREFIX);
285                                    if(IpSplit.length==2) {
286                                        extPrefix = IpSplit[1];
287                                    }
288                                    IPAddress externalIpAddr = new IPAddress(externalIp, Integer.parseInt(extPrefix));
289                                    LOG.debug("NAT Service : externalIp is {} and extPrefix  is {}", externalIpAddr.getIpAddress(), externalIpAddr.getPrefixLength());
290                                    naptManager.registerMapping(segmentId, subnetAddr, externalIpAddr);
291                                    LOG.debug("NAT Service : Called registerMapping for subnetIp {}, prefix {}, externalIp {}. prefix {}", subnetIp, subnetPrefix,
292                                             externalIp, extPrefix);
293
294                                    String externalIpAddrPrefix = externalIpAddr.getIpAddress() + "/" + externalIpAddr.getPrefixLength();
295                                    LOG.debug("NAT Service : Calling handleSnatReverseTraffic for primarySwitchId {}, routerName {} and externalIpAddPrefix {}", primarySwitchId, routerName, externalIpAddrPrefix);
296                                    handleSnatReverseTraffic(primarySwitchId, segmentId, externalIpAddrPrefix);
297
298                             }
299                       }
300                       counter++;
301                       LOG.debug("NAT Service : Counter on externalIps incremented to {}", counter);
302
303                   } else {
304                       LOG.warn("NAT Service : No internal subnets present in extRouters Model");
305                   }
306             }
307         }
308
309         LOG.info("NAT Service : handleEnableSnat() Exit");
310     }
311
312     private void addOrDelDefFibRouteToSNAT(String routerName, boolean create) {
313         //Router ID is used as the internal VPN's name, hence the vrf-id in VpnInstance Op DataStore
314         InstanceIdentifier<VpnInstanceOpDataEntry> id = NatUtil.getVpnInstanceOpDataIdentifier(routerName);
315         Optional<VpnInstanceOpDataEntry> vpnInstOp = NatUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
316         if (vpnInstOp.isPresent()) {
317             List<VpnToDpnList> dpnListInVpn = vpnInstOp.get().getVpnToDpnList();
318             if(dpnListInVpn == null) {
319                 LOG.debug("Current no dpns part of router {} to program default NAT route", routerName);
320                 return;
321             }
322             long vpnId = NatUtil.readVpnId(dataBroker, routerName);
323             if(vpnId == NatConstants.INVALID_ID) {
324                 LOG.error("Could not retrieve router Id for {} to program default NAT route in FIB", routerName);
325                 return;
326             }
327             for (VpnToDpnList dpn : dpnListInVpn) {
328                 BigInteger dpnId = dpn.getDpnId();
329                 if (create == true) {
330                     //installDefNATRouteInDPN(dpnId, vpnId);
331                     defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, vpnId);
332                 } else {
333                     //removeDefNATRouteInDPN(dpnId, vpnId);
334                     defaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, vpnId);
335                 }
336             }
337         }
338     }
339
340     public static <T extends DataObject> Optional<T> read(DataBroker broker, LogicalDatastoreType datastoreType, InstanceIdentifier<T> path)
341     {
342         ReadOnlyTransaction tx = broker.newReadOnlyTransaction();
343
344         Optional<T> result = Optional.absent();
345         try
346         {
347             result = tx.read(datastoreType, path).get();
348         }
349         catch (Exception e)
350         {
351             throw new RuntimeException(e);
352         }
353
354         return result;
355     }
356
357     public void close() throws Exception
358     {
359         if (listenerRegistration != null)
360         {
361             try
362             {
363                 listenerRegistration.close();
364             }
365             catch (final Exception e)
366             {
367                 LOG.error("Error when cleaning up ExternalRoutersListener.", e);
368             }
369
370             listenerRegistration = null;
371         }
372         LOG.debug("ExternalRoutersListener Closed");
373     }
374
375     protected void installOutboundMissEntry(String routerName, BigInteger primarySwitchId) {
376         long routerId = NatUtil.getVpnId(dataBroker, routerName);
377         LOG.debug("NAT Service : Router ID from getVpnId {}", routerId);
378         if(routerId != NatConstants.INVALID_ID) {
379             LOG.debug("NAT Service : Creating miss entry on primary {}, for router {}", primarySwitchId, routerId);
380             createOutboundTblEntry(primarySwitchId, routerId);
381         } else {
382             LOG.error("NAT Service : Unable to fetch Router Id  for RouterName {}, failed to createAndInstallMissEntry", routerName);
383         }
384     }
385
386     public String getFlowRefOutbound(BigInteger dpnId, short tableId, long routerID) {
387         return new StringBuilder().append(NatConstants.NAPT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR).
388                 append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString();
389     }
390
391     public BigInteger getCookieOutboundFlow(long routerId) {
392         return NatConstants.COOKIE_OUTBOUND_NAPT_TABLE.add(new BigInteger("0110001", 16)).add(
393                 BigInteger.valueOf(routerId));
394     }
395
396     protected FlowEntity buildOutboundFlowEntity(BigInteger dpId, long routerId) {
397         LOG.debug("NAT Service : buildOutboundFlowEntity called for dpId {} and routerId{}", dpId, routerId);
398         List<MatchInfo> matches = new ArrayList<MatchInfo>();
399         matches.add(new MatchInfo(MatchFieldType.eth_type,
400                 new long[] { 0x0800L }));
401         matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
402                 BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
403
404         List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
405         List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
406         actionsInfos.add(new ActionInfo(ActionType.punt_to_controller, new String[] {}));
407         instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
408         instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] { BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
409
410         String flowRef = getFlowRefOutbound(dpId, NatConstants.OUTBOUND_NAPT_TABLE, routerId);
411         BigInteger cookie = getCookieOutboundFlow(routerId);
412         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.OUTBOUND_NAPT_TABLE, flowRef,
413                 5, flowRef, 0, 0,
414                 cookie, matches, instructions);
415         LOG.debug("NAT Service : returning flowEntity {}", flowEntity);
416         return flowEntity;
417     }
418
419     public void createOutboundTblEntry(BigInteger dpnId, long routerId) {
420         LOG.debug("NAT Service : createOutboundTblEntry called for dpId {} and routerId {}", dpnId, routerId);
421         FlowEntity flowEntity = buildOutboundFlowEntity(dpnId, routerId);
422         LOG.debug("NAT Service : Installing flow {}", flowEntity);
423         mdsalManager.installFlow(flowEntity);
424     }
425
426     protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
427         try {
428             Future<RpcResult<GetTunnelInterfaceNameOutput>> result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
429                                                                                  .setSourceDpid(srcDpId)
430                                                                                  .setDestinationDpid(dstDpId).build());
431             RpcResult<GetTunnelInterfaceNameOutput> rpcResult = result.get();
432             if(!rpcResult.isSuccessful()) {
433                 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
434             } else {
435                 return rpcResult.getResult().getInterfaceName();
436             }
437         } catch (InterruptedException | ExecutionException | NullPointerException e) {
438             LOG.warn("NAT Service : Exception when getting tunnel interface Id for tunnel between {} and  {}", srcDpId, dstDpId);
439         }
440
441         return null;
442     }
443
444     protected List<ActionInfo> getEgressActionsForInterface(String ifName, long routerId) {
445         LOG.debug("NAT Service : getEgressActionsForInterface called for interface {}", ifName);
446         List<ActionInfo> listActionInfo = new ArrayList<ActionInfo>();
447         try {
448             Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
449                 interfaceManager.getEgressActionsForInterface(
450                     new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).setTunnelKey(routerId).build());
451             RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
452             if(!rpcResult.isSuccessful()) {
453                 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}", ifName, rpcResult.getErrors());
454             } else {
455                 List<Action> actions =
456                     rpcResult.getResult().getAction();
457                 for (Action action : actions) {
458                     org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action actionClass = action.getAction();
459                     if (actionClass instanceof OutputActionCase) {
460                         listActionInfo.add(new ActionInfo(ActionType.output,
461                                                           new String[] {((OutputActionCase)actionClass).getOutputAction()
462                                                                             .getOutputNodeConnector().getValue()}));
463                     } else if (actionClass instanceof PushVlanActionCase) {
464                         listActionInfo.add(new ActionInfo(ActionType.push_vlan, new String[] {}));
465                     } else if (actionClass instanceof SetFieldCase) {
466                         if (((SetFieldCase)actionClass).getSetField().getVlanMatch() != null) {
467                             int vlanVid = ((SetFieldCase)actionClass).getSetField().getVlanMatch().getVlanId().getVlanId().getValue();
468                             listActionInfo.add(new ActionInfo(ActionType.set_field_vlan_vid,
469                                                               new String[] { Long.toString(vlanVid) }));
470                         }
471                     }
472                 }
473             }
474         } catch (InterruptedException | ExecutionException e) {
475             LOG.warn("Exception when egress actions for interface {}", ifName, e);
476         }
477         return listActionInfo;
478     }
479
480     protected void installSnatMissEntry(BigInteger dpnId, List<BucketInfo> bucketInfo, String routerName) {
481         LOG.debug("NAT Service : installSnatMissEntry called for dpnId {} with primaryBucket {} ", dpnId, bucketInfo.get(0));
482         // Install the select group
483         long groupId = createGroupId(getGroupIdKey(routerName));
484         GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll, bucketInfo);
485         LOG.debug("NAT Service : installing the SNAT to NAPT GroupEntity:{}", groupEntity);
486         mdsalManager.installGroup(groupEntity);
487         // Install miss entry pointing to group
488         FlowEntity flowEntity = buildSnatFlowEntity(dpnId, routerName, groupId);
489         mdsalManager.installFlow(flowEntity);
490     }
491
492     public FlowEntity buildSnatFlowEntity(BigInteger dpId, String routerName, long groupId) {
493
494         LOG.debug("NAT Service : buildSnatFlowEntity is called for dpId {}, routerName {} and groupId {}", dpId, routerName, groupId );
495         long routerId = NatUtil.getVpnId(dataBroker, routerName);
496         List<MatchInfo> matches = new ArrayList<MatchInfo>();
497         matches.add(new MatchInfo(MatchFieldType.eth_type,
498                 new long[] { 0x0800L }));
499         matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
500                 BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
501
502
503         List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
504         List<ActionInfo> actionsInfo = new ArrayList<ActionInfo>();
505
506         ActionInfo actionSetField = new ActionInfo(ActionType.set_field_tunnel_id, new BigInteger[] {
507                         BigInteger.valueOf(routerId)}) ;
508         actionsInfo.add(actionSetField);
509         LOG.debug("NAT Service : Setting the tunnel to the list of action infos {}", actionsInfo);
510         actionsInfo.add(new ActionInfo(ActionType.group, new String[] {String.valueOf(groupId)}));
511         instructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfo));
512         String flowRef = getFlowRefSnat(dpId, NatConstants.PSNAT_TABLE, routerName);
513         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
514                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
515                 NatConstants.COOKIE_SNAT_TABLE, matches, instructions);
516
517         LOG.debug("NAT Service : Returning SNAT Flow Entity {}", flowEntity);
518         return flowEntity;
519     }
520
521     // TODO : Replace this with ITM Rpc once its available with full functionality
522     protected void installTerminatingServiceTblEntry(BigInteger dpnId, String routerName) {
523         LOG.debug("NAT Service : creating entry for Terminating Service Table for switch {}, routerName {}", dpnId, routerName);
524         FlowEntity flowEntity = buildTsFlowEntity(dpnId, routerName);
525         mdsalManager.installFlow(flowEntity);
526
527     }
528
529     private FlowEntity buildTsFlowEntity(BigInteger dpId, String routerName) {
530
531         BigInteger routerId = BigInteger.valueOf (NatUtil.getVpnId(dataBroker, routerName));
532         List<MatchInfo> matches = new ArrayList<MatchInfo>();
533         matches.add(new MatchInfo(MatchFieldType.eth_type,
534                 new long[] { 0x0800L }));
535         matches.add(new MatchInfo(MatchFieldType.tunnel_id, new  BigInteger[] {routerId }));
536
537         List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
538         instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]
539                 { routerId, MetaDataUtil.METADATA_MASK_VRFID }));
540         instructions.add(new InstructionInfo(InstructionType.goto_table, new long[]
541                 { NatConstants.OUTBOUND_NAPT_TABLE }));
542         String flowRef = getFlowRefTs(dpId, NatConstants.TERMINATING_SERVICE_TABLE, routerId.longValue());
543         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.TERMINATING_SERVICE_TABLE, flowRef,
544                 NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef, 0, 0,
545                 NatConstants.COOKIE_TS_TABLE, matches, instructions);
546         return flowEntity;
547     }
548
549     public String getFlowRefTs(BigInteger dpnId, short tableId, long routerID) {
550         return new StringBuilder().append(NatConstants.NAPT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR).
551                 append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString();
552     }
553
554     public static String getFlowRefSnat(BigInteger dpnId, short tableId, String routerID) {
555         return new StringBuilder().append(NatConstants.SNAT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR).
556             append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString();
557     }
558
559     private String getGroupIdKey(String routerName){
560         String groupIdKey = new String("snatmiss." + routerName);
561         return groupIdKey;
562     }
563
564     protected long createGroupId(String groupIdKey) {
565         AllocateIdInput getIdInput = new AllocateIdInputBuilder()
566             .setPoolName(NatConstants.SNAT_IDPOOL_NAME).setIdKey(groupIdKey)
567             .build();
568         try {
569             Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
570             RpcResult<AllocateIdOutput> rpcResult = result.get();
571             return rpcResult.getResult().getIdValue();
572         } catch (NullPointerException | InterruptedException | ExecutionException e) {
573             LOG.trace("",e);
574         }
575         return 0;
576     }
577
578     protected void createGroupIdPool() {
579         CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
580             .setPoolName(NatConstants.SNAT_IDPOOL_NAME)
581             .setLow(NatConstants.SNAT_ID_LOW_VALUE)
582             .setHigh(NatConstants.SNAT_ID_HIGH_VALUE)
583             .build();
584         try {
585             Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
586                 if ((result != null) && (result.get().isSuccessful())) {
587                     LOG.debug("NAT Service : Created GroupIdPool");
588                 } else {
589                     LOG.error("NAT Service : Unable to create GroupIdPool");
590                 }
591         } catch (InterruptedException | ExecutionException e) {
592             LOG.error("Failed to create PortPool for NAPT Service",e);
593         }
594     }
595
596     protected void handleSwitches (BigInteger dpnId, String routerName, BigInteger primarySwitchId) {
597         LOG.debug("NAT Service : Installing SNAT miss entry in switch {}", dpnId);
598         List<ActionInfo> listActionInfoPrimary = new ArrayList<>();
599         String ifNamePrimary = getTunnelInterfaceName( dpnId, primarySwitchId);
600         List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
601         long routerId = NatUtil.getVpnId(dataBroker, routerName);
602
603         if(ifNamePrimary != null) {
604             LOG.debug("NAT Service : On Non- Napt switch , Primary Tunnel interface is {}", ifNamePrimary);
605             listActionInfoPrimary = getEgressActionsForInterface(ifNamePrimary, routerId);
606         }
607         BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
608
609         listBucketInfo.add(0, bucketPrimary);
610         installSnatMissEntry(dpnId, listBucketInfo, routerName);
611
612     }
613
614     protected void handlePrimaryNaptSwitch (BigInteger dpnId, String routerName, BigInteger primarySwitchId) {
615
616            /*
617             * Primary NAPT Switch â€“ bucket Should always point back to its own Outbound Table
618             */
619
620             LOG.debug("NAT Service : Installing SNAT miss entry in Primary NAPT switch {} ", dpnId);
621
622             List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
623             List<ActionInfo> listActionInfoPrimary =  new ArrayList<ActionInfo>();
624             listActionInfoPrimary.add(new ActionInfo(ActionType.nx_resubmit, new String[]{String.valueOf(NatConstants.TERMINATING_SERVICE_TABLE)}));
625             BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
626             listBucketInfo.add(0, bucketPrimary);
627
628             long routerId = NatUtil.getVpnId(dataBroker, routerName);
629
630             installSnatMissEntry(dpnId, listBucketInfo, routerName);
631             installTerminatingServiceTblEntry(dpnId, routerName);
632             installNaptPfibEntry(dpnId, routerId);
633
634     }
635
636     public void installNaptPfibEntry(BigInteger dpnId, long routerId) {
637         LOG.debug("NAT Service : installNaptPfibEntry called for dpnId {} and routerId {} ", dpnId, routerId);
638         FlowEntity flowEntity = buildNaptPfibFlowEntity(dpnId, routerId);
639         mdsalManager.installFlow(flowEntity);
640     }
641
642     public FlowEntity buildNaptPfibFlowEntity(BigInteger dpId, long routerId) {
643
644         LOG.debug("NAT Service : buildNaptPfibFlowEntity is called for dpId {}, routerId {}", dpId, routerId );
645         List<MatchInfo> matches = new ArrayList<MatchInfo>();
646         matches.add(new MatchInfo(MatchFieldType.eth_type,
647                 new long[] { 0x0800L }));
648         matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
649                 BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID }));
650
651         ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
652         ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
653         listActionInfo.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NatConstants.L3_FIB_TABLE) }));
654         instructionInfo.add(new InstructionInfo(InstructionType.apply_actions, listActionInfo));
655
656         String flowRef = getFlowRefTs(dpId, NatConstants.NAPT_PFIB_TABLE, routerId);
657         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.NAPT_PFIB_TABLE, flowRef,
658                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
659                 NatConstants.COOKIE_SNAT_TABLE, matches, instructionInfo);
660
661         LOG.debug("NAT Service : Returning NaptPFib Flow Entity {}", flowEntity);
662         return flowEntity;
663     }
664
665     private void handleSnatReverseTraffic(BigInteger dpnId, long routerId, String externalIp) {
666         LOG.debug("NAT Service : handleSnatReverseTraffic() entry for DPN ID, routerId, externalIp : {}", dpnId, routerId, externalIp);
667         Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
668         if(networkId == null) {
669             LOG.error("NAT Service : networkId is null for the router ID {}", routerId);
670             return;
671         }
672         final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
673         if(vpnName == null) {
674             LOG.error("NAT Service : No VPN associated with ext nw {} to handle add external ip configuration {} in router {}",
675                     networkId, externalIp, routerId);
676             return;
677         }
678         advToBgpAndInstallFibAndTsFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, vpnName, routerId, externalIp, vpnService, fibService, bgpManager, dataBroker, LOG);
679         LOG.debug("NAT Service : handleSnatReverseTraffic() exit for DPN ID, routerId, externalIp : {}", dpnId, routerId, externalIp);
680     }
681
682     public void advToBgpAndInstallFibAndTsFlows(final BigInteger dpnId, final short tableId, final String vpnName, final long routerId, final String externalIp,
683                                                 VpnRpcService vpnService, final FibRpcService fibService, final IBgpManager bgpManager, final DataBroker dataBroker,
684                                                 final Logger log){
685         LOG.debug("NAT Service : advToBgpAndInstallFibAndTsFlows() entry for DPN ID {}, tableId {}, vpnname {} and externalIp {}", dpnId, tableId, vpnName, externalIp);
686         //Generate VPN label for the external IP
687         GenerateVpnLabelInput labelInput = new GenerateVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
688         Future<RpcResult<GenerateVpnLabelOutput>> labelFuture = vpnService.generateVpnLabel(labelInput);
689
690         //On successful generation of the VPN label, advertise the route to the BGP and install the FIB routes.
691         ListenableFuture<RpcResult<Void>> future = Futures.transform(JdkFutureAdapters.listenInPoolThread(labelFuture), new AsyncFunction<RpcResult<GenerateVpnLabelOutput>, RpcResult<Void>>() {
692
693             @Override
694             public ListenableFuture<RpcResult<Void>> apply(RpcResult<GenerateVpnLabelOutput> result) throws Exception {
695                 if (result.isSuccessful()) {
696                     LOG.debug("NAT Service : inside apply with result success");
697                     GenerateVpnLabelOutput output = result.getResult();
698                     long label = output.getLabel();
699
700                     //Inform BGP
701                     String rd = NatUtil.getVpnRd(dataBroker, vpnName);
702                     String nextHopIp = NatUtil.getEndpointIpAddressForDPN(dataBroker, dpnId);
703                     NatUtil.addPrefixToBGP(bgpManager, rd, externalIp, nextHopIp, label, log);
704
705                     //Get IPMaps from the DB for the router ID
706                     List<IpMap> dbIpMaps = naptManager.getIpMapList(dataBroker, routerId);
707
708                     for (IpMap dbIpMap : dbIpMaps) {
709                         String dbExternalIp = dbIpMap.getExternalIp();
710                         //Select the IPMap, whose external IP is the IP for which FIB is installed
711                         if (externalIp.contains(dbExternalIp)) {
712                             String dbInternalIp = dbIpMap.getInternalIp();
713                             IpMapKey dbIpMapKey = dbIpMap.getKey();
714                             IpMap newIpm = new IpMapBuilder().setKey(dbIpMapKey).setInternalIp(dbInternalIp).setExternalIp(dbExternalIp).setLabel(label).build();
715                             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, naptManager.getIpMapIdentifier(routerId, dbInternalIp), newIpm);
716                         }
717                     }
718
719                     //Install custom FIB routes
720                     List<Instruction> customInstructions = new ArrayList<>();
721                     customInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[]{tableId}).buildInstruction(0));
722                     makeTunnelTableEntry(dpnId, label, customInstructions);
723                     makeLFibTableEntry(dpnId, label, customInstructions);
724
725                     CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId)
726                             .setIpAddress(externalIp).setServiceId(label).setInstruction(customInstructions).build();
727                     Future<RpcResult<Void>> future = fibService.createFibEntry(input);
728                     return JdkFutureAdapters.listenInPoolThread(future);
729                 } else {
730                     LOG.error("NAT Service : inside apply with result failed");
731                     String errMsg = String.format("Could not retrieve the label for prefix %s in VPN %s, %s", externalIp, vpnName, result.getErrors());
732                     return Futures.immediateFailedFuture(new RuntimeException(errMsg));
733                 }
734             }
735         });
736
737             Futures.addCallback(future, new FutureCallback<RpcResult<Void>>() {
738
739                 @Override
740                 public void onFailure(Throwable error) {
741                     log.error("NAT Service : Error in generate label or fib install process", error);
742                 }
743
744                 @Override
745                 public void onSuccess(RpcResult<Void> result) {
746                     if (result.isSuccessful()) {
747                         log.info("NAT Service : Successfully installed custom FIB routes for prefix {}", externalIp);
748                     } else {
749                         log.error("NAT Service : Error in rpc call to create custom Fib entries for prefix {} in DPN {}, {}", externalIp, dpnId, result.getErrors());
750                     }
751                 }
752             });
753      }
754
755     private void makeLFibTableEntry(BigInteger dpId, long serviceId, List<Instruction> customInstructions) {
756         List<MatchInfo> matches = new ArrayList<MatchInfo>();
757         matches.add(new MatchInfo(MatchFieldType.eth_type,
758                 new long[] { 0x8847L }));
759         matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
760
761         List<Instruction> instructions = new ArrayList<Instruction>();
762         List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
763         actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
764         Instruction writeInstruction = new InstructionInfo(InstructionType.write_actions, actionsInfos).buildInstruction(0);
765         instructions.add(writeInstruction);
766         instructions.addAll(customInstructions);
767
768         // Install the flow entry in L3_LFIB_TABLE
769         String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, serviceId, "");
770
771         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
772                 10, flowRef, 0, 0,
773                 COOKIE_VM_LFIB_TABLE, matches, instructions);
774
775         mdsalManager.installFlow(dpId, flowEntity);
776
777         LOG.debug("NAT Service : LFIB Entry for dpID {} : label : {} modified successfully {}",dpId, serviceId );
778     }
779
780     private void makeTunnelTableEntry(BigInteger dpnId, long serviceId, List<Instruction> customInstructions) {
781         List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
782
783         LOG.debug("NAT Service : Create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}", dpnId , serviceId);
784
785         mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
786
787         Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
788                 getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""), 5, String.format("%s:%d","TST Flow Entry ",serviceId),
789                 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)),mkMatches, customInstructions);
790
791         mdsalManager.installFlow(dpnId, terminatingServiceTableFlowEntity);
792     }
793
794     protected InstanceIdentifier<RouterIds> getRoutersIdentifier(long routerId) {
795         InstanceIdentifier<RouterIds> id = InstanceIdentifier.builder(
796                 RouterIdName.class).child(RouterIds.class, new RouterIdsKey(routerId)).build();
797         return id;
798     }
799
800     private String getFlowRef(BigInteger dpnId, short tableId, long id, String ipAddress) {
801         return new StringBuilder(64).append(NatConstants.SNAT_FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
802                 .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
803                 .append(id).append(NwConstants.FLOWID_SEPARATOR).append(ipAddress).toString();
804     }
805
806     @Override
807     protected void update(InstanceIdentifier<Routers> identifier, Routers original, Routers update) {
808         boolean originalSNATEnabled = original.isEnableSnat();
809         boolean updatedSNATEnabled = update.isEnableSnat();
810         if(originalSNATEnabled != updatedSNATEnabled) {
811             if(originalSNATEnabled) {
812                 //SNAT disabled for the router
813                 String routerName = original.getRouterName();
814                 Uuid networkUuid = original.getNetworkId();
815                 List<String> externalIps = original.getExternalIps();
816                 LOG.info("NAT Service : SNAT disabled for Router {}", routerName);
817                 handleDisableSnat(routerName, networkUuid, externalIps);
818             } else {
819                 String routerName = original.getRouterName();
820                 LOG.info("NAT Service : SNAT enabled for Router {}", original.getRouterName());
821                 handleEnableSnat(routerName);
822             }
823         }
824     }
825
826     @Override
827     protected void remove(InstanceIdentifier<Routers> identifier, Routers router) {
828         LOG.trace("NAT Service : Router delete method");
829         {
830         /*
831             ROUTER DELETE SCENARIO
832             1) Get the router ID from the event.
833             2) Build the cookie information from the router ID.
834             3) Get the primary and secondary switch DPN IDs using the router ID from the model.
835             4) Build the flow with the cookie value.
836             5) Delete the flows which matches the cookie information from the NAPT outbound, inbound tables.
837             6) Remove the flows from the other switches which points to the primary and secondary switches for the flows related the router ID.
838             7) Get the list of external IP address maintained for the router ID.
839             8) Use the NaptMananager removeMapping API to remove the list of IP addresses maintained.
840             9) Withdraw the corresponding routes from the BGP.
841          */
842
843             if (identifier == null || router == null) {
844                 LOG.info("++++++++++++++NAT Service : ExternalRoutersListener:remove:: returning without processing since routers is null");
845                 return;
846             }
847
848             String routerName = router.getRouterName();
849             LOG.info("Removing default NAT route from FIB on all dpns part of router {} ", routerName);
850             addOrDelDefFibRouteToSNAT(routerName, false);
851             Uuid networkUuid = router.getNetworkId();
852             List<String> externalIps = router.getExternalIps();
853             handleDisableSnat(routerName, networkUuid, externalIps);
854         }
855     }
856
857     public void handleDisableSnat(String routerName, Uuid networkUuid, List<String> externalIps){
858         LOG.info("NAT Service : handleDisableSnat() Entry");
859         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
860
861         BigInteger naptSwitchDpnId = null;
862         InstanceIdentifier<RouterToNaptSwitch> routerToNaptSwitch = NatUtil.buildNaptSwitchRouterIdentifier(routerName);
863         Optional<RouterToNaptSwitch> rtrToNapt = read(dataBroker, LogicalDatastoreType.OPERATIONAL, routerToNaptSwitch );
864         if(rtrToNapt.isPresent()) {
865             naptSwitchDpnId = rtrToNapt.get().getPrimarySwitchId();
866         }
867         LOG.debug("NAT Service : got primarySwitch as dpnId{} ", naptSwitchDpnId);
868
869         removeNaptFlowsFromActiveSwitch(routerId, routerName, naptSwitchDpnId);
870         removeFlowsFromNonActiveSwitches(routerName, naptSwitchDpnId);
871         advToBgpAndRemoveFibAndTsFlows(naptSwitchDpnId, routerId, networkUuid, externalIps);
872
873         //Use the NaptMananager removeMapping API to remove the entire list of IP addresses maintained for the router ID.
874         LOG.debug("NAT Service : Remove the Internal to external IP address maintained for the router ID {} in the DS", routerId);
875         naptManager.removeMapping(routerId);
876
877         LOG.info("NAT Service : handleDisableSnat() Exit");
878     }
879
880     public void removeNaptFlowsFromActiveSwitch(long routerId, String routerName, BigInteger dpnId){
881         LOG.debug("NAT Service : Remove NAPT flows from Active switch");
882         BigInteger cookieSnatFlow = NatUtil.getCookieNaptFlow(routerId);
883
884         //Remove the PSNAT entry which forwards the packet to Terminating Service table
885         String pSNatFlowRef = getFlowRefSnat(dpnId, NatConstants.PSNAT_TABLE, routerName);
886         FlowEntity pSNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.PSNAT_TABLE, pSNatFlowRef);
887
888         LOG.info("NAT Service : Remove the flow in the " + NatConstants.PSNAT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
889         mdsalManager.removeFlow(pSNatFlowEntity);
890
891         //Remove the group entry which resubmits the packet to the Terminating Service table or to the out port accordingly.
892         long groupId = createGroupId(getGroupIdKey(routerName));
893         List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
894         GroupEntity pSNatGroupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll, listBucketInfo);
895
896         LOG.info("NAT Service : Remove the group {} for the active switch with the DPN ID {} and router ID {}", groupId, dpnId, routerId);
897         mdsalManager.removeGroup(pSNatGroupEntity);
898
899         //Remove the Terminating Service table entry which forwards the packet to Outbound NAPT Table
900         String tsFlowRef = getFlowRefTs(dpnId, NatConstants.TERMINATING_SERVICE_TABLE, routerId);
901         FlowEntity tsNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.TERMINATING_SERVICE_TABLE, tsFlowRef);
902
903         LOG.info("NAT Service : Remove the flow in the " + NatConstants.TERMINATING_SERVICE_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
904         mdsalManager.removeFlow(tsNatFlowEntity);
905
906         //Remove the Outbound flow entry which forwards the packet to FIB Table
907         String outboundNatFlowRef = getFlowRefOutbound(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, routerId);
908         FlowEntity outboundNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, outboundNatFlowRef);
909
910         LOG.info("NAT Service : Remove the flow in the " + NatConstants.OUTBOUND_NAPT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
911         mdsalManager.removeFlow(outboundNatFlowEntity);
912
913         //Remove the NAPT PFIB TABLE which forwards the packet to FIB Table
914         String natPfibFlowRef = getFlowRefTs(dpnId, NatConstants.NAPT_PFIB_TABLE, routerId);
915         FlowEntity natPfibFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.NAPT_PFIB_TABLE, natPfibFlowRef);
916
917         LOG.info("NAT Service : Remove the flow in the " + NatConstants.NAPT_PFIB_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
918         mdsalManager.removeFlow(natPfibFlowEntity);
919
920         //For the router ID get the internal IP , internal port and the corresponding external IP and external Port.
921         IpPortMapping ipPortMapping = NatUtil.getIportMapping(dataBroker, routerId);
922         if(ipPortMapping == null){
923             LOG.error("NAT Service : Unable to retrieve the IpPortMapping");
924             return;
925         }
926
927         List<IntextIpProtocolType> intextIpProtocolTypes = ipPortMapping.getIntextIpProtocolType();
928         for(IntextIpProtocolType intextIpProtocolType : intextIpProtocolTypes){
929             List<IpPortMap> ipPortMaps = intextIpProtocolType.getIpPortMap();
930             for(IpPortMap ipPortMap : ipPortMaps){
931                 String ipPortInternal = ipPortMap.getIpPortInternal();
932                 String[] ipPortParts = ipPortInternal.split(":");
933                 if(ipPortParts.length != 2) {
934                     LOG.error("NAT Service : Unable to retrieve the Internal IP and port");
935                     return;
936                 }
937                 String internalIp = ipPortParts[0];
938                 String internalPort = ipPortParts[1];
939
940                 //Build the flow for the outbound NAPT table
941                 String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, String.valueOf(routerId), internalIp, Integer.valueOf(internalPort));
942                 FlowEntity outboundNaptFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, cookieSnatFlow, switchFlowRef);
943
944                 LOG.info("NAT Service : Remove the flow in the " + NatConstants.OUTBOUND_NAPT_TABLE + " for the active switch with the DPN ID {} and router ID {}", dpnId, routerId);
945                 mdsalManager.removeFlow(outboundNaptFlowEntity);
946
947                 IpPortExternal ipPortExternal = ipPortMap.getIpPortExternal();
948                 String externalIp = ipPortExternal.getIpAddress();
949                 int externalPort = ipPortExternal.getPortNum();
950
951                 //Build the flow for the inbound NAPT table
952                 switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NatConstants.INBOUND_NAPT_TABLE, String.valueOf(routerId), externalIp, externalPort);
953                 FlowEntity inboundNaptFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.INBOUND_NAPT_TABLE, cookieSnatFlow, switchFlowRef);
954
955                 LOG.info("NAT Service : Remove the flow in the " + NatConstants.INBOUND_NAPT_TABLE + " for the active active switch with the DPN ID {} and router ID {}", dpnId, routerId);
956                 mdsalManager.removeFlow(inboundNaptFlowEntity);
957             }
958         }
959     }
960
961     public void removeFlowsFromNonActiveSwitches(String routerName, BigInteger naptSwitchDpnId){
962         LOG.debug("NAT Service : Remove NAPT related flows from non active switches");
963
964         //Remove the flows from the other switches which points to the primary and secondary switches for the flows related the router ID.
965         List<VpnToDpnList> allSwitchList = NatUtil.getVpnToDpnList(dataBroker, routerName);
966         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
967         for (VpnToDpnList eachSwitch : allSwitchList) {
968             BigInteger dpnId = eachSwitch.getDpnId();
969             if (naptSwitchDpnId != dpnId) {
970                 LOG.info("NAT Service : Handle Ordinary switch");
971
972                 //Remove the PSNAT entry which forwards the packet to Terminating Service table
973                 String pSNatFlowRef = getFlowRefSnat(dpnId, NatConstants.PSNAT_TABLE, String.valueOf(routerName));
974                 FlowEntity pSNatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.PSNAT_TABLE, pSNatFlowRef);
975
976                 LOG.info("Remove the flow in the " + NatConstants.PSNAT_TABLE + " for the non active switch with the DPN ID {} and router ID {}", dpnId, routerId);
977                 mdsalManager.removeFlow(pSNatFlowEntity);
978
979                 //Remove the group entry which resubmits the packet to the Terminating Service table or to the out port accordingly.
980                 long groupId = createGroupId(getGroupIdKey(routerName));
981                 List<BucketInfo> listBucketInfo = new ArrayList<BucketInfo>();
982                 GroupEntity pSNatGroupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll, listBucketInfo);
983
984                 LOG.info("NAT Service : Remove the group {} for the non active switch with the DPN ID {} and router ID {}", groupId, dpnId, routerId);
985                 mdsalManager.removeGroup(pSNatGroupEntity);
986
987             }
988         }
989     }
990
991     public void advToBgpAndRemoveFibAndTsFlows(final BigInteger dpnId, Long routerId, Uuid networkUuid, List<String> externalIps){
992         //Withdraw the corresponding routes from the BGP.
993         //Get the network ID using the router ID.
994         LOG.debug("NAT Service : Advertise to BGP and remove routes");
995         if(networkUuid == null ){
996             LOG.error("NAT Service : networkId is null");
997             return;
998         }
999
1000         //Get the VPN Name using the network ID
1001         final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkUuid, LOG);
1002         if (vpnName == null) {
1003             LOG.error("No VPN associated with ext nw {} for the router {}",
1004                     networkUuid, routerId);
1005             return;
1006         }
1007
1008         //Inform BGP about the route removal
1009         String rd = NatUtil.getVpnRd(dataBroker, vpnName);
1010         String prefix = "32";
1011         NatUtil.removePrefixFromBGP(bgpManager, rd, prefix, LOG);
1012
1013         //Remove custom FIB routes
1014         //Future<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
1015         final String externalIp = externalIps.get(0);
1016
1017         //Get IPMaps from the DB for the router ID
1018         List<IpMap> dbIpMaps = naptManager.getIpMapList(dataBroker, routerId);
1019         if(dbIpMaps == null ){
1020             LOG.error("NAT Service : IPMaps is null");
1021             return;
1022         }
1023
1024         long tempLabel = -1;
1025         for(IpMap dbIpMap: dbIpMaps) {
1026             String dbExternalIp = dbIpMap.getExternalIp();
1027             //Select the IPMap, whose external IP is the IP for which FIB is installed
1028             if (externalIp.contains(dbExternalIp)) {
1029                 tempLabel = dbIpMap.getLabel();
1030                 break;
1031             }
1032         }
1033         if(tempLabel == -1){
1034             LOG.error("NAT Service : Label is null");
1035             return;
1036         }
1037
1038         final long label = tempLabel;
1039         RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp + "/" +
1040                 NatConstants.DEFAULT_PREFIX).setServiceId(label).build();
1041         Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
1042
1043         ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
1044
1045             @Override
1046             public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
1047                 //Release label
1048                 if (result.isSuccessful()) {
1049                     removeTunnelTableEntry(dpnId, label);
1050                     removeLFibTableEntry(dpnId, label);
1051                     RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
1052                     Future<RpcResult<Void>> labelFuture = vpnService.removeVpnLabel(labelInput);
1053                     return JdkFutureAdapters.listenInPoolThread(labelFuture);
1054                 } else {
1055                     String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors());
1056                     LOG.error(errMsg);
1057                     return Futures.immediateFailedFuture(new RuntimeException(errMsg));
1058                 }
1059             }
1060
1061         });
1062
1063         Futures.addCallback(labelFuture, new FutureCallback<RpcResult<Void>>() {
1064
1065             @Override
1066             public void onFailure(Throwable error) {
1067                 LOG.error("NAT Service : Error in removing the label or custom fib entries", error);
1068             }
1069
1070             @Override
1071             public void onSuccess(RpcResult<Void> result) {
1072                 if (result.isSuccessful()) {
1073                     LOG.debug("NAT Service : Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName);
1074                 } else {
1075                     LOG.error("NAT Service : Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors());
1076                 }
1077             }
1078         });
1079     }
1080
1081     private void removeTunnelTableEntry(BigInteger dpnId, long serviceId) {
1082         LOG.info("NAT Service : remove terminatingServiceActions called with DpnId = {} and label = {}", dpnId , serviceId);
1083         List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
1084         // Matching metadata
1085         mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
1086         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
1087                 getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""),
1088                 5, String.format("%s:%d","TST Flow Entry ",serviceId), 0, 0,
1089                 COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)), mkMatches, null);
1090         mdsalManager.removeFlow(dpnId, flowEntity);
1091         LOG.debug("NAT Service : Terminating service Entry for dpID {} : label : {} removed successfully {}",dpnId, serviceId);
1092     }
1093
1094     private void removeLFibTableEntry(BigInteger dpnId, long serviceId) {
1095         List<MatchInfo> matches = new ArrayList<MatchInfo>();
1096         matches.add(new MatchInfo(MatchFieldType.eth_type,
1097                 new long[] { 0x8847L }));
1098         matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
1099
1100         String flowRef = getFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, serviceId, "");
1101
1102         LOG.debug("NAT Service : removing LFib entry with flow ref {}", flowRef);
1103
1104         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
1105                 10, flowRef, 0, 0,
1106                 COOKIE_VM_LFIB_TABLE, matches, null);
1107
1108         mdsalManager.removeFlow(dpnId, flowEntity);
1109
1110         LOG.debug("NAT Service : LFIB Entry for dpID : {} label : {} removed successfully {}",dpnId, serviceId);
1111     }
1112
1113     public static GroupEntity buildGroupEntity(BigInteger dpnId, long groupId) {
1114         GroupEntity groupEntity = new GroupEntity(dpnId);
1115         groupEntity.setGroupId(groupId);
1116         return groupEntity;
1117     }
1118
1119     protected InstanceIdentifier<Routers> getWildCardPath()
1120     {
1121         return InstanceIdentifier.create(ExtRouters.class).child(Routers.class);
1122     }
1123
1124     @Override
1125     protected ExternalRoutersListener getDataTreeChangeListener()
1126     {
1127         return ExternalRoutersListener.this;
1128     }
1129
1130 }