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 com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.List;
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
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.UpgradeState;
25 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
26 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
27 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
28 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
29 import org.opendaylight.genius.utils.batching.SubTransaction;
30 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
31 import org.opendaylight.netvirt.elanmanager.api.IElanService;
32 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.SubnetRoute;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
44 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
49 public class EvpnVrfEntryHandler extends BaseVrfEntryHandler implements IVrfEntryHandler {
50 private static final Logger LOG = LoggerFactory.getLogger(EvpnVrfEntryHandler.class);
51 private final ManagedNewTransactionRunner txRunner;
52 private final VrfEntryListener vrfEntryListener;
53 private final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler;
54 private final NexthopManager nexthopManager;
55 private final JobCoordinator jobCoordinator;
56 private final IElanService elanManager;
58 EvpnVrfEntryHandler(DataBroker broker, VrfEntryListener vrfEntryListener,
59 BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler, NexthopManager nexthopManager,
60 JobCoordinator jobCoordinator, IElanService elanManager, FibUtil fibUtil,
61 final UpgradeState upgradeState, final DataTreeEventCallbackRegistrar eventCallbacks) {
62 super(broker, nexthopManager, null, fibUtil, upgradeState, eventCallbacks);
63 this.txRunner = new ManagedNewTransactionRunnerImpl(broker);
64 this.vrfEntryListener = vrfEntryListener;
65 this.bgpRouteVrfEntryHandler = bgpRouteVrfEntryHandler;
66 this.nexthopManager = nexthopManager;
67 this.jobCoordinator = jobCoordinator;
68 this.elanManager = elanManager;
72 public void createFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
73 LOG.info("Initiating creation of Evpn Flows");
74 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
75 final VpnInstanceOpDataEntry vpnInstance = getFibUtil().getVpnInstanceOpData(
76 vrfTableKey.getRouteDistinguisher()).get();
77 Long vpnId = vpnInstance.getVpnId();
78 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
79 Preconditions.checkNotNull(vpnId, "Vpn Instance with rd " + vpnInstance.getVrfId()
80 + " has null vpnId!");
81 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.CONNECTED) {
82 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
83 final List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
84 final long elanTag = subnetRoute.getElantag();
85 LOG.trace("SubnetRoute augmented vrfentry found for rd {} prefix {} with elantag {}",
86 rd, vrfEntry.getDestPrefix(), elanTag);
87 if (vpnToDpnList != null) {
88 jobCoordinator.enqueueJob("FIB-" + rd + "-" + vrfEntry.getDestPrefix(),
89 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
90 for (final VpnToDpnList curDpn : vpnToDpnList) {
91 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
92 vrfEntryListener.installSubnetRouteInFib(curDpn.getDpnId(), elanTag, rd,
100 Prefixes localNextHopInfo = getFibUtil().getPrefixToInterface(vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
101 List<BigInteger> localDpnId = new ArrayList<>();
102 boolean isNatPrefix = false;
103 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
104 LOG.info("NAT Prefix {} with vpnId {} rd {}. Skip local dpn {} FIB processing",
105 vrfEntry.getDestPrefix(), vpnId, rd, localNextHopInfo.getDpnId());
106 localDpnId.add(localNextHopInfo.getDpnId());
109 localDpnId = createLocalEvpnFlows(vpnInstance.getVpnId(), rd, vrfEntry,
112 createRemoteEvpnFlows(rd, vrfEntry, vpnInstance, localDpnId, vrfTableKey, isNatPrefix);
116 public void removeFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
117 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
118 final VpnInstanceOpDataEntry vpnInstance = getFibUtil().getVpnInstanceOpData(
119 vrfTableKey.getRouteDistinguisher()).get();
120 if (vpnInstance == null) {
121 LOG.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
124 VpnNexthop localNextHopInfo = nexthopManager.getVpnNexthop(vpnInstance.getVpnId(),
125 vrfEntry.getDestPrefix());
126 List<BigInteger> localDpnId = checkDeleteLocalEvpnFLows(vpnInstance.getVpnId(), rd, vrfEntry, localNextHopInfo);
127 deleteRemoteEvpnFlows(rd, vrfEntry, vpnInstance, vrfTableKey, localDpnId);
128 vrfEntryListener.cleanUpOpDataForFib(vpnInstance.getVpnId(), rd, vrfEntry);
132 public void updateFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update, String rd) {
136 private List<BigInteger> createLocalEvpnFlows(long vpnId, String rd, VrfEntry vrfEntry,
137 Prefixes localNextHopInfo) {
138 List<BigInteger> returnLocalDpnId = new ArrayList<>();
139 String localNextHopIP = vrfEntry.getDestPrefix();
140 if (localNextHopInfo == null) {
141 //Handle extra routes and imported routes
142 Routes extraRoute = getVpnToExtraroute(vpnId, rd, vrfEntry.getDestPrefix());
143 if (extraRoute != null) {
144 for (String nextHopIp : extraRoute.getNexthopIpList()) {
145 LOG.info("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
146 if (nextHopIp != null) {
147 localNextHopInfo = getFibUtil().getPrefixToInterface(vpnId, nextHopIp + "/32");
148 if (localNextHopInfo != null) {
149 localNextHopIP = nextHopIp + "/32";
150 BigInteger dpnId = checkCreateLocalEvpnFlows(localNextHopInfo, localNextHopIP, vpnId,
152 returnLocalDpnId.add(dpnId);
158 LOG.info("Creating local EVPN flows for prefix {} rd {} route-paths {} evi {}.",
159 vrfEntry.getDestPrefix(), rd, vrfEntry.getRoutePaths(), vrfEntry.getL3vni());
160 BigInteger dpnId = checkCreateLocalEvpnFlows(localNextHopInfo, localNextHopIP, vpnId,
162 returnLocalDpnId.add(dpnId);
164 return returnLocalDpnId;
167 private BigInteger checkCreateLocalEvpnFlows(Prefixes localNextHopInfo, String localNextHopIP,
168 final Long vpnId, final String rd,
169 final VrfEntry vrfEntry) {
170 final BigInteger dpnId = localNextHopInfo.getDpnId();
171 String jobKey = "FIB-" + vpnId.toString() + "-" + dpnId.toString() + "-" + vrfEntry.getDestPrefix();
172 final long groupId = nexthopManager.createLocalNextHop(vpnId, dpnId,
173 localNextHopInfo.getVpnInterfaceName(), localNextHopIP, vrfEntry.getDestPrefix(),
174 vrfEntry.getGatewayMacAddress(), jobKey);
175 LOG.debug("LocalNextHopGroup {} created/reused for prefix {} rd {} evi {} route-paths {}", groupId,
176 vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), vrfEntry.getRoutePaths());
178 final List<InstructionInfo> instructions = Collections.singletonList(
179 new InstructionApplyActions(
180 Collections.singletonList(new ActionGroup(groupId))));
181 jobCoordinator.enqueueJob("FIB-" + vpnId.toString() + "-" + dpnId.toString()
182 + "-" + vrfEntry.getDestPrefix(),
183 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
184 tx -> makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions, NwConstants.ADD_FLOW, tx,
189 private void createRemoteEvpnFlows(String rd, VrfEntry vrfEntry, VpnInstanceOpDataEntry vpnInstance,
190 List<BigInteger> localDpnId, VrfTablesKey vrfTableKey, boolean isNatPrefix) {
191 LOG.info("Creating remote EVPN flows for prefix {} rd {} route-paths {} evi {}",
192 vrfEntry.getDestPrefix(), rd, vrfEntry.getRoutePaths(), vrfEntry.getL3vni());
193 List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
194 if (vpnToDpnList != null) {
195 jobCoordinator.enqueueJob("FIB" + rd + vrfEntry.getDestPrefix(),
196 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
197 for (VpnToDpnList vpnDpn : vpnToDpnList) {
198 if (!localDpnId.contains(vpnDpn.getDpnId())) {
199 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
200 createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(),
201 vrfTableKey, vrfEntry, isNatPrefix, tx);
209 private void createRemoteFibEntry(final BigInteger remoteDpnId, final long vpnId, final VrfTablesKey vrfTableKey,
210 final VrfEntry vrfEntry, boolean isNatPrefix, WriteTransaction tx) {
212 String rd = vrfTableKey.getRouteDistinguisher();
213 List<SubTransaction> subTxns = new ArrayList<>();
214 LOG.debug("createremotefibentry: adding route {} for rd {} with transaction {}",
215 vrfEntry.getDestPrefix(), rd, tx);
216 List<NexthopManager.AdjacencyResult> tunnelInterfaceList = resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
218 if (tunnelInterfaceList.isEmpty()) {
219 LOG.error("Could not get interface for route-paths: {} in vpn {}",
220 vrfEntry.getRoutePaths(), rd);
221 LOG.warn("Failed to add Route: {} in vpn: {}",
222 vrfEntry.getDestPrefix(), rd);
226 for (NexthopManager.AdjacencyResult adjacencyResult : tunnelInterfaceList) {
227 List<ActionInfo> actionInfos = new ArrayList<>();
229 String prefix = adjacencyResult.getPrefix();
230 Prefixes prefixInfo = getFibUtil().getPrefixToInterface(vpnId, prefix);
231 String interfaceName = prefixInfo.getVpnInterfaceName();
232 if (vrfEntry.getOrigin().equals(RouteOrigin.BGP.getValue()) || isNatPrefix) {
233 tunnelId = BigInteger.valueOf(vrfEntry.getL3vni());
234 } else if (elanManager.isOpenStackVniSemanticsEnforced()) {
235 tunnelId = BigInteger.valueOf(getFibUtil().getVniForVxlanNetwork(prefixInfo.getSubnetId()).get());
237 Interface interfaceState = getFibUtil().getInterfaceStateFromOperDS(interfaceName);
238 tunnelId = BigInteger.valueOf(interfaceState.getIfIndex());
240 LOG.debug("adding set tunnel id action for label {}", tunnelId);
241 String macAddress = null;
242 String vpnName = getFibUtil().getVpnNameFromId(vpnId);
243 if (vpnName == null) {
244 LOG.debug("Failed to get VPN name for vpnId {}", vpnId);
247 if (interfaceName != null) {
248 macAddress = getFibUtil().getMacAddressFromPrefix(interfaceName, vpnName, prefix);
249 actionInfos.add(new ActionSetFieldEthernetDestination(new MacAddress(macAddress)));
251 actionInfos.add(new ActionSetFieldTunnelId(tunnelId));
252 List<ActionInfo> egressActions =
253 nexthopManager.getEgressActionsForInterface(adjacencyResult.getInterfaceName(), actionInfos.size(),
255 if (egressActions.isEmpty()) {
256 LOG.error("Failed to retrieve egress action for prefix {} route-paths {} interface {}."
257 + " Aborting remote FIB entry creation..", vrfEntry.getDestPrefix(),
258 vrfEntry.getRoutePaths(), adjacencyResult.getInterfaceName());
261 actionInfos.addAll(egressActions);
262 List<InstructionInfo> instructions = new ArrayList<>();
263 instructions.add(new InstructionApplyActions(actionInfos));
264 makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions, NwConstants.ADD_FLOW, tx, subTxns);
266 LOG.debug("Successfully added FIB entry for prefix {} in rd {}", vrfEntry.getDestPrefix(), rd);
269 private void deleteRemoteEvpnFlows(String rd, VrfEntry vrfEntry, VpnInstanceOpDataEntry vpnInstance,
270 VrfTablesKey vrfTableKey, List<BigInteger> localDpnIdList) {
271 List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
272 List<SubTransaction> subTxns = new ArrayList<>();
273 if (vpnToDpnList != null) {
274 jobCoordinator.enqueueJob("FIB" + rd + vrfEntry.getDestPrefix(),
275 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
276 final Optional<Routes> extraRouteOptional = Optional.absent();
277 if (localDpnIdList.size() <= 0) {
278 for (VpnToDpnList curDpn1 : vpnToDpnList) {
279 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
280 if (curDpn1.getDpnState() == VpnToDpnList.DpnState.Active) {
281 bgpRouteVrfEntryHandler.deleteRemoteRoute(BigInteger.ZERO, curDpn1.getDpnId(),
282 vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
283 extraRouteOptional, tx, subTxns);
286 deleteRemoteRoute(BigInteger.ZERO, curDpn1.getDpnId(),
287 vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
288 extraRouteOptional, tx);
292 for (BigInteger localDpnId : localDpnIdList) {
293 for (VpnToDpnList curDpn2 : vpnToDpnList) {
294 if (!curDpn2.getDpnId().equals(localDpnId)) {
295 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
296 if (curDpn2.getDpnState() == VpnToDpnList.DpnState.Active) {
297 bgpRouteVrfEntryHandler.deleteRemoteRoute(localDpnId, curDpn2.getDpnId(),
298 vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
299 extraRouteOptional, tx, subTxns);
302 deleteRemoteRoute(localDpnId, curDpn2.getDpnId(),
303 vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
304 extraRouteOptional, tx);
314 private List<BigInteger> checkDeleteLocalEvpnFLows(long vpnId, String rd, VrfEntry vrfEntry,
315 VpnNexthop localNextHopInfo) {
316 List<BigInteger> returnLocalDpnId = new ArrayList<>();
317 if (localNextHopInfo == null) {
318 //Handle extra routes and imported routes
320 final BigInteger dpnId = localNextHopInfo.getDpnId();
321 jobCoordinator.enqueueJob("FIB-" + rd + "-" + vrfEntry.getDestPrefix(),
322 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
323 tx -> makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null, NwConstants.DEL_FLOW, tx,
325 //TODO: verify below adjacency call need to be optimized (?)
326 deleteLocalAdjacency(dpnId, vpnId, vrfEntry.getDestPrefix(), vrfEntry.getDestPrefix());
327 returnLocalDpnId.add(dpnId);
329 return returnLocalDpnId;