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 instructionSet.put(MDSALUtil.buildAndGetPopVlanActionInstruction(actionKey,
211 ++serviceInstructionsSize).key(), MDSALUtil.buildAndGetPopVlanActionInstruction(actionKey,
212 ++serviceInstructionsSize));
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,
224 MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask,
225 ++serviceInstructionsSize).key(),
226 MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask, ++serviceInstructionsSize));
229 if (instructions != null && !instructions.isEmpty()) {
230 for (Instruction info : instructions.values()) {
231 // Skip meta data write as that is handled already
232 if (info.getInstruction() instanceof WriteMetadataCase) {
234 } else if (info.getInstruction() instanceof WriteActionsCase) {
235 info = MDSALUtil.buildWriteActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
236 ((WriteActionsCase) info.getInstruction()).getWriteActions().getAction()));
237 } else if (info.getInstruction() instanceof ApplyActionsCase) {
238 info = MDSALUtil.buildApplyActionsInstruction(ActionConverterUtil.convertServiceActionToFlowAction(
239 ((ApplyActionsCase) info.getInstruction()).getApplyActions().getAction()));
241 instructions.put(info.key(),info);
245 String serviceRef = boundServiceNew.getServiceName();
246 String flowRef = getFlowRef(dpId, NwConstants.VLAN_INTERFACE_INGRESS_TABLE, iface.getName(),
247 boundServiceNew.getServicePriority().toJava());
248 StypeOpenflow stypeOpenflow = boundServiceNew.augmentation(StypeOpenflow.class);
249 Flow ingressFlow = MDSALUtil.buildFlowNew(tableId, flowRef, stypeOpenflow.getFlowPriority().toJava(),
250 serviceRef, 0, 0, stypeOpenflow.getFlowCookie(), matches, instructionSet);
251 installFlow(dpId, ingressFlow, tx);
254 public static void installFlow(Uint64 dpId, Flow flow, TypedWriteTransaction<Configuration> writeTransaction) {
255 FlowKey flowKey = new FlowKey(new FlowId(flow.getId()));
256 Node nodeDpn = buildInventoryDpnNode(dpId);
257 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
258 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
259 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
261 writeTransaction.mergeParentStructurePut(flowInstanceId, flow);
262 EVENT_LOGGER.debug("IFM,InstallFlow {}", flow.getId());
265 private static Node buildInventoryDpnNode(Uint64 dpnId) {
266 NodeId nodeId = new NodeId("openflow:" + dpnId);
267 return new NodeBuilder().setId(nodeId).withKey(new NodeKey(nodeId)).build();
270 public static void installLPortDispatcherFlow(Uint64 dpId, BoundServices boundService, String interfaceName,
271 TypedWriteTransaction<Configuration> tx, int interfaceTag, short currentServiceIndex,
272 short nextServiceIndex) {
273 String serviceRef = boundService.getServiceName();
274 List<MatchInfo> matches = FlowBasedServicesUtils.getMatchInfoForDispatcherTable(interfaceTag,
275 currentServiceIndex);
277 // Get the metadata and mask from the service's write metadata
279 StypeOpenflow stypeOpenFlow = boundService.augmentation(StypeOpenflow.class);
280 @Nullable Map<InstructionKey, Instruction> serviceInstructions = stypeOpenFlow.getInstruction();
281 int instructionSize = serviceInstructions != null ? serviceInstructions.size() : 0;
282 Uint64[] metadataValues = IfmUtil.mergeOpenflowMetadataWriteInstructions(serviceInstructions);
283 Uint64 metadata = MetaDataUtil.getMetaDataForLPortDispatcher(interfaceTag, nextServiceIndex, metadataValues[0]);
284 Uint64 metadataMask = MetaDataUtil.getWriteMetaDataMaskForDispatcherTable();
286 // build the final instruction for LPort Dispatcher table flow entry
287 Map<InstructionKey, Instruction> instructions = new HashMap<>();
288 instructions.put(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask, ++instructionSize).key(),
289 MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask, ++instructionSize));
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 instructions.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(), matches, instructions);
312 LOG.debug("Installing LPort Dispatcher Flow on DPN {}, for interface {}, with flowRef {}", dpId,
313 interfaceName, flowRef);
314 installFlow(dpId, ingressFlow, tx);
317 public static void installEgressDispatcherFlows(Uint64 dpId, BoundServices boundService, String interfaceName,
318 TypedWriteTransaction<Configuration> tx, int interfaceTag, short currentServiceIndex,
319 short nextServiceIndex, Interface iface) {
320 LOG.debug("Installing Egress Dispatcher Flows on dpn : {}, for interface : {}", dpId, interfaceName);
321 installEgressDispatcherFlow(dpId, boundService, interfaceName, tx, interfaceTag,
322 currentServiceIndex, nextServiceIndex);
324 // Install Split Horizon drop flow only for the default egress service -
325 // this flow drops traffic targeted to external interfaces if they
327 // from an external interface (marked with the SH bit)
328 if (boundService.getServicePriority().toJava() == ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
329 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX)) {
330 installEgressDispatcherSplitHorizonFlow(dpId, boundService, interfaceName, tx, interfaceTag,
331 currentServiceIndex, iface);
335 private static void installEgressDispatcherFlow(Uint64 dpId, BoundServices boundService, String interfaceName,
336 TypedWriteTransaction<Configuration> tx, int interfaceTag, short currentServiceIndex,
337 short nextServiceIndex) {
339 // Get the metadata and mask from the service's write metadata instruction
340 StypeOpenflow stypeOpenflow = boundService.augmentation(StypeOpenflow.class);
341 if (stypeOpenflow == null) {
342 LOG.warn("Could not install egress dispatcher flow, missing service openflow configuration");
345 Map<InstructionKey, Instruction> serviceInstructions = stypeOpenflow.getInstruction() != null
346 ? stypeOpenflow.getInstruction()
347 : Collections.emptyMap();
349 // build the final instruction for LPort Dispatcher table flow entry
350 List<Action> finalApplyActions = new ArrayList<>();
351 Map<InstructionKey, Instruction> instructions = new HashMap<>();
352 if (boundService.getServicePriority().toJava() != ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
353 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX)) {
354 Uint64[] metadataValues = IfmUtil.mergeOpenflowMetadataWriteInstructions(serviceInstructions);
355 Uint64 metadataMask = MetaDataUtil.getWriteMetaDataMaskForEgressDispatcherTable();
356 instructions.put(MDSALUtil.buildAndGetWriteMetadaInstruction(metadataValues[0], metadataMask,
357 instructions.size()).key(),MDSALUtil.buildAndGetWriteMetadaInstruction(metadataValues[0],
358 metadataMask, instructions.size()));
359 finalApplyActions.add(MDSALUtil.createSetReg6Action(finalApplyActions.size(), 0, 31,
360 MetaDataUtil.getReg6ValueForLPortDispatcher(interfaceTag, nextServiceIndex)));
363 final int applyActionsOffset = finalApplyActions.size();
364 for (Instruction info : serviceInstructions.values()) {
365 if (info.getInstruction() instanceof WriteActionsCase) {
366 List<Action> writeActions = ActionConverterUtil.convertServiceActionToFlowAction(
367 ((WriteActionsCase) info.getInstruction()).getWriteActions().getAction());
368 instructions.put(MDSALUtil.buildWriteActionsInstruction(writeActions, instructions.size()).key(),
369 MDSALUtil.buildWriteActionsInstruction(writeActions, instructions.size()));
370 } else if (info.getInstruction() instanceof ApplyActionsCase) {
371 List<Action> applyActions = ActionConverterUtil.convertServiceActionToFlowAction(
372 ((ApplyActionsCase) info.getInstruction()).getApplyActions().getAction(),
374 finalApplyActions.addAll(applyActions);
375 } else if (!(info.getInstruction() instanceof WriteMetadataCase)) {
376 // Skip meta data write as that is handled already
377 instructions.put(MDSALUtil.buildInstruction(info, instructions.size()).key(),
378 MDSALUtil.buildInstruction(info, instructions.size()));
381 if (!finalApplyActions.isEmpty()) {
382 instructions.put(MDSALUtil.buildApplyActionsInstruction(finalApplyActions, instructions.size()).key(),
383 MDSALUtil.buildApplyActionsInstruction(finalApplyActions, instructions.size()));
386 // build the flow and install it
387 String serviceRef = boundService.getServiceName();
388 List<? extends MatchInfoBase> matches = FlowBasedServicesUtils
389 .getMatchInfoForEgressDispatcherTable(interfaceTag, currentServiceIndex);
390 String flowRef = getFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, interfaceName,
391 currentServiceIndex);
392 Flow egressFlow = MDSALUtil.buildFlowNew(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, flowRef,
393 boundService.getServicePriority().toJava(), serviceRef, 0, 0, stypeOpenflow.getFlowCookie(),
394 matches, instructions);
395 LOG.debug("Installing Egress Dispatcher Flow for interface : {}, with flow-ref : {}", interfaceName, flowRef);
396 installFlow(dpId, egressFlow, tx);
399 public static void installEgressDispatcherSplitHorizonFlow(Uint64 dpId, BoundServices boundService,
400 String interfaceName, TypedWriteTransaction<Configuration> tx, int interfaceTag, short currentServiceIndex,
402 // only install split horizon drop flows for external interfaces
403 if (!isExternal(iface)) {
407 if (LOG.isDebugEnabled()) {
408 LOG.debug("Installing split horizon drop flow for external interface {} on dpId {}", interfaceName, dpId);
411 // Uint64.ONE is used for checking the Split-Horizon flag
412 Uint64 shFlagSet = Uint64.ONE;
413 List<MatchInfoBase> shMatches = FlowBasedServicesUtils.getMatchInfoForEgressDispatcherTable(interfaceTag,
414 currentServiceIndex);
415 shMatches.add(new MatchMetadata(shFlagSet, MetaDataUtil.METADATA_MASK_SH_FLAG));
416 List<InstructionInfo> shInstructions = new ArrayList<>();
417 List<ActionInfo> actionsInfos = new ArrayList<>();
418 actionsInfos.add(new ActionDrop());
419 shInstructions.add(new InstructionApplyActions(actionsInfos));
421 String flowRef = getSplitHorizonFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, interfaceName,
423 String serviceRef = boundService.getServiceName();
424 // This must be higher priority than the egress flow
425 int splitHorizonFlowPriority = boundService.getServicePriority().toJava() + 1;
426 StypeOpenflow stypeOpenFlow = boundService.augmentation(StypeOpenflow.class);
427 Flow egressSplitHorizonFlow = MDSALUtil.buildFlow(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, flowRef,
428 splitHorizonFlowPriority, serviceRef, 0, 0, stypeOpenFlow.getFlowCookie(),
429 shMatches, shInstructions);
431 installFlow(dpId, egressSplitHorizonFlow, tx);
434 public static BoundServices getBoundServices(String serviceName, short servicePriority, int flowPriority,
435 Uint64 cookie, List<Instruction> instructions) {
436 StypeOpenflowBuilder augBuilder = new StypeOpenflowBuilder().setFlowCookie(cookie).setFlowPriority(flowPriority)
437 .setInstruction(instructions);
438 return new BoundServicesBuilder().withKey(new BoundServicesKey(servicePriority)).setServiceName(serviceName)
439 .setServicePriority(servicePriority).setServiceType(ServiceTypeFlowBased.class)
440 .addAugmentation(StypeOpenflow.class, augBuilder.build()).build();
443 public static InstanceIdentifier<BoundServices> buildServiceId(String interfaceName, short serviceIndex) {
444 return InstanceIdentifier.builder(ServiceBindings.class)
445 .child(ServicesInfo.class, new ServicesInfoKey(interfaceName, ServiceModeIngress.class))
446 .child(BoundServices.class, new BoundServicesKey(serviceIndex)).build();
449 public static InstanceIdentifier<BoundServices> buildServiceId(String interfaceName, short serviceIndex,
450 Class<? extends ServiceModeBase> serviceMode) {
451 return InstanceIdentifier.builder(ServiceBindings.class)
452 .child(ServicesInfo.class, new ServicesInfoKey(interfaceName, serviceMode))
453 .child(BoundServices.class, new BoundServicesKey(serviceIndex)).build();
456 public static InstanceIdentifier<BoundServices> buildDefaultServiceId(String interfaceName) {
457 return buildServiceId(interfaceName, ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
458 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX), ServiceModeEgress.class);
461 public static ListenableFuture<Void> bindDefaultEgressDispatcherService(ManagedNewTransactionRunner txRunner,
462 Interface interfaceInfo, String portNo, String interfaceName, int ifIndex) {
463 List<Instruction> instructions =
464 IfmUtil.getEgressInstructionsForInterface(interfaceInfo, portNo, null, true, ifIndex, 0);
465 return bindDefaultEgressDispatcherService(txRunner, interfaceName, instructions);
468 public static void bindDefaultEgressDispatcherService(TypedWriteTransaction<Configuration> tx,
469 Interface interfaceInfo, String portNo, String interfaceName, int ifIndex) {
470 List<Instruction> instructions =
471 IfmUtil.getEgressInstructionsForInterface(interfaceInfo, portNo, null, true, ifIndex, 0);
472 bindDefaultEgressDispatcherService(tx, interfaceName, instructions);
475 public static ListenableFuture<Void> bindDefaultEgressDispatcherService(ManagedNewTransactionRunner txRunner,
476 Interface interfaceInfo, String interfaceName, int ifIndex, long groupId) {
477 List<Instruction> instructions =
478 IfmUtil.getEgressInstructionsForInterface(interfaceInfo, StringUtils.EMPTY, null, true, ifIndex, groupId);
479 return bindDefaultEgressDispatcherService(txRunner, interfaceName, instructions);
482 public static ListenableFuture<Void> bindDefaultEgressDispatcherService(ManagedNewTransactionRunner txRunner,
483 String interfaceName, List<Instruction> instructions) {
484 return txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
485 int priority = ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
486 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX);
489 getBoundServices(defaultInterfaceName(interfaceName),
490 ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
491 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX),
492 priority, NwConstants.EGRESS_DISPATCHER_TABLE_COOKIE, instructions);
493 IfmUtil.bindService(tx, interfaceName, serviceInfo, ServiceModeEgress.class);
497 public static void bindDefaultEgressDispatcherService(TypedWriteTransaction<Configuration> tx,
498 String interfaceName, List<Instruction> instructions) {
499 int priority = ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
500 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX);
503 getBoundServices(defaultInterfaceName(interfaceName),
504 ServiceIndex.getIndex(NwConstants.DEFAULT_EGRESS_SERVICE_NAME,
505 NwConstants.DEFAULT_EGRESS_SERVICE_INDEX),
506 priority, NwConstants.EGRESS_DISPATCHER_TABLE_COOKIE, instructions);
507 IfmUtil.bindService(tx, interfaceName, serviceInfo, ServiceModeEgress.class);
510 private static String defaultInterfaceName(final String interfaceName) {
511 return "default." + interfaceName;
514 public static void removeIngressFlow(String interfaceName, Uint64 dpId, ManagedNewTransactionRunner txRunner,
515 List<ListenableFuture<Void>> futures) {
519 LOG.debug("Removing Ingress Flows for {}", interfaceName);
520 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
521 String flowKeyStr = getFlowRef(IfmConstants.VLAN_INTERFACE_INGRESS_TABLE, dpId, interfaceName);
522 FlowKey flowKey = new FlowKey(new FlowId(flowKeyStr));
523 Node nodeDpn = buildInventoryDpnNode(dpId);
524 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
525 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
526 .child(Table.class, new TableKey(NwConstants.VLAN_INTERFACE_INGRESS_TABLE)).child(Flow.class,
530 tx.delete(flowInstanceId);
534 public static void removeIngressFlow(String name, BoundServices serviceOld, Uint64 dpId,
535 TypedWriteTransaction<Configuration> writeTransaction) {
536 String flowKeyStr = getFlowRef(dpId, NwConstants.VLAN_INTERFACE_INGRESS_TABLE, name,
537 serviceOld.getServicePriority().toJava());
538 LOG.debug("Removing Ingress Flow {}", flowKeyStr);
539 FlowKey flowKey = new FlowKey(new FlowId(flowKeyStr));
540 Node nodeDpn = buildInventoryDpnNode(dpId);
541 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
542 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
543 .child(Table.class, new TableKey(NwConstants.VLAN_INTERFACE_INGRESS_TABLE)).child(Flow.class, flowKey)
546 writeTransaction.delete(flowInstanceId);
549 public static void removeLPortDispatcherFlow(Uint64 dpId, String iface, BoundServices boundServicesOld,
550 TypedWriteTransaction<Configuration> writeTransaction, short currentServiceIndex) {
551 LOG.debug("Removing LPort Dispatcher Flows {}, {}", dpId, iface);
553 boundServicesOld.augmentation(StypeOpenflow.class);
554 // build the flow and install it
555 String flowRef = getFlowRef(dpId, NwConstants.LPORT_DISPATCHER_TABLE, iface,
556 currentServiceIndex);
557 FlowKey flowKey = new FlowKey(new FlowId(flowRef));
558 Node nodeDpn = buildInventoryDpnNode(dpId);
559 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
560 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
561 .child(Table.class, new TableKey(NwConstants.LPORT_DISPATCHER_TABLE)).child(Flow.class, flowKey)
564 writeTransaction.delete(flowInstanceId);
565 EVENT_LOGGER.debug("IFM,removeFlow {}", flowRef);
568 public static void removeEgressDispatcherFlows(Uint64 dpId, String iface,
569 TypedWriteTransaction<Configuration> writeTransaction, short currentServiceIndex) {
570 LOG.debug("Removing Egress Dispatcher Flows {}, {}", dpId, iface);
571 removeEgressDispatcherFlow(dpId, iface, writeTransaction, currentServiceIndex);
572 removeEgressSplitHorizonDispatcherFlow(dpId, iface, writeTransaction);
575 private static void removeEgressDispatcherFlow(Uint64 dpId, String iface,
576 TypedWriteTransaction<Configuration> writeTransaction, short currentServiceIndex) {
577 // build the flow and install it
578 String flowRef = getFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, iface,
579 currentServiceIndex);
580 FlowKey flowKey = new FlowKey(new FlowId(flowRef));
581 Node nodeDpn = buildInventoryDpnNode(dpId);
582 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
583 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
584 .child(Table.class, new TableKey(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE)).child(Flow.class, flowKey)
587 writeTransaction.delete(flowInstanceId);
588 EVENT_LOGGER.debug("IFM,removeFlow {}", flowRef);
591 public static void removeEgressSplitHorizonDispatcherFlow(Uint64 dpId, String iface,
592 TypedWriteTransaction<Configuration> writeTransaction) {
593 // Uint64.ONE is used for checking the Split-Horizon flag
594 Uint64 shFlagSet = Uint64.ONE;
595 String shFlowRef = getSplitHorizonFlowRef(dpId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, iface,
597 FlowKey shFlowKey = new FlowKey(new FlowId(shFlowRef));
598 Node nodeDpn = buildInventoryDpnNode(dpId);
599 InstanceIdentifier<Flow> shFlowInstanceId = InstanceIdentifier.builder(Nodes.class)
600 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
601 .child(Table.class, new TableKey(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE))
602 .child(Flow.class, shFlowKey).build();
604 writeTransaction.delete(shFlowInstanceId);
607 public static String getFlowRef(short tableId, Uint64 dpnId, String infName) {
608 return tableId + ":" + dpnId + ":" + infName;
611 private static String getFlowRef(Uint64 dpnId, short tableId, String iface, short currentServiceIndex) {
612 return String.valueOf(dpnId) + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR + iface
613 + NwConstants.FLOWID_SEPARATOR + currentServiceIndex;
616 private static String getSplitHorizonFlowRef(Uint64 dpnId, short tableId, String iface, Uint64 shFlag) {
617 return String.valueOf(dpnId) + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR + iface
618 + NwConstants.FLOWID_SEPARATOR + shFlag.toString();
622 * This utility method returns an array of ServiceInfo in which index 0 will
623 * have the immediate lower priority service and index 1 will have the
624 * immediate higher priority service among the list of existing
627 * @param serviceInfos
628 * list of services bound
629 * @param currentServiceInfo
630 * current service bound
631 * @return array bound services
633 public static BoundServices[] getHighAndLowPriorityService(List<BoundServices> serviceInfos,
634 BoundServices currentServiceInfo) {
635 if (serviceInfos == null || serviceInfos.isEmpty()) {
636 return new BoundServices[] { null, null };
639 // This will be used to hold the immediate higher service priority with respect to the currentServiceInfo
640 BoundServices higher = null;
641 // This will be used to hold the immediate lower service priority with respect to the currentServiceInfo
642 BoundServices lower = null;
644 List<BoundServices> availableServiceInfos = new ArrayList<>(serviceInfos);
645 availableServiceInfos.sort(Comparator.comparing(BoundServices::getServicePriority));
646 for (BoundServices availableServiceInfo : availableServiceInfos) {
647 if (currentServiceInfo.getServicePriority().toJava() < availableServiceInfo.getServicePriority().toJava()) {
648 lower = availableServiceInfo;
651 higher = availableServiceInfo;
654 return new BoundServices[] { lower, higher };
657 public static BoundServices getHighestPriorityService(List<BoundServices> serviceInfos) {
658 List<BoundServices> availableServiceInfos = new ArrayList<>(serviceInfos);
659 if (availableServiceInfos.isEmpty()) {
662 BoundServices highPriorityService = availableServiceInfos.get(0);
663 availableServiceInfos.remove(0);
664 for (BoundServices availableServiceInfo : availableServiceInfos) {
665 if (availableServiceInfo.getServicePriority().toJava()
666 < highPriorityService.getServicePriority().toJava()) {
667 highPriorityService = availableServiceInfo;
670 return highPriorityService;
673 public static void installLportIngressFlow(Uint64 dpId, long portNo, Interface iface,
674 List<ListenableFuture<Void>> futures, ManagedNewTransactionRunner txRunner, int lportTag) {
676 boolean isVlanTransparent = false;
678 IfL2vlan l2vlan = iface.augmentation(IfL2vlan.class);
679 if (l2vlan != null) {
680 vlanId = l2vlan.getVlanId() == null ? 0 : l2vlan.getVlanId().getValue().toJava();
681 isVlanTransparent = l2vlan.getL2vlanMode() == IfL2vlan.L2vlanMode.Transparent;
683 int instructionKey = 0;
685 Map<InstructionKey, Instruction> instructions = new HashMap<>();
687 final SplitHorizon splitHorizon = iface.augmentation(SplitHorizon.class);
688 boolean overrideSplitHorizonProtection = splitHorizon != null
689 && splitHorizon.isOverrideSplitHorizonProtection();
691 List<Action> actions = new ArrayList<>();
692 if (vlanId != 0 && !isVlanTransparent) {
693 actions.add(MDSALUtil.createPopVlanAction(++actionKey));
695 if (overrideSplitHorizonProtection) {
696 actions.add(MDSALUtil.createNxOfInPortAction(++actionKey, 0));
698 if (!actions.isEmpty()) {
699 instructions.put(MDSALUtil.buildApplyActionsInstruction(actions, instructionKey++).key(),
700 MDSALUtil.buildApplyActionsInstruction(actions, instructionKey++));
702 Uint64 metadata = MetaDataUtil.getMetaDataForLPortDispatcher(lportTag, (short) 0, Uint64.ZERO,
704 Uint64 metadataMask = MetaDataUtil
705 .getMetaDataMaskForLPortDispatcher(MetaDataUtil.METADATA_MASK_LPORT_TAG_SH_FLAG);
706 instructions.put(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask, instructionKey++).key(),
707 MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask, instructionKey++));
708 instructions.put(MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.LPORT_DISPATCHER_TABLE,
709 instructionKey++).key(),
710 MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.LPORT_DISPATCHER_TABLE, instructionKey++));
711 int priority = isVlanTransparent ? 1
712 : vlanId == 0 ? IfmConstants.FLOW_PRIORITY_FOR_UNTAGGED_VLAN : IfmConstants.FLOW_HIGH_PRIORITY;
713 String flowRef = getFlowRef(IfmConstants.VLAN_INTERFACE_INGRESS_TABLE, dpId, iface.getName());
714 List<MatchInfo> matches = getMatchInfoForVlanPortAtIngressTable(dpId, portNo, iface);
715 Flow ingressFlow = MDSALUtil.buildFlowNew(IfmConstants.VLAN_INTERFACE_INGRESS_TABLE, flowRef, priority, flowRef,
716 0, 0, NwConstants.VLAN_TABLE_COOKIE, matches, instructions);
717 LOG.debug("Installing ingress flow {} for {}", flowRef, iface.getName());
719 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> installFlow(dpId, ingressFlow, tx)));
722 public static BoundServicesState buildBoundServicesState(
723 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface
724 interfaceState, Class<? extends ServiceModeBase> serviceMode) {
725 NodeConnectorId nodeConnectorId = IfmUtil.getNodeConnectorIdFromInterface(interfaceState);
726 Uint64 dpId = IfmUtil.getDpnFromNodeConnectorId(nodeConnectorId);
727 long portNo = IfmUtil.getPortNumberFromNodeConnectorId(nodeConnectorId);
728 BoundServicesStateKey boundServicesStateKey = new BoundServicesStateKey(interfaceState.getName(), serviceMode);
729 return new BoundServicesStateBuilder().setDpid(dpId).setIfIndex(interfaceState.getIfIndex())
730 .setInterfaceName(interfaceState.getName()).setInterfaceType(interfaceState.getType()).setPortNo(portNo)
731 .setServiceMode(serviceMode).withKey(boundServicesStateKey).build();
734 public static BoundServicesState getBoundServicesState(ReadTransaction tx,
735 String interfaceName,
736 Class<? extends ServiceModeBase> serviceMode)
737 throws ExecutionException, InterruptedException {
738 InstanceIdentifier<BoundServicesState> id = InstanceIdentifier.builder(BoundServicesStateList.class)
739 .child(BoundServicesState.class, new BoundServicesStateKey(interfaceName, serviceMode)).build();
740 return tx.read(LogicalDatastoreType.OPERATIONAL, id).get().orElse(null);
743 public static BoundServicesState getBoundServicesState(TypedReadTransaction<Operational> tx, String interfaceName,
744 Class<? extends ServiceModeBase> serviceMode) throws ExecutionException, InterruptedException {
745 InstanceIdentifier<BoundServicesState> id = InstanceIdentifier.builder(BoundServicesStateList.class)
746 .child(BoundServicesState.class, new BoundServicesStateKey(interfaceName, serviceMode)).build();
747 return tx.read(id).get().orElse(null);
750 public static void addBoundServicesState(TypedWriteTransaction<Operational> tx, String interfaceName,
751 BoundServicesState interfaceBoundServicesState) {
752 LOG.info("adding bound-service state information for interface : {}, service-mode : {}",
753 interfaceBoundServicesState.getInterfaceName(),
754 interfaceBoundServicesState.getServiceMode().getSimpleName());
755 InstanceIdentifier<BoundServicesState> id = InstanceIdentifier.builder(BoundServicesStateList.class)
756 .child(BoundServicesState.class, new BoundServicesStateKey(interfaceName,
757 interfaceBoundServicesState.getServiceMode())).build();
758 tx.mergeParentStructurePut(id, interfaceBoundServicesState);
761 public static void removeBoundServicesState(TypedWriteTransaction<Operational> tx,
762 String interfaceName, Class<? extends ServiceModeBase> serviceMode) {
763 LOG.info("remove bound-service state information for interface : {}, service-mode : {}", interfaceName,
764 serviceMode.getSimpleName());
765 InstanceIdentifier<BoundServicesState> id = InstanceIdentifier.builder(BoundServicesStateList.class)
766 .child(BoundServicesState.class, new BoundServicesStateKey(interfaceName, serviceMode)).build();
770 public static boolean isInterfaceTypeBasedServiceBinding(String interfaceName) {
771 return INTERFACE_TYPE_BASED_SERVICE_BINDING_KEYWORDS.contains(interfaceName);
774 private static boolean isExternal(Interface iface) {
778 IfExternal ifExternal = iface.augmentation(IfExternal.class);
779 return ifExternal != null && Boolean.TRUE.equals(ifExternal.isExternal());