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