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;
11 import static org.opendaylight.netvirt.fibmanager.FibUtil.nullToEmpty;
13 import com.google.common.base.Optional;
14 import java.math.BigInteger;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.List;
18 import java.util.concurrent.BlockingQueue;
19 import java.util.concurrent.LinkedBlockingQueue;
20 import java.util.function.Consumer;
21 import javax.annotation.Nullable;
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.actions.ActionGroup;
35 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
36 import org.opendaylight.genius.mdsalutil.actions.ActionPushMpls;
37 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
38 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
39 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldMplsLabel;
40 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
41 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
42 import org.opendaylight.genius.utils.batching.ActionableResource;
43 import org.opendaylight.genius.utils.batching.ActionableResourceImpl;
44 import org.opendaylight.genius.utils.batching.ResourceBatchingManager;
45 import org.opendaylight.genius.utils.batching.ResourceHandler;
46 import org.opendaylight.genius.utils.batching.SubTransaction;
47 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
48 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
49 import org.opendaylight.serviceutils.upgrade.UpgradeState;
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);
186 LOG.trace("Creating fib entry for vrfEntry with destPrefix{}, rd {}",
187 vrfEntry.getDestPrefix(), vrfTableKey.getRouteDistinguisher());
188 final VpnInstanceOpDataEntry vpnInstance =
189 getFibUtil().getVpnInstance(vrfTableKey.getRouteDistinguisher());
190 if (vpnInstance == null || vpnInstance.getVpnId() == null) {
191 LOG.error("Vpn Instance not availabe {}", vrfTableKey.getRouteDistinguisher());
194 final Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
195 if (vpnToDpnList != null) {
196 for (VpnToDpnList vpnDpn : vpnToDpnList) {
197 LOG.trace("Dpnstate is {} for dpn {} in vpn {}", vpnDpn.getDpnState(), vpnDpn.getDpnId(),
198 vpnInstance.getVpnId());
199 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
200 createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(),
201 vrfEntry, writeTx, subTxns);
205 LOG.trace("Created fib entry for vrfEntry with destPrefix{}, rd {}",
206 vrfEntry.getDestPrefix(), vrfTableKey.getRouteDistinguisher());
210 Please note that the following deleteFibEntries will be invoked only for BGP Imported Routes.
211 The invocation of the following method is via delete() callback from the MDSAL Batching Infrastructure
212 provided by ResourceBatchingManager
214 private void deleteFibEntries(WriteTransaction writeTx, final InstanceIdentifier<VrfEntry> identifier,
215 final VrfEntry vrfEntry, List<SubTransaction> subTxns) {
216 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
217 String rd = vrfTableKey.getRouteDistinguisher();
218 final VpnInstanceOpDataEntry vpnInstance =
219 getFibUtil().getVpnInstance(vrfTableKey.getRouteDistinguisher());
220 if (vpnInstance == null) {
221 LOG.debug("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
224 String vpnName = getFibUtil().getVpnNameFromId(vpnInstance.getVpnId());
225 final Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
226 if (vpnToDpnList != null) {
227 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
228 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
229 Optional<Routes> extraRouteOptional;
230 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
231 if (usedRds != null && !usedRds.isEmpty()) {
232 if (usedRds.size() > 1) {
233 LOG.error("The extra route prefix is still present in some DPNs");
236 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
237 usedRds.get(0), vrfEntry.getDestPrefix());
240 extraRouteOptional = Optional.absent();
242 for (VpnToDpnList curDpn : vpnToDpnList) {
243 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
244 deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(), vpnInstance.getVpnId(),
245 vrfTableKey, vrfEntry, extraRouteOptional, writeTx, subTxns);
251 public void programRemoteFibForBgpRoutes(final BigInteger remoteDpnId,
253 final VrfEntry vrfEntry,
256 List<NexthopManager.AdjacencyResult> adjacencyResults,
257 List<SubTransaction> subTxns) {
258 if (nullToEmpty(vrfEntry.getRoutePaths()).size() > 2) {
259 LOG.error("DC-GW can advertise only 2 bestPaths for prefix {}", vrfEntry.getDestPrefix());
262 LOG.trace("Start programming remote fib for destPrefix {}, vpnId {}, dpnId {}",
263 vrfEntry.getDestPrefix(), vpnId, remoteDpnId);
264 if (adjacencyResults.size() == 1) {
265 programRemoteFib(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
268 // ECMP Use case, point to LB group. Move the mpls label accordingly.
269 List<String> tunnelList =
270 adjacencyResults.stream()
271 .map(NexthopManager.AdjacencyResult::getNextHopIp)
272 .sorted().collect(toList());
273 String lbGroupKey = FibUtil.getGreLbGroupKey(tunnelList);
274 long groupId = nexthopManager.createNextHopPointer(lbGroupKey);
276 List<ActionInfo> actionInfos = new ArrayList<>();
277 for (NexthopManager.AdjacencyResult adjResult : adjacencyResults) {
278 String nextHopIp = adjResult.getNextHopIp();
279 java.util.Optional<Long> optionalLabel = FibUtil.getLabelForNextHop(vrfEntry, nextHopIp);
280 if (!optionalLabel.isPresent()) {
281 LOG.warn("NextHopIp {} not found in vrfEntry {}", nextHopIp, vrfEntry);
284 long label = optionalLabel.get();
286 actionInfos.add(new ActionRegLoad(index, FibConstants.NXM_REG_MAPPING.get(index++), 0,
289 List<InstructionInfo> instructions = new ArrayList<>();
290 actionInfos.add(new ActionGroup(index, groupId));
291 instructions.add(new InstructionApplyActions(actionInfos));
292 makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions, NwConstants.ADD_FLOW, tx, subTxns);
293 LOG.trace("End programming remote fib for destPrefix {}, vpnId {}, dpnId {}",
294 vrfEntry.getDestPrefix(), vpnId, remoteDpnId);
297 public void createRemoteFibEntry(final BigInteger remoteDpnId,
300 final VrfEntry vrfEntry,
302 List<SubTransaction> subTxns) {
304 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
305 newTx -> createRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, newTx, subTxns)), LOG,
306 "Error creating remote FIB entry");
310 LOG.debug("createRemoteFibEntry: adding route {} for rd {} on remoteDpnId {}",
311 vrfEntry.getDestPrefix(), rd, remoteDpnId);
313 List<NexthopManager.AdjacencyResult> adjacencyResults =
314 resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
315 if (adjacencyResults.isEmpty()) {
316 LOG.error("Could not get interface for route-paths: {} in vpn {}", vrfEntry.getRoutePaths(), rd);
317 LOG.warn("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
321 programRemoteFibForBgpRoutes(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
323 LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
326 private void deleteFibEntryForBgpRoutes(BigInteger remoteDpnId, long vpnId, VrfEntry vrfEntry,
327 String rd, WriteTransaction tx, List<SubTransaction> subTxns) {
328 // When the tunnel is removed the fib entries should be reprogrammed/deleted depending on
329 // the adjacencyResults.
330 List<NexthopManager.AdjacencyResult> adjacencyResults = resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
331 if (!adjacencyResults.isEmpty()) {
332 programRemoteFibForBgpRoutes(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
336 public void deleteRemoteRoute(@Nullable final BigInteger localDpnId, final BigInteger remoteDpnId,
337 final long vpnId, final VrfTablesKey vrfTableKey,
338 final VrfEntry vrfEntry, Optional<Routes> extraRouteOptional,
339 @Nullable WriteTransaction tx, List<SubTransaction> subTxns) {
341 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
342 newTx -> deleteRemoteRoute(localDpnId, remoteDpnId, vpnId, vrfTableKey, vrfEntry,
343 extraRouteOptional, newTx)), LOG, "Error deleting remote route");
347 LOG.debug("deleting remote route: prefix={}, vpnId={} localDpnId {} remoteDpnId {}",
348 vrfEntry.getDestPrefix(), vpnId, localDpnId, remoteDpnId);
349 String rd = vrfTableKey.getRouteDistinguisher();
351 if (localDpnId != null && localDpnId != BigInteger.ZERO) {
352 // localDpnId is not known when clean up happens for last vm for a vpn on a dpn
353 if (extraRouteOptional.isPresent()) {
354 nexthopManager.deleteLoadBalancingNextHop(vpnId, remoteDpnId, vrfEntry.getDestPrefix());
356 deleteFibEntryForBgpRoutes(remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
360 // below two reads are kept as is, until best way is found to identify dpnID
361 VpnNexthop localNextHopInfo = nexthopManager.getVpnNexthop(vpnId, vrfEntry.getDestPrefix());
362 if (extraRouteOptional.isPresent()) {
363 nexthopManager.deleteLoadBalancingNextHop(vpnId, remoteDpnId, vrfEntry.getDestPrefix());
365 checkDpnDeleteFibEntry(localNextHopInfo, remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
369 public Consumer<? super VrfEntry> getConsumerForCreatingRemoteFib(
370 final BigInteger dpnId, final long vpnId, final String rd,
371 final String remoteNextHopIp, final Optional<VrfTables> vrfTable,
372 WriteTransaction writeCfgTxn, List<SubTransaction> subTxns) {
373 return vrfEntry -> nullToEmpty(vrfEntry.getRoutePaths()).stream()
374 .filter(routes -> !routes.getNexthopAddress().isEmpty()
375 && remoteNextHopIp.trim().equals(routes.getNexthopAddress().trim()))
377 .ifPresent(routes -> {
378 LOG.trace("creating remote FIB entry for prefix {} rd {} on Dpn {}",
379 vrfEntry.getDestPrefix(), rd, dpnId);
380 createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
381 vrfEntry, writeCfgTxn, subTxns);
385 public Consumer<? super VrfEntry> getConsumerForDeletingRemoteFib(
386 final BigInteger dpnId, final long vpnId,
387 final String remoteNextHopIp, final Optional<VrfTables> vrfTable,
388 WriteTransaction writeCfgTxn, List<SubTransaction> subTxns) {
389 return vrfEntry -> nullToEmpty(vrfEntry.getRoutePaths()).stream()
390 .filter(routes -> !routes.getNexthopAddress().isEmpty()
391 && remoteNextHopIp.trim().equals(routes.getNexthopAddress().trim()))
393 .ifPresent(routes -> {
394 LOG.trace(" deleting remote FIB entry {}", vrfEntry);
395 deleteRemoteRoute(null, dpnId, vpnId, vrfTable.get().key(), vrfEntry,
396 Optional.absent(), writeCfgTxn, subTxns);
401 protected void addTunnelInterfaceActions(NexthopManager.AdjacencyResult adjacencyResult, long vpnId,
402 VrfEntry vrfEntry, List<ActionInfo> actionInfos, String rd) {
403 Class<? extends TunnelTypeBase> tunnelType = VpnExtraRouteHelper
404 .getTunnelType(getNextHopManager().getItmManager(), adjacencyResult.getInterfaceName());
405 if (tunnelType == null) {
406 LOG.debug("Tunnel type not found for vrfEntry {}", vrfEntry);
409 String nextHopIp = adjacencyResult.getNextHopIp();
410 if (tunnelType.equals(TunnelTypeMplsOverGre.class)) {
411 java.util.Optional<Long> optionalLabel = FibUtil.getLabelForNextHop(vrfEntry, nextHopIp);
412 if (!optionalLabel.isPresent()) {
413 LOG.warn("NextHopIp {} not found in vrfEntry {}", nextHopIp, vrfEntry);
416 long label = optionalLabel.get();
417 LOG.debug("addTunnelInterfaceActions: Push label action for prefix {} rd {} l3vni {} nextHop {}",
418 vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), nextHopIp);
419 actionInfos.add(new ActionPushMpls());
420 actionInfos.add(new ActionSetFieldMplsLabel(label));
421 actionInfos.add(new ActionNxLoadInPort(BigInteger.ZERO));
422 } else if (tunnelType.equals(TunnelTypeVxlan.class)) {
423 actionInfos.add(new ActionSetFieldTunnelId(BigInteger.valueOf(vrfEntry.getL3vni())));
424 LOG.debug("addTunnelInterfaceActions: adding set tunnel id action for prefix {} rd {} l3vni {}"
425 + " nextHop {} ", vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), nextHopIp);
426 addRewriteDstMacAction(vpnId, vrfEntry, null /*prefixInfo*/, actionInfos);
431 protected void addRewriteDstMacAction(long vpnId, VrfEntry vrfEntry, @Nullable Prefixes prefixInfo,
432 List<ActionInfo> actionInfos) {
433 if (vrfEntry.getGatewayMacAddress() != null) {
434 actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
435 new MacAddress(vrfEntry.getGatewayMacAddress())));