9a5b263b4dd30704325857be10a30ecec5fac5e8
[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.Map;
15 import java.util.Optional;
16 import java.util.concurrent.ExecutionException;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.opendaylight.genius.infra.Datastore;
21 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
22 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
23 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
24 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
25 import org.opendaylight.mdsal.binding.api.DataBroker;
26 import org.opendaylight.mdsal.binding.api.ReadTransaction;
27 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
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.fibmanager.rev150330.macvrfentries.MacVrfEntryKey;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePaths;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.EvpnRdToNetworks;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.evpn.rd.to.networks.EvpnRdToNetwork;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.evpn.rd.to.networks.EvpnRdToNetworkKey;
46 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
47 import org.opendaylight.yangtools.yang.common.Uint32;
48 import org.opendaylight.yangtools.yang.common.Uint64;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 @Singleton
53 public class EvpnMacVrfUtils {
54
55     private static final Logger LOG = LoggerFactory.getLogger(EvpnMacVrfUtils.class);
56     private final DataBroker dataBroker;
57     private final ManagedNewTransactionRunner txRunner;
58     private final IdManagerService idManager;
59     private final ElanEvpnFlowUtils elanEvpnFlowUtils;
60     private final IMdsalApiManager mdsalManager;
61     private final EvpnUtils evpnUtils;
62     private final JobCoordinator jobCoordinator;
63     private final ElanUtils elanUtils;
64     private final ElanInstanceCache elanInstanceCache;
65
66     @Inject
67     public EvpnMacVrfUtils(final DataBroker dataBroker, final IdManagerService idManager,
68             final ElanEvpnFlowUtils elanEvpnFlowUtils, final IMdsalApiManager mdsalManager, final EvpnUtils evpnUtils,
69             final JobCoordinator jobCoordinator, final ElanUtils elanUtils, final ElanInstanceCache elanInstanceCache) {
70         this.dataBroker = dataBroker;
71         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
72         this.idManager = idManager;
73         this.elanEvpnFlowUtils = elanEvpnFlowUtils;
74         this.mdsalManager = mdsalManager;
75         this.evpnUtils = evpnUtils;
76         this.jobCoordinator = jobCoordinator;
77         this.elanUtils = elanUtils;
78         this.elanInstanceCache = elanInstanceCache;
79     }
80
81     @Nullable
82     private Uint32 getElanTagByMacvrfiid(InstanceIdentifier<MacVrfEntry> macVrfEntryIid) {
83         String elanName = getElanNameByMacvrfiid(macVrfEntryIid);
84         if (elanName == null) {
85             LOG.error("getElanTag: elanName is NULL for iid = {}", macVrfEntryIid);
86         }
87         ElanInstance elanInstance = elanInstanceCache.get(elanName).orElse(null);
88         if (elanInstance == null) {
89             return null;
90         }
91
92         Uint32 elanTag = elanInstance.getElanTag();
93         if (elanTag == null || elanTag.longValue() == 0L) {
94             elanTag = ElanUtils.retrieveNewElanTag(idManager, elanName);
95         }
96         return elanTag;
97     }
98
99     public String getElanNameByMacvrfiid(InstanceIdentifier<MacVrfEntry> instanceIdentifier) {
100         try (ReadTransaction tx = dataBroker.newReadOnlyTransaction()) {
101             String rd = instanceIdentifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
102             String elanName = null;
103             InstanceIdentifier<EvpnRdToNetwork> iidEvpnRdToNet =
104                     InstanceIdentifier.builder(EvpnRdToNetworks.class).child(EvpnRdToNetwork.class,
105                             new EvpnRdToNetworkKey(rd)).build();
106             try {
107                 Optional<EvpnRdToNetwork> evpnRdToNetwork =
108                         tx.read(LogicalDatastoreType.CONFIGURATION, iidEvpnRdToNet).get();
109                 if (evpnRdToNetwork.isPresent()) {
110                     elanName = evpnRdToNetwork.get().getNetworkId();
111                 }
112             } catch (InterruptedException | ExecutionException e) {
113                 LOG.error("getElanName: unable to read elanName, exception ", e);
114             }
115             return elanName;
116         }
117     }
118
119     public InstanceIdentifier<MacVrfEntry> getMacVrfEntryIid(String rd, MacVrfEntry macVrfEntry) {
120         return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
121                 .child(MacVrfEntry.class, macVrfEntry.key());
122     }
123
124     public void updateEvpnDmacFlows(final ElanInstance elanInstance, final boolean install) {
125         String rd = evpnUtils.getEvpnRd(elanInstance);
126         if (rd == null) {
127             return;
128         }
129         final InstanceIdentifier<VrfTables> iid = InstanceIdentifier.create(FibEntries.class)
130                 .child(VrfTables.class, new VrfTablesKey(rd));
131         evpnUtils.asyncReadAndExecute(LogicalDatastoreType.CONFIGURATION, iid,
132                 new StringBuilder(elanInstance.getElanInstanceName()).append(":").append(rd).toString(),
133             (vrfTablesOptional) -> {
134                 if (!vrfTablesOptional.isPresent()) {
135                     return null;
136                 }
137                 Map<MacVrfEntryKey, MacVrfEntry> keyMacVrfEntryMap = vrfTablesOptional.get().getMacVrfEntry();
138                 if (keyMacVrfEntryMap == null || keyMacVrfEntryMap.isEmpty()) {
139                     return null;
140                 }
141                 for (MacVrfEntry macVrfEntry : keyMacVrfEntryMap.values()) {
142                     InstanceIdentifier<MacVrfEntry> macVrfEntryIid = getMacVrfEntryIid(rd, macVrfEntry);
143                     if (install) {
144                         addEvpnDmacFlowOnAttach(macVrfEntryIid, macVrfEntry, elanInstance);
145                     } else {
146                         removeEvpnDmacFlowOnDetach(macVrfEntryIid, macVrfEntry, elanInstance);
147                     }
148                 }
149                 return null;
150             });
151     }
152
153     public boolean checkEvpnAttachedToNet(String elanName) {
154         ElanInstance elanInfo = elanInstanceCache.get(elanName).orElse(null);
155         String evpnName = EvpnUtils.getEvpnNameFromElan(elanInfo);
156         if (evpnName == null) {
157             LOG.error("Error : evpnName is null for elanName {}", elanName);
158             return false;
159         }
160         return true;
161     }
162
163     public void addEvpnDmacFlow(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry) {
164         String elanName = getElanNameByMacvrfiid(instanceIdentifier);
165         if (elanName == null) {
166             LOG.error("Error : elanName is null for iid {}", instanceIdentifier);
167             return;
168         }
169
170         List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
171         if (checkEvpnAttachedToNet(elanName)) {
172             //TODO(Riyaz) : Check if accessing first nexthop address is right solution
173             String nexthopIP = new ArrayList<RoutePaths>(macVrfEntry.getRoutePaths().values())
174                     .get(0).getNexthopAddress();
175             IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
176             Uint32 elanTag = getElanTagByMacvrfiid(instanceIdentifier);
177             if (elanTag == null) {
178                 return;
179             }
180
181             String dstMacAddress = macVrfEntry.getMac();
182             long vni = macVrfEntry.getL2vni().toJava();
183             jobCoordinator.enqueueJob(dstMacAddress, () -> Collections.singletonList(
184                 txRunner.callWithNewWriteOnlyTransactionAndSubmit(Datastore.CONFIGURATION,
185                     tx -> dpnInterfaceLists.forEach(dpnInterfaces -> {
186                         Uint64 dpId = dpnInterfaces.getDpId();
187                         LOG.info("ADD: Build DMAC flow with dpId {}, nexthopIP {}, elanTag {},"
188                                 + "vni {}, dstMacAddress {}, elanName {} ",
189                             dpId, nexthopIP, elanTag, vni, dstMacAddress, elanName);
190                         ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder =
191                             new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
192                         dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag.longValue())
193                             .setVni(vni).setDstMacAddress(dstMacAddress).setElanName(elanName);
194                         Flow flow =
195                             elanEvpnFlowUtils.evpnBuildDmacFlowForExternalRemoteMac(dmacFlowBuilder.build());
196
197                         mdsalManager.addFlow(tx, dpId, flow);
198                     }))), ElanConstants.JOB_MAX_RETRIES);
199         }
200     }
201
202     public void removeEvpnDmacFlow(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry) {
203         String elanName = getElanNameByMacvrfiid(instanceIdentifier);
204         if (elanName == null) {
205             LOG.error("Error : elanName is null for iid {}", instanceIdentifier);
206             return;
207         }
208         List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
209
210         //if (checkEvpnAttachedToNet(elanName)) {
211         //TODO(Riyaz) : Check if accessing first nexthop address is right
212         String nexthopIP = new ArrayList<RoutePaths>(macVrfEntry.getRoutePaths().values()).get(0).getNexthopAddress();
213         IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
214         Uint32 elanTag = getElanTagByMacvrfiid(instanceIdentifier);
215         if (elanTag == null) {
216             return;
217         }
218
219         String macToRemove = macVrfEntry.getMac();
220         jobCoordinator.enqueueJob(macToRemove, () -> {
221             List<ListenableFuture<Void>> futures = new ArrayList<>();
222             dpnInterfaceLists.forEach(dpnInterfaces -> {
223                 Uint64 dpId = dpnInterfaces.getDpId();
224                 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder = new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
225                 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag.longValue())
226                         .setDstMacAddress(macToRemove);
227                 LOG.info("REMOVE: Deleting DMAC Flows for external MAC. elanTag {}, dpId {},"
228                         + "nexthopIP {}, macToRemove {}", elanTag, dpId, nexthopIP, macToRemove);
229                 futures.addAll(elanEvpnFlowUtils.evpnDeleteDmacFlowsToExternalMac(dmacFlowBuilder.build()));
230             });
231             return futures;
232         }, ElanConstants.JOB_MAX_RETRIES);
233     }
234
235     public void addEvpnDmacFlowOnAttach(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry,
236                                         ElanInstance elanInstance) {
237         //String elanName = getElanNameByMacvrfiid(instanceIdentifier);
238         if (elanInstance == null) {
239             LOG.error("Error : elanName is null for iid {}", instanceIdentifier);
240             return;
241         }
242
243         String elanName = elanInstance.getElanInstanceName();
244         List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
245
246         if (checkEvpnAttachedToNet(elanName)) {
247             String nexthopIP = getRoutePathNexthopIp(macVrfEntry);
248             if (nexthopIP == null) {
249                 LOG.debug("nexthopIP is null for iid {}", instanceIdentifier);
250                 return;
251             }
252             IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
253             Uint32 elanTag = elanInstance.getElanTag();
254             String dstMacAddress = macVrfEntry.getMac();
255             long vni = macVrfEntry.getL2vni().toJava();
256             jobCoordinator.enqueueJob(dstMacAddress, () -> Collections.singletonList(
257                 txRunner.callWithNewWriteOnlyTransactionAndSubmit(Datastore.CONFIGURATION,
258                     tx -> dpnInterfaceLists.forEach(dpnInterfaces -> {
259                         Uint64 dpId = dpnInterfaces.getDpId();
260                         LOG.info("ADD: Build DMAC flow with dpId {}, nexthopIP {}, elanTag {},"
261                                 + "vni {}, dstMacAddress {}, elanName {} ",
262                             dpId, nexthopIP, elanTag, vni, dstMacAddress, elanName);
263                         ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder =
264                             new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
265                         dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag.longValue())
266                             .setVni(vni).setDstMacAddress(dstMacAddress).setElanName(elanName);
267                         Flow flow = elanEvpnFlowUtils.evpnBuildDmacFlowForExternalRemoteMac(dmacFlowBuilder.build());
268                         mdsalManager.addFlow(tx, dpId, flow);
269                     }))), ElanConstants.JOB_MAX_RETRIES);
270         }
271     }
272
273     @Nullable
274     public String getRoutePathNexthopIp(MacVrfEntry macVrfEntry) {
275         if (macVrfEntry.getRoutePaths() == null || macVrfEntry.getRoutePaths().isEmpty()) {
276             LOG.debug("RoutePaths is null or empty for macvrfentry {}", macVrfEntry);
277             return null;
278         }
279         return new ArrayList<RoutePaths>(macVrfEntry.getRoutePaths().values()).get(0).getNexthopAddress();
280     }
281
282     public void removeEvpnDmacFlowOnDetach(InstanceIdentifier<MacVrfEntry> instanceIdentifier, MacVrfEntry macVrfEntry,
283                                            ElanInstance elanInstance) {
284         //String elanName = getElanNameByMacvrfiid(instanceIdentifier);
285         if (elanInstance == null) {
286             LOG.error("Error : elanInstance is null for iid {}", instanceIdentifier);
287             return;
288         }
289         List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanInstance.getElanInstanceName());
290
291         //if (checkEvpnAttachedToNet(elanName)) {
292         String nexthopIP = getRoutePathNexthopIp(macVrfEntry);
293         if (nexthopIP == null) {
294             LOG.debug("nexthopIP is null for iid {}", instanceIdentifier);
295             return;
296         }
297         IpAddress ipAddress = new IpAddress(new Ipv4Address(nexthopIP));
298         Uint32 elanTag = elanInstance.getElanTag();
299         String macToRemove = macVrfEntry.getMac();
300         jobCoordinator.enqueueJob(macToRemove, () -> {
301             List<ListenableFuture<Void>> futures = new ArrayList<>();
302             dpnInterfaceLists.forEach(dpnInterfaces -> {
303                 Uint64 dpId = dpnInterfaces.getDpId();
304                 ElanEvpnFlowUtils.EvpnDmacFlowBuilder dmacFlowBuilder = new ElanEvpnFlowUtils.EvpnDmacFlowBuilder();
305                 dmacFlowBuilder.setDpId(dpId).setNexthopIP(ipAddress.toString()).setElanTag(elanTag.longValue())
306                         .setDstMacAddress(macToRemove);
307                 LOG.info("REMOVE: Deleting DMAC Flows for external MAC. elanTag {}, dpId {},"
308                         + "nexthopIP {}, macToRemove {}", elanTag, dpId, nexthopIP, macToRemove);
309                 elanEvpnFlowUtils.evpnDeleteDmacFlowsToExternalMac(dmacFlowBuilder.build());
310             });
311             return futures;
312         }, ElanConstants.JOB_MAX_RETRIES);
313     }
314 }