Bug 8982: avoid .transform() NPEs
[netvirt.git] / vpnservice / natservice / natservice-impl / src / main / java / org / opendaylight / netvirt / natservice / internal / NaptManager.java
1 /*
2  * Copyright © 2016, 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 /*
10  * Created eyugsar 2016/12/1
11  */
12 package org.opendaylight.netvirt.natservice.internal;
13
14 import com.google.common.base.Optional;
15 import com.google.common.util.concurrent.UncheckedExecutionException;
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.Future;
22 import javax.annotation.Nonnull;
23 import javax.inject.Inject;
24 import javax.inject.Singleton;
25 import org.apache.commons.net.util.SubnetUtils;
26 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
30 import org.opendaylight.genius.mdsalutil.MDSALUtil;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.DeleteIdPoolInput;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.DeleteIdPoolInputBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInputBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalIpsCounter;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.IntextIpMap;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.IntextIpPortMap;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProtocolTypes;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.SnatintIpPortMap;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.ips.counter.ExternalCounters;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.ips.counter.ExternalCountersKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.ips.counter.external.counters.ExternalIpCounter;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.ips.counter.external.counters.ExternalIpCounterBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.ips.counter.external.counters.ExternalIpCounterKey;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.IpMapping;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.IpMappingKey;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.ip.mapping.IpMap;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.ip.mapping.IpMapBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.ip.mapping.IpMapKey;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.IpPortMapping;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.IpPortMappingKey;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolTypeKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMapBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMapKey;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternalBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.IntipPortMap;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.IntipPortMapKey;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPort;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPortKey;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoType;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoTypeBuilder;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoTypeKey;
74 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
75 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
76 import org.opendaylight.yangtools.yang.common.RpcResult;
77 import org.slf4j.Logger;
78 import org.slf4j.LoggerFactory;
79
80 @Singleton
81 public class NaptManager {
82     private static final Logger LOG = LoggerFactory.getLogger(NaptManager.class);
83     private final DataBroker dataBroker;
84     private final IdManagerService idManager;
85     private static final long LOW_PORT = 49152L;
86     private static final long HIGH_PORT = 65535L;
87     private static boolean EXTSUBNET_FLAG = false;
88     private static boolean NEXT_EXTIP_FLAG = false;
89
90     @Inject
91     public NaptManager(final DataBroker dataBroker, final IdManagerService idManager) {
92         this.dataBroker = dataBroker;
93         this.idManager = idManager;
94     }
95
96     protected void createNaptPortPool(String poolName) {
97         LOG.debug("createNaptPortPool : requested for : {}", poolName);
98         CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
99             .setPoolName(poolName)
100             .setLow(LOW_PORT)
101             .setHigh(HIGH_PORT)
102             .build();
103         try {
104             Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
105             if ((result != null) && (result.get().isSuccessful())) {
106                 LOG.debug("createNaptPortPool : Created PortPool :{}", poolName);
107             } else {
108                 LOG.error("createNaptPortPool : Unable to create PortPool : {}", poolName);
109             }
110         } catch (InterruptedException | ExecutionException e) {
111             LOG.error("createNaptPortPool : Failed to create PortPool for NAPT Service", e);
112         }
113     }
114
115     void removeNaptPortPool(String poolName) {
116         DeleteIdPoolInput deleteIdPoolInput = new DeleteIdPoolInputBuilder().setPoolName(poolName).build();
117         LOG.debug("removeNaptPortPool : Remove Napt port pool requested for : {}", poolName);
118         try {
119             Future<RpcResult<Void>> result = idManager.deleteIdPool(deleteIdPoolInput);
120             if ((result != null) && (result.get().isSuccessful())) {
121                 LOG.debug("removeNaptPortPool : Deleted PortPool {}", poolName);
122             } else {
123                 LOG.error("removeNaptPortPool : Unable to delete PortPool {}", poolName);
124             }
125         } catch (InterruptedException | ExecutionException e) {
126             LOG.error("removeNaptPortPool : Failed to delete PortPool {} for NAPT Service", poolName, e);
127         }
128     }
129
130     // 1. napt service functions
131
132     /**
133      * This method is used to inform this service of what external IP address to be used
134      * as mapping when requested one for the internal IP address given in the input.
135      *
136      * @param segmentId – segmentation in which the mapping to be used. Eg; routerid
137      * @param internal  subnet prefix or ip address
138      * @param external  subnet prefix or ip address
139      */
140
141     public void registerMapping(long segmentId, IPAddress internal, IPAddress external) {
142         LOG.debug("registerMapping : called with segmentid {}, internalIp {}, prefix {}, externalIp {} "
143             + "and prefix {} ", segmentId, internal.getIpAddress(),
144             internal.getPrefixLength(), external.getIpAddress(), external.getPrefixLength());
145         // Create Pool per ExternalIp and not for all IPs in the subnet.
146         // Create new Pools during getExternalAddressMapping if exhausted.
147         String externalIpPool;
148         // subnet case
149         if (external.getPrefixLength() != 0 && external.getPrefixLength() != NatConstants.DEFAULT_PREFIX) {
150             String externalSubnet = external.getIpAddress() + "/" + external.getPrefixLength();
151             LOG.debug("registerMapping : externalSubnet is : {}", externalSubnet);
152             SubnetUtils subnetUtils = new SubnetUtils(externalSubnet);
153             SubnetInfo subnetInfo = subnetUtils.getInfo();
154             externalIpPool = subnetInfo.getLowAddress();
155         } else {  // ip case
156             externalIpPool = external.getIpAddress();
157         }
158         createNaptPortPool(externalIpPool);
159
160         // Store the ip to ip map in Operational DS
161         String internalIp = internal.getIpAddress();
162         if (internal.getPrefixLength() != 0) {
163             internalIp = internal.getIpAddress() + "/" + internal.getPrefixLength();
164         }
165         String externalIp = external.getIpAddress();
166         if (external.getPrefixLength() != 0) {
167             externalIp = external.getIpAddress() + "/" + external.getPrefixLength();
168         }
169         updateCounter(segmentId, externalIp, true);
170         //update the actual ip-map
171         IpMap ipm = new IpMapBuilder().setKey(new IpMapKey(internalIp)).setInternalIp(internalIp)
172             .setExternalIp(externalIp).build();
173         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
174             getIpMapIdentifier(segmentId, internalIp), ipm);
175         LOG.debug("registerMapping : registerMapping exit after updating DS with internalIP {}, externalIP {}",
176             internalIp, externalIp);
177     }
178
179     public void updateCounter(long segmentId, String externalIp, boolean isAdd) {
180         short counter = 0;
181         InstanceIdentifier<ExternalIpCounter> id = InstanceIdentifier.builder(ExternalIpsCounter.class)
182             .child(ExternalCounters.class, new ExternalCountersKey(segmentId))
183             .child(ExternalIpCounter.class, new ExternalIpCounterKey(externalIp)).build();
184         Optional<ExternalIpCounter> externalIpCounter =
185             MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
186         if (externalIpCounter.isPresent()) {
187             counter = externalIpCounter.get().getCounter();
188             if (isAdd) {
189                 counter++;
190                 LOG.debug("updateCounter : externalIp and counter after increment are {} and {}", externalIp, counter);
191             } else {
192                 if (counter > 0) {
193                     counter--;
194                 }
195                 LOG.debug("updateCounter : externalIp and counter after decrement are {} and {}", externalIp, counter);
196             }
197
198         } else if (isAdd) {
199             counter = 1;
200         }
201
202         //update the new counter value for this externalIp
203         ExternalIpCounter externalIpCounterData = new ExternalIpCounterBuilder()
204             .setKey(new ExternalIpCounterKey(externalIp)).setExternalIp(externalIp).setCounter(counter).build();
205         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
206             getExternalIpsIdentifier(segmentId, externalIp), externalIpCounterData);
207     }
208
209     /**
210      * method to get external ip/port mapping when provided with internal ip/port pair
211      * If already a mapping exist for the given input, then the existing mapping is returned
212      * instead of overwriting with new ip/port pair.
213      *
214      * @param segmentId     - Router ID
215      * @param sourceAddress - internal ip address/port pair
216      * @param protocol      - TCP/UDP
217      * @return external ip address/port
218      */
219     // TODO Clean up the exception handling
220     @SuppressWarnings("checkstyle:IllegalCatch")
221     public SessionAddress getExternalAddressMapping(long segmentId, SessionAddress sourceAddress,
222                                                     NAPTEntryEvent.Protocol protocol) {
223         LOG.debug("getExternalAddressMapping : called with segmentId {}, internalIp {} and port {}",
224             segmentId, sourceAddress.getIpAddress(), sourceAddress.getPortNumber());
225         /*
226          1. Get Internal IP, Port in IP:Port format
227          2. Inside DB with routerId get the list of entries and check if it matches with existing IP:Port
228          3. If True return SessionAddress of ExternalIp and Port
229          4. Else check ip Map and Form the ExternalIp and Port and update DB and then return ExternalIp and Port
230          */
231
232         //SessionAddress externalIpPort = new SessionAddress();
233         String internalIpPort = sourceAddress.getIpAddress() + ":" + sourceAddress.getPortNumber();
234
235         // First check existing Port Map.
236         SessionAddress existingIpPort = checkIpPortMap(segmentId, internalIpPort, protocol);
237         if (existingIpPort != null) {
238             // populate externalIpPort from IpPortMap and return
239             LOG.debug("getExternalAddressMapping : successfully returning existingIpPort as {} and {}",
240                 existingIpPort.getIpAddress(), existingIpPort.getPortNumber());
241             return existingIpPort;
242         } else {
243             // Now check in ip-map
244             String externalIp = checkIpMap(segmentId, sourceAddress.getIpAddress());
245             if (externalIp == null) {
246                 LOG.error("getExternalAddressMapping : Unexpected error, internal to external "
247                     + "ip map does not exist");
248                 return null;
249             } else {
250                  /* Logic assuming internalIp is always ip and not subnet
251                   * case 1: externalIp is ip
252                   *        a) goto externalIp pool and getPort and return
253                   *        b) else return error
254                   * case 2: externalIp is subnet
255                   *        a) Take first externalIp and goto that Pool and getPort
256                   *             if port -> return
257                   *             else Take second externalIp and create that Pool and getPort
258                   *             if port ->return
259                   *             else
260                   *             Continue same with third externalIp till we exhaust subnet
261                   *        b) Nothing worked return error
262                   */
263                 SubnetUtils externalIpSubnet;
264                 List<String> allIps = new ArrayList<>();
265                 String subnetPrefix = "/" + String.valueOf(NatConstants.DEFAULT_PREFIX);
266                 if (!externalIp.contains(subnetPrefix)) {
267                     EXTSUBNET_FLAG = true;
268                     externalIpSubnet = new SubnetUtils(externalIp);
269                     allIps = Arrays.asList(externalIpSubnet.getInfo().getAllAddresses());
270                     LOG.debug("getExternalAddressMapping : total count of externalIps available {}",
271                         externalIpSubnet.getInfo().getAddressCount());
272                 } else {
273                     LOG.debug("getExternalAddressMapping : getExternalAddress single ip case");
274                     if (externalIp.contains(subnetPrefix)) {
275                         //remove /32 what we got from checkIpMap
276                         externalIp = externalIp.substring(0, externalIp.indexOf(subnetPrefix));
277                     }
278                     allIps.add(externalIp);
279                 }
280
281                 for (String extIp : allIps) {
282                     LOG.info("getExternalAddressMapping : Looping externalIPs with externalIP now as {}", extIp);
283                     if (NEXT_EXTIP_FLAG) {
284                         createNaptPortPool(extIp);
285                         LOG.debug("getExternalAddressMapping : Created Pool for next Ext IP {}", extIp);
286                     }
287                     AllocateIdInput getIdInput = new AllocateIdInputBuilder()
288                         .setPoolName(extIp).setIdKey(internalIpPort)
289                         .build();
290                     try {
291                         Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
292                         RpcResult<AllocateIdOutput> rpcResult;
293                         if ((result != null) && (result.get().isSuccessful())) {
294                             LOG.debug("getExternalAddressMapping : Got id from idManager");
295                             rpcResult = result.get();
296                         } else {
297                             LOG.error("getExternalAddressMapping : getExternalAddressMapping, idManager could not "
298                                 + "allocate id retry if subnet");
299                             if (!EXTSUBNET_FLAG) {
300                                 LOG.error("getExternalAddressMapping : getExternalAddressMapping returning null "
301                                         + "for single IP case, may be ports exhausted");
302                                 return null;
303                             }
304                             LOG.debug("getExternalAddressMapping : Could be ports exhausted case, "
305                                     + "try with another externalIP if possible");
306                             NEXT_EXTIP_FLAG = true;
307                             continue;
308                         }
309                         int extPort = rpcResult.getResult().getIdValue().intValue();
310                         // Write to ip-port-map before returning
311                         IpPortExternalBuilder ipExt = new IpPortExternalBuilder();
312                         IpPortExternal ipPortExt = ipExt.setIpAddress(extIp).setPortNum(extPort).build();
313                         IpPortMap ipm = new IpPortMapBuilder().setKey(new IpPortMapKey(internalIpPort))
314                             .setIpPortInternal(internalIpPort).setIpPortExternal(ipPortExt).build();
315                         LOG.debug("getExternalAddressMapping : writing into ip-port-map with "
316                             + "externalIP {} and port {}",
317                             ipPortExt.getIpAddress(), ipPortExt.getPortNum());
318                         try {
319                             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
320                                 getIpPortMapIdentifier(segmentId, internalIpPort, protocol), ipm);
321                         } catch (UncheckedExecutionException uee) {
322                             LOG.error("getExternalAddressMapping : Failed to write into ip-port-map with exception",
323                                 uee);
324                         }
325
326                         // Write to snat-internal-ip-port-info
327                         String internalIpAddress = sourceAddress.getIpAddress();
328                         int ipPort = sourceAddress.getPortNumber();
329                         ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
330                         List<Integer> portList = new ArrayList<>(
331                                 NatUtil.getInternalIpPortListInfo(dataBroker, segmentId, internalIpAddress,
332                                         protocolType));
333                         portList.add(ipPort);
334
335                         IntIpProtoTypeBuilder builder = new IntIpProtoTypeBuilder();
336                         IntIpProtoType intIpProtocolType =
337                             builder.setKey(new IntIpProtoTypeKey(protocolType)).setPorts(portList).build();
338                         try {
339                             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
340                                 NatUtil.buildSnatIntIpPortIdentifier(segmentId, internalIpAddress, protocolType),
341                                 intIpProtocolType);
342                         } catch (Exception ex) {
343                             LOG.error("getExternalAddressMapping : Failed to write into snat-internal-ip-port-info "
344                                     + "with exception", ex);
345                         }
346
347                         SessionAddress externalIpPort = new SessionAddress(extIp, extPort);
348                         LOG.debug("getExternalAddressMapping : successfully returning externalIP {} "
349                             + "and port {}", externalIpPort.getIpAddress(), externalIpPort.getPortNumber());
350                         return externalIpPort;
351                     } catch (InterruptedException | ExecutionException e) {
352                         LOG.error("getExternalAddressMapping : Exception caught", e);
353                         return null;
354                     }
355                 } // end of for loop
356             } // end of else ipmap present
357         } // end of else check ipmap
358         LOG.error("getExternalAddressMapping : Unable to handle external IP address and port mapping with segmentId {},"
359                 + "internalIp {} and internalPort {}", segmentId, sourceAddress.getIpAddress(),
360                 sourceAddress.getPortNumber());
361         return null;
362     }
363
364     /**
365      * Release the existing mapping of internal ip/port to external ip/port pair
366      * if no mapping exist for given internal ip/port, it returns false.
367      *
368      * @param segmentId - Router ID
369      * @param address   - Session Address
370      * @param protocol  - TCP/UDP
371      * @return true if mapping exist and the mapping is removed successfully
372      */
373     // TODO Clean up the exception handling
374     @SuppressWarnings("checkstyle:IllegalCatch")
375     public boolean releaseAddressMapping(long segmentId, SessionAddress address, NAPTEntryEvent.Protocol protocol) {
376         LOG.debug("releaseAddressMapping : called with segmentId {}, internalIP {}, port {}",
377             segmentId, address.getIpAddress(), address.getPortNumber());
378         // delete entry from IpPort Map and IP Map if exists
379         String internalIpPort = address.getIpAddress() + ":" + address.getPortNumber();
380         SessionAddress existingIpPort = checkIpPortMap(segmentId, internalIpPort, protocol);
381         if (existingIpPort != null) {
382             // delete the entry from IpPortMap DS
383             try {
384                 removeFromIpPortMapDS(segmentId, internalIpPort, protocol);
385             } catch (Exception e) {
386                 LOG.error("releaseAddressMapping : failed, Removal of ipportmap {} for "
387                     + "router {} failed", internalIpPort, segmentId, e);
388                 return false;
389             }
390         } else {
391             LOG.error("releaseAddressMapping : failed, segmentId {} and internalIpPort {} "
392                 + "not found in IpPortMap DS", segmentId, internalIpPort);
393             return false;
394         }
395         String existingIp = checkIpMap(segmentId, address.getIpAddress());
396         if (existingIp != null) {
397             // delete the entry from IpMap DS
398             try {
399                 removeFromIpMapDS(segmentId, address.getIpAddress());
400             } catch (Exception e) {
401                 LOG.error("releaseAddressMapping : Removal of  ipmap {} for router {} failed {}",
402                     address.getIpAddress(), segmentId, e);
403                 return false;
404             }
405             //delete the entry from snatIntIpportinfo
406             try {
407                 removeFromSnatIpPortDS(segmentId, address.getIpAddress());
408             } catch (Exception e) {
409                 LOG.error("releaseAddressMapping : failed, Removal of snatipportmap {} for "
410                     + "router {} failed", address.getIpAddress(), segmentId, e);
411                 return false;
412             }
413         } else {
414             LOG.error("releaseAddressMapping : failed, segmentId {} and internalIpPort {} "
415                 + "not found in IpMap DS", segmentId, internalIpPort);
416             return false;
417         }
418         // Finally release port from idmanager
419         removePortFromPool(internalIpPort, existingIpPort.getIpAddress());
420
421         LOG.debug("releaseAddressMapping : Exited successfully for segmentId {} and internalIpPort {}",
422                 segmentId, internalIpPort);
423         return true;
424     }
425
426     // TODO Clean up the exception handling
427     @SuppressWarnings("checkstyle:IllegalCatch")
428     protected void releaseIpExtPortMapping(long segmentId, SessionAddress address, NAPTEntryEvent.Protocol protocol) {
429         String internalIpPort = address.getIpAddress() + ":" + address.getPortNumber();
430         SessionAddress existingIpPort = checkIpPortMap(segmentId, internalIpPort, protocol);
431         if (existingIpPort != null) {
432             // delete the entry from IpPortMap DS
433             try {
434                 removeFromIpPortMapDS(segmentId, internalIpPort, protocol);
435                 // Finally release port from idmanager
436                 removePortFromPool(internalIpPort, existingIpPort.getIpAddress());
437             } catch (Exception e) {
438                 LOG.error("releaseIpExtPortMapping : failed, Removal of ipportmap {} for "
439                     + "router {} failed", internalIpPort, segmentId, e);
440             }
441         } else {
442             LOG.error("releaseIpExtPortMapping : failed, segmentId {} and "
443                 + "internalIpPort {} not found in IpPortMap DS", segmentId, internalIpPort);
444         }
445
446         //delete the entry of port for InternalIp from snatIntIpportMappingDS
447         try {
448             removeSnatIntIpPortDS(segmentId, address, protocol);
449         } catch (Exception e) {
450             LOG.error("releaseSnatIpPortMapping : failed, Removal of snatipportmap {} for "
451                 + "router {} failed",address.getIpAddress(), segmentId, e);
452         }
453     }
454
455     /**
456      * Removes the internal ip to external ip mapping if present.
457      *
458      * @param segmentId - Router ID
459      * @return true if successfully removed
460      */
461     // TODO Clean up the exception handling
462     @SuppressWarnings("checkstyle:IllegalCatch")
463     public boolean removeMapping(long segmentId) {
464         try {
465             removeIpMappingForRouterID(segmentId);
466             removeIpPortMappingForRouterID(segmentId);
467             removeIntIpPortMappingForRouterID(segmentId);
468         } catch (Exception e) {
469             LOG.error("removeMapping : Removal of  IPMapping for router {} failed", segmentId, e);
470             return false;
471         }
472
473         //TODO :  This is when router is deleted then cleanup the entries in tables, ports etc - Delete scenarios
474         return false;
475     }
476
477     protected InstanceIdentifier<IpMap> getIpMapIdentifier(long segid, String internal) {
478         return InstanceIdentifier.builder(IntextIpMap.class)
479             .child(IpMapping.class, new IpMappingKey(segid))
480             .child(IpMap.class, new IpMapKey(internal)).build();
481     }
482
483     protected InstanceIdentifier<ExternalIpCounter> getExternalIpsIdentifier(long segmentId, String external) {
484         return InstanceIdentifier.builder(ExternalIpsCounter.class)
485             .child(ExternalCounters.class, new ExternalCountersKey(segmentId))
486             .child(ExternalIpCounter.class, new ExternalIpCounterKey(external)).build();
487     }
488
489     @Nonnull
490     public static List<IpMap> getIpMapList(DataBroker broker, Long routerId) {
491         InstanceIdentifier<IpMapping> id = getIpMapList(routerId);
492         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
493                 LogicalDatastoreType.OPERATIONAL, id).toJavaUtil().map(IpMapping::getIpMap).orElse(
494                 Collections.emptyList());
495     }
496
497     protected static InstanceIdentifier<IpMapping> getIpMapList(long routerId) {
498         return InstanceIdentifier.builder(
499             IntextIpMap.class).child(IpMapping.class, new IpMappingKey(routerId)).build();
500     }
501
502     protected InstanceIdentifier<IpPortMap> getIpPortMapIdentifier(long segid, String internal,
503                                                                    NAPTEntryEvent.Protocol protocol) {
504         ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
505         return InstanceIdentifier.builder(IntextIpPortMap.class)
506             .child(IpPortMapping.class, new IpPortMappingKey(segid))
507             .child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType))
508             .child(IpPortMap.class, new IpPortMapKey(internal)).build();
509     }
510
511     private SessionAddress checkIpPortMap(long segmentId, String internalIpPort,
512             NAPTEntryEvent.Protocol protocol) {
513         LOG.debug("checkIpPortMap : called with segmentId {} and internalIpPort {}",
514                 segmentId, internalIpPort);
515         ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
516         // check if ip-port-map node is there
517         InstanceIdentifierBuilder<IpPortMap> idBuilder =
518                 InstanceIdentifier.builder(IntextIpPortMap.class)
519                 .child(IpPortMapping.class, new IpPortMappingKey(segmentId))
520                 .child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType))
521                 .child(IpPortMap.class, new IpPortMapKey(internalIpPort));
522         InstanceIdentifier<IpPortMap> id = idBuilder.build();
523         Optional<IpPortMap> ipPortMapType =
524                 MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
525         if (ipPortMapType.isPresent()) {
526             LOG.debug("checkIpPortMap : {}", ipPortMapType.get());
527             SessionAddress externalIpPort = new SessionAddress(ipPortMapType.get().getIpPortExternal().getIpAddress(),
528                     ipPortMapType.get().getIpPortExternal().getPortNum());
529             LOG.debug("checkIpPortMap : returning successfully externalIP {} and port {}",
530                     externalIpPort.getIpAddress(), externalIpPort.getPortNumber());
531             return externalIpPort;
532         }
533         // return null if not found
534         LOG.warn("checkIpPortMap : no-entry in checkIpPortMap, returning NULL [should be OK] for "
535                 + "segmentId {} and internalIPPort {}", segmentId, internalIpPort);
536         return null;
537     }
538
539     protected String checkIpMap(long segmentId, String internalIp) {
540         LOG.debug("checkIpMap : called with segmentId {} and internalIp {}", segmentId, internalIp);
541         String externalIp;
542         // check if ip-map node is there
543         InstanceIdentifierBuilder<IpMapping> idBuilder =
544             InstanceIdentifier.builder(IntextIpMap.class).child(IpMapping.class, new IpMappingKey(segmentId));
545         InstanceIdentifier<IpMapping> id = idBuilder.build();
546         Optional<IpMapping> ipMapping = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
547         if (ipMapping.isPresent()) {
548             List<IpMap> ipMaps = ipMapping.get().getIpMap();
549             for (IpMap ipMap : ipMaps) {
550                 if (ipMap.getInternalIp().equals(internalIp)) {
551                     LOG.debug("checkIpMap : IpMap : {}", ipMap);
552                     externalIp = ipMap.getExternalIp();
553                     LOG.debug("checkIpMap : successfully returning externalIp {}", externalIp);
554                     return externalIp;
555                 } else if (ipMap.getInternalIp().contains("/")) { // subnet case
556                     SubnetUtils subnetUtils = new SubnetUtils(ipMap.getInternalIp());
557                     SubnetInfo subnetInfo = subnetUtils.getInfo();
558                     if (subnetInfo.isInRange(internalIp)) {
559                         LOG.debug("checkIpMap : internalIp {} found to be IpMap of internalIpSubnet {}",
560                             internalIp, ipMap.getInternalIp());
561                         externalIp = ipMap.getExternalIp();
562                         LOG.debug("checkIpMap : checkIpMap successfully returning externalIp {}", externalIp);
563                         return externalIp;
564                     }
565                 }
566             }
567         }
568         // return null if not found
569         LOG.error("checkIpMap : failed, returning NULL for segmentId {} and internalIp {}",
570             segmentId, internalIp);
571         return null;
572     }
573
574     // TODO Clean up the exception handling
575     @SuppressWarnings("checkstyle:IllegalCatch")
576     protected void removeSnatIntIpPortDS(long segmentId, SessionAddress address, NAPTEntryEvent.Protocol protocol) {
577         LOG.trace("removeSnatIntIpPortDS : method called for IntIpport {} of router {} ",
578             address, segmentId);
579         ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
580         List<Integer> portList =
581             NatUtil.getInternalIpPortListInfo(dataBroker, segmentId, address.getIpAddress(), protocolType);
582         if (portList.isEmpty() || !portList.contains(address.getPortNumber())) {
583             LOG.error("removeSnatIntIpPortDS : Internal IP {} for port {} entry not found in SnatIntIpPort DS",
584                 address.getIpAddress(), address.getPortNumber());
585             return;
586         }
587         LOG.trace("removeSnatIntIpPortDS : PortList {} retrieved for InternalIp {} of router {}",
588             portList, address.getIpAddress(), segmentId);
589         Integer port = address.getPortNumber();
590         portList.remove(port);
591
592         IntIpProtoTypeBuilder builder = new IntIpProtoTypeBuilder();
593         IntIpProtoType intIpProtocolType =
594             builder.setKey(new IntIpProtoTypeKey(protocolType)).setPorts(portList).build();
595         try {
596             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
597                 NatUtil.buildSnatIntIpPortIdentifier(segmentId, address.getIpAddress(), protocolType),
598                 intIpProtocolType);
599         } catch (Exception ex) {
600             LOG.error("removeSnatIntIpPortDS : Failed to write into snat-internal-ip-port-info with exception",ex);
601         }
602         LOG.debug("removeSnatIntIpPortDS : Removing SnatIp {} Port {} of router {} from SNATIntIpport datastore : {}",
603             address.getIpAddress(), address.getPortNumber(), segmentId);
604     }
605
606     protected void removeFromSnatIpPortDS(long segmentId, String internalIp) {
607         InstanceIdentifier<IpPort> intIp = InstanceIdentifier.builder(SnatintIpPortMap.class)
608             .child(IntipPortMap.class, new IntipPortMapKey(segmentId))
609             .child(IpPort.class, new IpPortKey(internalIp)).build();
610         // remove from SnatIpPortDS
611         LOG.debug("removeFromSnatIpPortDS : Removing SnatIpPort from datastore : {}", intIp);
612         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, intIp);
613     }
614
615     protected void removeFromIpPortMapDS(long segmentId, String internalIpPort, NAPTEntryEvent.Protocol protocol) {
616         ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
617         removeFromIpPortMapDS(segmentId, internalIpPort, protocolType);
618     }
619
620     protected void removeFromIpPortMapDS(long segmentId, String internalIpPort, ProtocolTypes protocolType) {
621         InstanceIdentifierBuilder<IpPortMap> idBuilder = InstanceIdentifier.builder(IntextIpPortMap.class)
622             .child(IpPortMapping.class, new IpPortMappingKey(segmentId))
623             .child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType))
624             .child(IpPortMap.class, new IpPortMapKey(internalIpPort));
625         InstanceIdentifier<IpPortMap> id = idBuilder.build();
626         // remove from ipportmap DS
627         LOG.debug("removeFromIpPortMapDS : Removing ipportmap from datastore : {}", id);
628         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
629     }
630
631     protected void removeFromIpMapDS(long segmentId, String internalIp) {
632         InstanceIdentifierBuilder<IpMap> idBuilder = InstanceIdentifier.builder(IntextIpMap.class)
633             .child(IpMapping.class, new IpMappingKey(segmentId))
634             .child(IpMap.class, new IpMapKey(internalIp));
635         InstanceIdentifier<IpMap> id = idBuilder.build();
636         // Get externalIp and decrement the counter
637         String externalIp = null;
638         Optional<IpMap> ipMap = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
639         if (ipMap.isPresent()) {
640             externalIp = ipMap.get().getExternalIp();
641             LOG.debug("removeFromIpMapDS : externalIP is {}", externalIp);
642         } else {
643             LOG.warn("removeFromIpMapDS : ipMap not present for the internal IP {}", internalIp);
644         }
645
646         if (externalIp != null) {
647             updateCounter(segmentId, externalIp, false);
648             // remove from ipmap DS
649             LOG.debug("removeFromIpMapDS : Removing ipmap from datastore");
650             MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
651         } else {
652             LOG.warn("removeFromIpMapDS : externalIp not present for the internal IP {}", internalIp);
653         }
654     }
655
656     protected void removeIntExtIpMapDS(long segmentId, String internalIp) {
657         InstanceIdentifierBuilder<IpMap> idBuilder = InstanceIdentifier.builder(IntextIpMap.class)
658             .child(IpMapping.class, new IpMappingKey(segmentId))
659             .child(IpMap.class, new IpMapKey(internalIp));
660         InstanceIdentifier<IpMap> id = idBuilder.build();
661
662         LOG.debug("removeIntExtIpMapDS : Removing ipmap from datastore");
663         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
664     }
665
666     protected String getExternalIpAllocatedForSubnet(long segmentId, String internalIp) {
667         InstanceIdentifierBuilder<IpMap> idBuilder = InstanceIdentifier.builder(IntextIpMap.class)
668             .child(IpMapping.class, new IpMappingKey(segmentId))
669             .child(IpMap.class, new IpMapKey(internalIp));
670         InstanceIdentifier<IpMap> id = idBuilder.build();
671
672         Optional<IpMap> ipMap = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
673         if (ipMap.isPresent()) {
674             return ipMap.get().getExternalIp();
675         }
676         return null;
677     }
678
679     private void removeIpMappingForRouterID(long segmentId) {
680         InstanceIdentifierBuilder<IpMapping> idBuilder = InstanceIdentifier.builder(IntextIpMap.class)
681             .child(IpMapping.class, new IpMappingKey(segmentId));
682         InstanceIdentifier<IpMapping> id = idBuilder.build();
683         // Get all externalIps and decrement their counters before deleting the ipmap
684         String externalIp = null;
685         Optional<IpMapping> ipMapping = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
686         if (ipMapping.isPresent()) {
687             List<IpMap> ipMaps = ipMapping.get().getIpMap();
688             for (IpMap ipMap : ipMaps) {
689                 externalIp = ipMap.getExternalIp();
690                 LOG.debug("removeIpMappingForRouterID : externalIP is {}", externalIp);
691                 if (externalIp != null) {
692                     updateCounter(segmentId, externalIp, false);
693                 }
694             }
695             // remove from ipmap DS
696             LOG.debug("removeIpMappingForRouterID : Removing Ipmap for router {} from datastore", segmentId);
697             MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
698         }
699     }
700
701     void removeIpPortMappingForRouterID(long segmentId) {
702         InstanceIdentifier<IpPortMapping> idBuilder = InstanceIdentifier.builder(IntextIpPortMap.class)
703             .child(IpPortMapping.class, new IpPortMappingKey(segmentId)).build();
704         Optional<IpPortMapping> ipPortMapping = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
705                 idBuilder);
706         if (ipPortMapping.isPresent()) {
707             // remove from IntExtIpPortmap DS
708             LOG.debug("removeIpPortMappingForRouterID : Removing IntExtIpPort map for router {} from datastore",
709                     segmentId);
710             MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, idBuilder);
711         }
712     }
713
714     void removeIntIpPortMappingForRouterID(long segmentId) {
715         InstanceIdentifier<IntipPortMap> intIp = InstanceIdentifier.builder(SnatintIpPortMap.class)
716             .child(IntipPortMap.class, new IntipPortMapKey(segmentId)).build();
717         Optional<IntipPortMap> intIpPortMap = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, intIp);
718         if (intIpPortMap.isPresent()) {
719             // remove from SnatIntIpPortmap DS
720             LOG.debug("removeIntIpPortMappingForRouterID : Removing SnatIntIpPort from datastore : {}", intIp);
721             MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, intIp);
722         }
723     }
724
725     void removePortFromPool(String internalIpPort, String externalIp) {
726         LOG.debug("removePortFromPool : method called");
727         ReleaseIdInput idInput = new ReleaseIdInputBuilder()
728             .setPoolName(externalIp)
729             .setIdKey(internalIpPort).build();
730         try {
731             Future<RpcResult<Void>> result = idManager.releaseId(idInput);
732             RpcResult<Void> rpcResult = result.get();
733             if (!rpcResult.isSuccessful()) {
734                 LOG.error("removePortFromPool : idmanager failed to remove port from pool {}", rpcResult.getErrors());
735             }
736             LOG.debug("removePortFromPool : Removed port from pool for InternalIpPort {} with externalIp {}",
737                 internalIpPort, externalIp);
738         } catch (InterruptedException | ExecutionException e) {
739             LOG.error("removePortFromPool : idmanager failed when removing entry in pool with key {} with Exception",
740                     internalIpPort, e);
741         }
742     }
743
744     protected void initialiseExternalCounter(Routers routers, long routerId) {
745         LOG.debug("initialiseExternalCounter : Initialise External IPs counter");
746         List<ExternalIps> externalIps = routers.getExternalIps();
747
748         //update the new counter value for this externalIp
749         for (ExternalIps externalIp : externalIps) {
750             String[] ipSplit = externalIp.getIpAddress().split("/");
751             String extIp = ipSplit[0];
752             String extPrefix = Short.toString(NatConstants.DEFAULT_PREFIX);
753             if (ipSplit.length == 2) {
754                 extPrefix = ipSplit[1];
755             }
756             extIp = extIp + "/" + extPrefix;
757             initialiseNewExternalIpCounter(routerId, extIp);
758         }
759     }
760
761     protected void initialiseNewExternalIpCounter(long routerId, String externalIp) {
762         ExternalIpCounter externalIpCounterData = new ExternalIpCounterBuilder()
763             .setKey(new ExternalIpCounterKey(externalIp)).setExternalIp(externalIp).setCounter((short) 0).build();
764         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
765             getExternalIpsIdentifier(routerId, externalIp), externalIpCounterData);
766     }
767
768     protected void removeExternalCounter(long routerId) {
769         // Remove from external-counters model
770         InstanceIdentifier<ExternalCounters> id = InstanceIdentifier.builder(ExternalIpsCounter.class)
771             .child(ExternalCounters.class, new ExternalCountersKey(routerId)).build();
772         LOG.debug("removeExternalCounter : Removing ExternalCounterd from datastore");
773         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
774     }
775
776     protected void removeExternalIpCounter(long routerId, String externalIp) {
777         // Remove from external-counters model
778         InstanceIdentifier<ExternalIpCounter> id = InstanceIdentifier.builder(ExternalIpsCounter.class)
779             .child(ExternalCounters.class, new ExternalCountersKey(routerId))
780             .child(ExternalIpCounter.class, new ExternalIpCounterKey(externalIp)).build();
781         LOG.debug("removeExternalIpCounter : Removing ExternalIpsCounter from datastore");
782         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
783     }
784 }