NETVIRT-1630 migrate to md-sal APIs
[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.util.concurrent.ListenableFuture;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.List;
14 import java.util.Optional;
15 import java.util.concurrent.ExecutionException;
16 import javax.inject.Inject;
17 import javax.inject.Singleton;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.opendaylight.genius.infra.Datastore;
20 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
21 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
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.common.api.LogicalDatastoreType;
27 import org.opendaylight.netvirt.elan.cache.ElanInstanceCache;
28 import org.opendaylight.netvirt.elan.utils.ElanConstants;
29 import org.opendaylight.netvirt.elan.utils.ElanUtils;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.macvrfentries.MacVrfEntry;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.EvpnRdToNetworks;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.evpn.rd.to.networks.EvpnRdToNetwork;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.evpn.rd.to.networks.EvpnRdToNetworkKey;
43 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
44 import org.opendaylight.yangtools.yang.common.Uint32;
45 import org.opendaylight.yangtools.yang.common.Uint64;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 @Singleton
50 public class EvpnMacVrfUtils {
51
52     private static final Logger LOG = LoggerFactory.getLogger(EvpnMacVrfUtils.class);
53     private final DataBroker dataBroker;
54     private final ManagedNewTransactionRunner txRunner;
55     private final IdManagerService idManager;
56     private final ElanEvpnFlowUtils elanEvpnFlowUtils;
57     private final IMdsalApiManager mdsalManager;
58     private final EvpnUtils evpnUtils;
59     private final JobCoordinator jobCoordinator;
60     private final ElanUtils elanUtils;
61     private final ElanInstanceCache elanInstanceCache;
62
63     @Inject
64     public EvpnMacVrfUtils(final DataBroker dataBroker, final IdManagerService idManager,
65             final ElanEvpnFlowUtils elanEvpnFlowUtils, final IMdsalApiManager mdsalManager, final EvpnUtils evpnUtils,
66             final JobCoordinator jobCoordinator, final ElanUtils elanUtils, final ElanInstanceCache elanInstanceCache) {
67         this.dataBroker = dataBroker;
68         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
69         this.idManager = idManager;
70         this.elanEvpnFlowUtils = elanEvpnFlowUtils;
71         this.mdsalManager = mdsalManager;
72         this.evpnUtils = evpnUtils;
73         this.jobCoordinator = jobCoordinator;
74         this.elanUtils = elanUtils;
75         this.elanInstanceCache = elanInstanceCache;
76     }
77
78     @Nullable
79     private Uint32 getElanTagByMacvrfiid(InstanceIdentifier<MacVrfEntry> macVrfEntryIid) {
80         String elanName = getElanNameByMacvrfiid(macVrfEntryIid);
81         if (elanName == null) {
82             LOG.error("getElanTag: elanName is NULL for iid = {}", macVrfEntryIid);
83         }
84         ElanInstance elanInstance = elanInstanceCache.get(elanName).orElse(null);
85         if (elanInstance == null) {
86             return null;
87         }
88
89         Uint32 elanTag = elanInstance.getElanTag();
90         if (elanTag == null || elanTag.longValue() == 0L) {
91             elanTag = ElanUtils.retrieveNewElanTag(idManager, elanName);
92         }
93         return elanTag;
94     }
95
96     public String getElanNameByMacvrfiid(InstanceIdentifier<MacVrfEntry> instanceIdentifier) {
97         try (ReadTransaction tx = dataBroker.newReadOnlyTransaction()) {
98             String rd = instanceIdentifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
99             String elanName = null;
100             InstanceIdentifier<EvpnRdToNetwork> iidEvpnRdToNet =
101                     InstanceIdentifier.builder(EvpnRdToNetworks.class).child(EvpnRdToNetwork.class,
102                             new EvpnRdToNetworkKey(rd)).build();
103             try {
104                 Optional<EvpnRdToNetwork> evpnRdToNetwork =
105                         tx.read(LogicalDatastoreType.CONFIGURATION, iidEvpnRdToNet).get();
106                 if (evpnRdToNetwork.isPresent()) {
107                     elanName = evpnRdToNetwork.get().getNetworkId();
108                 }
109             } catch (InterruptedException | ExecutionException e) {
110                 LOG.error("getElanName: unable to read elanName, exception ", e);
111             }
112             return elanName;
113         }
114     }
115
116     public InstanceIdentifier<MacVrfEntry> getMacVrfEntryIid(String rd, MacVrfEntry macVrfEntry) {
117         return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
118                 .child(MacVrfEntry.class, macVrfEntry.key());
119     }
120
121     public void updateEvpnDmacFlows(final ElanInstance elanInstance, final boolean install) {
122         String rd = evpnUtils.getEvpnRd(elanInstance);
123         if (rd == null) {
124             return;
125         }
126         final InstanceIdentifier<VrfTables> iid = InstanceIdentifier.create(FibEntries.class)
127                 .child(VrfTables.class, new VrfTablesKey(rd));
128         evpnUtils.asyncReadAndExecute(LogicalDatastoreType.CONFIGURATION, iid,
129                 new StringBuilder(elanInstance.getElanInstanceName()).append(":").append(rd).toString(),
130             (vrfTablesOptional) -> {
131                 if (!vrfTablesOptional.isPresent()) {
132                     return null;
133                 }
134                 List<MacVrfEntry> macVrfEntries = vrfTablesOptional.get().getMacVrfEntry();
135                 if (macVrfEntries == null || macVrfEntries.isEmpty()) {
136                     return null;
137                 }
138                 for (MacVrfEntry macVrfEntry : macVrfEntries) {
139                     InstanceIdentifier<MacVrfEntry> macVrfEntryIid = getMacVrfEntryIid(rd, macVrfEntry);
140                     if (install) {
141                         addEvpnDmacFlowOnAttach(macVrfEntryIid, macVrfEntry, elanInstance);
142                     } else {
143                         removeEvpnDmacFlowOnDetach(macVrfEntryIid, macVrfEntry, elanInstance);
144                     }
145                 }
146                 return null;
147             });
148     }
149
150     public boolean checkEvpnAttachedToNet(String elanName) {
151         ElanInstance elanInfo = elanInstanceCache.get(elanName).orElse(null);
152         String evpnName = EvpnUtils.getEvpnNameFromElan(elanInfo);
153         if (evpnName == null) {
154             LOG.error("Error : evpnName is null for elanName {}", elanName);
155             return false;
156         }
157         return true;
158     }
159
160     public void addEvpnDmacFlow(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry) {
161         String elanName = getElanNameByMacvrfiid(instanceIdentifier);
162         if (elanName == null) {
163             LOG.error("Error : elanName is null for iid {}", instanceIdentifier);
164             return;
165         }
166
167         List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
168         if (checkEvpnAttachedToNet(elanName)) {
169             //TODO(Riyaz) : Check if accessing first nexthop address is right solution
170             String nexthopIP = macVrfEntry.getRoutePaths().get(0).getNexthopAddress();
171             IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
172             Uint32 elanTag = getElanTagByMacvrfiid(instanceIdentifier);
173             if (elanTag == null) {
174                 return;
175             }
176
177             String dstMacAddress = macVrfEntry.getMac();
178             long vni = macVrfEntry.getL2vni().toJava();
179             jobCoordinator.enqueueJob(dstMacAddress, () -> Collections.singletonList(
180                 txRunner.callWithNewWriteOnlyTransactionAndSubmit(Datastore.CONFIGURATION,
181                     tx -> dpnInterfaceLists.forEach(dpnInterfaces -> {
182                         Uint64 dpId = dpnInterfaces.getDpId();
183                         LOG.info("ADD: Build DMAC flow with dpId {}, nexthopIP {}, elanTag {},"
184                                 + "vni {}, dstMacAddress {}, elanName {} ",
185                             dpId, nexthopIP, elanTag, vni, dstMacAddress, elanName);
186                         ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder =
187                             new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
188                         dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag.longValue())
189                             .setVni(vni).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         Uint32 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                 Uint64 dpId = dpnInterfaces.getDpId();
220                 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder = new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
221                 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag.longValue())
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             Uint32 elanTag = elanInstance.getElanTag();
250             String dstMacAddress = macVrfEntry.getMac();
251             long vni = macVrfEntry.getL2vni().toJava();
252             jobCoordinator.enqueueJob(dstMacAddress, () -> Collections.singletonList(
253                 txRunner.callWithNewWriteOnlyTransactionAndSubmit(Datastore.CONFIGURATION,
254                     tx -> dpnInterfaceLists.forEach(dpnInterfaces -> {
255                         Uint64 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.longValue())
262                             .setVni(vni).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         Uint32 elanTag = elanInstance.getElanTag();
295         String macToRemove = macVrfEntry.getMac();
296         jobCoordinator.enqueueJob(macToRemove, () -> {
297             List<ListenableFuture<Void>> futures = new ArrayList<>();
298             dpnInterfaceLists.forEach(dpnInterfaces -> {
299                 Uint64 dpId = dpnInterfaces.getDpId();
300                 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder = new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
301                 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag.longValue())
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 }