Cleanup
[netvirt.git] / vpnservice / fibmanager / fibmanager-impl / src / main / java / org / opendaylight / netvirt / fibmanager / BgpRouteVrfEntryHandler.java
1 /*
2  * Copyright © 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.fibmanager;
9
10 import static java.util.stream.Collectors.toList;
11
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14
15 import java.math.BigInteger;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.concurrent.BlockingQueue;
21 import java.util.concurrent.LinkedBlockingQueue;
22 import java.util.function.Consumer;
23
24 import javax.annotation.PostConstruct;
25 import javax.inject.Inject;
26 import javax.inject.Singleton;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.genius.mdsalutil.ActionInfo;
31 import org.opendaylight.genius.mdsalutil.InstructionInfo;
32 import org.opendaylight.genius.mdsalutil.NwConstants;
33 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
34 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
35 import org.opendaylight.genius.mdsalutil.actions.ActionPushMpls;
36 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
37 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
38 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldMplsLabel;
39 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
40 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
41 import org.opendaylight.genius.utils.batching.ActionableResource;
42 import org.opendaylight.genius.utils.batching.ActionableResourceImpl;
43 import org.opendaylight.genius.utils.batching.ResourceBatchingManager;
44 import org.opendaylight.genius.utils.batching.ResourceHandler;
45 import org.opendaylight.genius.utils.batching.SubTransaction;
46 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
59 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62
63
64 @Singleton
65 public class BgpRouteVrfEntryHandler extends BaseVrfEntryHandler
66         implements AutoCloseable, ResourceHandler, IVrfEntryHandler {
67
68     private static final Logger LOG = LoggerFactory.getLogger(BgpRouteVrfEntryHandler.class);
69     private final DataBroker dataBroker;
70
71     private static final int BATCH_INTERVAL = 500;
72     private static final int BATCH_SIZE = 1000;
73     private final BlockingQueue<ActionableResource> vrfEntryBufferQ = new LinkedBlockingQueue<>();
74     private final ResourceBatchingManager resourceBatchingManager;
75     private final NexthopManager nexthopManager;
76
77     @Inject
78     public BgpRouteVrfEntryHandler(final DataBroker dataBroker,
79                                    final NexthopManager nexthopManager) {
80         super(dataBroker, nexthopManager, null);
81         this.dataBroker = dataBroker;
82         this.nexthopManager = nexthopManager;
83
84         resourceBatchingManager = ResourceBatchingManager.getInstance();
85         resourceBatchingManager.registerBatchableResource("FIB-VRFENTRY", vrfEntryBufferQ, this);
86     }
87
88     @PostConstruct
89     public void init() {
90         LOG.info("{} start", getClass().getSimpleName());
91     }
92
93     @Override
94     public void close() throws Exception {
95         LOG.info("{} close", getClass().getSimpleName());
96     }
97
98     @Override
99     public DataBroker getResourceBroker() {
100         return dataBroker;
101     }
102
103     @Override
104     public int getBatchSize() {
105         return BATCH_SIZE;
106     }
107
108     @Override
109     public int getBatchInterval() {
110         return BATCH_INTERVAL;
111     }
112
113     @Override
114     public LogicalDatastoreType getDatastoreType() {
115         return LogicalDatastoreType.CONFIGURATION;
116     }
117
118     @Override
119     public void update(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
120                        Object original, Object update, List<SubTransaction> subTxns) {
121         if (original instanceof VrfEntry && update instanceof VrfEntry) {
122             createFibEntries(tx, identifier, (VrfEntry) update, subTxns);
123         }
124     }
125
126     @Override
127     public void create(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
128                        Object vrfEntry, List<SubTransaction> subTxns) {
129         if (vrfEntry instanceof VrfEntry) {
130             createFibEntries(tx, identifier, (VrfEntry) vrfEntry, subTxns);
131         }
132     }
133
134     @Override
135     public void delete(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
136                        Object vrfEntry, List<SubTransaction> subTxns) {
137         if (vrfEntry instanceof VrfEntry) {
138             deleteFibEntries(tx, identifier, (VrfEntry) vrfEntry, subTxns);
139         }
140     }
141
142     @Override
143     public void createFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
144         ActionableResource actResource = new ActionableResourceImpl(rd + vrfEntry.getDestPrefix());
145         actResource.setAction(ActionableResource.CREATE);
146         actResource.setInstanceIdentifier(identifier);
147         actResource.setInstance(vrfEntry);
148         vrfEntryBufferQ.add(actResource);
149     }
150
151     @Override
152     public void removeFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
153         ActionableResource actResource = new ActionableResourceImpl(rd + vrfEntry.getDestPrefix());
154         actResource.setAction(ActionableResource.DELETE);
155         actResource.setInstanceIdentifier(identifier);
156         actResource.setInstance(vrfEntry);
157         vrfEntryBufferQ.add(actResource);
158     }
159
160     @Override
161     public void updateFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update, String rd) {
162         ActionableResource actResource = new ActionableResourceImpl(rd + update.getDestPrefix());
163         actResource.setAction(ActionableResource.UPDATE);
164         actResource.setInstanceIdentifier(identifier);
165         actResource.setInstance(update);
166         actResource.setOldInstance(original);
167         vrfEntryBufferQ.add(actResource);
168     }
169
170     /*
171       Please note that the following createFibEntries will be invoked only for BGP Imported Routes.
172       The invocation of the following method is via create() callback from the MDSAL Batching Infrastructure
173       provided by ResourceBatchingManager
174      */
175     private void createFibEntries(WriteTransaction writeTx, final InstanceIdentifier<VrfEntry> vrfEntryIid,
176                                   final VrfEntry vrfEntry, List<SubTransaction> subTxns) {
177         final VrfTablesKey vrfTableKey = vrfEntryIid.firstKeyOf(VrfTables.class);
178
179         final VpnInstanceOpDataEntry vpnInstance =
180                 FibUtil.getVpnInstance(dataBroker, vrfTableKey.getRouteDistinguisher());
181         Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
182         Preconditions.checkNotNull(vpnInstance.getVpnId(), "Vpn Instance with rd " + vpnInstance.getVrfId()
183                 + " has null vpnId!");
184
185         final Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
186         if (vpnToDpnList != null) {
187             for (VpnToDpnList vpnDpn : vpnToDpnList) {
188                 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
189                     createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(),
190                             vrfEntry, writeTx, subTxns);
191                 }
192             }
193         }
194     }
195
196     /*
197       Please note that the following deleteFibEntries will be invoked only for BGP Imported Routes.
198       The invocation of the following method is via delete() callback from the MDSAL Batching Infrastructure
199       provided by ResourceBatchingManager
200      */
201     private void deleteFibEntries(WriteTransaction writeTx, final InstanceIdentifier<VrfEntry> identifier,
202                                   final VrfEntry vrfEntry, List<SubTransaction> subTxns) {
203         final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
204         String rd = vrfTableKey.getRouteDistinguisher();
205         final VpnInstanceOpDataEntry vpnInstance =
206                 FibUtil.getVpnInstance(dataBroker, vrfTableKey.getRouteDistinguisher());
207         if (vpnInstance == null) {
208             LOG.debug("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
209             return;
210         }
211         String vpnName = FibUtil.getVpnNameFromId(dataBroker, vpnInstance.getVpnId());
212         final Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
213         if (vpnToDpnList != null) {
214             List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
215                     vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
216             Optional<Routes> extraRouteOptional;
217             //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
218             if (usedRds != null && !usedRds.isEmpty()) {
219                 if (usedRds.size() > 1) {
220                     LOG.error("The extra route prefix is still present in some DPNs");
221                     return ;
222                 } else {
223                     extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
224                             usedRds.get(0), vrfEntry.getDestPrefix());
225                 }
226             } else {
227                 extraRouteOptional = Optional.absent();
228             }
229             for (VpnToDpnList curDpn : vpnToDpnList) {
230                 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
231                     deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(), vpnInstance.getVpnId(),
232                             vrfTableKey, vrfEntry, extraRouteOptional, writeTx, subTxns);
233                 }
234             }
235         }
236     }
237
238     public void programRemoteFibForBgpRoutes(final BigInteger remoteDpnId,
239                                              final long vpnId,
240                                              final VrfEntry vrfEntry,
241                                              WriteTransaction tx,
242                                              String rd,
243                                              List<NexthopManager.AdjacencyResult> adjacencyResults,
244                                              List<SubTransaction> subTxns) {
245         Preconditions.checkArgument(vrfEntry.getRoutePaths().size() <= 2);
246
247         if (adjacencyResults.size() == 1) {
248             programRemoteFib(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
249             return;
250         }
251         // ECMP Use case, point to LB group. Move the mpls label accordingly.
252         List<String> tunnelList =
253                 adjacencyResults.stream()
254                         .map(adjacencyResult -> adjacencyResult.getNextHopIp())
255                         .sorted().collect(toList());
256         String lbGroupKey = FibUtil.getGreLbGroupKey(tunnelList);
257         long groupId = nexthopManager.createNextHopPointer(lbGroupKey);
258         int index = 0;
259         List<ActionInfo> actionInfos = new ArrayList<>();
260         for (NexthopManager.AdjacencyResult adjResult : adjacencyResults) {
261             String nextHopIp = adjResult.getNextHopIp();
262             java.util.Optional<Long> optionalLabel = FibUtil.getLabelForNextHop(vrfEntry, nextHopIp);
263             if (!optionalLabel.isPresent()) {
264                 LOG.warn("NextHopIp {} not found in vrfEntry {}", nextHopIp, vrfEntry);
265                 continue;
266             }
267             long label = optionalLabel.get();
268
269             actionInfos.add(new ActionRegLoad(index, FibConstants.NXM_REG_MAPPING.get(index++), 0,
270                     31, label));
271         }
272         List<InstructionInfo> instructions = new ArrayList<>();
273         actionInfos.add(new ActionGroup(index, groupId));
274         instructions.add(new InstructionApplyActions(actionInfos));
275         makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions, NwConstants.ADD_FLOW, tx, subTxns);
276     }
277
278     public void createRemoteFibEntry(final BigInteger remoteDpnId,
279                                      final long vpnId,
280                                      final String rd,
281                                      final VrfEntry vrfEntry,
282                                      WriteTransaction tx,
283                                      List<SubTransaction> subTxns) {
284         Boolean wrTxPresent = true;
285         if (tx == null) {
286             wrTxPresent = false;
287             tx = dataBroker.newWriteOnlyTransaction();
288         }
289
290         LOG.debug("createRemoteFibEntry: adding route {} for rd {} on remoteDpnId {}",
291                 vrfEntry.getDestPrefix(), rd, remoteDpnId);
292
293         List<NexthopManager.AdjacencyResult> adjacencyResults =
294                 resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
295         if (adjacencyResults == null || adjacencyResults.isEmpty()) {
296             LOG.error("Could not get interface for route-paths: {} in vpn {}", vrfEntry.getRoutePaths(), rd);
297             LOG.warn("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
298             return;
299         }
300
301         programRemoteFibForBgpRoutes(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
302
303         if (!wrTxPresent) {
304             tx.submit();
305         }
306         LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
307     }
308
309     private void deleteFibEntryForBgpRoutes(BigInteger remoteDpnId, long vpnId, VrfEntry vrfEntry,
310                                              String rd, WriteTransaction tx, List<SubTransaction> subTxns) {
311         // When the tunnel is removed the fib entries should be reprogrammed/deleted depending on
312         // the adjacencyResults.
313         List<NexthopManager.AdjacencyResult> adjacencyResults = resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
314         if (!adjacencyResults.isEmpty()) {
315             programRemoteFibForBgpRoutes(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
316         }
317     }
318
319     public void deleteRemoteRoute(final BigInteger localDpnId, final BigInteger remoteDpnId,
320                                   final long vpnId, final VrfTablesKey vrfTableKey,
321                                   final VrfEntry vrfEntry, Optional<Routes> extraRouteOptional,
322                                   WriteTransaction tx, List<SubTransaction> subTxns) {
323
324         Boolean wrTxPresent = true;
325         if (tx == null) {
326             wrTxPresent = false;
327             tx = dataBroker.newWriteOnlyTransaction();
328         }
329
330         LOG.debug("deleting remote route: prefix={}, vpnId={} localDpnId {} remoteDpnId {}",
331                 vrfEntry.getDestPrefix(), vpnId, localDpnId, remoteDpnId);
332         String rd = vrfTableKey.getRouteDistinguisher();
333
334         if (localDpnId != null && localDpnId != BigInteger.ZERO) {
335             // localDpnId is not known when clean up happens for last vm for a vpn on a dpn
336             if (extraRouteOptional.isPresent()) {
337                 nexthopManager.setupLoadBalancingNextHop(vpnId, remoteDpnId, vrfEntry.getDestPrefix(),
338                         Collections.emptyList() /*listBucketInfo*/ , false);
339             }
340             deleteFibEntryForBgpRoutes(remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
341             return;
342         }
343
344         // below two reads are kept as is, until best way is found to identify dpnID
345         VpnNexthop localNextHopInfo = nexthopManager.getVpnNexthop(vpnId, vrfEntry.getDestPrefix());
346         if (extraRouteOptional.isPresent()) {
347             nexthopManager.setupLoadBalancingNextHop(vpnId, remoteDpnId, vrfEntry.getDestPrefix(),
348                     Collections.emptyList()  /*listBucketInfo*/ , false);
349         } else {
350             checkDpnDeleteFibEntry(localNextHopInfo, remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
351         }
352         if (!wrTxPresent) {
353             tx.submit();
354         }
355     }
356
357     public Consumer<? super VrfEntry> getConsumerForCreatingRemoteFib(
358             final BigInteger dpnId, final long vpnId, final String rd,
359             final String remoteNextHopIp, final Optional<VrfTables> vrfTable,
360             WriteTransaction writeCfgTxn, List<SubTransaction> subTxns) {
361         return vrfEntry -> vrfEntry.getRoutePaths().stream()
362                 .filter(routes -> !routes.getNexthopAddress().isEmpty()
363                         && remoteNextHopIp.trim().equals(routes.getNexthopAddress().trim()))
364                 .findFirst()
365                 .ifPresent(routes -> {
366                     LOG.trace("creating remote FIB entry for prefix {} rd {} on Dpn {}",
367                             vrfEntry.getDestPrefix(), rd, dpnId);
368                     createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
369                             vrfEntry, writeCfgTxn, subTxns);
370                 });
371     }
372
373     public Consumer<? super VrfEntry> getConsumerForDeletingRemoteFib(
374             final BigInteger dpnId, final long vpnId, final String rd,
375             final String remoteNextHopIp, final Optional<VrfTables> vrfTable,
376             WriteTransaction writeCfgTxn, List<SubTransaction> subTxns) {
377         return vrfEntry -> vrfEntry.getRoutePaths().stream()
378                 .filter(routes -> !routes.getNexthopAddress().isEmpty()
379                         && remoteNextHopIp.trim().equals(routes.getNexthopAddress().trim()))
380                 .findFirst()
381                 .ifPresent(routes -> {
382                     LOG.trace(" deleting remote FIB entry {}", vrfEntry);
383                     deleteRemoteRoute(null, dpnId, vpnId, vrfTable.get().getKey(), vrfEntry,
384                             Optional.absent(), writeCfgTxn, subTxns);
385                 });
386     }
387
388     @Override
389     protected void addTunnelInterfaceActions(NexthopManager.AdjacencyResult adjacencyResult, long vpnId,
390             VrfEntry vrfEntry, List<ActionInfo> actionInfos, String rd) {
391         Class<? extends TunnelTypeBase> tunnelType =
392                 VpnExtraRouteHelper.getTunnelType(nextHopManager.getInterfaceManager(),
393                         adjacencyResult.getInterfaceName());
394         if (tunnelType == null) {
395             LOG.debug("Tunnel type not found for vrfEntry {}", vrfEntry);
396             return;
397         }
398         String nextHopIp = adjacencyResult.getNextHopIp();
399         if (tunnelType.equals(TunnelTypeMplsOverGre.class)) {
400             java.util.Optional<Long> optionalLabel = FibUtil.getLabelForNextHop(vrfEntry, nextHopIp);
401             if (!optionalLabel.isPresent()) {
402                 LOG.warn("NextHopIp {} not found in vrfEntry {}", nextHopIp, vrfEntry);
403                 return;
404             }
405             long label = optionalLabel.get();
406             LOG.debug("addTunnelInterfaceActions: Push label action for prefix {} rd {} l3vni {} nextHop {}",
407                     vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), nextHopIp);
408             actionInfos.add(new ActionPushMpls());
409             actionInfos.add(new ActionSetFieldMplsLabel(label));
410             actionInfos.add(new ActionNxLoadInPort(BigInteger.ZERO));
411         } else if (tunnelType.equals(TunnelTypeVxlan.class)) {
412             actionInfos.add(new ActionSetFieldTunnelId(BigInteger.valueOf(vrfEntry.getL3vni())));
413             LOG.debug("addTunnelInterfaceActions: adding set tunnel id action for prefix {} rd {} l3vni {}"
414                     + " nextHop {} ", vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), nextHopIp);
415             addRewriteDstMacAction(vpnId, vrfEntry, null /*prefixInfo*/, actionInfos);
416         }
417     }
418
419     @Override
420     protected void addRewriteDstMacAction(long vpnId, VrfEntry vrfEntry, Prefixes prefixInfo,
421                                         List<ActionInfo> actionInfos) {
422         if (vrfEntry.getGatewayMacAddress() != null) {
423             actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
424                     new MacAddress(vrfEntry.getGatewayMacAddress())));
425         }
426     }
427
428 }