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