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