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 com.google.common.base.Optional;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.List;
16 import javax.annotation.Nullable;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
21 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
22 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
23 import org.opendaylight.genius.infra.Datastore;
24 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
25 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
26 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
27 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
28 import org.opendaylight.netvirt.elan.cache.ElanInstanceCache;
29 import org.opendaylight.netvirt.elan.utils.ElanConstants;
30 import org.opendaylight.netvirt.elan.utils.ElanUtils;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.macvrfentries.MacVrfEntry;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.EvpnRdToNetworks;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.evpn.rd.to.networks.EvpnRdToNetwork;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.evpn.rd.to.networks.EvpnRdToNetworkKey;
44 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
49 public class EvpnMacVrfUtils {
51 private static final Logger LOG = LoggerFactory.getLogger(EvpnMacVrfUtils.class);
52 private final DataBroker dataBroker;
53 private final ManagedNewTransactionRunner txRunner;
54 private final IdManagerService idManager;
55 private final ElanEvpnFlowUtils elanEvpnFlowUtils;
56 private final IMdsalApiManager mdsalManager;
57 private final EvpnUtils evpnUtils;
58 private final JobCoordinator jobCoordinator;
59 private final ElanUtils elanUtils;
60 private final ElanInstanceCache elanInstanceCache;
63 public EvpnMacVrfUtils(final DataBroker dataBroker, final IdManagerService idManager,
64 final ElanEvpnFlowUtils elanEvpnFlowUtils, final IMdsalApiManager mdsalManager, final EvpnUtils evpnUtils,
65 final JobCoordinator jobCoordinator, final ElanUtils elanUtils, final ElanInstanceCache elanInstanceCache) {
66 this.dataBroker = dataBroker;
67 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
68 this.idManager = idManager;
69 this.elanEvpnFlowUtils = elanEvpnFlowUtils;
70 this.mdsalManager = mdsalManager;
71 this.evpnUtils = evpnUtils;
72 this.jobCoordinator = jobCoordinator;
73 this.elanUtils = elanUtils;
74 this.elanInstanceCache = elanInstanceCache;
78 private Long getElanTagByMacvrfiid(InstanceIdentifier<MacVrfEntry> macVrfEntryIid) {
79 String elanName = getElanNameByMacvrfiid(macVrfEntryIid);
80 if (elanName == null) {
81 LOG.error("getElanTag: elanName is NULL for iid = {}", macVrfEntryIid);
83 ElanInstance elanInstance = elanInstanceCache.get(elanName).orNull();
84 if (elanInstance == null) {
88 Long elanTag = elanInstance.getElanTag();
89 if (elanTag == null || elanTag == 0L) {
90 elanTag = ElanUtils.retrieveNewElanTag(idManager, elanName);
95 public String getElanNameByMacvrfiid(InstanceIdentifier<MacVrfEntry> instanceIdentifier) {
96 try (ReadOnlyTransaction tx = dataBroker.newReadOnlyTransaction()) {
97 String rd = instanceIdentifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
98 String elanName = null;
99 InstanceIdentifier<EvpnRdToNetwork> iidEvpnRdToNet =
100 InstanceIdentifier.builder(EvpnRdToNetworks.class).child(EvpnRdToNetwork.class,
101 new EvpnRdToNetworkKey(rd)).build();
103 Optional<EvpnRdToNetwork> evpnRdToNetwork =
104 tx.read(LogicalDatastoreType.CONFIGURATION, iidEvpnRdToNet).checkedGet();
105 if (evpnRdToNetwork.isPresent()) {
106 elanName = evpnRdToNetwork.get().getNetworkId();
108 } catch (ReadFailedException e) {
109 LOG.error("getElanName: unable to read elanName, exception ", e);
115 public InstanceIdentifier<MacVrfEntry> getMacVrfEntryIid(String rd, MacVrfEntry macVrfEntry) {
116 return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
117 .child(MacVrfEntry.class, macVrfEntry.key());
120 public void updateEvpnDmacFlows(final ElanInstance elanInstance, final boolean install) {
121 String rd = evpnUtils.getEvpnRd(elanInstance);
125 final InstanceIdentifier<VrfTables> iid = InstanceIdentifier.create(FibEntries.class)
126 .child(VrfTables.class, new VrfTablesKey(rd));
127 evpnUtils.asyncReadAndExecute(LogicalDatastoreType.CONFIGURATION, iid,
128 new StringBuilder(elanInstance.getElanInstanceName()).append(":").append(rd).toString(),
129 (vrfTablesOptional) -> {
130 if (!vrfTablesOptional.isPresent()) {
133 List<MacVrfEntry> macVrfEntries = vrfTablesOptional.get().getMacVrfEntry();
134 if (macVrfEntries == null || macVrfEntries.isEmpty()) {
137 for (MacVrfEntry macVrfEntry : macVrfEntries) {
138 InstanceIdentifier<MacVrfEntry> macVrfEntryIid = getMacVrfEntryIid(rd, macVrfEntry);
140 addEvpnDmacFlowOnAttach(macVrfEntryIid, macVrfEntry, elanInstance);
142 removeEvpnDmacFlowOnDetach(macVrfEntryIid, macVrfEntry, elanInstance);
149 public boolean checkEvpnAttachedToNet(String elanName) {
150 ElanInstance elanInfo = elanInstanceCache.get(elanName).orNull();
151 String evpnName = EvpnUtils.getEvpnNameFromElan(elanInfo);
152 if (evpnName == null) {
153 LOG.error("Error : evpnName is null for elanName {}", elanName);
159 public void addEvpnDmacFlow(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry) {
160 String elanName = getElanNameByMacvrfiid(instanceIdentifier);
161 if (elanName == null) {
162 LOG.error("Error : elanName is null for iid {}", instanceIdentifier);
166 List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
167 if (checkEvpnAttachedToNet(elanName)) {
168 //TODO(Riyaz) : Check if accessing first nexthop address is right solution
169 String nexthopIP = macVrfEntry.getRoutePaths().get(0).getNexthopAddress();
170 IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
171 Long elanTag = getElanTagByMacvrfiid(instanceIdentifier);
172 if (elanTag == null) {
176 String dstMacAddress = macVrfEntry.getMac();
177 long vni = macVrfEntry.getL2vni();
178 jobCoordinator.enqueueJob(dstMacAddress, () -> Collections.singletonList(
179 txRunner.callWithNewWriteOnlyTransactionAndSubmit(Datastore.CONFIGURATION,
180 tx -> dpnInterfaceLists.forEach(dpnInterfaces -> {
181 BigInteger dpId = dpnInterfaces.getDpId();
182 LOG.info("ADD: Build DMAC flow with dpId {}, nexthopIP {}, elanTag {},"
183 + "vni {}, dstMacAddress {}, elanName {} ",
184 dpId, nexthopIP, elanTag, vni, dstMacAddress, elanName);
185 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder =
186 new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
187 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag).setVni(
189 .setDstMacAddress(dstMacAddress).setElanName(elanName);
191 elanEvpnFlowUtils.evpnBuildDmacFlowForExternalRemoteMac(dmacFlowBuilder.build());
193 mdsalManager.addFlow(tx, dpId, flow);
194 }))), ElanConstants.JOB_MAX_RETRIES);
198 public void removeEvpnDmacFlow(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry) {
199 String elanName = getElanNameByMacvrfiid(instanceIdentifier);
200 if (elanName == null) {
201 LOG.error("Error : elanName is null for iid {}", instanceIdentifier);
204 List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
206 //if (checkEvpnAttachedToNet(elanName)) {
207 //TODO(Riyaz) : Check if accessing first nexthop address is right
208 String nexthopIP = macVrfEntry.getRoutePaths().get(0).getNexthopAddress();
209 IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
210 Long elanTag = getElanTagByMacvrfiid(instanceIdentifier);
211 if (elanTag == null) {
215 String macToRemove = macVrfEntry.getMac();
216 jobCoordinator.enqueueJob(macToRemove, () -> {
217 List<ListenableFuture<Void>> futures = new ArrayList<>();
218 dpnInterfaceLists.forEach(dpnInterfaces -> {
219 BigInteger dpId = dpnInterfaces.getDpId();
220 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder = new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
221 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag)
222 .setDstMacAddress(macToRemove);
223 LOG.info("REMOVE: Deleting DMAC Flows for external MAC. elanTag {}, dpId {},"
224 + "nexthopIP {}, macToRemove {}", elanTag, dpId, nexthopIP, macToRemove);
225 futures.addAll(elanEvpnFlowUtils.evpnDeleteDmacFlowsToExternalMac(dmacFlowBuilder.build()));
228 }, ElanConstants.JOB_MAX_RETRIES);
231 public void addEvpnDmacFlowOnAttach(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry,
232 ElanInstance elanInstance) {
233 //String elanName = getElanNameByMacvrfiid(instanceIdentifier);
234 if (elanInstance == null) {
235 LOG.error("Error : elanName is null for iid {}", instanceIdentifier);
239 String elanName = elanInstance.getElanInstanceName();
240 List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
242 if (checkEvpnAttachedToNet(elanName)) {
243 String nexthopIP = getRoutePathNexthopIp(macVrfEntry);
244 if (nexthopIP == null) {
245 LOG.debug("nexthopIP is null for iid {}", instanceIdentifier);
248 IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
249 Long elanTag = elanInstance.getElanTag();
250 String dstMacAddress = macVrfEntry.getMac();
251 long vni = macVrfEntry.getL2vni();
252 jobCoordinator.enqueueJob(dstMacAddress, () -> Collections.singletonList(
253 txRunner.callWithNewWriteOnlyTransactionAndSubmit(Datastore.CONFIGURATION,
254 tx -> dpnInterfaceLists.forEach(dpnInterfaces -> {
255 BigInteger dpId = dpnInterfaces.getDpId();
256 LOG.info("ADD: Build DMAC flow with dpId {}, nexthopIP {}, elanTag {},"
257 + "vni {}, dstMacAddress {}, elanName {} ",
258 dpId, nexthopIP, elanTag, vni, dstMacAddress, elanName);
259 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder =
260 new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
261 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag).setVni(vni)
262 .setDstMacAddress(dstMacAddress).setElanName(elanName);
263 Flow flow = elanEvpnFlowUtils.evpnBuildDmacFlowForExternalRemoteMac(dmacFlowBuilder.build());
264 mdsalManager.addFlow(tx, dpId, flow);
265 }))), ElanConstants.JOB_MAX_RETRIES);
270 public String getRoutePathNexthopIp(MacVrfEntry macVrfEntry) {
271 if (macVrfEntry.getRoutePaths() == null || macVrfEntry.getRoutePaths().isEmpty()) {
272 LOG.debug("RoutePaths is null or empty for macvrfentry {}", macVrfEntry);
275 return macVrfEntry.getRoutePaths().get(0).getNexthopAddress();
278 public void removeEvpnDmacFlowOnDetach(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry,
279 ElanInstance elanInstance) {
280 //String elanName = getElanNameByMacvrfiid(instanceIdentifier);
281 if (elanInstance == null) {
282 LOG.error("Error : elanInstance is null for iid {}", instanceIdentifier);
285 List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanInstance.getElanInstanceName());
287 //if (checkEvpnAttachedToNet(elanName)) {
288 String nexthopIP = getRoutePathNexthopIp(macVrfEntry);
289 if (nexthopIP == null) {
290 LOG.debug("nexthopIP is null for iid {}", instanceIdentifier);
293 IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
294 Long elanTag = elanInstance.getElanTag();
295 String macToRemove = macVrfEntry.getMac();
296 jobCoordinator.enqueueJob(macToRemove, () -> {
297 List<ListenableFuture<Void>> futures = new ArrayList<>();
298 dpnInterfaceLists.forEach(dpnInterfaces -> {
299 BigInteger dpId = dpnInterfaces.getDpId();
300 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder = new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
301 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag)
302 .setDstMacAddress(macToRemove);
303 LOG.info("REMOVE: Deleting DMAC Flows for external MAC. elanTag {}, dpId {},"
304 + "nexthopIP {}, macToRemove {}", elanTag, dpId, nexthopIP, macToRemove);
305 elanEvpnFlowUtils.evpnDeleteDmacFlowsToExternalMac(dmacFlowBuilder.build());
308 }, ElanConstants.JOB_MAX_RETRIES);