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.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;
63 public class BgpRouteVrfEntryHandler extends BaseVrfEntryHandler
64 implements ResourceHandler, IVrfEntryHandler {
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;
70 private final DataBroker dataBroker;
71 private final BlockingQueue<ActionableResource> vrfEntryBufferQ = new LinkedBlockingQueue<>();
72 private final ResourceBatchingManager resourceBatchingManager;
73 private final NexthopManager nexthopManager;
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;
83 resourceBatchingManager = ResourceBatchingManager.getInstance();
84 resourceBatchingManager.registerBatchableResource("FIB-VRFENTRY", vrfEntryBufferQ, this);
89 LOG.info("{} start", getClass().getSimpleName());
94 LOG.info("{} close", getClass().getSimpleName());
98 public DataBroker getResourceBroker() {
103 public int getBatchSize() {
108 public int getBatchInterval() {
109 return BATCH_INTERVAL;
113 public LogicalDatastoreType getDatastoreType() {
114 return LogicalDatastoreType.CONFIGURATION;
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);
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);
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);
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);
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);
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);
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
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);
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!");
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);
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
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);
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");
222 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
223 usedRds.get(0), vrfEntry.getDestPrefix());
226 extraRouteOptional = Optional.absent();
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);
237 public void programRemoteFibForBgpRoutes(final BigInteger remoteDpnId,
239 final VrfEntry vrfEntry,
242 List<NexthopManager.AdjacencyResult> adjacencyResults,
243 List<SubTransaction> subTxns) {
244 Preconditions.checkArgument(vrfEntry.getRoutePaths().size() <= 2);
246 if (adjacencyResults.size() == 1) {
247 programRemoteFib(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
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);
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);
266 long label = optionalLabel.get();
268 actionInfos.add(new ActionRegLoad(index, FibConstants.NXM_REG_MAPPING.get(index++), 0,
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);
277 public void createRemoteFibEntry(final BigInteger remoteDpnId,
280 final VrfEntry vrfEntry,
282 List<SubTransaction> subTxns) {
283 Boolean wrTxPresent = true;
286 tx = dataBroker.newWriteOnlyTransaction();
289 LOG.debug("createRemoteFibEntry: adding route {} for rd {} on remoteDpnId {}",
290 vrfEntry.getDestPrefix(), rd, remoteDpnId);
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);
300 programRemoteFibForBgpRoutes(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
305 LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
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);
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) {
323 Boolean wrTxPresent = true;
326 tx = dataBroker.newWriteOnlyTransaction();
329 LOG.debug("deleting remote route: prefix={}, vpnId={} localDpnId {} remoteDpnId {}",
330 vrfEntry.getDestPrefix(), vpnId, localDpnId, remoteDpnId);
331 String rd = vrfTableKey.getRouteDistinguisher();
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);
339 deleteFibEntryForBgpRoutes(remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
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);
349 checkDpnDeleteFibEntry(localNextHopInfo, remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
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()))
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);
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()))
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);
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);
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);
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);
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())));