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