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 com.google.common.util.concurrent.ListenableFuture;
13 import java.math.BigInteger;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.concurrent.Callable;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
20 import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
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.netvirt.fibmanager.api.RouteOrigin;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 public class EVPNVrfEntryProcessor {
44 private final Logger logger = LoggerFactory.getLogger(EVPNVrfEntryProcessor.class);
45 private VrfEntry vrfEntry;
46 private VrfEntry updatedVrfEntry;
47 private InstanceIdentifier<VrfEntry> identifier;
48 private DataBroker dataBroker;
49 private VrfEntryListener vrfEntryListener;
51 EVPNVrfEntryProcessor(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, DataBroker broker,
52 VrfEntryListener vrfEntryListener) {
53 this.identifier = identifier;
54 this.vrfEntry = vrfEntry;
55 this.dataBroker = broker;
56 this.vrfEntryListener = vrfEntryListener;
59 EVPNVrfEntryProcessor(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update,
60 DataBroker broker, VrfEntryListener vrfEntryListener) {
61 this(identifier, original, broker, vrfEntryListener);
62 this.updatedVrfEntry = update;
65 public void installFlows() {
66 logger.info("Initiating creation of Evpn Flows");
67 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
68 final String rd = vrfTableKey.getRouteDistinguisher();
69 final VpnInstanceOpDataEntry vpnInstance = FibUtil.getVpnInstanceOpData(dataBroker,
70 vrfTableKey.getRouteDistinguisher()).get();
71 Long vpnId = vpnInstance.getVpnId();
72 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
73 Preconditions.checkNotNull(vpnId, "Vpn Instance with rd " + vpnInstance.getVrfId()
74 + " has null vpnId!");
75 Prefixes localNextHopInfo = FibUtil.getPrefixToInterface(dataBroker, vpnInstance.getVpnId(),
76 vrfEntry.getDestPrefix());
77 List<BigInteger> localDpnId = new ArrayList<>();
78 boolean isNatPrefix = false;
79 if (Boolean.TRUE.equals(localNextHopInfo.isNatPrefix())) {
80 logger.info("NAT Prefix {} with vpnId {} rd {}. Skip local dpn {} FIB processing",
81 vrfEntry.getDestPrefix(), vpnId, rd, localNextHopInfo.getDpnId());
82 localDpnId.add(localNextHopInfo.getDpnId());
85 localDpnId = createLocalEvpnFlows(vpnInstance.getVpnId(), rd, vrfEntry,
88 createRemoteEvpnFlows(rd, vrfEntry, vpnInstance, localDpnId, vrfTableKey, isNatPrefix);
91 private List<BigInteger> createLocalEvpnFlows(long vpnId, String rd, VrfEntry vrfEntry,
92 Prefixes localNextHopInfo) {
93 List<BigInteger> returnLocalDpnId = new ArrayList<>();
94 if (localNextHopInfo == null) {
95 //Handle extra routes and imported routes
96 Routes extraRoute = vrfEntryListener.getVpnToExtraroute(rd, vrfEntry.getDestPrefix());
97 if (extraRoute != null) {
98 for (String nextHopIp : extraRoute.getNexthopIpList()) {
99 logger.info("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
100 if (nextHopIp != null) {
101 localNextHopInfo = FibUtil.getPrefixToInterface(dataBroker, vpnId, nextHopIp + "/32");
102 if (localNextHopInfo != null) {
103 String localNextHopIP = nextHopIp + "/32";
104 BigInteger dpnId = checkCreateLocalEvpnFlows(localNextHopInfo, localNextHopIP, vpnId,
106 returnLocalDpnId.add(dpnId);
112 logger.info("Creating local EVPN flows for prefix {} rd {} route-paths {} evi {}.",
113 vrfEntry.getDestPrefix(), rd, vrfEntry.getRoutePaths(), vrfEntry.getL3vni());
114 String localNextHopIP = vrfEntry.getDestPrefix();
115 BigInteger dpnId = checkCreateLocalEvpnFlows(localNextHopInfo, localNextHopIP, vpnId,
117 returnLocalDpnId.add(dpnId);
119 return returnLocalDpnId;
122 private BigInteger checkCreateLocalEvpnFlows(Prefixes localNextHopInfo, String localNextHopIP,
123 final Long vpnId, final String rd,
124 final VrfEntry vrfEntry) {
125 final BigInteger dpnId = localNextHopInfo.getDpnId();
126 String jobKey = "FIB-" + vpnId.toString() + "-" + dpnId.toString() + "-" + vrfEntry.getDestPrefix();
127 final long groupId = vrfEntryListener.getNextHopManager().createLocalNextHop(vpnId, dpnId,
128 localNextHopInfo.getVpnInterfaceName(), localNextHopIP, vrfEntry.getDestPrefix(),
129 vrfEntry.getGatewayMacAddress(), jobKey);
130 logger.debug("LocalNextHopGroup {} created/reused for prefix {} rd {} evi {} route-paths {}", groupId,
131 vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), vrfEntry.getRoutePaths());
133 final List<InstructionInfo> instructions = Collections.singletonList(
134 new InstructionApplyActions(
135 Collections.singletonList(new ActionGroup(groupId))));
136 DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
137 dataStoreCoordinator.enqueueJob("FIB-" + vpnId.toString() + "-" + dpnId.toString()
138 + "-" + vrfEntry.getDestPrefix(),
139 new Callable<List<ListenableFuture<Void>>>() {
141 public List<ListenableFuture<Void>> call() throws Exception {
142 WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
143 vrfEntryListener.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
144 NwConstants.ADD_FLOW, tx);
146 List<ListenableFuture<Void>> futures = new ArrayList<>();
147 futures.add(tx.submit());
154 private void createRemoteEvpnFlows(String rd, VrfEntry vrfEntry, VpnInstanceOpDataEntry vpnInstance,
155 List<BigInteger> localDpnId, VrfTablesKey vrfTableKey, boolean isNatPrefix) {
156 logger.info("Creating remote EVPN flows for prefix {} rd {} route-paths {} evi {}",
157 vrfEntry.getDestPrefix(), rd, vrfEntry.getRoutePaths(), vrfEntry.getL3vni());
158 List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
159 if (vpnToDpnList != null) {
160 DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
161 dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
162 dataStoreCoordinator.enqueueJob("FIB" + rd.toString() + vrfEntry.getDestPrefix(),
163 new Callable<List<ListenableFuture<Void>>>() {
165 public List<ListenableFuture<Void>> call() throws Exception {
166 WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
167 List<ListenableFuture<Void>> futures = new ArrayList<>();
168 for (VpnToDpnList vpnDpn : vpnToDpnList) {
169 if (!localDpnId.contains(vpnDpn.getDpnId())) {
170 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
171 createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(),
172 vrfTableKey, vrfEntry, isNatPrefix, tx);
176 futures.add(tx.submit());
183 private void createRemoteFibEntry(final BigInteger remoteDpnId, final long vpnId, final VrfTablesKey vrfTableKey,
184 final VrfEntry vrfEntry, boolean isNatPrefix, WriteTransaction tx) {
185 Boolean wrTxPresent = true;
188 tx = dataBroker.newWriteOnlyTransaction();
190 String rd = vrfTableKey.getRouteDistinguisher();
191 logger.debug("createremotefibentry: adding route {} for rd {} with transaction {}",
192 vrfEntry.getDestPrefix(), rd, tx);
193 List<NexthopManager.AdjacencyResult> tunnelInterfaceList = vrfEntryListener
194 .resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
196 if (tunnelInterfaceList.isEmpty()) {
197 logger.error("Could not get interface for route-paths: {} in vpn {}",
198 vrfEntry.getRoutePaths(), rd);
199 logger.warn("Failed to add Route: {} in vpn: {}",
200 vrfEntry.getDestPrefix(), rd);
204 for (NexthopManager.AdjacencyResult adjacencyResult : tunnelInterfaceList) {
205 List<ActionInfo> actionInfos = new ArrayList<>();
207 String prefix = adjacencyResult.getPrefix();
208 Prefixes prefixInfo = FibUtil.getPrefixToInterface(dataBroker, vpnId, prefix);
209 String interfaceName = prefixInfo.getVpnInterfaceName();
210 if (vrfEntry.getOrigin().equals(RouteOrigin.BGP) || isNatPrefix) {
211 tunnelId = BigInteger.valueOf(vrfEntry.getL3vni());
213 Interface interfaceState = FibUtil.getInterfaceStateFromOperDS(dataBroker, interfaceName);
214 tunnelId = BigInteger.valueOf(interfaceState.getIfIndex());
216 logger.debug("adding set tunnel id action for label {}", tunnelId);
217 String macAddress = null;
218 if (interfaceName != null) {
219 macAddress = FibUtil.getMacAddressFromPrefix(dataBroker, interfaceName, prefix);
220 actionInfos.add(new ActionSetFieldEthernetDestination(new MacAddress(macAddress)));
222 actionInfos.add(new ActionSetFieldTunnelId(tunnelId));
223 List<ActionInfo> egressActions = vrfEntryListener.getNextHopManager()
224 .getEgressActionsForInterface(adjacencyResult.getInterfaceName(), actionInfos.size());
225 if (egressActions.isEmpty()) {
226 logger.error("Failed to retrieve egress action for prefix {} route-paths {} interface {}."
227 + " Aborting remote FIB entry creation..", vrfEntry.getDestPrefix(),
228 vrfEntry.getRoutePaths(), adjacencyResult.getInterfaceName());
231 actionInfos.addAll(egressActions);
232 List<InstructionInfo> instructions = new ArrayList<>();
233 instructions.add(new InstructionApplyActions(actionInfos));
234 vrfEntryListener.makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions,
235 NwConstants.ADD_FLOW, tx);
240 logger.debug("Successfully added FIB entry for prefix {} in rd {}", vrfEntry.getDestPrefix(), rd);
243 public void removeFlows() {
244 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
245 final String rd = vrfTableKey.getRouteDistinguisher();
246 final VpnInstanceOpDataEntry vpnInstance = FibUtil.getVpnInstanceOpData(dataBroker,
247 vrfTableKey.getRouteDistinguisher()).get();
248 if (vpnInstance == null) {
249 logger.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
252 VpnNexthop localNextHopInfo = vrfEntryListener.getNextHopManager().getVpnNexthop(vpnInstance.getVpnId(),
253 vrfEntry.getDestPrefix());
254 List<BigInteger> localDpnId = checkDeleteLocalEvpnFLows(vpnInstance.getVpnId(), rd, vrfEntry, localNextHopInfo);
255 deleteRemoteEvpnFlows(rd, vpnInstance, vrfTableKey, localDpnId);
256 vrfEntryListener.cleanUpOpDataForFib(vpnInstance.getVpnId(), rd, vrfEntry);
259 private void deleteRemoteEvpnFlows(String rd, VpnInstanceOpDataEntry vpnInstance, VrfTablesKey vrfTableKey,
260 List<BigInteger> localDpnIdList) {
261 List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
262 if (vpnToDpnList != null) {
263 DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
264 dataStoreCoordinator.enqueueJob("FIB" + rd.toString() + vrfEntry.getDestPrefix(),
265 new Callable<List<ListenableFuture<Void>>>() {
267 public List<ListenableFuture<Void>> call() throws Exception {
268 WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
269 final Optional<Routes> extraRouteOptional = Optional.absent();
270 if (localDpnIdList.size() <= 0) {
271 for (VpnToDpnList curDpn : vpnToDpnList) {
272 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
273 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
274 vrfEntryListener.deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(),
275 vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
276 extraRouteOptional, tx);
279 vrfEntryListener.deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(),
280 vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
281 extraRouteOptional, tx);
285 for (BigInteger localDpnId : localDpnIdList) {
286 for (VpnToDpnList curDpn : vpnToDpnList) {
287 if (!curDpn.getDpnId().equals(localDpnId)) {
288 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
289 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
290 vrfEntryListener.deleteRemoteRoute(localDpnId, curDpn.getDpnId(),
291 vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
292 extraRouteOptional, tx);
295 vrfEntryListener.deleteRemoteRoute(localDpnId, curDpn.getDpnId(),
296 vpnInstance.getVpnId(), vrfTableKey, vrfEntry,
297 extraRouteOptional, tx);
303 List<ListenableFuture<Void>> futures = new ArrayList<>();
304 futures.add(tx.submit());
311 private List<BigInteger> checkDeleteLocalEvpnFLows(long vpnId, String rd, VrfEntry vrfEntry,
312 VpnNexthop localNextHopInfo) {
313 List<BigInteger> returnLocalDpnId = new ArrayList<>();
314 if (localNextHopInfo == null) {
315 //Handle extra routes and imported routes
317 final BigInteger dpnId = localNextHopInfo.getDpnId();
318 DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
319 dataStoreCoordinator.enqueueJob("FIB-" + rd + "-" + vrfEntry.getDestPrefix(),
320 new Callable<List<ListenableFuture<Void>>>() {
322 public List<ListenableFuture<Void>> call() throws Exception {
323 WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
324 vrfEntryListener.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null /* instructions */,
325 NwConstants.DEL_FLOW, tx);
326 List<ListenableFuture<Void>> futures = new ArrayList<>();
327 futures.add(tx.submit());
331 //TODO: verify below adjacency call need to be optimized (?)
332 vrfEntryListener.deleteLocalAdjacency(dpnId, vpnId, vrfEntry.getDestPrefix(), vrfEntry.getDestPrefix());
333 returnLocalDpnId.add(dpnId);
335 return returnLocalDpnId;