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