Scale-in/Scale-out for NAT.
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / NatTepChangeListener.java
1 /*
2  * Copyright (c) 2019 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 com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import com.google.common.util.concurrent.MoreExecutors;
17
18 import java.math.BigInteger;
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.concurrent.ExecutionException;
22 import javax.annotation.PostConstruct;
23 import javax.inject.Inject;
24 import javax.inject.Singleton;
25
26 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
29 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
30 import org.opendaylight.genius.infra.Datastore.Configuration;
31 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
32 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
33 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
34 import org.opendaylight.genius.mdsalutil.MDSALUtil;
35 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
36 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
37 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
38 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
39 import org.opendaylight.netvirt.natservice.api.SnatServiceManager;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.DpnEndpoints;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.DPNTEPsInfo;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.TunnelEndPoints;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryOutput;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpn.routers.DpnRoutersList;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpn.routers.dpn.routers.list.RoutersList;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig.NatMode;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
59 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
60 import org.opendaylight.yangtools.yang.common.RpcResult;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64 @Singleton
65 public class NatTepChangeListener extends
66     AsyncDataTreeChangeListenerBase<TunnelEndPoints, NatTepChangeListener> {
67
68     private static final Logger LOG = LoggerFactory
69         .getLogger(NatTepChangeListener.class);
70     private final DataBroker dataBroker;
71     private SNATDefaultRouteProgrammer defaultRouteProgrammer;
72     private OdlInterfaceRpcService interfaceService;
73     private IdManagerService idManager;
74     private IFibManager fibManager;
75     private IBgpManager bgpManager;
76     private IMdsalApiManager mdsalManager;
77     private FloatingIPListener floatingIPListener;
78     private FibRpcService fibRpcService;
79     private NaptSwitchHA naptSwitchHA;
80     private final NatMode natMode;
81     private final SnatServiceManager natServiceManager;
82     private final JobCoordinator coordinator;
83     private final ManagedNewTransactionRunner txRunner;
84     private final NatOverVxlanUtil natOverVxlanUtil;
85
86     @Inject
87     public NatTepChangeListener(final DataBroker dataBroker,
88         final SNATDefaultRouteProgrammer defaultRouteProgrammer,
89         final OdlInterfaceRpcService interfaceService,
90         final IdManagerService idManager, final IFibManager fibManager,
91         final IBgpManager bgpManager,
92         final FloatingIPListener floatingIPListener,
93         final FibRpcService fibRpcService,
94         final IMdsalApiManager mdsalManager,
95         final NaptSwitchHA naptSwitchHA,
96         final NatserviceConfig config,
97         final SnatServiceManager natServiceManager,
98         final JobCoordinator coordinator,
99         final NatOverVxlanUtil natOverVxlanUtil) {
100         super(TunnelEndPoints.class, NatTepChangeListener.class);
101         this.dataBroker = dataBroker;
102         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
103         this.defaultRouteProgrammer = defaultRouteProgrammer;
104         this.interfaceService = interfaceService;
105         this.idManager = idManager;
106         this.fibManager = fibManager;
107         this.bgpManager = bgpManager;
108         this.floatingIPListener = floatingIPListener;
109         this.fibRpcService = fibRpcService;
110         this.naptSwitchHA = naptSwitchHA;
111         this.mdsalManager = mdsalManager;
112         this.coordinator = coordinator;
113         this.natServiceManager = natServiceManager;
114         this.natOverVxlanUtil = natOverVxlanUtil;
115         if (config != null) {
116             this.natMode = config.getNatMode();
117         } else {
118             this.natMode = NatMode.Controller;
119         }
120     }
121
122     @Override
123     @PostConstruct
124     public void init() {
125         LOG.info("{} init", getClass().getSimpleName());
126         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
127     }
128
129     @Override
130     protected InstanceIdentifier<TunnelEndPoints> getWildCardPath() {
131         return InstanceIdentifier.builder(DpnEndpoints.class)
132             .child(DPNTEPsInfo.class).child(TunnelEndPoints.class).build();
133     }
134
135     @Override
136     protected void remove(InstanceIdentifier<TunnelEndPoints> key,
137         TunnelEndPoints tep) {
138         /*
139          * Whenever the TEP on a given DPNID is removed, this API take care
140          * of withdrawing the FIB entries for those Floating-IP existing on this
141          * DPN and perform re-election of NAPT Switch for a VRF to which the current
142          * DPN is elected as NAPT Switch.
143          */
144         BigInteger srcDpnId = key.firstIdentifierOf(DPNTEPsInfo.class)
145             .firstKeyOf(DPNTEPsInfo.class).getDPNID();
146         final String srcTepIp = tep.getIpAddress().stringValue();
147         String tunnelType = tep.getTunnelType().getName();
148         LOG.debug(
149             "NAT Service : Remove Event triggered for Tep on DPN:{} having IP:{} and tunnelType:{}",
150             srcDpnId, srcTepIp, tunnelType);
151         handleTepDelForAllRtrs(srcDpnId, srcTepIp);
152     }
153
154     @Override
155     protected void update(InstanceIdentifier<TunnelEndPoints> key,
156         TunnelEndPoints origTep, TunnelEndPoints updatedTep) {
157         // Will be handled in NatTunnelInterfaceStateListener.add()
158         LOG.debug("NO ACTION duing update event : {}", updatedTep.key());
159     }
160
161     @Override
162     protected void add(InstanceIdentifier<TunnelEndPoints> key,
163         TunnelEndPoints tep) {
164         LOG.debug("NO ACTION duing add event : {}", tep.key());
165     }
166
167     @SuppressWarnings("checkstyle:IllegalCatch")
168     private void handleTepDelForAllRtrs(BigInteger srcDpnId, String srcTepIp) {
169         LOG.trace("handleTepDelForAllRtrs : TEP DEL ----- on DPN-ID {} having SRC IP : {}",
170             srcDpnId,
171             srcTepIp);
172
173         List<RoutersList> routersList = null;
174         InstanceIdentifier<DpnRoutersList> dpnRoutersListId = NatUtil.getDpnRoutersId(srcDpnId);
175         Optional<DpnRoutersList> optionalRouterDpnList =
176             SingleTransactionDataBroker
177                 .syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
178                     LogicalDatastoreType.OPERATIONAL, dpnRoutersListId);
179         if (optionalRouterDpnList.isPresent()) {
180             routersList = optionalRouterDpnList.get().getRoutersList();
181         } else {
182             LOG.debug(
183                 "NAT Service : RouterDpnList is empty for DPN {}. Hence ignoring TEP DEL event",
184                 srcDpnId);
185             return;
186         }
187
188         if (routersList == null) {
189             LOG.error("handleTepDelForAllRtrs : DPN {} does not have the Routers presence",
190                 srcDpnId);
191             return;
192         }
193
194         for (RoutersList router : routersList) {
195             String routerName = router.getRouter();
196             LOG.debug(
197                 "handleTepDelForAllRtrs :  TEP DEL : DNAT -> Withdrawing routes for router {} ",
198                 routerName);
199             long routerId = NatUtil.getVpnId(dataBroker, routerName);
200             if (routerId == NatConstants.INVALID_ID) {
201                 LOG.error("handleTepDelForAllRtrs :Invalid ROUTER-ID {} returned for routerName {}",
202                     routerId, routerName);
203                 return;
204             }
205             Uuid externalNetworkId = NatUtil.getNetworkIdFromRouterName(dataBroker, routerName);
206             ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker,
207                 routerName, externalNetworkId);
208             if (extNwProvType == null) {
209                 return;
210             }
211             boolean isFipExists = hndlTepDelForDnatInEachRtr(router, routerId, srcDpnId,
212                 extNwProvType);
213             LOG.debug(
214                 "handleTepDelForAllRtrs :  TEP DEL : SNAT -> Withdrawing and Advertising routes for router {} ",
215                 router.getRouter());
216             coordinator.enqueueJob((NatConstants.NAT_DJC_PREFIX + router.getRouter()), () -> {
217                 List<ListenableFuture<Void>> futures = new ArrayList<>();
218                 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, configTx -> {
219                     hndlTepDelForSnatInEachRtr(router, routerId, srcDpnId, srcTepIp, isFipExists,
220                         extNwProvType, configTx);
221                 });
222
223                 return futures;
224             }, NatConstants.NAT_DJC_MAX_RETRIES);
225         }
226         return;
227     }
228
229     private boolean hndlTepDelForDnatInEachRtr(RoutersList router, long routerId,
230         BigInteger tepDeletedDpnId,
231         ProviderTypes extNwProvType) {
232         //DNAT : Withdraw the routes from the BGP
233         String routerName = router.getRouter();
234         Boolean isFipExists = Boolean.FALSE;
235
236         LOG.debug("hndlTepDelForDnatInEachRtr : DNAT -> Trying to clear routes to the Floating IP "
237             + "associated to the router {}", routerName);
238
239         InstanceIdentifier<RouterPorts> routerPortsId = NatUtil.getRouterPortsId(routerName);
240         Optional<RouterPorts> optRouterPorts = MDSALUtil.read(dataBroker, LogicalDatastoreType
241             .CONFIGURATION, routerPortsId);
242         if (!optRouterPorts.isPresent()) {
243             LOG.debug(
244                 "hndlTepDelForDnatInEachRtr : DNAT -> Could not read Router Ports data object with id: {} "
245                     + "from DNAT FloatingIpInfo", routerName);
246             return isFipExists;
247         }
248         RouterPorts routerPorts = optRouterPorts.get();
249         Uuid extNwId = routerPorts.getExternalNetworkId();
250         final String vpnName = NatUtil.getAssociatedVPN(dataBroker, extNwId);
251         if (vpnName == null) {
252             LOG.error(
253                 "hndlTepDelForDnatInEachRtr : DNAT -> No External VPN associated with Ext N/W {} for Router {}",
254                 extNwId, routerName);
255             return isFipExists;
256         }
257         String rd = NatUtil.getVpnRd(dataBroker, vpnName);
258         if (extNwProvType == null) {
259             return isFipExists;
260         }
261         long l3Vni = 0;
262         if (extNwProvType == ProviderTypes.VXLAN) {
263             //get l3Vni value for external VPN
264             l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
265             if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
266                 LOG.debug(
267                     "hndlTepDelForDnatInEachRtr : L3VNI value is not configured in Internet VPN {} and RD {} "
268                         + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue to installing "
269                         + "NAT flows", vpnName, rd);
270                 l3Vni = natOverVxlanUtil.getInternetVpnVni(vpnName, routerId).longValue();
271             }
272         }
273         List<Ports> interfaces = routerPorts.getPorts();
274         for (Ports port : interfaces) {
275             //Get the DPN on which this interface resides
276             String interfaceName = port.getPortName();
277             BigInteger fipCfgdDpnId = NatUtil.getDpnForInterface(interfaceService, interfaceName);
278             if (fipCfgdDpnId.equals(BigInteger.ZERO)) {
279                 LOG.info(
280                     "hndlTepDelForDnatInEachRtr : DNAT -> Abort processing Floating ip configuration. "
281                         + "No DPN for port : {}", interfaceName);
282                 continue;
283             }
284             if (!fipCfgdDpnId.equals(tepDeletedDpnId)) {
285                 LOG.info(
286                     "hndlTepDelForDnatInEachRtr : DNAT -> TEP deleted DPN {} is not the DPN {} which has the "
287                         + "floating IP configured for the port: {}",
288                     tepDeletedDpnId, fipCfgdDpnId, interfaceName);
289                 continue;
290             }
291             isFipExists = Boolean.TRUE;
292             List<InternalToExternalPortMap> intExtPortMapList = port.getInternalToExternalPortMap();
293             for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
294                 String internalIp = intExtPortMap.getInternalIp();
295                 String externalIp = intExtPortMap.getExternalIp();
296                 externalIp = NatUtil.validateAndAddNetworkMask(externalIp);
297                 LOG.debug(
298                     "hndlTepDelForDnatInEachRtr : DNAT -> Withdrawing the FIB route to the floating IP {} "
299                         + "configured for the port: {}",
300                     externalIp, interfaceName);
301                 NatUtil.removePrefixFromBGP(bgpManager, fibManager, rd, externalIp, vpnName);
302                 long serviceId = 0;
303                 if (extNwProvType == ProviderTypes.VXLAN) {
304                     serviceId = l3Vni;
305                 } else {
306                     long label = floatingIPListener
307                         .getOperationalIpMapping(routerName, interfaceName, internalIp);
308                     if (label == NatConstants.INVALID_ID) {
309                         LOG.error(
310                             "hndlTepDelForDnatInEachRtr : DNAT -> Unable to remove the table 21 entry pushing the"
311                                 + " MPLS label to the tunnel since label is invalid");
312                         continue;
313                     }
314                     serviceId = label;
315                 }
316
317                 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName)
318                     .setSourceDpid(fipCfgdDpnId).setIpAddress(externalIp).setServiceId(serviceId)
319                     .setIpAddressSource(RemoveFibEntryInput.IpAddressSource.FloatingIP).build();
320                 ListenableFuture<RpcResult<RemoveFibEntryOutput>> future = fibRpcService
321                     .removeFibEntry(input);
322
323                 Futures.addCallback(future, new FutureCallback<RpcResult<RemoveFibEntryOutput>>() {
324
325                     @Override
326                     public void onFailure(Throwable error) {
327                         LOG.error(
328                             "hndlTepDelForDnatInEachRtr : DNAT -> Error in removing the table 21 entry pushing "
329                                 + "the MPLS label to the tunnel since label is invalid ", error);
330                     }
331
332                     @Override
333                     public void onSuccess(RpcResult<RemoveFibEntryOutput> result) {
334                         if (result.isSuccessful()) {
335                             LOG.info(
336                                 "hndlTepDelForDnatInEachRtr : DNAT -> Successfully removed the entry pushing the "
337                                     + "MPLS label to the tunnel");
338                         } else {
339                             LOG.error(
340                                 "hndlTepDelForDnatInEachRtr : DNAT -> Error in fib rpc call to remove the table "
341                                     + "21 entry pushing the MPLS label to the tunnnel due to {}",
342                                 result.getErrors());
343                         }
344                     }
345                 }, MoreExecutors.directExecutor());
346             }
347         }
348         return isFipExists;
349     }
350
351     private void hndlTepDelForSnatInEachRtr(RoutersList router, long routerId, BigInteger dpnId,
352         String srcTepIp, Boolean isFipExists, ProviderTypes extNwProvType,
353         TypedReadWriteTransaction<Configuration> confTx)
354         throws ExecutionException, InterruptedException {
355
356         /*SNAT :
357         1) Elect a new switch as the primary NAPT
358         2) Advertise the new routes to BGP for the newly elected TEP IP as the DPN IP
359         3) This will make sure old routes are withdrawn and new routes are advertised.
360          */
361
362         String routerName = router.getRouter();
363         LOG.debug(
364             "hndlTepDelForSnatInEachRtr : SNAT -> Trying to clear routes to the External fixed IP associated "
365                 + "to the router {}", routerName);
366
367         // Check if this is externalRouter else ignore
368         InstanceIdentifier<Routers> extRoutersId = NatUtil.buildRouterIdentifier(routerName);
369         Optional<Routers> routerData = Optional.absent();
370         try {
371             routerData = confTx.read(extRoutersId).get();
372         } catch (InterruptedException | ExecutionException e) {
373             LOG.error("Error retrieving routers {}", extRoutersId, e);
374         }
375         if (!routerData.isPresent()) {
376             LOG.debug(
377                 "hndlTepDelForSnatInEachRtr : SNAT->Ignoring TEP del for router {} since its not External Router",
378                 routerName);
379             return;
380         }
381
382         //Check if the DPN having the router is the NAPT switch
383         BigInteger naptId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
384         if (naptId == null || naptId.equals(BigInteger.ZERO) || !naptId.equals(dpnId)) {
385             LOG.error(
386                 "hndlTepDelForSnatInEachRtr : SNAT -> Ignoring TEP delete for the DPN {} since"
387                     + "srcTepIp : {} is NOT a NAPT switch", dpnId, srcTepIp);
388             return;
389         }
390         if (natMode == NatMode.Conntrack) {
391             Routers extRouter = routerData.get();
392             natServiceManager.notify(confTx, extRouter, null, naptId, dpnId,
393                 SnatServiceManager.Action.CNT_ROUTER_DISBL);
394             if (extRouter.isEnableSnat()) {
395                 natServiceManager.notify(confTx, extRouter, null, naptId, dpnId,
396                     SnatServiceManager.Action.SNAT_ROUTER_DISBL);
397             }
398         } else {
399
400             Uuid networkId = routerData.get().getNetworkId();
401             if (networkId == null) {
402                 LOG.error(
403                     "hndlTepDelForSnatInEachRtr : SNAT -> Ignoring TEP delete for the DPN {} for router {}"
404                         + "as external network configuraton is missing", dpnId, routerName);
405                 return;
406             }
407
408             LOG.debug("hndlTepDelForSnatInEachRtr : SNAT->Router {} is associated with ext nw {}",
409                 routerId, networkId);
410             Uuid bgpVpnUuid = NatUtil.getVpnForRouter(dataBroker, routerName);
411             Long bgpVpnId;
412             if (bgpVpnUuid == null) {
413                 LOG.debug(
414                     "hndlTepDelForSnatInEachRtr : SNAT->Internal VPN-ID {} associated to router {}",
415                     routerId, routerName);
416                 bgpVpnId = routerId;
417             } else {
418                 bgpVpnId = NatUtil.getVpnId(dataBroker, bgpVpnUuid.getValue());
419                 if (bgpVpnId == NatConstants.INVALID_ID) {
420                     LOG.error(
421                         "hndlTepDelForSnatInEachRtr :SNAT->Invalid Private BGP VPN ID returned for routerName {}",
422                         routerName);
423                     return;
424                 }
425             }
426             if (!isFipExists) {
427                 // Remove default entry in FIB to SNAT table
428                 LOG.debug(
429                     "NAT Service : Installing default route in FIB on DPN {} for router {} with"
430                         + " vpn {}...",
431                     dpnId, routerName, bgpVpnId);
432                 defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, bgpVpnId, routerId, confTx);
433             }
434
435             if (routerData.get().isEnableSnat()) {
436                 LOG.info("hndlTepDelForSnatInEachRtr : SNAT enabled for router {}", routerId);
437
438                 long routerVpnId = routerId;
439                 if (bgpVpnId != NatConstants.INVALID_ID) {
440                     LOG.debug(
441                         "hndlTepDelForSnatInEachRtr : SNAT -> Private BGP VPN ID (Internal BGP VPN ID) {} "
442                             + "associated to the router {}", bgpVpnId, routerName);
443                     routerVpnId = bgpVpnId;
444                 } else {
445                     LOG.debug(
446                         "hndlTepDelForSnatInEachRtr : SNAT -> Internal L3 VPN ID (Router ID) {} "
447                             + "associated to the router {}", routerVpnId, routerName);
448                 }
449                 //Re-elect the other available switch as the NAPT switch and program the NAT flows.
450                 NatUtil
451                     .removeSNATFromDPN(dataBroker, mdsalManager, idManager, naptSwitchHA, dpnId,
452                         routerName, routerId, routerVpnId, networkId, extNwProvType, confTx);
453             } else {
454                 LOG.info(
455                     "hndlTepDelForSnatInEachRtr : SNAT is not enabled for router {} to handle addDPN event {}",
456                     routerId, dpnId);
457             }
458         }
459     }
460
461     @Override
462     protected NatTepChangeListener getDataTreeChangeListener() {
463         return this;
464     }
465 }