Refactor ElanUtil interface maps to a non-static singleton
[netvirt.git] / elanmanager / impl / src / main / java / org / opendaylight / netvirt / elan / utils / ElanDmacUtils.java
1 /*
2  * Copyright © 2017 Red Hat, Inc. and others.
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.utils;
9
10 import com.google.common.base.Optional;
11 import com.google.common.collect.Lists;
12 import com.google.common.util.concurrent.Futures;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import java.math.BigInteger;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.List;
18 import javax.inject.Inject;
19 import javax.inject.Singleton;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.genius.mdsalutil.MDSALUtil;
22 import org.opendaylight.genius.mdsalutil.MatchInfo;
23 import org.opendaylight.genius.mdsalutil.NwConstants;
24 import org.opendaylight.genius.utils.batching.ResourceBatchingManager;
25 import org.opendaylight.netvirt.elan.ElanException;
26 import org.opendaylight.netvirt.elan.cache.ElanInterfaceCache;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInterface;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeLeafTagName;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 @Singleton
39 public class ElanDmacUtils {
40     private static final Logger LOG = LoggerFactory.getLogger(ElanDmacUtils.class);
41
42     private static final boolean SH_FLAG_SET = true;
43     private static final boolean SH_FLAG_UNSET = false;
44
45     private final DataBroker broker;
46     private final ElanItmUtils elanItmUtils;
47     private final ElanEtreeUtils elanEtreeUtils;
48     private final ElanInterfaceCache elanInterfaceCache;
49
50     @Inject
51     public ElanDmacUtils(DataBroker broker, ElanItmUtils elanItmUtils, ElanEtreeUtils elanEtreeUtils,
52             ElanInterfaceCache elanInterfaceCache) {
53         this.broker = broker;
54         this.elanItmUtils = elanItmUtils;
55         this.elanEtreeUtils = elanEtreeUtils;
56         this.elanInterfaceCache = elanInterfaceCache;
57     }
58
59     /**
60      * Builds a Flow to be programmed in a DPN's DMAC table. This method must be
61      * used when the MAC is located in an External Device (TOR). The flow
62      * matches on the specified MAC and 1) sends the packet over the CSS-TOR
63      * tunnel if SHFlag is not set, or 2) drops it if SHFlag is set (what means
64      * the packet came from an external tunnel)
65      *
66      * @param dpId
67      *            DPN whose DMAC table is going to be modified
68      * @param extDeviceNodeId
69      *            Hwvtep node where the mac is attached to
70      * @param elanTag
71      *            ElanId to which the MAC is being added to
72      * @param vni
73      *            the vni
74      * @param dstMacAddress
75      *            The mac address to be programmed
76      * @param displayName
77      *            the display name
78      * @return the flow
79      * @throws ElanException in case of issues creating the flow objects
80      */
81     @SuppressWarnings("checkstyle:IllegalCatch")
82     public Flow buildDmacFlowForExternalRemoteMac(BigInteger dpId, String extDeviceNodeId, long elanTag,
83             Long vni, String dstMacAddress, String displayName) throws ElanException {
84         List<MatchInfo> mkMatches =
85                 ElanUtils.buildMatchesForElanTagShFlagAndDstMac(elanTag, /* shFlag */ false, dstMacAddress);
86         List<Instruction> mkInstructions = new ArrayList<>();
87         try {
88             List<Action> actions =
89                     elanItmUtils.getExternalTunnelItmEgressAction(dpId, new NodeId(extDeviceNodeId), vni);
90             mkInstructions.add(MDSALUtil.buildApplyActionsInstruction(actions));
91         } catch (Exception e) {
92             LOG.error("Could not get Egress Actions for DpId=" + dpId + ", externalNode=" + extDeviceNodeId, e);
93         }
94
95         return MDSALUtil.buildFlowNew(NwConstants.ELAN_DMAC_TABLE,
96                 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, dstMacAddress,
97                         elanTag, false),
98                 20, /* prio */
99                 displayName, 0, /* idleTimeout */
100                 0, /* hardTimeout */
101                 ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
102     }
103
104     /**
105      * Installs a Flow in the specified DPN's DMAC table. The flow is for a MAC
106      * that is connected remotely in an External Device (TOR) and that is
107      * accessible through an external tunnel. It also installs the flow for
108      * dropping the packet if it came over an ITM tunnel (that is, if the
109      * Split-Horizon flag is set)
110      *
111      * @param dpnId
112      *            Id of the DPN where the flow must be installed
113      * @param extDeviceNodeId
114      *            the ext device node id
115      * @param elanTag
116      *            the elan tag
117      * @param vni
118      *            the vni
119      * @param macAddress
120      *            the mac address
121      * @param displayName
122      *            the display name
123      * @param interfaceName
124      *            the interface name
125      *
126      * @return the dmac flows
127      * @throws ElanException in case of issues creating the flow objects
128      */
129     public List<ListenableFuture<Void>> installDmacFlowsToExternalRemoteMac(BigInteger dpnId,
130             String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName,
131             String interfaceName) throws ElanException {
132         synchronized (ElanUtils.getElanMacDPNKey(elanTag, macAddress, dpnId)) {
133             Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId, elanTag, vni, macAddress,
134                     displayName);
135             ResourceBatchingManager.getInstance().put(
136                     ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow);
137
138             Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId, elanTag, macAddress);
139             ResourceBatchingManager.getInstance().put(ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY,
140                     ElanUtils.getFlowIid(dropFlow, dpnId), dropFlow);
141             installEtreeDmacFlowsToExternalRemoteMac(dpnId, extDeviceNodeId, elanTag, vni, macAddress, displayName,
142                     interfaceName);
143         }
144         return Collections.emptyList();
145     }
146
147     /**
148      * Installs or removes flows in DMAC table for MACs that are/were located in
149      * an external Elan Device.
150      *
151      * @param dpId
152      *            Id of the DPN where the DMAC table is going to be modified
153      * @param extNodeId
154      *            Id of the External Device where the MAC is located
155      * @param elanTag
156      *            Id of the ELAN
157      * @param vni
158      *            VNI of the LogicalSwitch to which the MAC belongs to, and that
159      *            is associated with the ELAN
160      * @param macAddress
161      *            the mac address
162      * @param elanInstanceName
163      *            the elan instance name
164      * @param addOrRemove
165      *            Indicates if flows must be installed or removed.
166      * @param interfaceName
167      *            the interface name
168      * @throws ElanException in case of issues creating the flow objects
169      * @see org.opendaylight.genius.mdsalutil.MDSALUtil.MdsalOp
170      */
171     public void setupDmacFlowsToExternalRemoteMac(BigInteger dpId, String extNodeId, Long elanTag, Long vni,
172             String macAddress, String elanInstanceName, MDSALUtil.MdsalOp addOrRemove, String interfaceName)
173             throws ElanException {
174         if (addOrRemove == MDSALUtil.MdsalOp.CREATION_OP) {
175             installDmacFlowsToExternalRemoteMac(dpId, extNodeId, elanTag, vni, macAddress, elanInstanceName,
176                     interfaceName);
177         } else if (addOrRemove == MDSALUtil.MdsalOp.REMOVAL_OP) {
178             deleteDmacFlowsToExternalMac(elanTag, dpId, extNodeId, macAddress);
179         }
180     }
181
182     /**
183      * Delete dmac flows to external mac.
184      *
185      * @param elanTag
186      *            the elan tag
187      * @param dpId
188      *            the dp id
189      * @param extDeviceNodeId
190      *            the ext device node id
191      * @param macToRemove
192      *            the mac to remove
193      * @return dmac flow
194      */
195     public List<ListenableFuture<Void>> deleteDmacFlowsToExternalMac(long elanTag, BigInteger dpId,
196             String extDeviceNodeId, String macToRemove) {
197         List<ListenableFuture<Void>> result = Lists.newArrayList(
198             removeFlowThatSendsThePacketOnAnExternalTunnel(elanTag, dpId, extDeviceNodeId, macToRemove),
199             removeTheDropFlow(elanTag, dpId, extDeviceNodeId, macToRemove));
200         result.addAll(deleteEtreeDmacFlowsToExternalMac(elanTag, dpId, extDeviceNodeId, macToRemove));
201         return result;
202     }
203
204     private List<ListenableFuture<Void>> deleteEtreeDmacFlowsToExternalMac(
205             long elanTag, BigInteger dpId, String extDeviceNodeId, String macToRemove) {
206         EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
207         if (etreeLeafTag != null) {
208             return Lists.newArrayList(
209                     removeFlowThatSendsThePacketOnAnExternalTunnel(
210                             etreeLeafTag.getEtreeLeafTag().getValue(), dpId, extDeviceNodeId, macToRemove),
211                     removeTheDropFlow(etreeLeafTag.getEtreeLeafTag().getValue(), dpId, extDeviceNodeId, macToRemove));
212         }
213         return Collections.emptyList();
214     }
215
216     private ListenableFuture<Void> removeTheDropFlow(
217             long elanTag, BigInteger dpId, String extDeviceNodeId, String macToRemove) {
218         String flowId =
219                 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, macToRemove,
220                         elanTag, true);
221         Flow flowToRemove = new FlowBuilder().setId(new FlowId(flowId)).setTableId(NwConstants.ELAN_DMAC_TABLE).build();
222         return ResourceBatchingManager.getInstance().delete(
223                 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flowToRemove, dpId));
224     }
225
226     private ListenableFuture<Void> removeFlowThatSendsThePacketOnAnExternalTunnel(long elanTag, BigInteger dpId,
227             String extDeviceNodeId, String macToRemove) {
228         String flowId =
229                 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpId, extDeviceNodeId, macToRemove,
230                         elanTag, false);
231         Flow flowToRemove = new FlowBuilder().setId(new FlowId(flowId)).setTableId(NwConstants.ELAN_DMAC_TABLE).build();
232         return ResourceBatchingManager.getInstance().delete(
233                 ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flowToRemove, dpId));
234     }
235
236     private List<ListenableFuture<Void>> installEtreeDmacFlowsToExternalRemoteMac(
237             BigInteger dpnId, String extDeviceNodeId, Long elanTag,
238             Long vni, String macAddress, String displayName, String interfaceName) throws ElanException {
239         EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
240         if (etreeLeafTag != null) {
241             return Lists.newArrayList(
242                 buildEtreeDmacFlowDropIfPacketComingFromTunnel(
243                     dpnId, extDeviceNodeId, macAddress, etreeLeafTag),
244                 buildEtreeDmacFlowForExternalRemoteMac(
245                         dpnId, extDeviceNodeId, vni, macAddress, displayName, interfaceName, etreeLeafTag));
246         }
247         return Collections.emptyList();
248     }
249
250     private ListenableFuture<Void> buildEtreeDmacFlowForExternalRemoteMac(
251             BigInteger dpnId, String extDeviceNodeId, Long vni,
252             String macAddress, String displayName, String interfaceName,
253             EtreeLeafTagName etreeLeafTag) throws ElanException {
254         boolean isRoot;
255         if (interfaceName == null) {
256             isRoot = true;
257         } else {
258             Optional<EtreeInterface> etreeInterface = elanInterfaceCache.getEtreeInterface(interfaceName);
259             isRoot = etreeInterface.isPresent() ? etreeInterface.get().getEtreeInterfaceType()
260                     == EtreeInterface.EtreeInterfaceType.Root : false;
261         }
262         if (isRoot) {
263             Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId,
264                     etreeLeafTag.getEtreeLeafTag().getValue(), vni, macAddress, displayName);
265             return ResourceBatchingManager.getInstance().put(
266                     ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow);
267         }
268         return Futures.immediateFuture(null);
269     }
270
271     private ListenableFuture<Void> buildEtreeDmacFlowDropIfPacketComingFromTunnel(
272             BigInteger dpnId, String extDeviceNodeId, String macAddress, EtreeLeafTagName etreeLeafTag) {
273         if (etreeLeafTag != null) {
274             Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId,
275                     etreeLeafTag.getEtreeLeafTag().getValue(), macAddress);
276             return ResourceBatchingManager.getInstance().put(
277                     ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(dropFlow, dpnId),
278                     dropFlow);
279         }
280         return Futures.immediateFuture(null);
281     }
282
283     /**
284      * Builds the flow that drops the packet if it came through an external
285      * tunnel, that is, if the Split-Horizon flag is set.
286      *
287      * @param dpnId
288      *            DPN whose DMAC table is going to be modified
289      * @param extDeviceNodeId
290      *            Hwvtep node where the mac is attached to
291      * @param elanTag
292      *            ElanId to which the MAC is being added to
293      * @param dstMacAddress
294      *            The mac address to be programmed
295      */
296     private static Flow buildDmacFlowDropIfPacketComingFromTunnel(BigInteger dpnId, String extDeviceNodeId,
297             Long elanTag, String dstMacAddress) {
298         List<MatchInfo> mkMatches =
299                 ElanUtils.buildMatchesForElanTagShFlagAndDstMac(elanTag, SH_FLAG_SET, dstMacAddress);
300         List<Instruction> mkInstructions = MDSALUtil.buildInstructionsDrop();
301         String flowId =
302                 ElanUtils.getKnownDynamicmacFlowRef(NwConstants.ELAN_DMAC_TABLE, dpnId, extDeviceNodeId, dstMacAddress,
303                         elanTag, true);
304
305         return MDSALUtil.buildFlowNew(NwConstants.ELAN_DMAC_TABLE, flowId, 20, /* prio */
306                 "Drop", 0, /* idleTimeout */
307                 0, /* hardTimeout */
308                 ElanConstants.COOKIE_ELAN_KNOWN_DMAC.add(BigInteger.valueOf(elanTag)), mkMatches, mkInstructions);
309     }
310
311     private ListenableFuture<Void> buildEtreeDmacFlowForExternalRemoteMacWithBatch(
312             BigInteger dpnId, String extDeviceNodeId, Long vni, String macAddress, String displayName,
313             String interfaceName, EtreeLeafTagName etreeLeafTag)throws ElanException {
314
315         boolean isRoot;
316         if (interfaceName == null) {
317             isRoot = true;
318         } else {
319             Optional<EtreeInterface> etreeInterface = elanInterfaceCache.getEtreeInterface(interfaceName);
320             isRoot = etreeInterface.isPresent() ? etreeInterface.get().getEtreeInterfaceType()
321                     == EtreeInterface.EtreeInterfaceType.Root : false;
322         }
323         if (isRoot) {
324             Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId,
325                     etreeLeafTag.getEtreeLeafTag().getValue(), vni, macAddress, displayName);
326             return ResourceBatchingManager.getInstance().put(
327                     ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow);
328         }
329         return Futures.immediateFuture(null);
330     }
331
332     private ListenableFuture<Void> buildEtreeDmacFlowDropIfPacketComingFromTunnelwithBatch(
333             BigInteger dpnId, String extDeviceNodeId, Long elanTag, String macAddress,EtreeLeafTagName etreeLeafTag) {
334         if (etreeLeafTag != null) {
335             Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId,
336                     etreeLeafTag.getEtreeLeafTag().getValue(), macAddress);
337             return ResourceBatchingManager.getInstance().put(ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY,
338                     ElanUtils.getFlowIid(dropFlow, dpnId),dropFlow);
339         }
340         return Futures.immediateFuture(null);
341     }
342
343     public List<ListenableFuture<Void>> installDmacFlowsToExternalRemoteMacInBatch(
344             BigInteger dpnId, String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName,
345             String interfaceName) throws ElanException {
346
347         Flow flow = buildDmacFlowForExternalRemoteMac(dpnId, extDeviceNodeId, elanTag, vni, macAddress,
348                 displayName);
349         Flow dropFlow = buildDmacFlowDropIfPacketComingFromTunnel(dpnId, extDeviceNodeId, elanTag, macAddress);
350         List<ListenableFuture<Void>> result = Lists.newArrayList(
351                 ResourceBatchingManager.getInstance().put(
352                     ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(flow, dpnId), flow),
353                 ResourceBatchingManager.getInstance().put(
354                     ResourceBatchingManager.ShardResource.CONFIG_TOPOLOGY, ElanUtils.getFlowIid(dropFlow, dpnId),
355                         dropFlow));
356         result.addAll(installEtreeDmacFlowsToExternalRemoteMacInBatch(
357                 dpnId, extDeviceNodeId, elanTag, vni, macAddress, displayName, interfaceName));
358         return result;
359     }
360
361     private List<ListenableFuture<Void>> installEtreeDmacFlowsToExternalRemoteMacInBatch(
362             BigInteger dpnId, String extDeviceNodeId, Long elanTag, Long vni, String macAddress, String displayName,
363             String interfaceName) throws ElanException {
364
365         EtreeLeafTagName etreeLeafTag = elanEtreeUtils.getEtreeLeafTagByElanTag(elanTag);
366         if (etreeLeafTag != null) {
367             return Lists.newArrayList(
368                 buildEtreeDmacFlowDropIfPacketComingFromTunnelwithBatch(
369                         dpnId, extDeviceNodeId, elanTag, macAddress, etreeLeafTag),
370                 buildEtreeDmacFlowForExternalRemoteMacWithBatch(
371                         dpnId, extDeviceNodeId, vni, macAddress, displayName, interfaceName, etreeLeafTag));
372         }
373         return Collections.emptyList();
374     }
375 }