InstructionInfo redesign
[genius.git] / interfacemanager / interfacemanager-impl / src / main / java / org / opendaylight / genius / interfacemanager / servicebindings / flowbased / utilities / FlowBasedServicesUtils.java
1 /*
2  * Copyright © 2016, 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.genius.interfacemanager.servicebindings.flowbased.utilities;
9
10 import com.google.common.collect.ImmutableBiMap;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.List;
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
19 import org.opendaylight.genius.interfacemanager.IfmConstants;
20 import org.opendaylight.genius.interfacemanager.IfmUtil;
21 import org.opendaylight.genius.interfacemanager.commons.InterfaceManagerCommonUtils;
22 import org.opendaylight.genius.mdsalutil.ActionInfo;
23 import org.opendaylight.genius.mdsalutil.InstructionInfo;
24 import org.opendaylight.genius.mdsalutil.MDSALUtil;
25 import org.opendaylight.genius.mdsalutil.MatchFieldType;
26 import org.opendaylight.genius.mdsalutil.MatchInfo;
27 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
28 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
29 import org.opendaylight.genius.mdsalutil.NwConstants;
30 import org.opendaylight.genius.mdsalutil.NxMatchFieldType;
31 import org.opendaylight.genius.mdsalutil.NxMatchInfo;
32 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
33 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
34 import org.opendaylight.genius.utils.ServiceIndex;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCase;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.WriteActionsCase;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.WriteMetadataCase;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfExternal;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.SplitHorizon;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeBase;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeEgress;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceTypeFlowBased;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflow;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflowBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfoKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
68 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71
72 public class FlowBasedServicesUtils {
73     private static final Logger LOG = LoggerFactory.getLogger(FlowBasedServicesUtils.class);
74
75     public enum ServiceMode  {
76         INGRESS,
77         EGRESS
78     }
79
80     public static final ImmutableBiMap SERVICE_MODE_MAP =
81             new ImmutableBiMap.Builder<ServiceMode, Class<? extends ServiceModeBase>>()
82                     .put(ServiceMode.EGRESS, ServiceModeEgress.class)
83                     .put(ServiceMode.INGRESS, ServiceModeIngress.class)
84                     .build();
85
86     public static ServicesInfo getServicesInfoForInterface(String interfaceName, Class<? extends ServiceModeBase> serviceMode,
87                                                            DataBroker dataBroker) {
88         ServicesInfoKey servicesInfoKey = new ServicesInfoKey(interfaceName,serviceMode);
89         InstanceIdentifier.InstanceIdentifierBuilder<ServicesInfo> servicesInfoIdentifierBuilder =
90                 InstanceIdentifier.builder(ServiceBindings.class).child(ServicesInfo.class, servicesInfoKey);
91         return IfmUtil.read(LogicalDatastoreType.CONFIGURATION, servicesInfoIdentifierBuilder.build(),
92                 dataBroker).orNull();
93     }
94
95     public static NodeConnectorId getNodeConnectorIdFromInterface(String interfaceName, DataBroker dataBroker) {
96         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface ifState =
97                 InterfaceManagerCommonUtils.getInterfaceStateFromOperDS(interfaceName, dataBroker);
98         if(ifState != null) {
99             List<String> ofportIds = ifState.getLowerLayerIf();
100             return new NodeConnectorId(ofportIds.get(0));
101         }
102         return null;
103     }
104
105     public static NodeConnectorId getNodeConnectorIdFromInterface(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface ifState) {
106         if(ifState != null) {
107             List<String> ofportIds = ifState.getLowerLayerIf();
108             return new NodeConnectorId(ofportIds.get(0));
109         }
110         return null;
111     }
112
113     public static BigInteger getDpnIdFromInterface(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface ifState) {
114         NodeConnectorId nodeConnectorId = null;
115         if(ifState != null) {
116             List<String> ofportIds = ifState.getLowerLayerIf();
117             nodeConnectorId = new NodeConnectorId(ofportIds.get(0));
118         }
119         return IfmUtil.getDpnFromNodeConnectorId(nodeConnectorId);
120     }
121
122     public static List<MatchInfo> getMatchInfoForVlanPortAtIngressTable(BigInteger dpId, long portNo, Interface iface) {
123         List<MatchInfo> matches = new ArrayList<>();
124         matches.add(new MatchInfo(MatchFieldType.in_port, new BigInteger[] {dpId, BigInteger.valueOf(portNo)}));
125         int vlanId = 0;
126         IfL2vlan l2vlan = iface.getAugmentation(IfL2vlan.class);
127         if(l2vlan != null && l2vlan.getL2vlanMode() != IfL2vlan.L2vlanMode.Transparent){
128             vlanId = l2vlan.getVlanId() == null ? 0 : l2vlan.getVlanId().getValue();
129         }
130         if (vlanId > 0) {
131             matches.add(new MatchInfo(MatchFieldType.vlan_vid, new long[]{vlanId}));
132         }
133         return matches;
134     }
135
136     public static List<MatchInfo> getMatchInfoForTunnelPortAtIngressTable(BigInteger dpId, long portNo) {
137         List<MatchInfo> matches = new ArrayList<>();
138         matches.add(new MatchInfo(MatchFieldType.in_port, new BigInteger[]{dpId, BigInteger.valueOf(portNo)}));
139         return matches;
140     }
141
142     public static List<MatchInfo> getMatchInfoForDispatcherTable(BigInteger dpId,
143                                                                  int interfaceTag, short servicePriority) {
144         List<MatchInfo> matches = new ArrayList<>();
145         matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
146                 MetaDataUtil.getMetaDataForLPortDispatcher(interfaceTag, servicePriority),
147                 MetaDataUtil.getMetaDataMaskForLPortDispatcher() }));
148         return matches;
149     }
150
151     public static List<MatchInfoBase> getMatchInfoForEgressDispatcherTable(int interfaceTag, short serviceIndex) {
152         List<MatchInfoBase> matches = new ArrayList<>();
153         matches.add(new NxMatchInfo(NxMatchFieldType.nxm_reg_6, new long[] {
154                 MetaDataUtil.getReg6ValueForLPortDispatcher(interfaceTag, serviceIndex)}));
155         return matches;
156     }
157
158     public static void installInterfaceIngressFlow(BigInteger dpId, Interface iface,
159                                                    BoundServices boundServiceNew,
160                                                    WriteTransaction t,
161                                                    List<MatchInfo> matches, int lportTag, short tableId) {
162         List<Instruction> instructions = boundServiceNew.getAugmentation(StypeOpenflow.class).getInstruction();
163
164         int serviceInstructionsSize = instructions.size();
165         List<Instruction> instructionSet = new ArrayList<>();
166         int vlanId = 0;
167         IfL2vlan l2vlan = iface.getAugmentation(IfL2vlan.class);
168         if(l2vlan != null && l2vlan.getVlanId() != null){
169             vlanId = l2vlan.getVlanId().getValue();
170         }
171         if (vlanId != 0) {
172             // incrementing instructionSize and using it as actionKey. Because it won't clash with any other instructions
173             int actionKey = ++serviceInstructionsSize;
174             instructionSet.add(MDSALUtil.buildAndGetPopVlanActionInstruction(actionKey, ++serviceInstructionsSize));
175         }
176
177         if (lportTag != 0L) {
178             BigInteger[] metadataValues = IfmUtil.mergeOpenflowMetadataWriteInstructions(instructions);
179             short sIndex = boundServiceNew.getServicePriority();
180             BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(lportTag,
181                     ++sIndex, metadataValues[0], isExternal(iface));
182             BigInteger metadataMask = MetaDataUtil.getMetaDataMaskForLPortDispatcher(
183                     MetaDataUtil.METADATA_MASK_SERVICE_INDEX,
184                     MetaDataUtil.METADATA_MASK_LPORT_TAG_SH_FLAG, metadataValues[1]);
185             instructionSet.add(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask,
186                     ++serviceInstructionsSize));
187         }
188
189         if (instructions != null && !instructions.isEmpty()) {
190             for (Instruction info : instructions) {
191                 // Skip meta data write as that is handled already
192                 if (info.getInstruction() instanceof WriteMetadataCase) {
193                     continue;
194                 } else if (info.getInstruction() instanceof WriteActionsCase) {
195                     info = MDSALUtil.buildWriteActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
196                         ((WriteActionsCase)info.getInstruction()).getWriteActions().getAction()));
197                 } else if (info.getInstruction() instanceof ApplyActionsCase) {
198                     info = MDSALUtil.buildApplyActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
199                         ((ApplyActionsCase)info.getInstruction()).getApplyActions().getAction()));
200                 }
201                 instructionSet.add(info);
202             }
203         }
204
205         String serviceRef = boundServiceNew.getServiceName();
206         String flowRef = getFlowRef(dpId, NwConstants.VLAN_INTERFACE_INGRESS_TABLE, iface.getName(),
207                 boundServiceNew, boundServiceNew.getServicePriority());
208         StypeOpenflow stypeOpenflow = boundServiceNew.getAugmentation(StypeOpenflow.class);
209         Flow ingressFlow = MDSALUtil.buildFlowNew(tableId, flowRef,
210                 stypeOpenflow.getFlowPriority(), serviceRef, 0, 0,
211                 stypeOpenflow.getFlowCookie(), matches, instructionSet);
212         installFlow(dpId, ingressFlow, t);
213     }
214
215     public static void installFlow(BigInteger dpId, Flow flow, WriteTransaction t) {
216         FlowKey flowKey = new FlowKey(new FlowId(flow.getId()));
217         Node nodeDpn = buildInventoryDpnNode(dpId);
218         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
219                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
220                 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class,flowKey).build();
221
222         t.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId, flow, true);
223     }
224
225     public static void removeFlow(String flowRef, BigInteger dpId, WriteTransaction t) {
226         LOG.debug("Removing Ingress Flows");
227         FlowKey flowKey = new FlowKey(new FlowId(flowRef));
228         Node nodeDpn = buildInventoryDpnNode(dpId);
229         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
230                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
231                 .child(Table.class, new TableKey(NwConstants.VLAN_INTERFACE_INGRESS_TABLE)).child(Flow.class, flowKey).build();
232
233         t.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
234     }
235
236     private static Node buildInventoryDpnNode(BigInteger dpnId) {
237         NodeId nodeId = new NodeId("openflow:" + dpnId);
238         Node nodeDpn = new NodeBuilder().setId(nodeId).setKey(new NodeKey(nodeId)).build();
239
240         return nodeDpn;
241     }
242
243     public static void installLPortDispatcherFlow(BigInteger dpId, BoundServices boundService, String interfaceName,
244                                                   WriteTransaction t, int interfaceTag, short currentServiceIndex, short nextServiceIndex) {
245         LOG.debug("Installing LPort Dispatcher Flow {}, {}", dpId, interfaceName);
246         String serviceRef = boundService.getServiceName();
247         List<MatchInfo> matches = FlowBasedServicesUtils.getMatchInfoForDispatcherTable(dpId, interfaceTag, currentServiceIndex);
248
249         // Get the metadata and mask from the service's write metadata instruction
250         StypeOpenflow stypeOpenFlow = boundService.getAugmentation(StypeOpenflow.class);
251         List<Instruction> serviceInstructions = stypeOpenFlow.getInstruction();
252         int instructionSize = serviceInstructions.size();
253         BigInteger[] metadataValues = IfmUtil.mergeOpenflowMetadataWriteInstructions(serviceInstructions);
254         BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(interfaceTag, nextServiceIndex, metadataValues[0]);
255         BigInteger metadataMask = MetaDataUtil.getWriteMetaDataMaskForDispatcherTable();
256
257         // build the final instruction for LPort Dispatcher table flow entry
258         List<Instruction> instructions = new ArrayList<>();
259         instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask, ++instructionSize));
260         if (serviceInstructions != null && !serviceInstructions.isEmpty()) {
261             for (Instruction info : serviceInstructions) {
262                 // Skip meta data write as that is handled already
263                 if (info.getInstruction() instanceof WriteMetadataCase) {
264                     continue;
265                 } else if (info.getInstruction() instanceof WriteActionsCase) {
266                     info = MDSALUtil.buildWriteActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
267                         ((WriteActionsCase)info.getInstruction()).getWriteActions().getAction()));
268                 } else if (info.getInstruction() instanceof ApplyActionsCase) {
269                     info = MDSALUtil.buildApplyActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
270                         ((ApplyActionsCase)info.getInstruction()).getApplyActions().getAction()));
271                 }
272                 instructions.add(info);
273             }
274         }
275
276         // build the flow and install it
277         String flowRef = getFlowRef(dpId, NwConstants.LPORT_DISPATCHER_TABLE, interfaceName, boundService, currentServiceIndex);
278         Flow ingressFlow = MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
279                 boundService.getServicePriority(), serviceRef, 0, 0, stypeOpenFlow.getFlowCookie(), matches, instructions);
280         installFlow(dpId, ingressFlow, t);
281     }
282
283     public static void installEgressDispatcherFlows(BigInteger dpId, BoundServices boundService, String interfaceName,
284                                                   WriteTransaction t, int interfaceTag, short currentServiceIndex,
285                                                   short nextServiceIndex, Interface iface) {
286         LOG.debug("Installing Egress Dispatcher Flows {}, {}", dpId, interfaceName);
287         installEgressDispatcherFlow(dpId, boundService, interfaceName, t, interfaceTag, currentServiceIndex, nextServiceIndex);
288
289         // Install Split Horizon drop flow only for the default egress service - this flow drops traffic targeted to external interfaces if they arrived
290         // from an external interface (marked with the SH bit)
291         if (boundService.getServicePriority() == ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME, NwConstants.DEFAULT_EGRESS_SERVICE_INDEX)) {
292             installEgressDispatcherSplitHorizonFlow(dpId, boundService, interfaceName, t, interfaceTag, currentServiceIndex, iface);
293         }
294     }
295
296     private static void installEgressDispatcherFlow(BigInteger dpId, BoundServices boundService, String interfaceName,
297         WriteTransaction t, int interfaceTag, short currentServiceIndex, short nextServiceIndex) {
298         String serviceRef = boundService.getServiceName();
299         List<? extends MatchInfoBase> matches = FlowBasedServicesUtils.getMatchInfoForEgressDispatcherTable(interfaceTag, currentServiceIndex);
300
301         // Get the metadata and mask from the service's write metadata instruction
302         StypeOpenflow stypeOpenFlow = boundService.getAugmentation(StypeOpenflow.class);
303         List<Instruction> serviceInstructions = stypeOpenFlow.getInstruction();
304         int instructionSize = serviceInstructions.size();
305
306         // build the final instruction for LPort Dispatcher table flow entry
307         List<Instruction> instructions = new ArrayList<>();
308         if(boundService.getServicePriority() != ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME, NwConstants.DEFAULT_EGRESS_SERVICE_INDEX)) {
309             BigInteger[] metadataValues = IfmUtil.mergeOpenflowMetadataWriteInstructions(serviceInstructions);
310             BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(interfaceTag, nextServiceIndex, metadataValues[0]);
311             BigInteger metadataMask = MetaDataUtil.getWriteMetaDataMaskForDispatcherTable();
312             instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask, ++instructionSize));
313             instructions.add(MDSALUtil.buildAndGetSetReg6ActionInstruction(0, ++instructionSize, 0, 31,
314                     MetaDataUtil.getReg6ValueForLPortDispatcher(interfaceTag, nextServiceIndex)));
315         }
316         if (serviceInstructions != null && !serviceInstructions.isEmpty()) {
317             for (Instruction info : serviceInstructions) {
318                 // Skip meta data write as that is handled already
319                 if (info.getInstruction() instanceof WriteMetadataCase) {
320                     continue;
321                 } else if (info.getInstruction() instanceof WriteActionsCase) {
322                     info = MDSALUtil.buildWriteActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
323                         ((WriteActionsCase)info.getInstruction()).getWriteActions().getAction()));
324                 } else if (info.getInstruction() instanceof ApplyActionsCase) {
325                     info = MDSALUtil.buildApplyActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
326                         ((ApplyActionsCase)info.getInstruction()).getApplyActions().getAction()));
327                 }
328                 instructions.add(info);
329             }
330         }
331
332         // build the flow and install it
333         String flowRef = getFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, interfaceName, boundService, currentServiceIndex);
334         Flow egressFlow = MDSALUtil.buildFlowNew(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, flowRef,
335                 boundService.getServicePriority(), serviceRef, 0, 0, stypeOpenFlow.getFlowCookie(), matches, instructions);
336         installFlow(dpId, egressFlow, t);
337     }
338
339     public static void installEgressDispatcherSplitHorizonFlow(BigInteger dpId, BoundServices boundService, String interfaceName,
340             WriteTransaction t, int interfaceTag, short currentServiceIndex, Interface iface) {
341         // only install split horizon drop flows for external interfaces
342         if (!isExternal(iface)) {
343             return;
344         }
345
346         if (LOG.isDebugEnabled()) {
347             LOG.debug("Installing split horizon drop flow for external interface {} on dpId {}", interfaceName, dpId);
348         }
349
350         BigInteger shFlagSet = BigInteger.ONE; // BigInteger.ONE is used for checking the Split-Horizon flag
351         StypeOpenflow stypeOpenFlow = boundService.getAugmentation(StypeOpenflow.class);
352         List<MatchInfoBase> shMatches = FlowBasedServicesUtils.getMatchInfoForEgressDispatcherTable(interfaceTag, currentServiceIndex);
353         shMatches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] { shFlagSet, MetaDataUtil.METADATA_MASK_SH_FLAG }));
354         List<InstructionInfo> shInstructions = new ArrayList<>();
355         List<ActionInfo> actionsInfos = new ArrayList<>();
356         actionsInfos.add(new ActionDrop());
357         shInstructions.add(new InstructionApplyActions(actionsInfos));
358
359         String flowRef = getSplitHorizonFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, interfaceName, currentServiceIndex, shFlagSet);
360         String serviceRef = boundService.getServiceName();
361         int splitHorizonFlowPriority = boundService.getServicePriority() + 1; // this must be higher priority than the egress flow
362         Flow egressSplitHorizonFlow = MDSALUtil.buildFlow(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, flowRef,
363                 splitHorizonFlowPriority, serviceRef, 0, 0, stypeOpenFlow.getFlowCookie(), shMatches, shInstructions);
364
365         installFlow(dpId, egressSplitHorizonFlow, t);
366     }
367
368     public static BoundServices getBoundServices(String serviceName, short servicePriority, int flowPriority,
369                                                  BigInteger cookie, List<Instruction> instructions) {
370         StypeOpenflowBuilder augBuilder = new StypeOpenflowBuilder().setFlowCookie(cookie).setFlowPriority(flowPriority).setInstruction(instructions);
371         return new BoundServicesBuilder().setKey(new BoundServicesKey(servicePriority))
372                 .setServiceName(serviceName).setServicePriority(servicePriority)
373                 .setServiceType(ServiceTypeFlowBased.class).addAugmentation(StypeOpenflow.class, augBuilder.build()).build();
374     }
375
376     public static InstanceIdentifier<BoundServices> buildServiceId(String interfaceName, short serviceIndex) {
377         return InstanceIdentifier.builder(ServiceBindings.class).child(ServicesInfo.class,
378                 new ServicesInfoKey(interfaceName, ServiceModeIngress.class))
379                 .child(BoundServices.class, new BoundServicesKey(serviceIndex)).build();
380     }
381
382     public static InstanceIdentifier<BoundServices> buildServiceId(String interfaceName, short serviceIndex, Class<? extends ServiceModeBase> serviceMode) {
383         return InstanceIdentifier.builder(ServiceBindings.class).child(ServicesInfo.class,
384                 new ServicesInfoKey(interfaceName, serviceMode))
385                 .child(BoundServices.class, new BoundServicesKey(serviceIndex)).build();
386     }
387
388     public static void unbindDefaultEgressDispatcherService(DataBroker dataBroker, String interfaceName) {
389         IfmUtil.unbindService(dataBroker, interfaceName, buildServiceId(interfaceName,
390                 ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME, NwConstants.DEFAULT_EGRESS_SERVICE_INDEX),
391                 ServiceModeEgress.class));
392     }
393
394     public static void bindDefaultEgressDispatcherService(DataBroker dataBroker, List<ListenableFuture<Void>> futures,
395                                                           Interface interfaceInfo, String portNo,
396                                                           String interfaceName, int ifIndex) {
397         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
398         int priority = ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME, NwConstants.DEFAULT_EGRESS_SERVICE_INDEX);
399         List<Instruction> instructions = IfmUtil.getEgressInstructionsForInterface(interfaceInfo, portNo, null, true, ifIndex);
400         BoundServices
401                 serviceInfo =
402                 getBoundServices(String.format("%s.%s", "default", interfaceName),
403                         ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME, NwConstants.DEFAULT_EGRESS_SERVICE_INDEX), priority,
404                         NwConstants.EGRESS_DISPATCHER_TABLE_COOKIE, instructions);
405         IfmUtil.bindService(tx, interfaceName, serviceInfo, ServiceModeEgress.class);
406         futures.add(tx.submit());
407     }
408
409     public static void removeIngressFlow(String name, BoundServices serviceOld, BigInteger dpId, WriteTransaction t) {
410         LOG.debug("Removing Ingress Flows");
411         String flowKeyStr = getFlowRef(dpId, NwConstants.VLAN_INTERFACE_INGRESS_TABLE, name, serviceOld, serviceOld.getServicePriority());
412         FlowKey flowKey = new FlowKey(new FlowId(flowKeyStr));
413         Node nodeDpn = buildInventoryDpnNode(dpId);
414         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
415                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
416                 .child(Table.class, new TableKey(NwConstants.VLAN_INTERFACE_INGRESS_TABLE)).child(Flow.class, flowKey).build();
417
418         t.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
419     }
420
421     public static void removeLPortDispatcherFlow(BigInteger dpId, String iface, BoundServices boundServicesOld, WriteTransaction t, short currentServiceIndex) {
422         LOG.debug("Removing LPort Dispatcher Flows {}, {}", dpId, iface);
423
424         boundServicesOld.getAugmentation(StypeOpenflow.class);
425         // build the flow and install it
426         String flowRef = getFlowRef(dpId, NwConstants.LPORT_DISPATCHER_TABLE, iface, boundServicesOld, currentServiceIndex);
427         FlowKey flowKey = new FlowKey(new FlowId(flowRef));
428         Node nodeDpn = buildInventoryDpnNode(dpId);
429         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
430                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
431                 .child(Table.class, new TableKey(NwConstants.LPORT_DISPATCHER_TABLE)).child(Flow.class, flowKey).build();
432
433         t.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
434     }
435
436     public static void removeEgressDispatcherFlows(BigInteger dpId, String iface, BoundServices boundServicesOld, WriteTransaction t, short currentServiceIndex) {
437         LOG.debug("Removing Egress Dispatcher Flows {}, {}", dpId, iface);
438         removeEgressDispatcherFlow(dpId, iface, t, currentServiceIndex, boundServicesOld);
439         removeEgressSplitHorizonDispatcherFlow(dpId, iface, t, currentServiceIndex);
440     }
441
442     private static void removeEgressDispatcherFlow(BigInteger dpId, String iface, WriteTransaction t,
443             short currentServiceIndex, BoundServices boundServicesOld) {
444         // build the flow and install it
445         String flowRef = getFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, iface, boundServicesOld, currentServiceIndex);
446         FlowKey flowKey = new FlowKey(new FlowId(flowRef));
447         Node nodeDpn = buildInventoryDpnNode(dpId);
448         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
449                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
450                 .child(Table.class, new TableKey(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE)).child(Flow.class, flowKey).build();
451
452         t.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
453     }
454
455     public static void removeEgressSplitHorizonDispatcherFlow(BigInteger dpId, String iface, WriteTransaction t, short currentServiceIndex) {
456         BigInteger shFlagSet = BigInteger.ONE; // BigInteger.ONE is used for checking the Split-Horizon flag
457         String shFlowRef = getSplitHorizonFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, iface, currentServiceIndex, shFlagSet);
458         FlowKey shFlowKey = new FlowKey(new FlowId(shFlowRef));
459         Node nodeDpn = buildInventoryDpnNode(dpId);
460         InstanceIdentifier<Flow> shFlowInstanceId = InstanceIdentifier.builder(Nodes.class)
461                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
462                 .child(Table.class, new TableKey(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE)).child(Flow.class, shFlowKey).build();
463
464         t.delete(LogicalDatastoreType.CONFIGURATION, shFlowInstanceId);
465     }
466
467     private static String getFlowRef(BigInteger dpnId, short tableId, String iface, BoundServices service, short currentServiceIndex) {
468         return String.valueOf(dpnId) + tableId + NwConstants.FLOWID_SEPARATOR +
469                 iface + NwConstants.FLOWID_SEPARATOR + currentServiceIndex;
470     }
471
472     private static String getSplitHorizonFlowRef(BigInteger dpnId, short tableId, String iface, short currentServiceIndex, BigInteger shFlag) {
473         return new StringBuffer().append(dpnId).append(tableId).append(NwConstants.FLOWID_SEPARATOR)
474                 .append(iface).append(NwConstants.FLOWID_SEPARATOR).append(shFlag.toString()).toString();
475     }
476     /**
477      * This util method returns an array of ServiceInfo in which index 0 will
478      * have the immediate lower priority service and index 1 will have the
479      * immediate higher priority service among the list of existing serviceInfos
480      *
481      * @param serviceInfos
482      * @param currentServiceInfo
483      * @return
484      */
485     public static BoundServices[] getHighAndLowPriorityService(
486             List<BoundServices> serviceInfos, BoundServices currentServiceInfo) {
487         BoundServices higher = null; // this will be used to hold the immediate higher service priority with respect to the currentServiceInfo
488         BoundServices lower = null; // this will be used to hold the immediate lower service priority with respect to the currentServiceInfo
489         if (serviceInfos == null || serviceInfos.isEmpty()) {
490             return new BoundServices[]{lower, higher};
491         }
492         List <BoundServices> availableServiceInfos = new ArrayList<>(serviceInfos);
493         Collections.sort(availableServiceInfos,
494                 (serviceInfo1, serviceInfo2) -> serviceInfo1.getServicePriority().compareTo(serviceInfo2.getServicePriority()));
495         for (BoundServices availableServiceInfo: availableServiceInfos) {
496             if (currentServiceInfo.getServicePriority() < availableServiceInfo.getServicePriority()) {
497                 lower = availableServiceInfo;
498                 break;
499             } else {
500                 higher = availableServiceInfo;
501             }
502         }
503         return new BoundServices[]{lower,higher};
504     }
505
506     public static BoundServices getHighestPriorityService(List<BoundServices> serviceInfos) {
507         List <BoundServices> availableServiceInfos = new ArrayList<>(serviceInfos);
508         if (availableServiceInfos.isEmpty()) {
509             return null;
510         }
511         BoundServices highPriorityService = availableServiceInfos.get(0);
512         availableServiceInfos.remove(0);
513         for (BoundServices availableServiceInfo: availableServiceInfos) {
514             if (availableServiceInfo.getServicePriority() < highPriorityService.getServicePriority()) {
515                 highPriorityService = availableServiceInfo;
516             }
517         }
518         return highPriorityService;
519     }
520
521     public static void installLportIngressFlow(BigInteger dpId, long portNo, Interface iface,
522                                                List<ListenableFuture<Void>> futures, DataBroker dataBroker,
523                                                int lportTag) {
524         int vlanId = 0;
525         boolean isVlanTransparent = false;
526         WriteTransaction  inventoryConfigShardTransaction = dataBroker.newWriteOnlyTransaction();
527         List<MatchInfo> matches = getMatchInfoForVlanPortAtIngressTable(dpId, portNo, iface);
528         IfL2vlan l2vlan = iface.getAugmentation(IfL2vlan.class);
529         if(l2vlan != null){
530             vlanId = l2vlan.getVlanId() == null ? 0 : l2vlan.getVlanId().getValue();
531             isVlanTransparent = l2vlan.getL2vlanMode() == IfL2vlan.L2vlanMode.Transparent;
532         }
533         int instructionKey = 0;
534         BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(lportTag, (short) 0, BigInteger.ZERO, isExternal(iface));
535         BigInteger metadataMask = MetaDataUtil.getMetaDataMaskForLPortDispatcher(MetaDataUtil.METADATA_MASK_LPORT_TAG_SH_FLAG);
536         List<Instruction> instructions = new ArrayList<>();
537
538         final SplitHorizon splitHorizon = iface.getAugmentation(SplitHorizon.class);
539         boolean overrideSplitHorizonProtection = splitHorizon != null && splitHorizon.isOverrideSplitHorizonProtection();
540         int actionKey = 0;
541         List<Action> actions = new ArrayList<>();
542         if (vlanId != 0 && !isVlanTransparent) {
543             actions.add(MDSALUtil.createPopVlanAction(actionKey++));
544         }
545         if (overrideSplitHorizonProtection) {
546             actions.add(MDSALUtil.createNxOfInPortAction(actionKey++,0));
547         }
548         if (actions.size() != 0) {
549             instructions.add(MDSALUtil.buildApplyActionsInstruction(actions,instructionKey++));
550         }
551
552         instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask, instructionKey++));
553         instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.LPORT_DISPATCHER_TABLE, instructionKey++));
554         int priority =  isVlanTransparent ? 1 : vlanId == 0 ? IfmConstants.FLOW_PRIORITY_FOR_UNTAGGED_VLAN : IfmConstants.FLOW_HIGH_PRIORITY;
555         String flowRef = getFlowRef(IfmConstants.VLAN_INTERFACE_INGRESS_TABLE, dpId, iface.getName());
556         Flow ingressFlow = MDSALUtil.buildFlowNew(IfmConstants.VLAN_INTERFACE_INGRESS_TABLE, flowRef, priority, flowRef, 0, 0,
557                 NwConstants.VLAN_TABLE_COOKIE, matches, instructions);
558         installFlow(dpId, ingressFlow, inventoryConfigShardTransaction);
559         futures.add(inventoryConfigShardTransaction.submit());
560     }
561
562     public static String getFlowRef(short tableId, BigInteger dpnId, String infName) {
563         return String.format("%d:%s:%s", tableId, dpnId, infName);
564     }
565
566     public static void removeIngressFlow(String interfaceName, BigInteger dpId, DataBroker dataBroker,
567                                          List<ListenableFuture<Void>> futures) {
568         if(dpId == null){
569             return;
570         }
571         LOG.debug("Removing Ingress Flows for {}", interfaceName);
572         WriteTransaction t = dataBroker.newWriteOnlyTransaction();
573         String flowKeyStr = getFlowRef(IfmConstants.VLAN_INTERFACE_INGRESS_TABLE, dpId, interfaceName);
574         FlowKey flowKey = new FlowKey(new FlowId(flowKeyStr));
575         Node nodeDpn = buildInventoryDpnNode(dpId);
576         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
577                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
578                 .child(Table.class, new TableKey(NwConstants.VLAN_INTERFACE_INGRESS_TABLE)).child(Flow.class, flowKey).build();
579
580         t.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
581         futures.add(t.submit());
582     }
583
584     private static boolean isExternal(Interface iface) {
585         if (iface == null) {
586             return false;
587         }
588         IfExternal ifExternal = iface.getAugmentation(IfExternal.class);
589         return ifExternal != null && Boolean.TRUE.equals(ifExternal.isExternal());
590     }
591 }