e3916b03e6c2fc327e59c7795cc541eac9603c3b
[netvirt.git] / elanmanager / impl / src / main / java / org / opendaylight / netvirt / elan / evpn / utils / EvpnMacVrfUtils.java
1 /*
2  * Copyright © 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netvirt.elan.evpn.utils;
9
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;
47
48 @Singleton
49 public class EvpnMacVrfUtils {
50
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;
61
62     @Inject
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;
75     }
76
77     @Nullable
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);
82         }
83         ElanInstance elanInstance = elanInstanceCache.get(elanName).orNull();
84         if (elanInstance == null) {
85             return null;
86         }
87
88         Long elanTag = elanInstance.getElanTag();
89         if (elanTag == null || elanTag == 0L) {
90             elanTag = ElanUtils.retrieveNewElanTag(idManager, elanName);
91         }
92         return elanTag;
93     }
94
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();
102             try {
103                 Optional<EvpnRdToNetwork> evpnRdToNetwork =
104                         tx.read(LogicalDatastoreType.CONFIGURATION, iidEvpnRdToNet).checkedGet();
105                 if (evpnRdToNetwork.isPresent()) {
106                     elanName = evpnRdToNetwork.get().getNetworkId();
107                 }
108             } catch (ReadFailedException e) {
109                 LOG.error("getElanName: unable to read elanName, exception ", e);
110             }
111             return elanName;
112         }
113     }
114
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());
118     }
119
120     public void updateEvpnDmacFlows(final ElanInstance elanInstance, final boolean install) {
121         String rd = evpnUtils.getEvpnRd(elanInstance);
122         if (rd == null) {
123             return;
124         }
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()) {
131                     return null;
132                 }
133                 List<MacVrfEntry> macVrfEntries = vrfTablesOptional.get().getMacVrfEntry();
134                 if (macVrfEntries == null || macVrfEntries.isEmpty()) {
135                     return null;
136                 }
137                 for (MacVrfEntry macVrfEntry : macVrfEntries) {
138                     InstanceIdentifier<MacVrfEntry> macVrfEntryIid = getMacVrfEntryIid(rd, macVrfEntry);
139                     if (install) {
140                         addEvpnDmacFlowOnAttach(macVrfEntryIid, macVrfEntry, elanInstance);
141                     } else {
142                         removeEvpnDmacFlowOnDetach(macVrfEntryIid, macVrfEntry, elanInstance);
143                     }
144                 }
145                 return null;
146             });
147     }
148
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);
154             return false;
155         }
156         return true;
157     }
158
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);
163             return;
164         }
165
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) {
173                 return;
174             }
175
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(
188                             vni)
189                             .setDstMacAddress(dstMacAddress).setElanName(elanName);
190                         Flow flow =
191                             elanEvpnFlowUtils.evpnBuildDmacFlowForExternalRemoteMac(dmacFlowBuilder.build());
192
193                         mdsalManager.addFlow(tx, dpId, flow);
194                     }))), ElanConstants.JOB_MAX_RETRIES);
195         }
196     }
197
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);
202             return;
203         }
204         List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
205
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) {
212             return;
213         }
214
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()));
226             });
227             return futures;
228         }, ElanConstants.JOB_MAX_RETRIES);
229     }
230
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);
236             return;
237         }
238
239         String elanName = elanInstance.getElanInstanceName();
240         List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
241
242         if (checkEvpnAttachedToNet(elanName)) {
243             String nexthopIP = getRoutePathNexthopIp(macVrfEntry);
244             if (nexthopIP == null) {
245                 LOG.debug("nexthopIP is null for iid {}", instanceIdentifier);
246                 return;
247             }
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);
266         }
267     }
268
269     @Nullable
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);
273             return null;
274         }
275         return macVrfEntry.getRoutePaths().get(0).getNexthopAddress();
276     }
277
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);
283             return;
284         }
285         List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanInstance.getElanInstanceName());
286
287         //if (checkEvpnAttachedToNet(elanName)) {
288         String nexthopIP = getRoutePathNexthopIp(macVrfEntry);
289         if (nexthopIP == null) {
290             LOG.debug("nexthopIP is null for iid {}", instanceIdentifier);
291             return;
292         }
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());
306             });
307             return futures;
308         }, ElanConstants.JOB_MAX_RETRIES);
309     }
310 }