elanmanager: use transaction manager
[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.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;
43
44 @Singleton
45 public class EvpnMacVrfUtils {
46
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;
56
57     @Inject
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;
69     }
70
71     @Nullable
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);
76         }
77         ElanInstance elanInstance = elanInstanceCache.get(elanName).orNull();
78         if (elanInstance == null) {
79             return null;
80         }
81
82         Long elanTag = elanInstance.getElanTag();
83         if (elanTag == null || elanTag == 0L) {
84             elanTag = ElanUtils.retrieveNewElanTag(idManager, elanName);
85         }
86         return elanTag;
87     }
88
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();
96             try {
97                 Optional<EvpnRdToNetwork> evpnRdToNetwork =
98                         tx.read(LogicalDatastoreType.CONFIGURATION, iidEvpnRdToNet).checkedGet();
99                 if (evpnRdToNetwork.isPresent()) {
100                     elanName = evpnRdToNetwork.get().getNetworkId();
101                 }
102             } catch (ReadFailedException e) {
103                 LOG.error("getElanName: unable to read elanName, exception ", e);
104             }
105             return elanName;
106         }
107     }
108
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());
112     }
113
114     public void updateEvpnDmacFlows(final ElanInstance elanInstance, final boolean install) {
115         String rd = evpnUtils.getEvpnRd(elanInstance);
116         if (rd == null) {
117             return;
118         }
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()) {
125                     return null;
126                 }
127                 List<MacVrfEntry> macVrfEntries = vrfTablesOptional.get().getMacVrfEntry();
128                 if (macVrfEntries == null || macVrfEntries.isEmpty()) {
129                     return null;
130                 }
131                 for (MacVrfEntry macVrfEntry : macVrfEntries) {
132                     InstanceIdentifier<MacVrfEntry> macVrfEntryIid = getMacVrfEntryIid(rd, macVrfEntry);
133                     if (install) {
134                         addEvpnDmacFlowOnAttach(macVrfEntryIid, macVrfEntry, elanInstance);
135                     } else {
136                         removeEvpnDmacFlowOnDetach(macVrfEntryIid, macVrfEntry, elanInstance);
137                     }
138                 }
139                 return null;
140             });
141     }
142
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);
148             return false;
149         }
150         return true;
151     }
152
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);
157             return;
158         }
159
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) {
167                 return;
168             }
169
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());
183
184                     futures.add(mdsalManager.installFlow(dpId, flow));
185                 });
186                 return futures;
187             }, ElanConstants.JOB_MAX_RETRIES);
188         }
189     }
190
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);
195             return;
196         }
197         List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
198
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) {
205             return;
206         }
207
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()));
219             });
220             return futures;
221         }, ElanConstants.JOB_MAX_RETRIES);
222     }
223
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);
229             return;
230         }
231
232         String elanName = elanInstance.getElanInstanceName();
233         List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanName);
234
235         if (checkEvpnAttachedToNet(elanName)) {
236             String nexthopIP = getRoutePathNexthopIp(macVrfEntry);
237             if (nexthopIP == null) {
238                 LOG.debug("nexthopIP is null for iid {}", instanceIdentifier);
239                 return;
240             }
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));
257                 });
258                 return futures;
259             }, ElanConstants.JOB_MAX_RETRIES);
260         }
261     }
262
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);
266             return null;
267         }
268         return macVrfEntry.getRoutePaths().get(0).getNexthopAddress();
269     }
270
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);
276             return;
277         }
278         List<DpnInterfaces> dpnInterfaceLists = elanUtils.getElanDPNByName(elanInstance.getElanInstanceName());
279
280         //if (checkEvpnAttachedToNet(elanName)) {
281         String nexthopIP = getRoutePathNexthopIp(macVrfEntry);
282         if (nexthopIP == null) {
283             LOG.debug("nexthopIP is null for iid {}", instanceIdentifier);
284             return;
285         }
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());
299             });
300             return futures;
301         }, ElanConstants.JOB_MAX_RETRIES);
302     }
303 }