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.List;
15 import javax.annotation.Nullable;
16 import javax.inject.Inject;
17 import javax.inject.Singleton;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
22 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
23 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
24 import org.opendaylight.netvirt.elan.cache.ElanInstanceCache;
25 import org.opendaylight.netvirt.elan.utils.ElanConstants;
26 import org.opendaylight.netvirt.elan.utils.ElanUtils;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.macvrfentries.MacVrfEntry;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.EvpnRdToNetworks;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.evpn.rd.to.networks.EvpnRdToNetwork;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.evpn.rd.to.networks.EvpnRdToNetworkKey;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 public class EvpnMacVrfUtils {
47 private static final Logger LOG = LoggerFactory.getLogger(EvpnMacVrfUtils.class);
48 private final DataBroker dataBroker;
49 private final IdManagerService idManager;
50 private final ElanEvpnFlowUtils elanEvpnFlowUtils;
51 private final IMdsalApiManager mdsalManager;
52 private final EvpnUtils evpnUtils;
53 private final JobCoordinator jobCoordinator;
54 private final ElanUtils elanUtils;
55 private final ElanInstanceCache elanInstanceCache;
58 public EvpnMacVrfUtils(final DataBroker dataBroker, final IdManagerService idManager,
59 final ElanEvpnFlowUtils elanEvpnFlowUtils, final IMdsalApiManager mdsalManager, final EvpnUtils evpnUtils,
60 final JobCoordinator jobCoordinator, final ElanUtils elanUtils, final ElanInstanceCache elanInstanceCache) {
61 this.dataBroker = dataBroker;
62 this.idManager = idManager;
63 this.elanEvpnFlowUtils = elanEvpnFlowUtils;
64 this.mdsalManager = mdsalManager;
65 this.evpnUtils = evpnUtils;
66 this.jobCoordinator = jobCoordinator;
67 this.elanUtils = elanUtils;
68 this.elanInstanceCache = elanInstanceCache;
72 private Long getElanTagByMacvrfiid(InstanceIdentifier<MacVrfEntry> macVrfEntryIid) {
73 String elanName = getElanNameByMacvrfiid(macVrfEntryIid);
74 if (elanName == null) {
75 LOG.error("getElanTag: elanName is NULL for iid = {}", macVrfEntryIid);
77 ElanInstance elanInstance = elanInstanceCache.get(elanName).orNull();
78 if (elanInstance == null) {
82 Long elanTag = elanInstance.getElanTag();
83 if (elanTag == null || elanTag == 0L) {
84 elanTag = ElanUtils.retrieveNewElanTag(idManager, elanName);
89 public String getElanNameByMacvrfiid(InstanceIdentifier<MacVrfEntry> instanceIdentifier) {
90 try (ReadOnlyTransaction tx = dataBroker.newReadOnlyTransaction()) {
91 String rd = instanceIdentifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
92 String elanName = null;
93 InstanceIdentifier<EvpnRdToNetwork> iidEvpnRdToNet =
94 InstanceIdentifier.builder(EvpnRdToNetworks.class).child(EvpnRdToNetwork.class,
95 new EvpnRdToNetworkKey(rd)).build();
97 Optional<EvpnRdToNetwork> evpnRdToNetwork =
98 tx.read(LogicalDatastoreType.CONFIGURATION, iidEvpnRdToNet).checkedGet();
99 if (evpnRdToNetwork.isPresent()) {
100 elanName = evpnRdToNetwork.get().getNetworkId();
102 } catch (ReadFailedException e) {
103 LOG.error("getElanName: unable to read elanName, exception ", e);
109 public InstanceIdentifier<MacVrfEntry> getMacVrfEntryIid(String rd, MacVrfEntry macVrfEntry) {
110 return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
111 .child(MacVrfEntry.class, macVrfEntry.getKey());
114 public void updateEvpnDmacFlows(final ElanInstance elanInstance, final boolean install) {
115 String rd = evpnUtils.getEvpnRd(elanInstance);
119 final InstanceIdentifier<VrfTables> iid = InstanceIdentifier.create(FibEntries.class)
120 .child(VrfTables.class, new VrfTablesKey(rd));
121 evpnUtils.asyncReadAndExecute(LogicalDatastoreType.CONFIGURATION, iid,
122 new StringBuilder(elanInstance.getElanInstanceName()).append(":").append(rd).toString(),
123 (vrfTablesOptional) -> {
124 if (!vrfTablesOptional.isPresent()) {
127 List<MacVrfEntry> macVrfEntries = vrfTablesOptional.get().getMacVrfEntry();
128 if (macVrfEntries == null || macVrfEntries.isEmpty()) {
131 for (MacVrfEntry macVrfEntry : macVrfEntries) {
132 InstanceIdentifier<MacVrfEntry> macVrfEntryIid = getMacVrfEntryIid(rd, macVrfEntry);
134 addEvpnDmacFlowOnAttach(macVrfEntryIid, macVrfEntry, elanInstance);
136 removeEvpnDmacFlowOnDetach(macVrfEntryIid, macVrfEntry, elanInstance);
143 public boolean checkEvpnAttachedToNet(String elanName) {
144 ElanInstance elanInfo = elanInstanceCache.get(elanName).orNull();
145 String evpnName = EvpnUtils.getEvpnNameFromElan(elanInfo);
146 if (evpnName == null) {
147 LOG.error("Error : evpnName is null for elanName {}", elanName);
153 public void addEvpnDmacFlow(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry) {
154 String elanName = getElanNameByMacvrfiid(instanceIdentifier);
155 if (elanName == null) {
156 LOG.error("Error : elanName is null for iid {}", instanceIdentifier);
160 List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
161 if (checkEvpnAttachedToNet(elanName)) {
162 //TODO(Riyaz) : Check if accessing first nexthop address is right solution
163 String nexthopIP = macVrfEntry.getRoutePaths().get(0).getNexthopAddress();
164 IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
165 Long elanTag = getElanTagByMacvrfiid(instanceIdentifier);
166 if (elanTag == null) {
170 String dstMacAddress = macVrfEntry.getMac();
171 long vni = macVrfEntry.getL2vni();
172 jobCoordinator.enqueueJob(dstMacAddress, () -> {
173 List<ListenableFuture<Void>> futures = new ArrayList<>();
174 dpnInterfaceLists.forEach(dpnInterfaces -> {
175 BigInteger dpId = dpnInterfaces.getDpId();
176 LOG.info("ADD: Build DMAC flow with dpId {}, nexthopIP {}, elanTag {},"
177 + "vni {}, dstMacAddress {}, elanName {} ",
178 dpId, nexthopIP, elanTag, vni, dstMacAddress, elanName);
179 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder = new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
180 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag).setVni(vni)
181 .setDstMacAddress(dstMacAddress).setElanName(elanName);
182 Flow flow = elanEvpnFlowUtils.evpnBuildDmacFlowForExternalRemoteMac(dmacFlowBuilder.build());
184 futures.add(mdsalManager.installFlow(dpId, flow));
187 }, ElanConstants.JOB_MAX_RETRIES);
191 public void removeEvpnDmacFlow(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry) {
192 String elanName = getElanNameByMacvrfiid(instanceIdentifier);
193 if (elanName == null) {
194 LOG.error("Error : elanName is null for iid {}", instanceIdentifier);
197 List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
199 //if (checkEvpnAttachedToNet(elanName)) {
200 //TODO(Riyaz) : Check if accessing first nexthop address is right
201 String nexthopIP = macVrfEntry.getRoutePaths().get(0).getNexthopAddress();
202 IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
203 Long elanTag = getElanTagByMacvrfiid(instanceIdentifier);
204 if (elanTag == null) {
208 String macToRemove = macVrfEntry.getMac();
209 jobCoordinator.enqueueJob(macToRemove, () -> {
210 List<ListenableFuture<Void>> futures = new ArrayList<>();
211 dpnInterfaceLists.forEach(dpnInterfaces -> {
212 BigInteger dpId = dpnInterfaces.getDpId();
213 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder = new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
214 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag)
215 .setDstMacAddress(macToRemove);
216 LOG.info("REMOVE: Deleting DMAC Flows for external MAC. elanTag {}, dpId {},"
217 + "nexthopIP {}, macToRemove {}", elanTag, dpId, nexthopIP, macToRemove);
218 futures.addAll(elanEvpnFlowUtils.evpnDeleteDmacFlowsToExternalMac(dmacFlowBuilder.build()));
221 }, ElanConstants.JOB_MAX_RETRIES);
224 public void addEvpnDmacFlowOnAttach(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry,
225 ElanInstance elanInstance) {
226 //String elanName = getElanNameByMacvrfiid(instanceIdentifier);
227 if (elanInstance == null) {
228 LOG.error("Error : elanName is null for iid {}", instanceIdentifier);
232 String elanName = elanInstance.getElanInstanceName();
233 List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
235 if (checkEvpnAttachedToNet(elanName)) {
236 String nexthopIP = getRoutePathNexthopIp(macVrfEntry);
237 if (nexthopIP == null) {
238 LOG.debug("nexthopIP is null for iid {}", instanceIdentifier);
241 IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
242 Long elanTag = elanInstance.getElanTag();
243 String dstMacAddress = macVrfEntry.getMac();
244 long vni = macVrfEntry.getL2vni();
245 jobCoordinator.enqueueJob(dstMacAddress, () -> {
246 List<ListenableFuture<Void>> futures = new ArrayList<>();
247 dpnInterfaceLists.forEach(dpnInterfaces -> {
248 BigInteger dpId = dpnInterfaces.getDpId();
249 LOG.info("ADD: Build DMAC flow with dpId {}, nexthopIP {}, elanTag {},"
250 + "vni {}, dstMacAddress {}, elanName {} ",
251 dpId, nexthopIP, elanTag, vni, dstMacAddress, elanName);
252 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder = new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
253 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag).setVni(vni)
254 .setDstMacAddress(dstMacAddress).setElanName(elanName);
255 Flow flow = elanEvpnFlowUtils.evpnBuildDmacFlowForExternalRemoteMac(dmacFlowBuilder.build());
256 futures.add(mdsalManager.installFlow(dpId, flow));
259 }, ElanConstants.JOB_MAX_RETRIES);
263 public String getRoutePathNexthopIp(MacVrfEntry macVrfEntry) {
264 if (macVrfEntry.getRoutePaths() == null || macVrfEntry.getRoutePaths().isEmpty()) {
265 LOG.debug("RoutePaths is null or empty for macvrfentry {}", macVrfEntry);
268 return macVrfEntry.getRoutePaths().get(0).getNexthopAddress();
271 public void removeEvpnDmacFlowOnDetach(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry,
272 ElanInstance elanInstance) {
273 //String elanName = getElanNameByMacvrfiid(instanceIdentifier);
274 if (elanInstance == null) {
275 LOG.error("Error : elanInstance is null for iid {}", instanceIdentifier);
278 List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanInstance.getElanInstanceName());
280 //if (checkEvpnAttachedToNet(elanName)) {
281 String nexthopIP = getRoutePathNexthopIp(macVrfEntry);
282 if (nexthopIP == null) {
283 LOG.debug("nexthopIP is null for iid {}", instanceIdentifier);
286 IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
287 Long elanTag = elanInstance.getElanTag();
288 String macToRemove = macVrfEntry.getMac();
289 jobCoordinator.enqueueJob(macToRemove, () -> {
290 List<ListenableFuture<Void>> futures = new ArrayList<>();
291 dpnInterfaceLists.forEach(dpnInterfaces -> {
292 BigInteger dpId = dpnInterfaces.getDpId();
293 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder = new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
294 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag)
295 .setDstMacAddress(macToRemove);
296 LOG.info("REMOVE: Deleting DMAC Flows for external MAC. elanTag {}, dpId {},"
297 + "nexthopIP {}, macToRemove {}", elanTag, dpId, nexthopIP, macToRemove);
298 elanEvpnFlowUtils.evpnDeleteDmacFlowsToExternalMac(dmacFlowBuilder.build());
301 }, ElanConstants.JOB_MAX_RETRIES);