2 * Copyright © 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
8 package org.opendaylight.netvirt.fibmanager;
10 import static java.util.stream.Collectors.toList;
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.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
29 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
30 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
31 import org.opendaylight.genius.mdsalutil.ActionInfo;
32 import org.opendaylight.genius.mdsalutil.InstructionInfo;
33 import org.opendaylight.genius.mdsalutil.NwConstants;
34 import org.opendaylight.genius.mdsalutil.UpgradeState;
35 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
36 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
37 import org.opendaylight.genius.mdsalutil.actions.ActionPushMpls;
38 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
39 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
40 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldMplsLabel;
41 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
42 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
43 import org.opendaylight.genius.utils.batching.ActionableResource;
44 import org.opendaylight.genius.utils.batching.ActionableResourceImpl;
45 import org.opendaylight.genius.utils.batching.ResourceBatchingManager;
46 import org.opendaylight.genius.utils.batching.ResourceHandler;
47 import org.opendaylight.genius.utils.batching.SubTransaction;
48 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
49 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
62 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
68 public class BgpRouteVrfEntryHandler extends BaseVrfEntryHandler
69 implements ResourceHandler, IVrfEntryHandler {
71 private static final Logger LOG = LoggerFactory.getLogger(BgpRouteVrfEntryHandler.class);
72 private static final int BATCH_INTERVAL = 500;
73 private static final int BATCH_SIZE = 1000;
75 private final DataBroker dataBroker;
76 private final ManagedNewTransactionRunner txRunner;
77 private final BlockingQueue<ActionableResource> vrfEntryBufferQ = new LinkedBlockingQueue<>();
78 private final ResourceBatchingManager resourceBatchingManager;
79 private final NexthopManager nexthopManager;
82 public BgpRouteVrfEntryHandler(final DataBroker dataBroker,
83 final NexthopManager nexthopManager,
84 final FibUtil fibUtil,
85 final UpgradeState upgradeState,
86 final DataTreeEventCallbackRegistrar eventCallbacks) {
87 super(dataBroker, nexthopManager, null, fibUtil, upgradeState, eventCallbacks);
88 this.dataBroker = dataBroker;
89 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
90 this.nexthopManager = nexthopManager;
92 resourceBatchingManager = ResourceBatchingManager.getInstance();
93 resourceBatchingManager.registerBatchableResource("FIB-VRFENTRY", vrfEntryBufferQ, this);
98 LOG.info("{} start", getClass().getSimpleName());
102 public void close() {
103 LOG.info("{} close", getClass().getSimpleName());
107 public DataBroker getResourceBroker() {
112 public int getBatchSize() {
117 public int getBatchInterval() {
118 return BATCH_INTERVAL;
122 public LogicalDatastoreType getDatastoreType() {
123 return LogicalDatastoreType.CONFIGURATION;
127 public void update(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
128 Object original, Object update, List<SubTransaction> subTxns) {
129 if (original instanceof VrfEntry && update instanceof VrfEntry) {
130 createFibEntries(tx, identifier, (VrfEntry) update, subTxns);
135 public void create(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
136 Object vrfEntry, List<SubTransaction> subTxns) {
137 if (vrfEntry instanceof VrfEntry) {
138 createFibEntries(tx, identifier, (VrfEntry) vrfEntry, subTxns);
143 public void delete(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
144 Object vrfEntry, List<SubTransaction> subTxns) {
145 if (vrfEntry instanceof VrfEntry) {
146 deleteFibEntries(tx, identifier, (VrfEntry) vrfEntry, subTxns);
151 public void createFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
152 ActionableResource actResource = new ActionableResourceImpl(rd + vrfEntry.getDestPrefix());
153 actResource.setAction(ActionableResource.CREATE);
154 actResource.setInstanceIdentifier(identifier);
155 actResource.setInstance(vrfEntry);
156 vrfEntryBufferQ.add(actResource);
160 public void removeFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
161 ActionableResource actResource = new ActionableResourceImpl(rd + vrfEntry.getDestPrefix());
162 actResource.setAction(ActionableResource.DELETE);
163 actResource.setInstanceIdentifier(identifier);
164 actResource.setInstance(vrfEntry);
165 vrfEntryBufferQ.add(actResource);
169 public void updateFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update, String rd) {
170 ActionableResource actResource = new ActionableResourceImpl(rd + update.getDestPrefix());
171 actResource.setAction(ActionableResource.UPDATE);
172 actResource.setInstanceIdentifier(identifier);
173 actResource.setInstance(update);
174 actResource.setOldInstance(original);
175 vrfEntryBufferQ.add(actResource);
179 Please note that the following createFibEntries will be invoked only for BGP Imported Routes.
180 The invocation of the following method is via create() callback from the MDSAL Batching Infrastructure
181 provided by ResourceBatchingManager
183 private void createFibEntries(WriteTransaction writeTx, final InstanceIdentifier<VrfEntry> vrfEntryIid,
184 final VrfEntry vrfEntry, List<SubTransaction> subTxns) {
185 final VrfTablesKey vrfTableKey = vrfEntryIid.firstKeyOf(VrfTables.class);
187 final VpnInstanceOpDataEntry vpnInstance =
188 getFibUtil().getVpnInstance(vrfTableKey.getRouteDistinguisher());
189 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
190 Preconditions.checkNotNull(vpnInstance.getVpnId(), "Vpn Instance with rd " + vpnInstance.getVrfId()
191 + " has null vpnId!");
193 final Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
194 if (vpnToDpnList != null) {
195 for (VpnToDpnList vpnDpn : vpnToDpnList) {
196 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
197 createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(),
198 vrfEntry, writeTx, subTxns);
205 Please note that the following deleteFibEntries will be invoked only for BGP Imported Routes.
206 The invocation of the following method is via delete() callback from the MDSAL Batching Infrastructure
207 provided by ResourceBatchingManager
209 private void deleteFibEntries(WriteTransaction writeTx, final InstanceIdentifier<VrfEntry> identifier,
210 final VrfEntry vrfEntry, List<SubTransaction> subTxns) {
211 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
212 String rd = vrfTableKey.getRouteDistinguisher();
213 final VpnInstanceOpDataEntry vpnInstance =
214 getFibUtil().getVpnInstance(vrfTableKey.getRouteDistinguisher());
215 if (vpnInstance == null) {
216 LOG.debug("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
219 String vpnName = getFibUtil().getVpnNameFromId(vpnInstance.getVpnId());
220 final Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
221 if (vpnToDpnList != null) {
222 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
223 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
224 Optional<Routes> extraRouteOptional;
225 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
226 if (usedRds != null && !usedRds.isEmpty()) {
227 if (usedRds.size() > 1) {
228 LOG.error("The extra route prefix is still present in some DPNs");
231 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
232 usedRds.get(0), vrfEntry.getDestPrefix());
235 extraRouteOptional = Optional.absent();
237 for (VpnToDpnList curDpn : vpnToDpnList) {
238 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
239 deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(), vpnInstance.getVpnId(),
240 vrfTableKey, vrfEntry, extraRouteOptional, writeTx, subTxns);
246 public void programRemoteFibForBgpRoutes(final BigInteger remoteDpnId,
248 final VrfEntry vrfEntry,
251 List<NexthopManager.AdjacencyResult> adjacencyResults,
252 List<SubTransaction> subTxns) {
253 Preconditions.checkArgument(vrfEntry.getRoutePaths().size() <= 2);
255 if (adjacencyResults.size() == 1) {
256 programRemoteFib(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
259 // ECMP Use case, point to LB group. Move the mpls label accordingly.
260 List<String> tunnelList =
261 adjacencyResults.stream()
262 .map(NexthopManager.AdjacencyResult::getNextHopIp)
263 .sorted().collect(toList());
264 String lbGroupKey = FibUtil.getGreLbGroupKey(tunnelList);
265 long groupId = nexthopManager.createNextHopPointer(lbGroupKey);
267 List<ActionInfo> actionInfos = new ArrayList<>();
268 for (NexthopManager.AdjacencyResult adjResult : adjacencyResults) {
269 String nextHopIp = adjResult.getNextHopIp();
270 java.util.Optional<Long> optionalLabel = FibUtil.getLabelForNextHop(vrfEntry, nextHopIp);
271 if (!optionalLabel.isPresent()) {
272 LOG.warn("NextHopIp {} not found in vrfEntry {}", nextHopIp, vrfEntry);
275 long label = optionalLabel.get();
277 actionInfos.add(new ActionRegLoad(index, FibConstants.NXM_REG_MAPPING.get(index++), 0,
280 List<InstructionInfo> instructions = new ArrayList<>();
281 actionInfos.add(new ActionGroup(index, groupId));
282 instructions.add(new InstructionApplyActions(actionInfos));
283 makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions, NwConstants.ADD_FLOW, tx, subTxns);
286 public void createRemoteFibEntry(final BigInteger remoteDpnId,
289 final VrfEntry vrfEntry,
291 List<SubTransaction> subTxns) {
293 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
294 newTx -> createRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, newTx, subTxns)), LOG,
295 "Error creating remote FIB entry");
299 LOG.debug("createRemoteFibEntry: adding route {} for rd {} on remoteDpnId {}",
300 vrfEntry.getDestPrefix(), rd, remoteDpnId);
302 List<NexthopManager.AdjacencyResult> adjacencyResults =
303 resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
304 if (adjacencyResults.isEmpty()) {
305 LOG.error("Could not get interface for route-paths: {} in vpn {}", vrfEntry.getRoutePaths(), rd);
306 LOG.warn("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
310 programRemoteFibForBgpRoutes(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
312 LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
315 private void deleteFibEntryForBgpRoutes(BigInteger remoteDpnId, long vpnId, VrfEntry vrfEntry,
316 String rd, WriteTransaction tx, List<SubTransaction> subTxns) {
317 // When the tunnel is removed the fib entries should be reprogrammed/deleted depending on
318 // the adjacencyResults.
319 List<NexthopManager.AdjacencyResult> adjacencyResults = resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
320 if (!adjacencyResults.isEmpty()) {
321 programRemoteFibForBgpRoutes(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
325 public void deleteRemoteRoute(final BigInteger localDpnId, final BigInteger remoteDpnId,
326 final long vpnId, final VrfTablesKey vrfTableKey,
327 final VrfEntry vrfEntry, Optional<Routes> extraRouteOptional,
328 WriteTransaction tx, List<SubTransaction> subTxns) {
330 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
331 newTx -> deleteRemoteRoute(localDpnId, remoteDpnId, vpnId, vrfTableKey, vrfEntry,
332 extraRouteOptional, newTx)), LOG, "Error deleting remote route");
336 LOG.debug("deleting remote route: prefix={}, vpnId={} localDpnId {} remoteDpnId {}",
337 vrfEntry.getDestPrefix(), vpnId, localDpnId, remoteDpnId);
338 String rd = vrfTableKey.getRouteDistinguisher();
340 if (localDpnId != null && localDpnId != BigInteger.ZERO) {
341 // localDpnId is not known when clean up happens for last vm for a vpn on a dpn
342 if (extraRouteOptional.isPresent()) {
343 nexthopManager.setupLoadBalancingNextHop(vpnId, remoteDpnId, vrfEntry.getDestPrefix(),
344 Collections.emptyList() /*listBucketInfo*/ , false);
346 deleteFibEntryForBgpRoutes(remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
350 // below two reads are kept as is, until best way is found to identify dpnID
351 VpnNexthop localNextHopInfo = nexthopManager.getVpnNexthop(vpnId, vrfEntry.getDestPrefix());
352 if (extraRouteOptional.isPresent()) {
353 nexthopManager.setupLoadBalancingNextHop(vpnId, remoteDpnId, vrfEntry.getDestPrefix(),
354 Collections.emptyList() /*listBucketInfo*/ , false);
356 checkDpnDeleteFibEntry(localNextHopInfo, remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
360 public Consumer<? super VrfEntry> getConsumerForCreatingRemoteFib(
361 final BigInteger dpnId, final long vpnId, final String rd,
362 final String remoteNextHopIp, final Optional<VrfTables> vrfTable,
363 WriteTransaction writeCfgTxn, List<SubTransaction> subTxns) {
364 return vrfEntry -> vrfEntry.getRoutePaths().stream()
365 .filter(routes -> !routes.getNexthopAddress().isEmpty()
366 && remoteNextHopIp.trim().equals(routes.getNexthopAddress().trim()))
368 .ifPresent(routes -> {
369 LOG.trace("creating remote FIB entry for prefix {} rd {} on Dpn {}",
370 vrfEntry.getDestPrefix(), rd, dpnId);
371 createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
372 vrfEntry, writeCfgTxn, subTxns);
376 public Consumer<? super VrfEntry> getConsumerForDeletingRemoteFib(
377 final BigInteger dpnId, final long vpnId,
378 final String remoteNextHopIp, final Optional<VrfTables> vrfTable,
379 WriteTransaction writeCfgTxn, List<SubTransaction> subTxns) {
380 return vrfEntry -> vrfEntry.getRoutePaths().stream()
381 .filter(routes -> !routes.getNexthopAddress().isEmpty()
382 && remoteNextHopIp.trim().equals(routes.getNexthopAddress().trim()))
384 .ifPresent(routes -> {
385 LOG.trace(" deleting remote FIB entry {}", vrfEntry);
386 deleteRemoteRoute(null, dpnId, vpnId, vrfTable.get().key(), vrfEntry,
387 Optional.absent(), writeCfgTxn, subTxns);
392 protected void addTunnelInterfaceActions(NexthopManager.AdjacencyResult adjacencyResult, long vpnId,
393 VrfEntry vrfEntry, List<ActionInfo> actionInfos, String rd) {
394 Class<? extends TunnelTypeBase> tunnelType = VpnExtraRouteHelper
395 .getTunnelType(getNextHopManager().getItmManager(), adjacencyResult.getInterfaceName());
396 if (tunnelType == null) {
397 LOG.debug("Tunnel type not found for vrfEntry {}", vrfEntry);
400 String nextHopIp = adjacencyResult.getNextHopIp();
401 if (tunnelType.equals(TunnelTypeMplsOverGre.class)) {
402 java.util.Optional<Long> optionalLabel = FibUtil.getLabelForNextHop(vrfEntry, nextHopIp);
403 if (!optionalLabel.isPresent()) {
404 LOG.warn("NextHopIp {} not found in vrfEntry {}", nextHopIp, vrfEntry);
407 long label = optionalLabel.get();
408 LOG.debug("addTunnelInterfaceActions: Push label action for prefix {} rd {} l3vni {} nextHop {}",
409 vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), nextHopIp);
410 actionInfos.add(new ActionPushMpls());
411 actionInfos.add(new ActionSetFieldMplsLabel(label));
412 actionInfos.add(new ActionNxLoadInPort(BigInteger.ZERO));
413 } else if (tunnelType.equals(TunnelTypeVxlan.class)) {
414 actionInfos.add(new ActionSetFieldTunnelId(BigInteger.valueOf(vrfEntry.getL3vni())));
415 LOG.debug("addTunnelInterfaceActions: adding set tunnel id action for prefix {} rd {} l3vni {}"
416 + " nextHop {} ", vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), nextHopIp);
417 addRewriteDstMacAction(vpnId, vrfEntry, null /*prefixInfo*/, actionInfos);
422 protected void addRewriteDstMacAction(long vpnId, VrfEntry vrfEntry, Prefixes prefixInfo,
423 List<ActionInfo> actionInfos) {
424 if (vrfEntry.getGatewayMacAddress() != null) {
425 actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
426 new MacAddress(vrfEntry.getGatewayMacAddress())));