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;
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;
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;
65 public class BgpRouteVrfEntryHandler extends BaseVrfEntryHandler
66 implements AutoCloseable, ResourceHandler, IVrfEntryHandler {
68 private static final Logger LOG = LoggerFactory.getLogger(BgpRouteVrfEntryHandler.class);
69 private final DataBroker dataBroker;
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;
78 public BgpRouteVrfEntryHandler(final DataBroker dataBroker,
79 final NexthopManager nexthopManager) {
80 super(dataBroker, nexthopManager, null);
81 this.dataBroker = dataBroker;
82 this.nexthopManager = nexthopManager;
84 resourceBatchingManager = ResourceBatchingManager.getInstance();
85 resourceBatchingManager.registerBatchableResource("FIB-VRFENTRY", vrfEntryBufferQ, this);
90 LOG.info("{} start", getClass().getSimpleName());
94 public void close() throws Exception {
95 LOG.info("{} close", getClass().getSimpleName());
99 public DataBroker getResourceBroker() {
104 public int getBatchSize() {
109 public int getBatchInterval() {
110 return BATCH_INTERVAL;
114 public LogicalDatastoreType getDatastoreType() {
115 return LogicalDatastoreType.CONFIGURATION;
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);
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);
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);
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);
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);
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);
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
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);
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!");
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);
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
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);
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");
223 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
224 usedRds.get(0), vrfEntry.getDestPrefix());
227 extraRouteOptional = Optional.absent();
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);
238 public void programRemoteFibForBgpRoutes(final BigInteger remoteDpnId,
240 final VrfEntry vrfEntry,
243 List<NexthopManager.AdjacencyResult> adjacencyResults,
244 List<SubTransaction> subTxns) {
245 Preconditions.checkArgument(vrfEntry.getRoutePaths().size() <= 2);
247 if (adjacencyResults.size() == 1) {
248 programRemoteFib(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
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);
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);
267 long label = optionalLabel.get();
269 actionInfos.add(new ActionRegLoad(index, FibConstants.NXM_REG_MAPPING.get(index++), 0,
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);
278 public void createRemoteFibEntry(final BigInteger remoteDpnId,
281 final VrfEntry vrfEntry,
283 List<SubTransaction> subTxns) {
284 Boolean wrTxPresent = true;
287 tx = dataBroker.newWriteOnlyTransaction();
290 LOG.debug("createRemoteFibEntry: adding route {} for rd {} on remoteDpnId {}",
291 vrfEntry.getDestPrefix(), rd, remoteDpnId);
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);
301 programRemoteFibForBgpRoutes(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
306 LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
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);
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) {
324 Boolean wrTxPresent = true;
327 tx = dataBroker.newWriteOnlyTransaction();
330 LOG.debug("deleting remote route: prefix={}, vpnId={} localDpnId {} remoteDpnId {}",
331 vrfEntry.getDestPrefix(), vpnId, localDpnId, remoteDpnId);
332 String rd = vrfTableKey.getRouteDistinguisher();
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);
340 deleteFibEntryForBgpRoutes(remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
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);
350 checkDpnDeleteFibEntry(localNextHopInfo, remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
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()))
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);
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()))
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);
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);
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);
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);
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())));