Allow MDSALManager exceptions
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / FloatingIPListener.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 package org.opendaylight.netvirt.natservice.internal;
9
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11
12 import com.google.common.base.Optional;
13 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
14 import java.math.BigInteger;
15 import java.net.InetAddress;
16 import java.net.UnknownHostException;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.List;
20 import javax.annotation.PostConstruct;
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
26 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
27 import org.opendaylight.genius.infra.Datastore.Configuration;
28 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
29 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
30 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
31 import org.opendaylight.genius.mdsalutil.ActionInfo;
32 import org.opendaylight.genius.mdsalutil.FlowEntity;
33 import org.opendaylight.genius.mdsalutil.InstructionInfo;
34 import org.opendaylight.genius.mdsalutil.MDSALUtil;
35 import org.opendaylight.genius.mdsalutil.MatchInfo;
36 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
37 import org.opendaylight.genius.mdsalutil.NwConstants;
38 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
39 import org.opendaylight.genius.mdsalutil.actions.ActionSetDestinationIp;
40 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
41 import org.opendaylight.genius.mdsalutil.actions.ActionSetSourceIp;
42 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
43 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
44 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
45 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
46 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetDestination;
47 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
48 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
49 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Source;
50 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
51 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
52 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalNetworks;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.FloatingIpInfo;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.Networks;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.NetworksKey;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.PortsBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.PortsKey;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMapBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMapKey;
68 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71
72 @Singleton
73 public class FloatingIPListener extends AsyncDataTreeChangeListenerBase<InternalToExternalPortMap, FloatingIPListener> {
74     private static final Logger LOG = LoggerFactory.getLogger(FloatingIPListener.class);
75     private final DataBroker dataBroker;
76     private final ManagedNewTransactionRunner txRunner;
77     private final IMdsalApiManager mdsalManager;
78     private final OdlInterfaceRpcService interfaceManager;
79     private final FloatingIPHandler floatingIPHandler;
80     private final SNATDefaultRouteProgrammer defaultRouteProgrammer;
81     private final JobCoordinator coordinator;
82
83     @Inject
84     public FloatingIPListener(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
85                               final OdlInterfaceRpcService interfaceManager,
86                               final FloatingIPHandler floatingIPHandler,
87                               final SNATDefaultRouteProgrammer snatDefaultRouteProgrammer,
88                               final JobCoordinator coordinator) {
89         super(InternalToExternalPortMap.class, FloatingIPListener.class);
90         this.dataBroker = dataBroker;
91         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
92         this.mdsalManager = mdsalManager;
93         this.interfaceManager = interfaceManager;
94         this.floatingIPHandler = floatingIPHandler;
95         this.defaultRouteProgrammer = snatDefaultRouteProgrammer;
96         this.coordinator = coordinator;
97     }
98
99     @Override
100     @PostConstruct
101     public void init() {
102         LOG.info("{} init", getClass().getSimpleName());
103         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
104     }
105
106     @Override
107     protected InstanceIdentifier<InternalToExternalPortMap> getWildCardPath() {
108         return InstanceIdentifier.create(FloatingIpInfo.class).child(RouterPorts.class).child(Ports.class)
109                 .child(InternalToExternalPortMap.class);
110     }
111
112     @Override
113     protected FloatingIPListener getDataTreeChangeListener() {
114         return FloatingIPListener.this;
115     }
116
117     @Override
118     protected void add(final InstanceIdentifier<InternalToExternalPortMap> identifier,
119                        final InternalToExternalPortMap mapping) {
120         LOG.trace("FloatingIPListener add ip mapping method - key: {} value: {}",mapping.key(), mapping);
121         processFloatingIPAdd(identifier, mapping);
122     }
123
124     @Override
125     protected void remove(InstanceIdentifier<InternalToExternalPortMap> identifier, InternalToExternalPortMap mapping) {
126         LOG.trace("FloatingIPListener remove ip mapping method - kkey: {} value: {}",mapping.key(), mapping);
127         processFloatingIPDel(identifier, mapping);
128     }
129
130     @Override
131     protected void update(InstanceIdentifier<InternalToExternalPortMap> identifier, InternalToExternalPortMap
132             original, InternalToExternalPortMap update) {
133         LOG.trace("FloatingIPListener update ip mapping method - key: {}, original: {}, update: {}",
134                 update.key(), original, update);
135     }
136
137     private FlowEntity buildPreDNATFlowEntity(BigInteger dpId, InternalToExternalPortMap mapping, long routerId, long
138             associatedVpn) {
139         String externalIp = mapping.getExternalIp();
140         Uuid floatingIpId = mapping.getExternalId();
141         //Get the FIP MAC address for DNAT
142         String floatingIpPortMacAddress = NatUtil.getFloatingIpPortMacFromFloatingIpId(dataBroker, floatingIpId);
143         if (floatingIpPortMacAddress == null) {
144             LOG.error("buildPreDNATFlowEntity : Unable to retrieve floatingIpPortMacAddress from floating IP UUID {} "
145                     + "for floating IP {}", floatingIpId, externalIp);
146             return null;
147         }
148         LOG.debug("buildPreDNATFlowEntity : Bulding DNAT Flow entity for ip {} ", externalIp);
149         long segmentId = associatedVpn == NatConstants.INVALID_ID ? routerId : associatedVpn;
150         LOG.debug("buildPreDNATFlowEntity : Segment id {} in build preDNAT Flow", segmentId);
151
152         List<MatchInfo> matches = new ArrayList<>();
153         matches.add(MatchEthernetType.IPV4);
154
155         matches.add(new MatchIpv4Destination(externalIp, "32"));
156         //Match Destination Floating IP MAC Address on table = 25 (PDNAT_TABLE)
157         matches.add(new MatchEthernetDestination(new MacAddress(floatingIpPortMacAddress)));
158
159 //        matches.add(new MatchMetadata(
160 //                BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
161         List<ActionInfo> actionsInfos = new ArrayList<>();
162         String internalIp = mapping.getInternalIp();
163         actionsInfos.add(new ActionSetDestinationIp(internalIp, "32"));
164
165         List<InstructionInfo> instructions = new ArrayList<>();
166         instructions.add(new InstructionWriteMetadata(MetaDataUtil.getVpnIdMetadata(segmentId),
167                 MetaDataUtil.METADATA_MASK_VRFID));
168         instructions.add(new InstructionApplyActions(actionsInfos));
169         instructions.add(new InstructionGotoTable(NwConstants.DNAT_TABLE));
170
171         String flowRef = NatUtil.getFlowRef(dpId, NwConstants.PDNAT_TABLE, routerId, externalIp);
172
173         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PDNAT_TABLE, flowRef,
174                 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
175                 NwConstants.COOKIE_DNAT_TABLE, matches, instructions);
176
177         return flowEntity;
178     }
179
180     private FlowEntity buildDNATFlowEntity(BigInteger dpId, InternalToExternalPortMap mapping, long routerId, long
181             associatedVpn) {
182         String externalIp = mapping.getExternalIp();
183         LOG.info("buildDNATFlowEntity : Bulding DNAT Flow entity for ip {} ", externalIp);
184
185         long segmentId = associatedVpn == NatConstants.INVALID_ID ? routerId : associatedVpn;
186         LOG.debug("buildDNATFlowEntity : Segment id {} in build DNAT", segmentId);
187
188         List<MatchInfo> matches = new ArrayList<>();
189         matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(segmentId), MetaDataUtil.METADATA_MASK_VRFID));
190
191         matches.add(MatchEthernetType.IPV4);
192         String internalIp = mapping.getInternalIp();
193         matches.add(new MatchIpv4Destination(internalIp, "32"));
194
195         List<ActionInfo> actionsInfos = new ArrayList<>();
196 //        actionsInfos.add(new ActionSetDestinationIp(internalIp, "32"));
197
198         List<InstructionInfo> instructions = new ArrayList<>();
199 //        instructions.add(new InstructionWriteMetadata(BigInteger.valueOf
200 //                (routerId), MetaDataUtil.METADATA_MASK_VRFID));
201         actionsInfos.add(new ActionNxResubmit(NwConstants.L3_FIB_TABLE));
202         instructions.add(new InstructionApplyActions(actionsInfos));
203         //instructions.add(new InstructionGotoTable(NatConstants.L3_FIB_TABLE));
204
205         String flowRef = NatUtil.getFlowRef(dpId, NwConstants.DNAT_TABLE, routerId, internalIp);
206
207         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.DNAT_TABLE, flowRef,
208                 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
209                 NwConstants.COOKIE_DNAT_TABLE, matches, instructions);
210
211         return flowEntity;
212
213     }
214
215     private FlowEntity buildPreSNATFlowEntity(BigInteger dpId, String internalIp, String externalIp, long vpnId, long
216             routerId, long associatedVpn) {
217
218         LOG.debug("buildPreSNATFlowEntity : Building PSNAT Flow entity for ip {} ", internalIp);
219
220         long segmentId = associatedVpn == NatConstants.INVALID_ID ? routerId : associatedVpn;
221
222         LOG.debug("buildPreSNATFlowEntity : Segment id {} in build preSNAT flow", segmentId);
223
224         List<MatchInfo> matches = new ArrayList<>();
225         matches.add(MatchEthernetType.IPV4);
226
227         matches.add(new MatchIpv4Source(internalIp, "32"));
228
229         matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(segmentId), MetaDataUtil.METADATA_MASK_VRFID));
230
231         List<ActionInfo> actionsInfos = new ArrayList<>();
232         actionsInfos.add(new ActionSetSourceIp(externalIp, "32"));
233
234         List<InstructionInfo> instructions = new ArrayList<>();
235         instructions.add(
236                 new InstructionWriteMetadata(MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
237         instructions.add(new InstructionApplyActions(actionsInfos));
238         instructions.add(new InstructionGotoTable(NwConstants.SNAT_TABLE));
239
240         String flowRef = NatUtil.getFlowRef(dpId, NwConstants.PSNAT_TABLE, routerId, internalIp);
241
242         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
243                 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
244                 NwConstants.COOKIE_DNAT_TABLE, matches, instructions);
245
246         return flowEntity;
247     }
248
249     private FlowEntity buildSNATFlowEntity(BigInteger dpId, InternalToExternalPortMap mapping, long vpnId, Uuid
250             externalNetworkId) {
251         String internalIp = mapping.getInternalIp();
252         LOG.debug("buildSNATFlowEntity : Building SNAT Flow entity for ip {} ", internalIp);
253
254         ProviderTypes provType = NatUtil.getProviderTypefromNetworkId(dataBroker, externalNetworkId);
255         if (provType == null) {
256             LOG.error("buildSNATFlowEntity : Unable to get Network Provider Type for network {}", externalNetworkId);
257             return null;
258         }
259
260         List<MatchInfo> matches = new ArrayList<>();
261         matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
262         matches.add(MatchEthernetType.IPV4);
263         String externalIp = mapping.getExternalIp();
264         matches.add(new MatchIpv4Source(externalIp, "32"));
265
266         List<ActionInfo> actionsInfo = new ArrayList<>();
267         Uuid floatingIpId = mapping.getExternalId();
268         String macAddress = NatUtil.getFloatingIpPortMacFromFloatingIpId(dataBroker, floatingIpId);
269         if (macAddress != null) {
270             actionsInfo.add(new ActionSetFieldEthernetSource(new MacAddress(macAddress)));
271         } else {
272             LOG.warn("buildSNATFlowEntity : No MAC address found for floating IP {}", externalIp);
273         }
274
275         LOG.trace("buildSNATFlowEntity : External Network Provider Type is {}, resubmit to FIB", provType.toString());
276         actionsInfo.add(new ActionNxResubmit(NwConstants.L3_FIB_TABLE));
277         List<InstructionInfo> instructions = new ArrayList<>();
278         instructions.add(new InstructionApplyActions(actionsInfo));
279         String flowRef = NatUtil.getFlowRef(dpId, NwConstants.SNAT_TABLE, vpnId, externalIp);
280
281         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.SNAT_TABLE, flowRef,
282                 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
283                 NwConstants.COOKIE_DNAT_TABLE, matches, instructions);
284
285         return flowEntity;
286
287     }
288
289     private void createDNATTblEntry(BigInteger dpnId, InternalToExternalPortMap mapping, long routerId,
290                                     long associatedVpnId, TypedReadWriteTransaction<Configuration> confTx) {
291         FlowEntity preFlowEntity = buildPreDNATFlowEntity(dpnId, mapping, routerId, associatedVpnId);
292         if (preFlowEntity == null) {
293             LOG.error("createDNATTblEntry : Flow entity received as NULL. "
294                     + "Cannot proceed with installation of Pre-DNAT flow table {} --> table {} on DpnId {}",
295                     NwConstants.PDNAT_TABLE, NwConstants.DNAT_TABLE, dpnId);
296         } else {
297             mdsalManager.addFlow(confTx, preFlowEntity);
298             FlowEntity flowEntity = buildDNATFlowEntity(dpnId, mapping, routerId, associatedVpnId);
299             if (flowEntity != null) {
300                 mdsalManager.addFlow(confTx, flowEntity);
301             }
302         }
303     }
304
305     // TODO skitt Fix the exception handling here
306     @SuppressWarnings("checkstyle:IllegalCatch")
307     @SuppressFBWarnings("REC_CATCH_EXCEPTION")
308     private void removeDNATTblEntry(BigInteger dpnId, String internalIp, String externalIp, long routerId,
309             TypedReadWriteTransaction<Configuration> confTx) {
310         try {
311             FlowEntity preFlowEntity = buildPreDNATDeleteFlowEntity(dpnId, externalIp, routerId);
312             mdsalManager.removeFlow(confTx, preFlowEntity);
313
314             FlowEntity flowEntity = buildDNATDeleteFlowEntity(dpnId, internalIp, routerId);
315             if (flowEntity != null) {
316                 mdsalManager.removeFlow(confTx, flowEntity);
317             }
318         } catch (Exception e) {
319             LOG.error("Error removing flow", e);
320             throw new RuntimeException("Error removing flow", e);
321         }
322     }
323
324     private void createSNATTblEntry(BigInteger dpnId, InternalToExternalPortMap mapping, long vpnId, long routerId,
325         long associatedVpnId, Uuid externalNetworkId, TypedReadWriteTransaction<Configuration> confTx) {
326         FlowEntity preFlowEntity = buildPreSNATFlowEntity(dpnId, mapping.getInternalIp(), mapping.getExternalIp(),
327             vpnId, routerId, associatedVpnId);
328         mdsalManager.addFlow(confTx, preFlowEntity);
329
330         FlowEntity flowEntity = buildSNATFlowEntity(dpnId, mapping, vpnId, externalNetworkId);
331         if (flowEntity != null) {
332             mdsalManager.addFlow(confTx, flowEntity);
333         }
334     }
335
336     // TODO skitt Fix the exception handling here
337     @SuppressWarnings("checkstyle:IllegalCatch")
338     @SuppressFBWarnings("REC_CATCH_EXCEPTION")
339     private void removeSNATTblEntry(BigInteger dpnId, String internalIp, String externalIp, long routerId, long vpnId,
340                                     TypedReadWriteTransaction<Configuration> removeFlowInvTx) {
341         try {
342             FlowEntity preFlowEntity = buildPreSNATDeleteFlowEntity(dpnId, internalIp, routerId);
343             mdsalManager.removeFlow(removeFlowInvTx, preFlowEntity);
344
345             FlowEntity flowEntity = buildSNATDeleteFlowEntity(dpnId, externalIp, vpnId);
346             if (flowEntity != null) {
347                 mdsalManager.removeFlow(removeFlowInvTx, flowEntity);
348             }
349         } catch (Exception e) {
350             LOG.error("Error removing flow", e);
351             throw new RuntimeException("Error removing flow", e);
352         }
353     }
354
355     private Uuid getExtNetworkId(final InstanceIdentifier<RouterPorts> portIid,
356                                  LogicalDatastoreType dataStoreType) {
357         Optional<RouterPorts> rtrPort =
358                 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
359                         dataStoreType, portIid);
360         if (!rtrPort.isPresent()) {
361             LOG.error("getExtNetworkId : Unable to read router port entry for {}", portIid);
362             return null;
363         }
364
365         Uuid extNwId = rtrPort.get().getExternalNetworkId();
366         return extNwId;
367     }
368
369     private long getVpnId(Uuid extNwId, Uuid floatingIpExternalId) {
370         Uuid subnetId = NatUtil.getFloatingIpPortSubnetIdFromFloatingIpId(dataBroker, floatingIpExternalId);
371         if (subnetId != null) {
372             long vpnId = NatUtil.getVpnId(dataBroker, subnetId.getValue());
373             if (vpnId != NatConstants.INVALID_ID) {
374                 LOG.debug("getVpnId : Got vpnId {} for floatingIpExternalId {}", vpnId, floatingIpExternalId);
375                 return vpnId;
376             }
377         }
378
379         InstanceIdentifier<Networks> nwId = InstanceIdentifier.builder(ExternalNetworks.class).child(Networks.class,
380                 new NetworksKey(extNwId)).build();
381         Optional<Networks> nw =
382                 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
383                         LogicalDatastoreType.CONFIGURATION, nwId);
384         if (!nw.isPresent()) {
385             LOG.error("getVpnId : Unable to read external network for {}", extNwId);
386             return NatConstants.INVALID_ID;
387         }
388
389         Uuid vpnUuid = nw.get().getVpnid();
390         if (vpnUuid == null) {
391             LOG.error("getVpnId : Unable to read vpn from External network: {}", extNwId);
392             return NatConstants.INVALID_ID;
393         }
394
395         //Get the id using the VPN UUID (also vpn instance name)
396         return NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
397     }
398
399     private void processFloatingIPAdd(final InstanceIdentifier<InternalToExternalPortMap> identifier,
400                                       final InternalToExternalPortMap mapping) {
401         LOG.trace("processFloatingIPAdd key: {}, value: {}", mapping.key(), mapping);
402
403         final String routerId = identifier.firstKeyOf(RouterPorts.class).getRouterId();
404         final PortsKey pKey = identifier.firstKeyOf(Ports.class);
405         String interfaceName = pKey.getPortName();
406
407         InstanceIdentifier<RouterPorts> portIid = identifier.firstIdentifierOf(RouterPorts.class);
408         coordinator.enqueueJob(NatConstants.NAT_DJC_PREFIX + mapping.key(), () -> Collections.singletonList(
409                 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
410                     tx -> createNATFlowEntries(interfaceName, mapping, portIid, routerId, tx))),
411                 NatConstants.NAT_DJC_MAX_RETRIES);
412     }
413
414     private void processFloatingIPDel(final InstanceIdentifier<InternalToExternalPortMap> identifier,
415                                       final InternalToExternalPortMap mapping) {
416         LOG.trace("processFloatingIPDel : key: {}, value: {}", mapping.key(), mapping);
417
418         final String routerId = identifier.firstKeyOf(RouterPorts.class).getRouterId();
419         final PortsKey pKey = identifier.firstKeyOf(Ports.class);
420         String interfaceName = pKey.getPortName();
421
422         InstanceIdentifier<RouterPorts> portIid = identifier.firstIdentifierOf(RouterPorts.class);
423         coordinator.enqueueJob(NatConstants.NAT_DJC_PREFIX + mapping.key(), () -> Collections.singletonList(
424                 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
425                     tx -> removeNATFlowEntries(interfaceName, mapping, portIid, routerId, null, tx))),
426                 NatConstants.NAT_DJC_MAX_RETRIES);
427     }
428
429     private InetAddress getInetAddress(String ipAddr) {
430         InetAddress ipAddress = null;
431         try {
432             ipAddress = InetAddress.getByName(ipAddr);
433         } catch (UnknownHostException e) {
434             LOG.error("getInetAddress : UnknowHostException for ip {}", ipAddr, e);
435         }
436         return ipAddress;
437     }
438
439     private boolean validateIpMapping(InternalToExternalPortMap mapping) {
440         return getInetAddress(mapping.getInternalIp()) != null && getInetAddress(mapping.getExternalIp()) != null;
441     }
442
443     void createNATFlowEntries(String interfaceName, final InternalToExternalPortMap mapping,
444                               final InstanceIdentifier<RouterPorts> portIid, final String routerName,
445         TypedReadWriteTransaction<Configuration> confTx) {
446         if (!validateIpMapping(mapping)) {
447             LOG.error("createNATFlowEntries : Not a valid ip addresses in the mapping {}", mapping);
448             return;
449         }
450
451         //Get the DPN on which this interface resides
452         BigInteger dpnId = NatUtil.getDpnForInterface(interfaceManager, interfaceName);
453
454         if (dpnId.equals(BigInteger.ZERO)) {
455             LOG.warn("createNATFlowEntries : No DPN for interface {}. NAT flow entries for ip mapping {} will "
456                 + "not be installed", interfaceName, mapping);
457             return;
458         }
459
460         long routerId = NatUtil.getVpnId(dataBroker, routerName);
461         if (routerId == NatConstants.INVALID_ID) {
462             LOG.error("createNATFlowEntries : Could not retrieve router id for {} to create NAT Flow entries",
463                     routerName);
464             return;
465         }
466         //Check if the router to vpn association is present
467         //long associatedVpnId = NatUtil.getAssociatedVpn(dataBroker, routerName);
468         Uuid associatedVpn = NatUtil.getVpnForRouter(dataBroker, routerName);
469         long associatedVpnId = NatConstants.INVALID_ID;
470         if (associatedVpn == null) {
471             LOG.debug("createNATFlowEntries : Router {} is not assicated with any BGP VPN instance", routerName);
472         } else {
473             LOG.debug("createNATFlowEntries : Router {} is associated with VPN Instance with Id {}",
474                     routerName, associatedVpn);
475             associatedVpnId = NatUtil.getVpnId(dataBroker, associatedVpn.getValue());
476             LOG.debug("createNATFlowEntries : vpninstance Id is {} for VPN {}", associatedVpnId, associatedVpn);
477             //routerId = associatedVpnId;
478         }
479
480         Uuid extNwId = getExtNetworkId(portIid, LogicalDatastoreType.CONFIGURATION);
481         if (extNwId == null) {
482             LOG.error("createNATFlowEntries : External network associated with interface {} could not be retrieved",
483                 interfaceName);
484             return;
485         }
486         long vpnId = getVpnId(extNwId, mapping.getExternalId());
487         if (vpnId < 0) {
488             LOG.error("createNATFlowEntries : No VPN associated with Ext nw {}. Unable to create SNAT table entry "
489                     + "for fixed ip {}", extNwId, mapping.getInternalIp());
490             return;
491         }
492         //Install the DNAT default FIB flow L3_FIB_TABLE (21) -> PSNAT_TABLE (26) if SNAT is disabled
493         boolean isSnatEnabled = NatUtil.isSnatEnabledForRouterId(dataBroker, routerName);
494         if (!isSnatEnabled) {
495             addOrDelDefaultFibRouteForDnat(dpnId, routerName, routerId, confTx, true);
496         }
497         //Create the DNAT and SNAT table entries
498         createDNATTblEntry(dpnId, mapping, routerId, associatedVpnId, confTx);
499         createSNATTblEntry(dpnId, mapping, vpnId, routerId, associatedVpnId, extNwId, confTx);
500         floatingIPHandler.onAddFloatingIp(dpnId, routerName, routerId, extNwId, interfaceName, mapping, confTx);
501     }
502
503     void createNATFlowEntries(BigInteger dpnId,  String interfaceName, String routerName, Uuid externalNetworkId,
504                               InternalToExternalPortMap mapping, TypedReadWriteTransaction<Configuration> confTx) {
505         String internalIp = mapping.getInternalIp();
506         long routerId = NatUtil.getVpnId(dataBroker, routerName);
507         if (routerId == NatConstants.INVALID_ID) {
508             LOG.error("createNATFlowEntries : Could not retrieve router id for {} to create NAT Flow entries",
509                     routerName);
510             return;
511         }
512         //Check if the router to vpn association is present
513         long associatedVpnId = NatUtil.getAssociatedVpn(dataBroker, routerName);
514         if (associatedVpnId == NatConstants.INVALID_ID) {
515             LOG.debug("createNATFlowEntries : Router {} is not assicated with any BGP VPN instance", routerName);
516         } else {
517             LOG.debug("createNATFlowEntries : Router {} is associated with VPN Instance with Id {}",
518                 routerName, associatedVpnId);
519             //routerId = associatedVpnId;
520         }
521
522         long vpnId = getVpnId(externalNetworkId, mapping.getExternalId());
523         if (vpnId < 0) {
524             LOG.error("createNATFlowEntries : Unable to create SNAT table entry for fixed ip {}", internalIp);
525             return;
526         }
527         //Install the DNAT default FIB flow L3_FIB_TABLE (21) -> PSNAT_TABLE (26) if SNAT is disabled
528         boolean isSnatEnabled = NatUtil.isSnatEnabledForRouterId(dataBroker, routerName);
529         if (!isSnatEnabled) {
530             addOrDelDefaultFibRouteForDnat(dpnId, routerName, routerId, confTx, true);
531         }
532         //Create the DNAT and SNAT table entries
533         createDNATTblEntry(dpnId, mapping, routerId, associatedVpnId, confTx);
534         createSNATTblEntry(dpnId, mapping, vpnId, routerId, associatedVpnId, externalNetworkId, confTx);
535         floatingIPHandler.onAddFloatingIp(dpnId, routerName, routerId, externalNetworkId, interfaceName, mapping,
536                 confTx);
537     }
538
539     void createNATOnlyFlowEntries(BigInteger dpnId, String routerName, String associatedVPN,
540                                   Uuid externalNetworkId, InternalToExternalPortMap mapping) {
541         String internalIp = mapping.getInternalIp();
542         //String segmentId = associatedVPN == null ? routerName : associatedVPN;
543         LOG.debug("createNATOnlyFlowEntries : Retrieving vpn id for VPN {} to proceed with create NAT Flows",
544                 routerName);
545         long routerId = NatUtil.getVpnId(dataBroker, routerName);
546         if (routerId == NatConstants.INVALID_ID) {
547             LOG.error("createNATOnlyFlowEntries : Could not retrieve vpn id for {} to create NAT Flow entries",
548                     routerName);
549             return;
550         }
551         long associatedVpnId = NatUtil.getVpnId(dataBroker, associatedVPN);
552         LOG.debug("createNATOnlyFlowEntries : Associated VPN Id {} for router {}", associatedVpnId, routerName);
553         long vpnId = getVpnId(externalNetworkId, mapping.getExternalId());
554         if (vpnId < 0) {
555             LOG.error("createNATOnlyFlowEntries : Unable to create SNAT table entry for fixed ip {}", internalIp);
556             return;
557         }
558         //Install the DNAT default FIB flow L3_FIB_TABLE (21) -> PSNAT_TABLE (26) if SNAT is disabled
559         boolean isSnatEnabled = NatUtil.isSnatEnabledForRouterId(dataBroker, routerName);
560         if (!isSnatEnabled) {
561             addOrDelDefaultFibRouteForDnat(dpnId, routerName, routerId, null, true);
562         }
563         //Create the DNAT and SNAT table entries
564         FlowEntity preFlowEntity = buildPreDNATFlowEntity(dpnId, mapping, routerId, associatedVpnId);
565         mdsalManager.installFlow(preFlowEntity);
566
567         FlowEntity flowEntity = buildDNATFlowEntity(dpnId, mapping, routerId, associatedVpnId);
568         mdsalManager.installFlow(flowEntity);
569
570         String externalIp = mapping.getExternalIp();
571         preFlowEntity = buildPreSNATFlowEntity(dpnId, internalIp, externalIp, vpnId , routerId, associatedVpnId);
572         mdsalManager.installFlow(preFlowEntity);
573
574         flowEntity = buildSNATFlowEntity(dpnId, mapping, vpnId, externalNetworkId);
575         if (flowEntity != null) {
576             mdsalManager.installFlow(flowEntity);
577         }
578
579     }
580
581     void removeNATFlowEntries(String interfaceName, final InternalToExternalPortMap mapping,
582                               InstanceIdentifier<RouterPorts> portIid, final String routerName, BigInteger dpnId,
583                               TypedReadWriteTransaction<Configuration> removeFlowInvTx) {
584         String internalIp = mapping.getInternalIp();
585         String externalIp = mapping.getExternalIp();
586         //Get the DPN on which this interface resides
587         if (dpnId == null) {
588             dpnId = NatUtil.getDpnForInterface(interfaceManager, interfaceName);
589             if (dpnId.equals(BigInteger.ZERO)) {
590                 LOG.warn("removeNATFlowEntries: Abort processing Floating ip configuration. No DPN for port: {}",
591                         interfaceName);
592                 return;
593             }
594         }
595
596         long routerId = NatUtil.getVpnId(dataBroker, routerName);
597         if (routerId == NatConstants.INVALID_ID) {
598             LOG.error("removeNATFlowEntries : Could not retrieve router id for {} to remove NAT Flow entries",
599                     routerName);
600             return;
601         }
602
603         //Delete the DNAT and SNAT table entries
604         removeDNATTblEntry(dpnId, internalIp, externalIp, routerId, removeFlowInvTx);
605
606         Uuid extNwId = getExtNetworkId(portIid, LogicalDatastoreType.OPERATIONAL);
607         if (extNwId == null) {
608             LOG.error("removeNATFlowEntries : External network associated with interface {} could not be retrieved",
609                 interfaceName);
610             return;
611         }
612         long vpnId = getVpnId(extNwId, mapping.getExternalId());
613         if (vpnId < 0) {
614             LOG.error("removeNATFlowEntries : No VPN associated with ext nw {}. Unable to delete SNAT table "
615                 + "entry for fixed ip {}", extNwId, internalIp);
616             return;
617         }
618         removeSNATTblEntry(dpnId, internalIp, externalIp, routerId, vpnId, removeFlowInvTx);
619         //Remove the DNAT default FIB flow L3_FIB_TABLE (21) -> PSNAT_TABLE (26) if SNAT is disabled
620         boolean isSnatEnabled = NatUtil.isSnatEnabledForRouterId(dataBroker, routerName);
621         if (!isSnatEnabled) {
622             addOrDelDefaultFibRouteForDnat(dpnId, routerName, routerId, removeFlowInvTx, false);
623         }
624         ProviderTypes provType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker, routerName, extNwId);
625         if (provType == null) {
626             LOG.error("removeNATFlowEntries : External Network Provider Type missing");
627             return;
628         }
629         if (provType == ProviderTypes.VXLAN) {
630             floatingIPHandler.onRemoveFloatingIp(dpnId, routerName, routerId, extNwId, mapping,
631                     NatConstants.DEFAULT_L3VNI_VALUE, removeFlowInvTx);
632             removeOperationalDS(routerName, interfaceName, internalIp);
633             return;
634         }
635         long label = getOperationalIpMapping(routerName, interfaceName, internalIp);
636         if (label < 0) {
637             LOG.error("removeNATFlowEntries : Could not retrieve label for prefix {} in router {}",
638                     internalIp, routerId);
639             return;
640         }
641         floatingIPHandler.onRemoveFloatingIp(dpnId, routerName, routerId, extNwId, mapping, (int) label,
642                 removeFlowInvTx);
643         removeOperationalDS(routerName, interfaceName, internalIp);
644     }
645
646     void removeNATFlowEntries(BigInteger dpnId, String interfaceName, String vpnName, String routerName,
647                               InternalToExternalPortMap mapping, TypedReadWriteTransaction<Configuration> confTx) {
648         String internalIp = mapping.getInternalIp();
649         String externalIp = mapping.getExternalIp();
650         long routerId = NatUtil.getVpnId(dataBroker, routerName);
651         if (routerId == NatConstants.INVALID_ID) {
652             LOG.error("removeNATFlowEntries : Could not retrieve router id for {} to remove NAT Flow entries",
653                     routerName);
654             return;
655         }
656
657         long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
658         if (vpnId == NatConstants.INVALID_ID) {
659             LOG.warn("removeNATFlowEntries : VPN Id not found for {} to remove NAT flow entries {}",
660                     vpnName, internalIp);
661         }
662
663         //Delete the DNAT and SNAT table entries
664         removeDNATTblEntry(dpnId, internalIp, externalIp, routerId, confTx);
665         removeSNATTblEntry(dpnId, internalIp, externalIp, routerId, vpnId, confTx);
666         //Remove the DNAT default FIB flow L3_FIB_TABLE (21) -> PSNAT_TABLE (26) if SNAT is disabled
667         boolean isSnatEnabled = NatUtil.isSnatEnabledForRouterId(dataBroker, routerName);
668         if (!isSnatEnabled) {
669             addOrDelDefaultFibRouteForDnat(dpnId, routerName, routerId, confTx, false);
670         }
671         Uuid externalNetworkId = NatUtil.getNetworkIdFromRouterName(dataBroker,routerName);
672         ProviderTypes provType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker, routerName, externalNetworkId);
673         if (provType == null) {
674             LOG.error("removeNATFlowEntries : External Network Provider Type Missing");
675             return;
676         }
677         if (provType == ProviderTypes.VXLAN) {
678             floatingIPHandler.cleanupFibEntries(dpnId, vpnName, externalIp, NatConstants.DEFAULT_L3VNI_VALUE,
679                     confTx, provType);
680             removeOperationalDS(routerName, interfaceName, internalIp);
681             return;
682         }
683         long label = getOperationalIpMapping(routerName, interfaceName, internalIp);
684         if (label < 0) {
685             LOG.error("removeNATFlowEntries : Could not retrieve label for prefix {} in router {}",
686                     internalIp, routerId);
687             return;
688         }
689         floatingIPHandler.cleanupFibEntries(dpnId, vpnName, externalIp, label, confTx, provType);
690         removeOperationalDS(routerName, interfaceName, internalIp);
691     }
692
693     protected long getOperationalIpMapping(String routerId, String interfaceName, String internalIp) {
694         InstanceIdentifier<InternalToExternalPortMap> intExtPortMapIdentifier =
695             NatUtil.getIntExtPortMapIdentifier(routerId, interfaceName, internalIp);
696         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
697                 LogicalDatastoreType.OPERATIONAL, intExtPortMapIdentifier).toJavaUtil().map(
698                 InternalToExternalPortMap::getLabel).orElse(NatConstants.INVALID_ID);
699     }
700
701     static void updateOperationalDS(DataBroker dataBroker, String routerId, String interfaceName, long label,
702                                     String internalIp, String externalIp) {
703
704         LOG.info("updateOperationalDS : Updating operational DS for floating ip config : {} with label {}",
705                 internalIp, label);
706         InstanceIdentifier<Ports> portsId = NatUtil.getPortsIdentifier(routerId, interfaceName);
707         Optional<Ports> optPorts =
708                 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
709                         LogicalDatastoreType.OPERATIONAL, portsId);
710         InternalToExternalPortMap intExtPortMap = new InternalToExternalPortMapBuilder().withKey(new
711                 InternalToExternalPortMapKey(internalIp)).setInternalIp(internalIp).setExternalIp(externalIp)
712                 .setLabel(label).build();
713         if (optPorts.isPresent()) {
714             LOG.debug("updateOperationalDS : Ports {} entry already present. Updating intExtPortMap for internal ip {}",
715                     interfaceName, internalIp);
716             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, portsId.child(InternalToExternalPortMap
717                     .class, new InternalToExternalPortMapKey(internalIp)), intExtPortMap);
718         } else {
719             LOG.debug("updateOperationalDS : Adding Ports entry {} along with intExtPortMap {}",
720                     interfaceName, internalIp);
721             List<InternalToExternalPortMap> intExtPortMapList = new ArrayList<>();
722             intExtPortMapList.add(intExtPortMap);
723             Ports ports = new PortsBuilder().withKey(new PortsKey(interfaceName)).setPortName(interfaceName)
724                     .setInternalToExternalPortMap(intExtPortMapList).build();
725             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, portsId, ports);
726         }
727     }
728
729     void removeOperationalDS(String routerId, String interfaceName, String internalIp) {
730         LOG.info("removeOperationalDS : Remove operational DS for floating ip config: {}", internalIp);
731         InstanceIdentifier<InternalToExternalPortMap> intExtPortMapId = NatUtil.getIntExtPortMapIdentifier(routerId,
732                 interfaceName, internalIp);
733         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, intExtPortMapId);
734     }
735
736     private FlowEntity buildPreDNATDeleteFlowEntity(BigInteger dpId, String externalIp, long routerId) {
737
738         LOG.info("buildPreDNATDeleteFlowEntity : Bulding Delete DNAT Flow entity for ip {} ", externalIp);
739
740         String flowRef = NatUtil.getFlowRef(dpId, NwConstants.PDNAT_TABLE, routerId, externalIp);
741
742         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PDNAT_TABLE, flowRef,
743                 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
744                 NwConstants.COOKIE_DNAT_TABLE, null, null);
745
746         return flowEntity;
747     }
748
749
750
751     private FlowEntity buildDNATDeleteFlowEntity(BigInteger dpId, String internalIp, long routerId) {
752
753         LOG.info("buildDNATDeleteFlowEntity : Bulding Delete DNAT Flow entity for ip {} ", internalIp);
754
755         String flowRef = NatUtil.getFlowRef(dpId, NwConstants.DNAT_TABLE, routerId, internalIp);
756
757         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.DNAT_TABLE, flowRef,
758                 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
759                 NwConstants.COOKIE_DNAT_TABLE, null, null);
760
761         return flowEntity;
762
763     }
764
765     private FlowEntity buildPreSNATDeleteFlowEntity(BigInteger dpId, String internalIp, long routerId) {
766
767         LOG.info("buildPreSNATDeleteFlowEntity : Building Delete PSNAT Flow entity for ip {} ", internalIp);
768
769         String flowRef = NatUtil.getFlowRef(dpId, NwConstants.PSNAT_TABLE, routerId, internalIp);
770
771         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
772                 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
773                 NwConstants.COOKIE_DNAT_TABLE, null, null);
774         return flowEntity;
775     }
776
777     private FlowEntity buildSNATDeleteFlowEntity(BigInteger dpId, String externalIp, long routerId) {
778
779         LOG.info("buildSNATDeleteFlowEntity : Building Delete SNAT Flow entity for ip {} ", externalIp);
780
781         String flowRef = NatUtil.getFlowRef(dpId, NwConstants.SNAT_TABLE, routerId, externalIp);
782
783         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.SNAT_TABLE, flowRef,
784                 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
785                 NwConstants.COOKIE_DNAT_TABLE, null, null);
786
787         return flowEntity;
788     }
789
790     private void addOrDelDefaultFibRouteForDnat(BigInteger dpnId, String routerName, long routerId,
791         TypedReadWriteTransaction<Configuration> confTx, boolean create) {
792         if (confTx == null) {
793             ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
794                 newTx -> addOrDelDefaultFibRouteForDnat(dpnId, routerName, routerId, newTx, create)), LOG,
795                 "Error handling default FIB route for DNAT");
796             return;
797         }
798         //Check if the router to bgp-vpn association is present
799         long associatedVpnId = NatConstants.INVALID_ID;
800         Uuid associatedVpn = NatUtil.getVpnForRouter(dataBroker, routerName);
801         if (associatedVpn != null) {
802             associatedVpnId = NatUtil.getVpnId(dataBroker, associatedVpn.getValue());
803         }
804         if (create) {
805             if (associatedVpnId != NatConstants.INVALID_ID) {
806                 LOG.debug("addOrDelDefaultFibRouteForDnat: Install NAT default route on DPN {} for the router {} with "
807                         + "vpn-id {}", dpnId, routerName, associatedVpnId);
808                 defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, associatedVpnId, routerId, confTx);
809             } else {
810                 LOG.debug("addOrDelDefaultFibRouteForDnat: Install NAT default route on DPN {} for the router {} with "
811                         + "vpn-id {}", dpnId, routerName, routerId);
812                 defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, routerId, confTx);
813             }
814         } else {
815             if (associatedVpnId != NatConstants.INVALID_ID) {
816                 LOG.debug("addOrDelDefaultFibRouteForDnat: Remove NAT default route on DPN {} for the router {} "
817                         + "with vpn-id {}", dpnId, routerName, associatedVpnId);
818                 defaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, associatedVpnId, routerId, confTx);
819             } else {
820                 LOG.debug("addOrDelDefaultFibRouteForDnat: Remove NAT default route on DPN {} for the router {} "
821                         + "with vpn-id {}", dpnId, routerName, routerId);
822                 defaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, routerId, confTx);
823             }
824         }
825     }
826 }