2 * Copyright © 2016, 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
8 package org.opendaylight.genius.interfacemanager.servicebindings.flowbased.utilities;
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
12 import com.google.common.collect.ImmutableBiMap;
13 import com.google.common.collect.ImmutableSet;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.Comparator;
19 import java.util.HashMap;
20 import java.util.List;
22 import java.util.concurrent.ExecutionException;
23 import org.apache.commons.lang3.StringUtils;
24 import org.eclipse.jdt.annotation.NonNull;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.opendaylight.genius.infra.Datastore.Configuration;
27 import org.opendaylight.genius.infra.Datastore.Operational;
28 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
29 import org.opendaylight.genius.infra.TypedReadTransaction;
30 import org.opendaylight.genius.infra.TypedWriteTransaction;
31 import org.opendaylight.genius.interfacemanager.IfmConstants;
32 import org.opendaylight.genius.interfacemanager.IfmUtil;
33 import org.opendaylight.genius.interfacemanager.commons.InterfaceManagerCommonUtils;
34 import org.opendaylight.genius.mdsalutil.ActionInfo;
35 import org.opendaylight.genius.mdsalutil.InstructionInfo;
36 import org.opendaylight.genius.mdsalutil.MDSALUtil;
37 import org.opendaylight.genius.mdsalutil.MatchInfo;
38 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
39 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
40 import org.opendaylight.genius.mdsalutil.NwConstants;
41 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
42 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
43 import org.opendaylight.genius.mdsalutil.matches.MatchInPort;
44 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
45 import org.opendaylight.genius.mdsalutil.matches.MatchVlanVid;
46 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchRegister;
47 import org.opendaylight.genius.utils.ServiceIndex;
48 import org.opendaylight.mdsal.binding.api.ReadTransaction;
49 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCase;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.WriteActionsCase;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.WriteMetadataCase;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfExternal;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.SplitHorizon;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.BoundServicesStateList;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeBase;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeEgress;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceTypeFlowBased;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflow;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflowBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.bound.services.state.list.BoundServicesState;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.bound.services.state.list.BoundServicesStateBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.bound.services.state.list.BoundServicesStateKey;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfoKey;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesBuilder;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesKey;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
89 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
90 import org.opendaylight.yangtools.yang.common.Uint64;
91 import org.slf4j.Logger;
92 import org.slf4j.LoggerFactory;
94 public final class FlowBasedServicesUtils {
95 private static final Logger LOG = LoggerFactory.getLogger(FlowBasedServicesUtils.class);
96 private static final Logger EVENT_LOGGER = LoggerFactory.getLogger("GeniusEventLogger");
97 private static final int DEFAULT_DISPATCHER_PRIORITY = 10;
99 private FlowBasedServicesUtils() {
102 public enum ServiceMode {
106 public static final ImmutableBiMap<ServiceMode, Class<? extends ServiceModeBase>>
107 SERVICE_MODE_MAP = new ImmutableBiMap.Builder<ServiceMode, Class<? extends ServiceModeBase>>()
108 .put(ServiceMode.EGRESS, ServiceModeEgress.class).put(ServiceMode.INGRESS, ServiceModeIngress.class)
111 // To keep the mapping between Tunnel Types and Tunnel Interfaces
112 public static final Collection<String> INTERFACE_TYPE_BASED_SERVICE_BINDING_KEYWORDS =
113 ImmutableSet.of(org.opendaylight.genius.interfacemanager.globals.IfmConstants.ALL_VXLAN_INTERNAL,
114 org.opendaylight.genius.interfacemanager.globals.IfmConstants.ALL_VXLAN_EXTERNAL,
115 org.opendaylight.genius.interfacemanager.globals.IfmConstants.ALL_MPLS_OVER_GRE);
117 public static ServicesInfo getServicesInfoForInterface(TypedReadTransaction<Configuration> tx, String interfaceName,
118 Class<? extends ServiceModeBase> serviceMode) throws ExecutionException, InterruptedException {
119 ServicesInfoKey servicesInfoKey = new ServicesInfoKey(interfaceName, serviceMode);
120 InstanceIdentifier.InstanceIdentifierBuilder<ServicesInfo> servicesInfoIdentifierBuilder = InstanceIdentifier
121 .builder(ServiceBindings.class).child(ServicesInfo.class, servicesInfoKey);
122 return tx.read(servicesInfoIdentifierBuilder.build()).get().orElse(null);
125 public static NodeConnectorId getNodeConnectorIdFromInterface(String interfaceName,
126 InterfaceManagerCommonUtils interfaceManagerCommonUtils) {
127 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
128 .ietf.interfaces.rev140508.interfaces.state.Interface ifState = interfaceManagerCommonUtils
129 .getInterfaceState(interfaceName);
130 if (ifState != null) {
131 List<String> ofportIds = ifState.getLowerLayerIf();
132 return new NodeConnectorId(ofportIds.get(0));
137 public static NodeConnectorId getNodeConnectorIdFromInterface(
138 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
139 .ietf.interfaces.rev140508.interfaces.state.Interface ifState) {
140 if (ifState != null) {
141 List<String> ofportIds = ifState.getLowerLayerIf();
142 return new NodeConnectorId(ofportIds.get(0));
148 public static Uint64 getDpnIdFromInterface(
149 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
150 .ietf.interfaces.rev140508.interfaces.state.Interface ifState) {
151 if (ifState != null) {
152 List<String> ofportIds = ifState.getLowerLayerIf();
153 NodeConnectorId nodeConnectorId = new NodeConnectorId(ofportIds.get(0));
154 return IfmUtil.getDpnFromNodeConnectorId(nodeConnectorId);
159 public static List<MatchInfo> getMatchInfoForVlanPortAtIngressTable(Uint64 dpId, long portNo, Interface iface) {
160 List<MatchInfo> matches = new ArrayList<>();
161 matches.add(new MatchInPort(dpId, portNo));
163 IfL2vlan l2vlan = iface.augmentation(IfL2vlan.class);
164 if (l2vlan != null) {
165 vlanId = l2vlan.getVlanId() == null ? 0 : l2vlan.getVlanId().getValue().toJava();
167 if (vlanId >= 0 && l2vlan.getL2vlanMode() != IfL2vlan.L2vlanMode.Transparent) {
168 matches.add(new MatchVlanVid(vlanId));
174 public static List<MatchInfo> getMatchInfoForTunnelPortAtIngressTable(Uint64 dpId, long portNo) {
175 List<MatchInfo> matches = new ArrayList<>();
176 matches.add(new MatchInPort(dpId, portNo));
180 public static List<MatchInfo> getMatchInfoForDispatcherTable(int interfaceTag, short servicePriority) {
181 List<MatchInfo> matches = new ArrayList<>();
182 matches.add(new MatchMetadata(MetaDataUtil.getMetaDataForLPortDispatcher(interfaceTag, servicePriority),
183 MetaDataUtil.getMetaDataMaskForLPortDispatcher()));
187 public static List<MatchInfoBase> getMatchInfoForEgressDispatcherTable(int interfaceTag, short serviceIndex) {
188 List<MatchInfoBase> matches = new ArrayList<>();
189 matches.add(new NxMatchRegister(NxmNxReg6.class,
190 MetaDataUtil.getReg6ValueForLPortDispatcher(interfaceTag, serviceIndex)));
194 public static void installInterfaceIngressFlow(Uint64 dpId, Interface iface, BoundServices boundServiceNew,
195 TypedWriteTransaction<Configuration> tx, List<MatchInfo> matches, int lportTag, short tableId) {
196 Map<InstructionKey, Instruction> instructions =
197 boundServiceNew.augmentation(StypeOpenflow.class).getInstruction();
199 int serviceInstructionsSize = instructions != null ? instructions.size() : 0;
200 Map<InstructionKey, Instruction> instructionSet = new HashMap<>();
202 IfL2vlan l2vlan = iface.augmentation(IfL2vlan.class);
203 if (l2vlan != null && l2vlan.getVlanId() != null) {
204 vlanId = l2vlan.getVlanId().getValue().toJava();
207 // incrementing instructionSize and using it as actionKey. Because
208 // it won't clash with any other instructions
209 int actionKey = ++serviceInstructionsSize;
210 Instruction getPopVlanActionInstruction = MDSALUtil.buildAndGetPopVlanActionInstruction(actionKey,
211 ++serviceInstructionsSize);
212 instructionSet.put(getPopVlanActionInstruction.key(), getPopVlanActionInstruction);
215 if (lportTag != 0L) {
216 Uint64[] metadataValues = IfmUtil.mergeOpenflowMetadataWriteInstructions(instructions);
217 short index = boundServiceNew.getServicePriority().toJava();
218 Uint64 metadata = MetaDataUtil.getMetaDataForLPortDispatcher(lportTag, ++index, metadataValues[0],
220 Uint64 metadataMask = MetaDataUtil.getMetaDataMaskForLPortDispatcher(
221 MetaDataUtil.METADATA_MASK_SERVICE_INDEX, MetaDataUtil.METADATA_MASK_LPORT_TAG_SH_FLAG,
223 Instruction writeMetadaInstruction = MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask,
224 ++serviceInstructionsSize);
225 instructionSet.put(writeMetadaInstruction.key(), writeMetadaInstruction);
228 if (instructions != null && !instructions.isEmpty()) {
229 for (Instruction info : instructions.values()) {
230 // Skip meta data write as that is handled already
231 if (info.getInstruction() instanceof WriteMetadataCase) {
233 } else if (info.getInstruction() instanceof WriteActionsCase) {
234 info = MDSALUtil.buildWriteActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
235 ((WriteActionsCase) info.getInstruction()).getWriteActions().getAction()));
236 } else if (info.getInstruction() instanceof ApplyActionsCase) {
237 info = MDSALUtil.buildApplyActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
238 ((ApplyActionsCase) info.getInstruction()).getApplyActions().getAction()));
240 instructions.put(info.key(),info);
244 String serviceRef = boundServiceNew.getServiceName();
245 String flowRef = getFlowRef(dpId, NwConstants.VLAN_INTERFACE_INGRESS_TABLE, iface.getName(),
246 boundServiceNew.getServicePriority().toJava());
247 StypeOpenflow stypeOpenflow = boundServiceNew.augmentation(StypeOpenflow.class);
248 Flow ingressFlow = MDSALUtil.buildFlowNew(tableId, flowRef, stypeOpenflow.getFlowPriority().toJava(),
249 serviceRef, 0, 0, stypeOpenflow.getFlowCookie(), matches, instructionSet);
250 installFlow(dpId, ingressFlow, tx);
253 public static void installFlow(Uint64 dpId, Flow flow, TypedWriteTransaction<Configuration> writeTransaction) {
254 FlowKey flowKey = new FlowKey(new FlowId(flow.getId()));
255 Node nodeDpn = buildInventoryDpnNode(dpId);
256 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
257 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
258 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
260 writeTransaction.mergeParentStructurePut(flowInstanceId, flow);
261 EVENT_LOGGER.debug("IFM,InstallFlow {}", flow.getId());
264 private static Node buildInventoryDpnNode(Uint64 dpnId) {
265 NodeId nodeId = new NodeId("openflow:" + dpnId);
266 return new NodeBuilder().setId(nodeId).withKey(new NodeKey(nodeId)).build();
269 public static void installLPortDispatcherFlow(Uint64 dpId, BoundServices boundService, String interfaceName,
270 TypedWriteTransaction<Configuration> tx, int interfaceTag, short currentServiceIndex,
271 short nextServiceIndex) {
272 String serviceRef = boundService.getServiceName();
273 List<MatchInfo> matches = FlowBasedServicesUtils.getMatchInfoForDispatcherTable(interfaceTag,
274 currentServiceIndex);
276 // Get the metadata and mask from the service's write metadata
278 StypeOpenflow stypeOpenFlow = boundService.augmentation(StypeOpenflow.class);
279 @Nullable Map<InstructionKey, Instruction> serviceInstructions = stypeOpenFlow.getInstruction();
280 int instructionSize = serviceInstructions != null ? serviceInstructions.size() : 0;
281 Uint64[] metadataValues = IfmUtil.mergeOpenflowMetadataWriteInstructions(serviceInstructions);
282 Uint64 metadata = MetaDataUtil.getMetaDataForLPortDispatcher(interfaceTag, nextServiceIndex, metadataValues[0]);
283 Uint64 metadataMask = MetaDataUtil.getWriteMetaDataMaskForDispatcherTable();
285 // build the final instruction for LPort Dispatcher table flow entry
286 Map<InstructionKey, Instruction> instructionsMap = new HashMap<>();
287 Instruction instruction = MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask,
289 instructionsMap.put(instruction.key(),instruction);
290 if (serviceInstructions != null && !serviceInstructions.isEmpty()) {
291 for (Instruction info : serviceInstructions.values()) {
292 // Skip meta data write as that is handled already
293 if (info.getInstruction() instanceof WriteMetadataCase) {
295 } else if (info.getInstruction() instanceof WriteActionsCase) {
296 info = MDSALUtil.buildWriteActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
297 ((WriteActionsCase) info.getInstruction()).getWriteActions().getAction()));
298 } else if (info.getInstruction() instanceof ApplyActionsCase) {
299 info = MDSALUtil.buildApplyActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
300 ((ApplyActionsCase) info.getInstruction()).getApplyActions().getAction()));
302 //instructions.values().add(info);
303 instructionsMap.put(info.key(),info);
307 // build the flow and install it
308 String flowRef = getFlowRef(dpId, NwConstants.LPORT_DISPATCHER_TABLE, interfaceName,
309 currentServiceIndex);
310 Flow ingressFlow = MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
311 DEFAULT_DISPATCHER_PRIORITY, serviceRef, 0, 0, stypeOpenFlow.getFlowCookie(),
312 matches, instructionsMap);
313 LOG.debug("Installing LPort Dispatcher Flow on DPN {}, for interface {}, with flowRef {}", dpId,
314 interfaceName, flowRef);
315 installFlow(dpId, ingressFlow, tx);
318 public static void installEgressDispatcherFlows(Uint64 dpId, BoundServices boundService, String interfaceName,
319 TypedWriteTransaction<Configuration> tx, int interfaceTag, short currentServiceIndex,
320 short nextServiceIndex, Interface iface) {
321 LOG.debug("Installing Egress Dispatcher Flows on dpn : {}, for interface : {}", dpId, interfaceName);
322 installEgressDispatcherFlow(dpId, boundService, interfaceName, tx, interfaceTag,
323 currentServiceIndex, nextServiceIndex);
325 // Install Split Horizon drop flow only for the default egress service -
326 // this flow drops traffic targeted to external interfaces if they
328 // from an external interface (marked with the SH bit)
329 if (boundService.getServicePriority().toJava() == ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
330 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX)) {
331 installEgressDispatcherSplitHorizonFlow(dpId, boundService, interfaceName, tx, interfaceTag,
332 currentServiceIndex, iface);
336 private static void installEgressDispatcherFlow(Uint64 dpId, BoundServices boundService, String interfaceName,
337 TypedWriteTransaction<Configuration> tx, int interfaceTag, short currentServiceIndex,
338 short nextServiceIndex) {
340 // Get the metadata and mask from the service's write metadata instruction
341 StypeOpenflow stypeOpenflow = boundService.augmentation(StypeOpenflow.class);
342 if (stypeOpenflow == null) {
343 LOG.warn("Could not install egress dispatcher flow, missing service openflow configuration");
346 Map<InstructionKey, Instruction> serviceInstructions = stypeOpenflow.getInstruction() != null
347 ? stypeOpenflow.getInstruction()
348 : Collections.emptyMap();
350 // build the final instruction for LPort Dispatcher table flow entry
351 List<Action> finalApplyActions = new ArrayList<>();
352 Map<InstructionKey, Instruction> instructionMap = new HashMap<>();
353 if (boundService.getServicePriority().toJava() != ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
354 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX)) {
355 Uint64[] metadataValues = IfmUtil.mergeOpenflowMetadataWriteInstructions(serviceInstructions);
356 Uint64 metadataMask = MetaDataUtil.getWriteMetaDataMaskForEgressDispatcherTable();
357 Instruction instruction = MDSALUtil.buildAndGetWriteMetadaInstruction(metadataValues[0], metadataMask,
358 instructionMap.size());
359 instructionMap.put(instruction.key(), instruction);
360 finalApplyActions.add(MDSALUtil.createSetReg6Action(finalApplyActions.size(), 0, 31,
361 MetaDataUtil.getReg6ValueForLPortDispatcher(interfaceTag, nextServiceIndex)));
364 final int applyActionsOffset = finalApplyActions.size();
365 for (Instruction info : serviceInstructions.values()) {
366 if (info.getInstruction() instanceof WriteActionsCase) {
367 List<Action> writeActions = ActionConverterUtil.convertServiceActionToFlowAction(
368 ((WriteActionsCase) info.getInstruction()).getWriteActions().getAction());
369 Instruction writeActionsInstruction = MDSALUtil.buildWriteActionsInstruction(writeActions,
370 instructionMap.size());
371 instructionMap.put(writeActionsInstruction.key(), writeActionsInstruction);
372 } else if (info.getInstruction() instanceof ApplyActionsCase) {
373 List<Action> applyActions = ActionConverterUtil.convertServiceActionToFlowAction(
374 ((ApplyActionsCase) info.getInstruction()).getApplyActions().getAction(),
376 finalApplyActions.addAll(applyActions);
377 } else if (!(info.getInstruction() instanceof WriteMetadataCase)) {
378 // Skip meta data write as that is handled already
379 Instruction buildInstruction = MDSALUtil.buildInstruction(info, instructionMap.size());
380 instructionMap.put(buildInstruction.key(), buildInstruction);
383 if (!finalApplyActions.isEmpty()) {
384 Instruction buildApplyActionsInstruction = MDSALUtil.buildApplyActionsInstruction(finalApplyActions,
385 instructionMap.size());
386 instructionMap.put(buildApplyActionsInstruction.key(), buildApplyActionsInstruction);
389 // build the flow and install it
390 String serviceRef = boundService.getServiceName();
391 List<? extends MatchInfoBase> matches = FlowBasedServicesUtils
392 .getMatchInfoForEgressDispatcherTable(interfaceTag, currentServiceIndex);
393 String flowRef = getFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, interfaceName,
394 currentServiceIndex);
395 Flow egressFlow = MDSALUtil.buildFlowNew(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, flowRef,
396 boundService.getServicePriority().toJava(), serviceRef, 0, 0, stypeOpenflow.getFlowCookie(),
397 matches, instructionMap);
398 LOG.debug("Installing Egress Dispatcher Flow for interface : {}, with flow-ref : {}", interfaceName, flowRef);
399 installFlow(dpId, egressFlow, tx);
402 public static void installEgressDispatcherSplitHorizonFlow(Uint64 dpId, BoundServices boundService,
403 String interfaceName, TypedWriteTransaction<Configuration> tx, int interfaceTag, short currentServiceIndex,
405 // only install split horizon drop flows for external interfaces
406 if (!isExternal(iface)) {
410 if (LOG.isDebugEnabled()) {
411 LOG.debug("Installing split horizon drop flow for external interface {} on dpId {}", interfaceName, dpId);
414 // Uint64.ONE is used for checking the Split-Horizon flag
415 Uint64 shFlagSet = Uint64.ONE;
416 List<MatchInfoBase> shMatches = FlowBasedServicesUtils.getMatchInfoForEgressDispatcherTable(interfaceTag,
417 currentServiceIndex);
418 shMatches.add(new MatchMetadata(shFlagSet, MetaDataUtil.METADATA_MASK_SH_FLAG));
419 List<InstructionInfo> shInstructions = new ArrayList<>();
420 List<ActionInfo> actionsInfos = new ArrayList<>();
421 actionsInfos.add(new ActionDrop());
422 shInstructions.add(new InstructionApplyActions(actionsInfos));
424 String flowRef = getSplitHorizonFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, interfaceName,
426 String serviceRef = boundService.getServiceName();
427 // This must be higher priority than the egress flow
428 int splitHorizonFlowPriority = boundService.getServicePriority().toJava() + 1;
429 StypeOpenflow stypeOpenFlow = boundService.augmentation(StypeOpenflow.class);
430 Flow egressSplitHorizonFlow = MDSALUtil.buildFlow(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, flowRef,
431 splitHorizonFlowPriority, serviceRef, 0, 0, stypeOpenFlow.getFlowCookie(),
432 shMatches, shInstructions);
434 installFlow(dpId, egressSplitHorizonFlow, tx);
437 public static BoundServices getBoundServices(String serviceName, short servicePriority, int flowPriority,
438 Uint64 cookie, List<Instruction> instructions) {
439 StypeOpenflowBuilder augBuilder = new StypeOpenflowBuilder().setFlowCookie(cookie).setFlowPriority(flowPriority)
440 .setInstruction(instructions);
441 return new BoundServicesBuilder().withKey(new BoundServicesKey(servicePriority)).setServiceName(serviceName)
442 .setServicePriority(servicePriority).setServiceType(ServiceTypeFlowBased.class)
443 .addAugmentation(StypeOpenflow.class, augBuilder.build()).build();
446 public static InstanceIdentifier<BoundServices> buildServiceId(String interfaceName, short serviceIndex) {
447 return InstanceIdentifier.builder(ServiceBindings.class)
448 .child(ServicesInfo.class, new ServicesInfoKey(interfaceName, ServiceModeIngress.class))
449 .child(BoundServices.class, new BoundServicesKey(serviceIndex)).build();
452 public static InstanceIdentifier<BoundServices> buildServiceId(String interfaceName, short serviceIndex,
453 Class<? extends ServiceModeBase> serviceMode) {
454 return InstanceIdentifier.builder(ServiceBindings.class)
455 .child(ServicesInfo.class, new ServicesInfoKey(interfaceName, serviceMode))
456 .child(BoundServices.class, new BoundServicesKey(serviceIndex)).build();
459 public static InstanceIdentifier<BoundServices> buildDefaultServiceId(String interfaceName) {
460 return buildServiceId(interfaceName, ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
461 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX), ServiceModeEgress.class);
464 public static ListenableFuture<Void> bindDefaultEgressDispatcherService(ManagedNewTransactionRunner txRunner,
465 Interface interfaceInfo, String portNo, String interfaceName, int ifIndex) {
466 List<Instruction> instructions =
467 IfmUtil.getEgressInstructionsForInterface(interfaceInfo, portNo, null, true, ifIndex, 0);
468 return bindDefaultEgressDispatcherService(txRunner, interfaceName, instructions);
471 public static void bindDefaultEgressDispatcherService(TypedWriteTransaction<Configuration> tx,
472 Interface interfaceInfo, String portNo, String interfaceName, int ifIndex) {
473 List<Instruction> instructions =
474 IfmUtil.getEgressInstructionsForInterface(interfaceInfo, portNo, null, true, ifIndex, 0);
475 bindDefaultEgressDispatcherService(tx, interfaceName, instructions);
478 public static ListenableFuture<Void> bindDefaultEgressDispatcherService(ManagedNewTransactionRunner txRunner,
479 Interface interfaceInfo, String interfaceName, int ifIndex, long groupId) {
480 List<Instruction> instructions =
481 IfmUtil.getEgressInstructionsForInterface(interfaceInfo, StringUtils.EMPTY, null, true, ifIndex, groupId);
482 return bindDefaultEgressDispatcherService(txRunner, interfaceName, instructions);
485 public static ListenableFuture<Void> bindDefaultEgressDispatcherService(ManagedNewTransactionRunner txRunner,
486 String interfaceName, List<Instruction> instructions) {
487 return txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
488 int priority = ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
489 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX);
492 getBoundServices(defaultInterfaceName(interfaceName),
493 ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
494 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX),
495 priority, NwConstants.EGRESS_DISPATCHER_TABLE_COOKIE, instructions);
496 IfmUtil.bindService(tx, interfaceName, serviceInfo, ServiceModeEgress.class);
500 public static void bindDefaultEgressDispatcherService(TypedWriteTransaction<Configuration> tx,
501 String interfaceName, List<Instruction> instructions) {
502 int priority = ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
503 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX);
506 getBoundServices(defaultInterfaceName(interfaceName),
507 ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
508 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX),
509 priority, NwConstants.EGRESS_DISPATCHER_TABLE_COOKIE, instructions);
510 IfmUtil.bindService(tx, interfaceName, serviceInfo, ServiceModeEgress.class);
513 private static String defaultInterfaceName(final String interfaceName) {
514 return "default." + interfaceName;
517 public static void removeIngressFlow(String interfaceName, Uint64 dpId, ManagedNewTransactionRunner txRunner,
518 List<ListenableFuture<Void>> futures) {
522 LOG.debug("Removing Ingress Flows for {}", interfaceName);
523 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
524 String flowKeyStr = getFlowRef(IfmConstants.VLAN_INTERFACE_INGRESS_TABLE, dpId, interfaceName);
525 FlowKey flowKey = new FlowKey(new FlowId(flowKeyStr));
526 Node nodeDpn = buildInventoryDpnNode(dpId);
527 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
528 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
529 .child(Table.class, new TableKey(NwConstants.VLAN_INTERFACE_INGRESS_TABLE)).child(Flow.class,
533 tx.delete(flowInstanceId);
537 public static void removeIngressFlow(String name, BoundServices serviceOld, Uint64 dpId,
538 TypedWriteTransaction<Configuration> writeTransaction) {
539 String flowKeyStr = getFlowRef(dpId, NwConstants.VLAN_INTERFACE_INGRESS_TABLE, name,
540 serviceOld.getServicePriority().toJava());
541 LOG.debug("Removing Ingress Flow {}", flowKeyStr);
542 FlowKey flowKey = new FlowKey(new FlowId(flowKeyStr));
543 Node nodeDpn = buildInventoryDpnNode(dpId);
544 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
545 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
546 .child(Table.class, new TableKey(NwConstants.VLAN_INTERFACE_INGRESS_TABLE)).child(Flow.class, flowKey)
549 writeTransaction.delete(flowInstanceId);
552 public static void removeLPortDispatcherFlow(Uint64 dpId, String iface, BoundServices boundServicesOld,
553 TypedWriteTransaction<Configuration> writeTransaction, short currentServiceIndex) {
554 LOG.debug("Removing LPort Dispatcher Flows {}, {}", dpId, iface);
556 boundServicesOld.augmentation(StypeOpenflow.class);
557 // build the flow and install it
558 String flowRef = getFlowRef(dpId, NwConstants.LPORT_DISPATCHER_TABLE, iface,
559 currentServiceIndex);
560 FlowKey flowKey = new FlowKey(new FlowId(flowRef));
561 Node nodeDpn = buildInventoryDpnNode(dpId);
562 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
563 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
564 .child(Table.class, new TableKey(NwConstants.LPORT_DISPATCHER_TABLE)).child(Flow.class, flowKey)
567 writeTransaction.delete(flowInstanceId);
568 EVENT_LOGGER.debug("IFM,removeFlow {}", flowRef);
571 public static void removeEgressDispatcherFlows(Uint64 dpId, String iface,
572 TypedWriteTransaction<Configuration> writeTransaction, short currentServiceIndex) {
573 LOG.debug("Removing Egress Dispatcher Flows {}, {}", dpId, iface);
574 removeEgressDispatcherFlow(dpId, iface, writeTransaction, currentServiceIndex);
575 removeEgressSplitHorizonDispatcherFlow(dpId, iface, writeTransaction);
578 private static void removeEgressDispatcherFlow(Uint64 dpId, String iface,
579 TypedWriteTransaction<Configuration> writeTransaction, short currentServiceIndex) {
580 // build the flow and install it
581 String flowRef = getFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, iface,
582 currentServiceIndex);
583 FlowKey flowKey = new FlowKey(new FlowId(flowRef));
584 Node nodeDpn = buildInventoryDpnNode(dpId);
585 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
586 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
587 .child(Table.class, new TableKey(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE)).child(Flow.class, flowKey)
590 writeTransaction.delete(flowInstanceId);
591 EVENT_LOGGER.debug("IFM,removeFlow {}", flowRef);
594 public static void removeEgressSplitHorizonDispatcherFlow(Uint64 dpId, String iface,
595 TypedWriteTransaction<Configuration> writeTransaction) {
596 // Uint64.ONE is used for checking the Split-Horizon flag
597 Uint64 shFlagSet = Uint64.ONE;
598 String shFlowRef = getSplitHorizonFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, iface,
600 FlowKey shFlowKey = new FlowKey(new FlowId(shFlowRef));
601 Node nodeDpn = buildInventoryDpnNode(dpId);
602 InstanceIdentifier<Flow> shFlowInstanceId = InstanceIdentifier.builder(Nodes.class)
603 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
604 .child(Table.class, new TableKey(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE))
605 .child(Flow.class, shFlowKey).build();
607 writeTransaction.delete(shFlowInstanceId);
610 public static String getFlowRef(short tableId, Uint64 dpnId, String infName) {
611 return tableId + ":" + dpnId + ":" + infName;
614 private static String getFlowRef(Uint64 dpnId, short tableId, String iface, short currentServiceIndex) {
615 return String.valueOf(dpnId) + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR + iface
616 + NwConstants.FLOWID_SEPARATOR + currentServiceIndex;
619 private static String getSplitHorizonFlowRef(Uint64 dpnId, short tableId, String iface, Uint64 shFlag) {
620 return String.valueOf(dpnId) + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR + iface
621 + NwConstants.FLOWID_SEPARATOR + shFlag.toString();
625 * This utility method returns an array of ServiceInfo in which index 0 will
626 * have the immediate lower priority service and index 1 will have the
627 * immediate higher priority service among the list of existing
630 * @param serviceInfos
631 * list of services bound
632 * @param currentServiceInfo
633 * current service bound
634 * @return array bound services
636 public static BoundServices[] getHighAndLowPriorityService(List<BoundServices> serviceInfos,
637 BoundServices currentServiceInfo) {
638 if (serviceInfos == null || serviceInfos.isEmpty()) {
639 return new BoundServices[] { null, null };
642 // This will be used to hold the immediate higher service priority with respect to the currentServiceInfo
643 BoundServices higher = null;
644 // This will be used to hold the immediate lower service priority with respect to the currentServiceInfo
645 BoundServices lower = null;
647 List<BoundServices> availableServiceInfos = new ArrayList<>(serviceInfos);
648 availableServiceInfos.sort(Comparator.comparing(BoundServices::getServicePriority));
649 for (BoundServices availableServiceInfo : availableServiceInfos) {
650 if (currentServiceInfo.getServicePriority().toJava() < availableServiceInfo.getServicePriority().toJava()) {
651 lower = availableServiceInfo;
654 higher = availableServiceInfo;
657 return new BoundServices[] { lower, higher };
660 public static BoundServices getHighestPriorityService(List<BoundServices> serviceInfos) {
661 List<BoundServices> availableServiceInfos = new ArrayList<>(serviceInfos);
662 if (availableServiceInfos.isEmpty()) {
665 BoundServices highPriorityService = availableServiceInfos.get(0);
666 availableServiceInfos.remove(0);
667 for (BoundServices availableServiceInfo : availableServiceInfos) {
668 if (availableServiceInfo.getServicePriority().toJava()
669 < highPriorityService.getServicePriority().toJava()) {
670 highPriorityService = availableServiceInfo;
673 return highPriorityService;
676 public static void installLportIngressFlow(Uint64 dpId, long portNo, Interface iface,
677 List<ListenableFuture<Void>> futures, ManagedNewTransactionRunner txRunner, int lportTag) {
679 boolean isVlanTransparent = false;
681 IfL2vlan l2vlan = iface.augmentation(IfL2vlan.class);
682 if (l2vlan != null) {
683 vlanId = l2vlan.getVlanId() == null ? 0 : l2vlan.getVlanId().getValue().toJava();
684 isVlanTransparent = l2vlan.getL2vlanMode() == IfL2vlan.L2vlanMode.Transparent;
686 int instructionKey = 0;
688 Map<InstructionKey, Instruction> instructions = new HashMap<>();
690 final SplitHorizon splitHorizon = iface.augmentation(SplitHorizon.class);
691 boolean overrideSplitHorizonProtection = splitHorizon != null
692 && splitHorizon.isOverrideSplitHorizonProtection();
694 List<Action> actions = new ArrayList<>();
695 if (vlanId != 0 && !isVlanTransparent) {
696 actions.add(MDSALUtil.createPopVlanAction(++actionKey));
698 if (overrideSplitHorizonProtection) {
699 actions.add(MDSALUtil.createNxOfInPortAction(++actionKey, 0));
701 if (!actions.isEmpty()) {
702 Instruction buildApplyActionsInstruction = MDSALUtil.buildApplyActionsInstruction(actions,
704 instructions.put(buildApplyActionsInstruction.key(), buildApplyActionsInstruction);
706 Uint64 metadata = MetaDataUtil.getMetaDataForLPortDispatcher(lportTag, (short) 0, Uint64.ZERO,
708 Uint64 metadataMask = MetaDataUtil
709 .getMetaDataMaskForLPortDispatcher(MetaDataUtil.METADATA_MASK_LPORT_TAG_SH_FLAG);
710 Instruction metadaInstruction = MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask,
712 instructions.put(metadaInstruction.key(),metadaInstruction);
713 Instruction gotoTableInstruction = MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.LPORT_DISPATCHER_TABLE,
715 instructions.put(gotoTableInstruction.key(), gotoTableInstruction);
716 int priority = isVlanTransparent ? 1
717 : vlanId == 0 ? IfmConstants.FLOW_PRIORITY_FOR_UNTAGGED_VLAN : IfmConstants.FLOW_HIGH_PRIORITY;
718 String flowRef = getFlowRef(IfmConstants.VLAN_INTERFACE_INGRESS_TABLE, dpId, iface.getName());
719 List<MatchInfo> matches = getMatchInfoForVlanPortAtIngressTable(dpId, portNo, iface);
720 Flow ingressFlow = MDSALUtil.buildFlowNew(IfmConstants.VLAN_INTERFACE_INGRESS_TABLE, flowRef, priority, flowRef,
721 0, 0, NwConstants.VLAN_TABLE_COOKIE, matches, instructions);
722 LOG.debug("Installing ingress flow {} for {}", flowRef, iface.getName());
724 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> installFlow(dpId, ingressFlow, tx)));
727 public static BoundServicesState buildBoundServicesState(
728 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface
729 interfaceState, Class<? extends ServiceModeBase> serviceMode) {
730 NodeConnectorId nodeConnectorId = IfmUtil.getNodeConnectorIdFromInterface(interfaceState);
731 Uint64 dpId = IfmUtil.getDpnFromNodeConnectorId(nodeConnectorId);
732 long portNo = IfmUtil.getPortNumberFromNodeConnectorId(nodeConnectorId);
733 BoundServicesStateKey boundServicesStateKey = new BoundServicesStateKey(interfaceState.getName(), serviceMode);
734 return new BoundServicesStateBuilder().setDpid(dpId).setIfIndex(interfaceState.getIfIndex())
735 .setInterfaceName(interfaceState.getName()).setInterfaceType(interfaceState.getType()).setPortNo(portNo)
736 .setServiceMode(serviceMode).withKey(boundServicesStateKey).build();
739 public static BoundServicesState getBoundServicesState(ReadTransaction tx,
740 String interfaceName,
741 Class<? extends ServiceModeBase> serviceMode)
742 throws ExecutionException, InterruptedException {
743 InstanceIdentifier<BoundServicesState> id = InstanceIdentifier.builder(BoundServicesStateList.class)
744 .child(BoundServicesState.class, new BoundServicesStateKey(interfaceName, serviceMode)).build();
745 return tx.read(LogicalDatastoreType.OPERATIONAL, id).get().orElse(null);
748 public static BoundServicesState getBoundServicesState(TypedReadTransaction<Operational> tx, String interfaceName,
749 Class<? extends ServiceModeBase> serviceMode) throws ExecutionException, InterruptedException {
750 InstanceIdentifier<BoundServicesState> id = InstanceIdentifier.builder(BoundServicesStateList.class)
751 .child(BoundServicesState.class, new BoundServicesStateKey(interfaceName, serviceMode)).build();
752 return tx.read(id).get().orElse(null);
755 public static void addBoundServicesState(TypedWriteTransaction<Operational> tx, String interfaceName,
756 BoundServicesState interfaceBoundServicesState) {
757 LOG.info("adding bound-service state information for interface : {}, service-mode : {}",
758 interfaceBoundServicesState.getInterfaceName(),
759 interfaceBoundServicesState.getServiceMode().getSimpleName());
760 InstanceIdentifier<BoundServicesState> id = InstanceIdentifier.builder(BoundServicesStateList.class)
761 .child(BoundServicesState.class, new BoundServicesStateKey(interfaceName,
762 interfaceBoundServicesState.getServiceMode())).build();
763 tx.mergeParentStructurePut(id, interfaceBoundServicesState);
766 public static void removeBoundServicesState(TypedWriteTransaction<Operational> tx,
767 String interfaceName, Class<? extends ServiceModeBase> serviceMode) {
768 LOG.info("remove bound-service state information for interface : {}, service-mode : {}", interfaceName,
769 serviceMode.getSimpleName());
770 InstanceIdentifier<BoundServicesState> id = InstanceIdentifier.builder(BoundServicesStateList.class)
771 .child(BoundServicesState.class, new BoundServicesStateKey(interfaceName, serviceMode)).build();
775 public static boolean isInterfaceTypeBasedServiceBinding(String interfaceName) {
776 return INTERFACE_TYPE_BASED_SERVICE_BINDING_KEYWORDS.contains(interfaceName);
779 private static boolean isExternal(Interface iface) {
783 IfExternal ifExternal = iface.augmentation(IfExternal.class);
784 return ifExternal != null && Boolean.TRUE.equals(ifExternal.isExternal());