2 * Copyright (c) 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 org.opendaylight.genius.infra.Datastore.CONFIGURATION;
12 import com.google.common.base.Preconditions;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.List;
16 import java.util.Objects;
17 import java.util.Optional;
18 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
19 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
20 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
21 import org.opendaylight.genius.mdsalutil.ActionInfo;
22 import org.opendaylight.genius.mdsalutil.InstructionInfo;
23 import org.opendaylight.genius.mdsalutil.NwConstants;
24 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
25 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
26 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
27 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
28 import org.opendaylight.genius.utils.batching.SubTransaction;
29 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
30 import org.opendaylight.mdsal.binding.api.DataBroker;
31 import org.opendaylight.mdsal.binding.api.WriteTransaction;
32 import org.opendaylight.mdsal.common.api.ReadFailedException;
33 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
34 import org.opendaylight.serviceutils.upgrade.UpgradeState;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelOperStatus;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelList;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.SubnetRoute;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
47 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
48 import org.opendaylight.yangtools.yang.common.Uint32;
49 import org.opendaylight.yangtools.yang.common.Uint64;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
54 public class EvpnVrfEntryHandler extends BaseVrfEntryHandler {
55 private static final Logger LOG = LoggerFactory.getLogger(EvpnVrfEntryHandler.class);
56 private final ManagedNewTransactionRunner txRunner;
57 private final VrfEntryListener vrfEntryListener;
58 private final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler;
59 private final NexthopManager nexthopManager;
60 private final JobCoordinator jobCoordinator;
62 EvpnVrfEntryHandler(DataBroker broker, VrfEntryListener vrfEntryListener,
63 BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler, NexthopManager nexthopManager,
64 JobCoordinator jobCoordinator, FibUtil fibUtil,
65 final UpgradeState upgradeState, final DataTreeEventCallbackRegistrar eventCallbacks) {
66 super(broker, nexthopManager, null, fibUtil, upgradeState, eventCallbacks);
67 this.txRunner = new ManagedNewTransactionRunnerImpl(broker);
68 this.vrfEntryListener = vrfEntryListener;
69 this.bgpRouteVrfEntryHandler = bgpRouteVrfEntryHandler;
70 this.nexthopManager = nexthopManager;
71 this.jobCoordinator = jobCoordinator;
74 void createFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
75 LOG.info("Initiating creation of Evpn Flows");
76 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
77 final VpnInstanceOpDataEntry vpnInstance = getFibUtil().getVpnInstanceOpData(
78 vrfTableKey.getRouteDistinguisher()).get();
79 Uint32 vpnId = vpnInstance.getVpnId();
80 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
81 Preconditions.checkNotNull(vpnId, "Vpn Instance with rd " + vpnInstance.getVrfId()
82 + " has null vpnId!");
83 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.CONNECTED) {
84 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
85 final List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
86 final long elanTag = subnetRoute.getElantag().toJava();
87 LOG.trace("SubnetRoute augmented vrfentry found for rd {} prefix {} with elantag {}",
88 rd, vrfEntry.getDestPrefix(), elanTag);
89 if (vpnToDpnList != null) {
90 jobCoordinator.enqueueJob("FIB-" + rd + "-" + vrfEntry.getDestPrefix(),
91 () -> Collections.singletonList(
92 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
93 for (final VpnToDpnList curDpn : vpnToDpnList) {
94 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
95 vrfEntryListener.installSubnetRouteInFib(curDpn.getDpnId(), elanTag, rd,
103 Prefixes localNextHopInfo = getFibUtil().getPrefixToInterface(vpnInstance.getVpnId(),
104 vrfEntry.getDestPrefix());
105 List<Uint64> localDpnId = new ArrayList<>();
106 boolean isNatPrefix = false;
107 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
108 LOG.info("NAT Prefix {} with vpnId {} rd {}. Skip local dpn {} FIB processing",
109 vrfEntry.getDestPrefix(), vpnId, rd, localNextHopInfo.getDpnId());
110 localDpnId.add(localNextHopInfo.getDpnId());
113 localDpnId = createLocalEvpnFlows(vpnInstance.getVpnId(), rd, vrfEntry,
116 createRemoteEvpnFlows(rd, vrfEntry, vpnInstance, localDpnId, vrfTableKey, isNatPrefix);
119 void removeFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
120 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
121 final VpnInstanceOpDataEntry vpnInstance = getFibUtil().getVpnInstanceOpData(
122 vrfTableKey.getRouteDistinguisher()).get();
123 if (vpnInstance == null) {
124 LOG.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
127 VpnNexthop localNextHopInfo = nexthopManager.getVpnNexthop(vpnInstance.getVpnId(),
128 vrfEntry.getDestPrefix());
129 List<Uint64> localDpnId = checkDeleteLocalEvpnFLows(vpnInstance.getVpnId(),
130 rd, vrfEntry, localNextHopInfo);
131 deleteRemoteEvpnFlows(rd, vrfEntry, vpnInstance, vrfTableKey, localDpnId);
132 vrfEntryListener.cleanUpOpDataForFib(vpnInstance.getVpnId(), rd, vrfEntry);
135 private List<Uint64> createLocalEvpnFlows(Uint32 vpnId, String rd, VrfEntry vrfEntry,
136 Prefixes localNextHopInfo) {
137 List<Uint64> returnLocalDpnId = new ArrayList<>();
138 String localNextHopIP = vrfEntry.getDestPrefix();
139 if (localNextHopInfo == null) {
140 //Handle extra routes and imported routes
141 Routes extraRoute = getVpnToExtraroute(vpnId, rd, vrfEntry.getDestPrefix());
142 if (extraRoute != null && extraRoute.getNexthopIpList() != null) {
143 for (String nextHopIp : extraRoute.getNexthopIpList()) {
144 LOG.info("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
145 if (nextHopIp != null) {
146 localNextHopInfo = getFibUtil().getPrefixToInterface(vpnId, nextHopIp + "/32");
147 if (localNextHopInfo != null) {
148 localNextHopIP = nextHopIp + "/32";
149 Uint64 dpnId = checkCreateLocalEvpnFlows(localNextHopInfo, localNextHopIP, vpnId,
151 returnLocalDpnId.add(dpnId);
157 LOG.info("Creating local EVPN flows for prefix {} rd {} route-paths {} evi {}.",
158 vrfEntry.getDestPrefix(), rd, vrfEntry.getRoutePaths(), vrfEntry.getL3vni());
159 Uint64 dpnId = checkCreateLocalEvpnFlows(localNextHopInfo, localNextHopIP, vpnId,
161 returnLocalDpnId.add(dpnId);
163 return returnLocalDpnId;
166 // Allow deprecated TransactionRunner calls for now
167 @SuppressWarnings("ForbidCertainMethod")
168 private Uint64 checkCreateLocalEvpnFlows(Prefixes localNextHopInfo, String localNextHopIP,
169 final Uint32 vpnId, final String rd,
170 final VrfEntry vrfEntry) {
171 final Uint64 dpnId = localNextHopInfo.getDpnId();
172 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix());
173 final long groupId = nexthopManager.createLocalNextHop(vpnId, dpnId,
174 localNextHopInfo.getVpnInterfaceName(), localNextHopIP, vrfEntry.getDestPrefix(),
175 vrfEntry.getGatewayMacAddress(), /*parentVpnId*/ null);
176 LOG.debug("LocalNextHopGroup {} created/reused for prefix {} rd {} evi {} route-paths {}", groupId,
177 vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), vrfEntry.getRoutePaths());
179 final List<InstructionInfo> instructions = Collections.singletonList(
180 new InstructionApplyActions(
181 Collections.singletonList(new ActionGroup(groupId))));
182 jobCoordinator.enqueueJob(jobKey,
183 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
184 tx -> makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions, NwConstants.ADD_FLOW, tx,
189 // Allow deprecated TransactionRunner calls for now
190 @SuppressWarnings("ForbidCertainMethod")
191 private void createRemoteEvpnFlows(String rd, VrfEntry vrfEntry, VpnInstanceOpDataEntry vpnInstance,
192 List<Uint64> localDpnId, VrfTablesKey vrfTableKey, boolean isNatPrefix) {
193 LOG.info("Creating remote EVPN flows for prefix {} rd {} route-paths {} evi {}",
194 vrfEntry.getDestPrefix(), rd, vrfEntry.getRoutePaths(), vrfEntry.getL3vni());
195 List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
196 if (vpnToDpnList != null) {
197 jobCoordinator.enqueueJob("FIB" + rd + vrfEntry.getDestPrefix(),
198 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
199 for (VpnToDpnList vpnDpn : vpnToDpnList) {
200 if (!localDpnId.contains(vpnDpn.getDpnId())) {
201 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
202 createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(),
203 vrfTableKey, vrfEntry, isNatPrefix, tx);
211 private void createRemoteFibEntry(final Uint64 remoteDpnId, final Uint32 vpnId, final VrfTablesKey vrfTableKey,
212 final VrfEntry vrfEntry, boolean isNatPrefix, WriteTransaction tx) {
214 String rd = vrfTableKey.getRouteDistinguisher();
215 List<SubTransaction> subTxns = new ArrayList<>();
216 LOG.debug("createremotefibentry: adding route {} for rd {} with transaction {}",
217 vrfEntry.getDestPrefix(), rd, tx);
218 List<NexthopManager.AdjacencyResult> tunnelInterfaceList = resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
220 if (tunnelInterfaceList.isEmpty()) {
221 LOG.error("Could not get interface for route-paths: {} in vpn {}",
222 vrfEntry.getRoutePaths(), rd);
223 LOG.warn("Failed to add Route: {} in vpn: {}",
224 vrfEntry.getDestPrefix(), rd);
228 for (NexthopManager.AdjacencyResult adjacencyResult : tunnelInterfaceList) {
229 List<ActionInfo> actionInfos = new ArrayList<>();
230 Uint64 tunnelId = Uint64.ZERO;
231 String prefix = adjacencyResult.getPrefix();
232 Prefixes prefixInfo = getFibUtil().getPrefixToInterface(vpnId, prefix);
233 String interfaceName = prefixInfo.getVpnInterfaceName();
234 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin()) || isNatPrefix) {
235 tunnelId = Uint64.valueOf(vrfEntry.getL3vni().longValue());
236 } else if (FibUtil.isVxlanNetwork(prefixInfo.getNetworkType())) {
237 tunnelId = Uint64.valueOf(prefixInfo.getSegmentationId().longValue());
240 StateTunnelList stateTunnelList = getFibUtil().getTunnelState(interfaceName);
241 if (stateTunnelList == null || stateTunnelList.getOperState() != TunnelOperStatus.Up) {
242 LOG.trace("Tunnel is not up for interface {}", interfaceName);
245 tunnelId = Uint64.valueOf(stateTunnelList.getIfIndex().intValue());
246 } catch (ReadFailedException e) {
247 LOG.error("createRemoteFibEntry: error in fetching tunnel state for interface {}",
252 LOG.debug("adding set tunnel id action for label {}", tunnelId);
253 String macAddress = null;
254 String vpnName = getFibUtil().getVpnNameFromId(vpnId);
255 if (vpnName == null) {
256 LOG.debug("Failed to get VPN name for vpnId {}", vpnId);
259 if (interfaceName != null) {
260 macAddress = getFibUtil().getMacAddressFromPrefix(interfaceName, vpnName, prefix);
261 actionInfos.add(new ActionSetFieldEthernetDestination(new MacAddress(macAddress)));
263 actionInfos.add(new ActionSetFieldTunnelId(tunnelId));
264 List<ActionInfo> egressActions =
265 nexthopManager.getEgressActionsForInterface(adjacencyResult.getInterfaceName(), actionInfos.size(),
266 true, vpnId, vrfEntry.getDestPrefix());
267 if (egressActions.isEmpty()) {
268 LOG.error("Failed to retrieve egress action for prefix {} route-paths {} interface {}."
269 + " Aborting remote FIB entry creation..", vrfEntry.getDestPrefix(),
270 vrfEntry.getRoutePaths(), adjacencyResult.getInterfaceName());
273 actionInfos.addAll(egressActions);
274 List<InstructionInfo> instructions = new ArrayList<>();
275 instructions.add(new InstructionApplyActions(actionInfos));
276 makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions, NwConstants.ADD_FLOW, tx, subTxns);
278 LOG.debug("Successfully added FIB entry for prefix {} in rd {}", vrfEntry.getDestPrefix(), rd);
281 // Allow deprecated TransactionRunner calls for now
282 @SuppressWarnings("ForbidCertainMethod")
283 private void deleteRemoteEvpnFlows(String rd, VrfEntry vrfEntry, VpnInstanceOpDataEntry vpnInstance,
284 VrfTablesKey vrfTableKey, List<Uint64> localDpnIdList) {
285 List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
286 List<SubTransaction> subTxns = new ArrayList<>();
287 if (vpnToDpnList != null) {
288 jobCoordinator.enqueueJob("FIB" + rd + vrfEntry.getDestPrefix(),
289 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
290 final Optional<Routes> extraRouteOptional = Optional.empty();
291 if (localDpnIdList.size() <= 0) {
292 for (VpnToDpnList curDpn1 : vpnToDpnList) {
293 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
294 if (curDpn1.getDpnState() == VpnToDpnList.DpnState.Active) {
295 bgpRouteVrfEntryHandler.deleteRemoteRoute(Uint64.ZERO,
297 vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
298 extraRouteOptional, tx, subTxns);
301 deleteRemoteRoute(Uint64.ZERO, curDpn1.getDpnId(),
302 vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
303 extraRouteOptional, tx);
307 for (Uint64 localDpnId : localDpnIdList) {
308 for (VpnToDpnList curDpn2 : vpnToDpnList) {
309 if (!Objects.equals(curDpn2.getDpnId(), localDpnId)) {
310 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
311 if (curDpn2.getDpnState() == VpnToDpnList.DpnState.Active) {
312 bgpRouteVrfEntryHandler.deleteRemoteRoute(localDpnId,
314 vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
315 extraRouteOptional, tx, subTxns);
318 deleteRemoteRoute(localDpnId, curDpn2.getDpnId(),
319 vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
320 extraRouteOptional, tx);
330 // Allow deprecated TransactionRunner calls for now
331 @SuppressWarnings("ForbidCertainMethod")
332 private List<Uint64> checkDeleteLocalEvpnFLows(Uint32 vpnId, String rd, VrfEntry vrfEntry,
333 VpnNexthop localNextHopInfo) {
334 List<Uint64> returnLocalDpnId = new ArrayList<>();
335 if (localNextHopInfo == null) {
336 //Handle extra routes and imported routes
338 final Uint64 dpnId = localNextHopInfo.getDpnId();
339 jobCoordinator.enqueueJob("FIB-" + rd + "-" + vrfEntry.getDestPrefix(),
340 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
341 tx -> makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null, NwConstants.DEL_FLOW, tx,
343 //TODO: verify below adjacency call need to be optimized (?)
344 deleteLocalAdjacency(dpnId, vpnId, vrfEntry.getDestPrefix(), vrfEntry.getDestPrefix());
345 returnLocalDpnId.add(dpnId);
347 return returnLocalDpnId;