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