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.elan.evpn.utils;
10 import static org.opendaylight.mdsal.binding.util.Datastore.CONFIGURATION;
12 import com.google.common.util.concurrent.ListenableFuture;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.List;
17 import java.util.Optional;
18 import java.util.concurrent.ExecutionException;
19 import javax.inject.Inject;
20 import javax.inject.Singleton;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
23 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
24 import org.opendaylight.mdsal.binding.api.DataBroker;
25 import org.opendaylight.mdsal.binding.api.ReadTransaction;
26 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
27 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
28 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
29 import org.opendaylight.netvirt.elan.cache.ElanInstanceCache;
30 import org.opendaylight.netvirt.elan.utils.ElanConstants;
31 import org.opendaylight.netvirt.elan.utils.ElanUtils;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
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.macvrfentries.MacVrfEntry;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.macvrfentries.MacVrfEntryKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePaths;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.EvpnRdToNetworks;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.evpn.rd.to.networks.EvpnRdToNetwork;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.evpn.rd.to.networks.EvpnRdToNetworkKey;
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 EvpnMacVrfUtils {
56 private static final Logger LOG = LoggerFactory.getLogger(EvpnMacVrfUtils.class);
57 private final DataBroker dataBroker;
58 private final ManagedNewTransactionRunner txRunner;
59 private final IdManagerService idManager;
60 private final ElanEvpnFlowUtils elanEvpnFlowUtils;
61 private final IMdsalApiManager mdsalManager;
62 private final EvpnUtils evpnUtils;
63 private final JobCoordinator jobCoordinator;
64 private final ElanUtils elanUtils;
65 private final ElanInstanceCache elanInstanceCache;
68 public EvpnMacVrfUtils(final DataBroker dataBroker, final IdManagerService idManager,
69 final ElanEvpnFlowUtils elanEvpnFlowUtils, final IMdsalApiManager mdsalManager, final EvpnUtils evpnUtils,
70 final JobCoordinator jobCoordinator, final ElanUtils elanUtils, final ElanInstanceCache elanInstanceCache) {
71 this.dataBroker = dataBroker;
72 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
73 this.idManager = idManager;
74 this.elanEvpnFlowUtils = elanEvpnFlowUtils;
75 this.mdsalManager = mdsalManager;
76 this.evpnUtils = evpnUtils;
77 this.jobCoordinator = jobCoordinator;
78 this.elanUtils = elanUtils;
79 this.elanInstanceCache = elanInstanceCache;
83 private Uint32 getElanTagByMacvrfiid(InstanceIdentifier<MacVrfEntry> macVrfEntryIid) {
84 String elanName = getElanNameByMacvrfiid(macVrfEntryIid);
85 if (elanName == null) {
86 LOG.error("getElanTag: elanName is NULL for iid = {}", macVrfEntryIid);
88 ElanInstance elanInstance = elanInstanceCache.get(elanName).orElse(null);
89 if (elanInstance == null) {
93 Uint32 elanTag = elanInstance.getElanTag();
94 if (elanTag == null || elanTag.longValue() == 0L) {
95 elanTag = ElanUtils.retrieveNewElanTag(idManager, elanName);
100 public String getElanNameByMacvrfiid(InstanceIdentifier<MacVrfEntry> instanceIdentifier) {
101 try (ReadTransaction tx = dataBroker.newReadOnlyTransaction()) {
102 String rd = instanceIdentifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
103 String elanName = null;
104 InstanceIdentifier<EvpnRdToNetwork> iidEvpnRdToNet =
105 InstanceIdentifier.builder(EvpnRdToNetworks.class).child(EvpnRdToNetwork.class,
106 new EvpnRdToNetworkKey(rd)).build();
108 Optional<EvpnRdToNetwork> evpnRdToNetwork =
109 tx.read(LogicalDatastoreType.CONFIGURATION, iidEvpnRdToNet).get();
110 if (evpnRdToNetwork.isPresent()) {
111 elanName = evpnRdToNetwork.get().getNetworkId();
113 } catch (InterruptedException | ExecutionException e) {
114 LOG.error("getElanName: unable to read elanName, exception ", e);
120 public InstanceIdentifier<MacVrfEntry> getMacVrfEntryIid(String rd, MacVrfEntry macVrfEntry) {
121 return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
122 .child(MacVrfEntry.class, macVrfEntry.key());
125 public void updateEvpnDmacFlows(final ElanInstance elanInstance, final boolean install) {
126 String rd = evpnUtils.getEvpnRd(elanInstance);
130 final InstanceIdentifier<VrfTables> iid = InstanceIdentifier.create(FibEntries.class)
131 .child(VrfTables.class, new VrfTablesKey(rd));
132 evpnUtils.asyncReadAndExecute(LogicalDatastoreType.CONFIGURATION, iid,
133 new StringBuilder(elanInstance.getElanInstanceName()).append(":").append(rd).toString(),
134 (vrfTablesOptional) -> {
135 if (!vrfTablesOptional.isPresent()) {
138 Map<MacVrfEntryKey, MacVrfEntry> keyMacVrfEntryMap = vrfTablesOptional.get().nonnullMacVrfEntry();
139 if (keyMacVrfEntryMap == null || keyMacVrfEntryMap.isEmpty()) {
142 for (MacVrfEntry macVrfEntry : keyMacVrfEntryMap.values()) {
143 InstanceIdentifier<MacVrfEntry> macVrfEntryIid = getMacVrfEntryIid(rd, macVrfEntry);
145 addEvpnDmacFlowOnAttach(macVrfEntryIid, macVrfEntry, elanInstance);
147 removeEvpnDmacFlowOnDetach(macVrfEntryIid, macVrfEntry, elanInstance);
154 public boolean checkEvpnAttachedToNet(String elanName) {
155 ElanInstance elanInfo = elanInstanceCache.get(elanName).orElse(null);
156 String evpnName = EvpnUtils.getEvpnNameFromElan(elanInfo);
157 if (evpnName == null) {
158 LOG.error("Error : evpnName is null for elanName {}", elanName);
164 public void addEvpnDmacFlow(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry) {
165 String elanName = getElanNameByMacvrfiid(instanceIdentifier);
166 if (elanName == null) {
167 LOG.error("Error : elanName is null for iid {}", instanceIdentifier);
171 List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
172 if (checkEvpnAttachedToNet(elanName)) {
173 //TODO(Riyaz) : Check if accessing first nexthop address is right solution
174 String nexthopIP = new ArrayList<RoutePaths>(macVrfEntry.nonnullRoutePaths().values())
175 .get(0).getNexthopAddress();
176 IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
177 Uint32 elanTag = getElanTagByMacvrfiid(instanceIdentifier);
178 if (elanTag == null) {
182 String dstMacAddress = macVrfEntry.getMac();
183 long vni = macVrfEntry.getL2vni().toJava();
184 jobCoordinator.enqueueJob(dstMacAddress, () -> Collections.singletonList(
185 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
186 tx -> dpnInterfaceLists.forEach(dpnInterfaces -> {
187 Uint64 dpId = dpnInterfaces.getDpId();
188 LOG.info("ADD: Build DMAC flow with dpId {}, nexthopIP {}, elanTag {},"
189 + "vni {}, dstMacAddress {}, elanName {} ",
190 dpId, nexthopIP, elanTag, vni, dstMacAddress, elanName);
191 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder =
192 new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
193 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag.longValue())
194 .setVni(vni).setDstMacAddress(dstMacAddress).setElanName(elanName);
196 elanEvpnFlowUtils.evpnBuildDmacFlowForExternalRemoteMac(dmacFlowBuilder.build());
198 mdsalManager.addFlow(tx, dpId, flow);
199 }))), ElanConstants.JOB_MAX_RETRIES);
203 public void removeEvpnDmacFlow(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry) {
204 String elanName = getElanNameByMacvrfiid(instanceIdentifier);
205 if (elanName == null) {
206 LOG.error("Error : elanName is null for iid {}", instanceIdentifier);
209 List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
211 //if (checkEvpnAttachedToNet(elanName)) {
212 //TODO(Riyaz) : Check if accessing first nexthop address is right
213 String nexthopIP = new ArrayList<RoutePaths>(macVrfEntry.nonnullRoutePaths().values())
214 .get(0).getNexthopAddress();
215 IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
216 Uint32 elanTag = getElanTagByMacvrfiid(instanceIdentifier);
217 if (elanTag == null) {
221 String macToRemove = macVrfEntry.getMac();
222 jobCoordinator.enqueueJob(macToRemove, () -> {
223 List<ListenableFuture<?>> futures = new ArrayList<>();
224 dpnInterfaceLists.forEach(dpnInterfaces -> {
225 Uint64 dpId = dpnInterfaces.getDpId();
226 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder = new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
227 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag.longValue())
228 .setDstMacAddress(macToRemove);
229 LOG.info("REMOVE: Deleting DMAC Flows for external MAC. elanTag {}, dpId {},"
230 + "nexthopIP {}, macToRemove {}", elanTag, dpId, nexthopIP, macToRemove);
231 futures.addAll(elanEvpnFlowUtils.evpnDeleteDmacFlowsToExternalMac(dmacFlowBuilder.build()));
234 }, ElanConstants.JOB_MAX_RETRIES);
237 public void addEvpnDmacFlowOnAttach(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry,
238 ElanInstance elanInstance) {
239 //String elanName = getElanNameByMacvrfiid(instanceIdentifier);
240 if (elanInstance == null) {
241 LOG.error("Error : elanName is null for iid {}", instanceIdentifier);
245 String elanName = elanInstance.getElanInstanceName();
246 List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
248 if (checkEvpnAttachedToNet(elanName)) {
249 String nexthopIP = getRoutePathNexthopIp(macVrfEntry);
250 if (nexthopIP == null) {
251 LOG.debug("nexthopIP is null for iid {}", instanceIdentifier);
254 IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
255 Uint32 elanTag = elanInstance.getElanTag();
256 String dstMacAddress = macVrfEntry.getMac();
257 long vni = macVrfEntry.getL2vni().toJava();
258 jobCoordinator.enqueueJob(dstMacAddress, () -> Collections.singletonList(
259 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
260 tx -> dpnInterfaceLists.forEach(dpnInterfaces -> {
261 Uint64 dpId = dpnInterfaces.getDpId();
262 LOG.info("ADD: Build DMAC flow with dpId {}, nexthopIP {}, elanTag {},"
263 + "vni {}, dstMacAddress {}, elanName {} ",
264 dpId, nexthopIP, elanTag, vni, dstMacAddress, elanName);
265 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder =
266 new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
267 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag.longValue())
268 .setVni(vni).setDstMacAddress(dstMacAddress).setElanName(elanName);
269 Flow flow = elanEvpnFlowUtils.evpnBuildDmacFlowForExternalRemoteMac(dmacFlowBuilder.build());
270 mdsalManager.addFlow(tx, dpId, flow);
271 }))), ElanConstants.JOB_MAX_RETRIES);
276 public String getRoutePathNexthopIp(MacVrfEntry macVrfEntry) {
277 if (macVrfEntry.getRoutePaths() == null || macVrfEntry.getRoutePaths().isEmpty()) {
278 LOG.debug("RoutePaths is null or empty for macvrfentry {}", macVrfEntry);
281 return new ArrayList<RoutePaths>(macVrfEntry.nonnullRoutePaths().values()).get(0).getNexthopAddress();
284 public void removeEvpnDmacFlowOnDetach(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry,
285 ElanInstance elanInstance) {
286 //String elanName = getElanNameByMacvrfiid(instanceIdentifier);
287 if (elanInstance == null) {
288 LOG.error("Error : elanInstance is null for iid {}", instanceIdentifier);
291 List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanInstance.getElanInstanceName());
293 //if (checkEvpnAttachedToNet(elanName)) {
294 String nexthopIP = getRoutePathNexthopIp(macVrfEntry);
295 if (nexthopIP == null) {
296 LOG.debug("nexthopIP is null for iid {}", instanceIdentifier);
299 IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
300 Uint32 elanTag = elanInstance.getElanTag();
301 String macToRemove = macVrfEntry.getMac();
302 jobCoordinator.enqueueJob(macToRemove, () -> {
303 List<ListenableFuture<Void>> futures = new ArrayList<>();
304 dpnInterfaceLists.forEach(dpnInterfaces -> {
305 Uint64 dpId = dpnInterfaces.getDpId();
306 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder = new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
307 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag.longValue())
308 .setDstMacAddress(macToRemove);
309 LOG.info("REMOVE: Deleting DMAC Flows for external MAC. elanTag {}, dpId {},"
310 + "nexthopIP {}, macToRemove {}", elanTag, dpId, nexthopIP, macToRemove);
311 elanEvpnFlowUtils.evpnDeleteDmacFlowsToExternalMac(dmacFlowBuilder.build());
314 }, ElanConstants.JOB_MAX_RETRIES);