2 * Copyright (c) 2016 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.netvirt.cloudservicechain.utils;
10 import java.math.BigInteger;
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.List;
16 import java.util.Optional;
17 import java.util.stream.Collectors;
18 import org.opendaylight.genius.mdsalutil.ActionInfo;
19 import org.opendaylight.genius.mdsalutil.MDSALUtil;
20 import org.opendaylight.genius.mdsalutil.MatchInfo;
21 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
22 import org.opendaylight.genius.mdsalutil.NwConstants;
23 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
24 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
25 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
26 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
27 import org.opendaylight.genius.utils.ServiceIndex;
28 import org.opendaylight.mdsal.binding.api.DataBroker;
29 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
30 import org.opendaylight.netvirt.cloudservicechain.CloudServiceChainConstants;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev160711.ElanServiceChainState;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev160711.elan.to.pseudo.port.data.list.ElanToPseudoPortData;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev160711.elan.to.pseudo.port.data.list.ElanToPseudoPortDataBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev160711.elan.to.pseudo.port.data.list.ElanToPseudoPortDataKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanDpnInterfaces;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesList;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesListKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
47 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
51 public final class ElanServiceChainUtils {
53 private static final Logger LOG = LoggerFactory.getLogger(ElanServiceChainUtils.class);
55 private ElanServiceChainUtils() {
59 public static InstanceIdentifier<ElanInstance> getElanInstanceConfigDataPath(String elanInstanceName) {
60 return InstanceIdentifier.builder(ElanInstances.class).child(ElanInstance.class,
61 new ElanInstanceKey(elanInstanceName)).build();
64 public static Optional<ElanInstance> getElanInstanceByName(DataBroker broker, String elanName) {
65 return MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, getElanInstanceConfigDataPath(elanName));
68 public static Collection<BigInteger> getElanDpnsByName(DataBroker broker, String elanInstanceName) {
69 InstanceIdentifier<ElanDpnInterfacesList> elanDpnIfacesIid =
70 InstanceIdentifier.builder(ElanDpnInterfaces.class)
71 .child(ElanDpnInterfacesList.class,new ElanDpnInterfacesListKey(elanInstanceName))
73 Optional<ElanDpnInterfacesList> elanDpnIfacesOpc =
74 MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, elanDpnIfacesIid);
75 if (!elanDpnIfacesOpc.isPresent()) {
76 LOG.warn("Could not find and DpnInterface for elan {}", elanInstanceName);
77 return Collections.emptySet();
80 return elanDpnIfacesOpc.get().getDpnInterfaces().stream().map(DpnInterfaces::getDpId).collect(
84 public static BigInteger getElanMetadataLabel(long elanTag) {
85 return BigInteger.valueOf(elanTag).shiftLeft(24);
89 * This flow is in charge of handling packets coming from ExtTunnelTable
90 * that must be redirected to the SCF Pipeline.
91 * + Matches on lportTag=ElanPseudoLportTag + SI=1
92 * + Sets scfTag and sends to the DlSubsFilter table.
94 * @param dpnId Dpn Id where the flow must be installed
95 * @param elanLportTag the Elan Pseudo Lport Id in Dataplane
96 * @param elanTag the Elan Id in the Dataplane
97 * @param addOrRemove States if the flow must be added or removed
99 public static void programLPortDispatcherToScf(IMdsalApiManager mdsalManager, BigInteger dpnId, long elanTag,
100 int elanLportTag, short tableId, long scfTag, int addOrRemove) {
101 LOG.info("L2-ServiceChaining: programLPortDispatcherToScf dpId={} elanLportTag={} scfTag={} addOrRemove={} ",
102 dpnId, elanLportTag, scfTag, addOrRemove);
103 String flowRef = buildLportDispToScfFlowRef(elanLportTag, scfTag);
104 if (addOrRemove == NwConstants.ADD_FLOW) {
105 int instructionKey = 0;
106 List<Instruction> instructions = new ArrayList<>();
107 List<ActionInfo> actionsInfos = new ArrayList<>();
108 actionsInfos.add(new ActionRegLoad(NxmNxReg2.class, 0, 31, scfTag));
109 instructions.add(MDSALUtil.buildApplyActionsInstruction(MDSALUtil
110 .buildActions(actionsInfos),instructionKey++));
111 instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(tableId, instructionKey++));
112 List<MatchInfo> matches = Collections.singletonList(
114 MetaDataUtil.getMetaDataForLPortDispatcher(elanLportTag,
115 ServiceIndex.getIndex(NwConstants.SCF_SERVICE_NAME, NwConstants.SCF_SERVICE_INDEX)),
116 MetaDataUtil.getMetaDataMaskForLPortDispatcher()));
118 Flow flow = MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
119 CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY, flowRef,
120 0, 0, CloudServiceChainConstants.COOKIE_LPORT_DISPATCHER_BASE.add(BigInteger.valueOf(elanTag)),
121 matches, instructions);
122 mdsalManager.installFlow(dpnId, flow);
124 Flow flow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
125 .setId(new FlowId(flowRef)).build();
126 mdsalManager.removeFlow(dpnId, flow);
131 * This flow is in charge of handling packets coming from the SCF Pipeline
132 * when there is no matching ServiceChain.
134 * <li> Matches on ElanPseudoPortTag and SI=3 (ELAN)</li>
135 * <li> Sets elanTag and sends to DMAC table</li>
137 * @param dpnId Dpn Id where the flow must be installed
138 * @param elanLportTag the Elan Pseudo Lport Id to be used in the Dataplane
139 * @param elanTag the Elan Id to be used in the Dataplane
140 * @param addOrRemove States if the flow must be added or removed
142 public static void programLPortDispatcherFromScf(IMdsalApiManager mdsalManager, BigInteger dpnId,
143 int elanLportTag, long elanTag, int addOrRemove) {
144 LOG.info("L2-ServiceChaining: programLPortDispatcherFromScf dpId={} elanLportTag={} elanTag={} addOrRemove={} ",
145 dpnId, elanLportTag, elanTag, addOrRemove);
146 String flowRef = buildLportDispFromScfFlowRef(elanTag, elanLportTag);
147 if (addOrRemove == NwConstants.ADD_FLOW) {
148 List<MatchInfo> matches = Collections.singletonList(
149 new MatchMetadata(MetaDataUtil.getMetaDataForLPortDispatcher(elanLportTag,
150 ServiceIndex.getIndex(NwConstants.ELAN_SERVICE_NAME, NwConstants.ELAN_SERVICE_INDEX)),
151 MetaDataUtil.getMetaDataMaskForLPortDispatcher()));
152 int instructionKey = 0;
153 List<Instruction> instructions = Arrays.asList(
154 // BigInter.ONE is for setting also the Split-Horizon flag since it could have been cleared
155 // while going through the SCF Pipeline
156 MDSALUtil.buildAndGetWriteMetadaInstruction(getElanMetadataLabel(elanTag).or(BigInteger.ONE),
157 MetaDataUtil.METADATA_MASK_SERVICE.or(BigInteger.ONE),
159 MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.ELAN_SMAC_TABLE,
163 MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
164 CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY,
166 CloudServiceChainConstants.COOKIE_LPORT_DISPATCHER_BASE.add(BigInteger.valueOf(elanTag)),
167 matches, instructions);
168 mdsalManager.installFlow(dpnId, flow);
170 Flow flow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
171 .setId(new FlowId(flowRef)).build();
172 mdsalManager.removeFlow(dpnId, flow);
178 * This flow is in charge of receiving packets from the TOR and sending
179 * them to the SCF Pipeline by setting the LportTag of ElanPseudoPort.
180 * Note that ELAN already has a flow in this table that redirects packets
181 * to the ELAN Pipeline. However, the flow for the SCF Pipeline will have
182 * higher priority, and will only be present when there is a ServiceChain
183 * using this ElanPseudoPort.
185 * <li> Matches on the VNI
186 * <li> Sets SI=1 and ElanPseudoPort tag in the Metadata and sends to
187 * LPortDispatcher via table 80.
189 * @param dpnId Dpn Id where the flow must be installed
190 * @param elanLportTag the Elan Pseudo Lport Id to be used in the Dataplane
191 * @param vni the VNI to which the Elan is related
192 * @param elanTag the Elan Id to be used in the Dataplane
193 * @param addOrRemove States if the flow must be added or removed
195 public static void programExternalTunnelTable(IMdsalApiManager mdsalManager, BigInteger dpnId, int elanLportTag,
196 long vni, int elanTag, int addOrRemove) {
197 LOG.info("L2-ServiceChaining: programExternalTunnelTable dpId={} vni={} elanLportTag={} addOrRemove={} ",
198 dpnId, vni, elanLportTag, addOrRemove);
199 String flowRef = buildExtTunnelTblToLportDispFlowRef(vni, elanLportTag);
200 if (addOrRemove == NwConstants.ADD_FLOW) {
201 List<MatchInfo> matches = Collections.singletonList(new MatchTunnelId(BigInteger.valueOf(vni)));
202 List<Instruction> instructions = buildSetLportTagAndGotoLportDispInstructions(elanLportTag);
203 Flow flow = MDSALUtil.buildFlowNew(NwConstants.EXTERNAL_TUNNEL_TABLE, flowRef,
204 CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY, flowRef,
205 0, 0, NwConstants.TUNNEL_TABLE_COOKIE.add(BigInteger.valueOf(elanTag)),
206 matches, instructions);
207 mdsalManager.installFlow(dpnId, flow);
210 new FlowBuilder().setTableId(NwConstants.EXTERNAL_TUNNEL_TABLE).setId(new FlowId(flowRef)).build();
211 mdsalManager.removeFlow(dpnId, flow);
216 * Builds a List of Instructions that set the ElanPseudoPort Tag in
217 * metadata and sends to LPortDispatcher table (via Table 80).
219 * @param lportTag Dataplane identifier of the ElanPseudoPort
221 * @return the List of Instructions
223 public static List<Instruction> buildSetLportTagAndGotoLportDispInstructions(int lportTag) {
224 int instructionKey = 0;
225 BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(lportTag,
226 ServiceIndex.getIndex(NwConstants.SCF_SERVICE_NAME, NwConstants.SCF_SERVICE_INDEX));
227 List<Instruction> result =
228 Arrays.asList(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata,
229 MetaDataUtil.getMetaDataMaskForLPortDispatcher(),
231 MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.L3_INTERFACE_TABLE, ++instructionKey));
235 private static String buildExtTunnelTblToLportDispFlowRef(long vni, int elanLportTag) {
236 return CloudServiceChainConstants.L2_FLOWID_PREFIX + vni
237 + NwConstants.FLOWID_SEPARATOR + elanLportTag;
240 private static String buildLportDispToScfFlowRef(int elanLportTag, long scfTag) {
241 return CloudServiceChainConstants.ELAN_TO_SCF_L2_FLOWID_PREFIX + elanLportTag
242 + NwConstants.FLOWID_SEPARATOR + scfTag;
245 private static String buildLportDispFromScfFlowRef(long elanTag, int elanLportTag) {
246 return CloudServiceChainConstants.SCF_TO_ELAN_L2_FLOWID_PREFIX + elanTag
247 + NwConstants.FLOWID_SEPARATOR + elanLportTag;
251 * Stores the relation between ElanLport and scfTag.
253 * @param broker dataBroker service reference
254 * @param elanInstanceName Name of the ELAN. Typically its UUID
255 * @param lportTag Dataplane identifier of the ElanPseudoPort
256 * @param scfTag Dataplane identifier of the SCF
257 * @param addOrRemove States if flows must be added or removed
259 public static void updateElanToLportTagMap(final DataBroker broker, final String elanInstanceName,
260 final int lportTag, final long scfTag, final int addOrRemove) {
261 Long portTag = Long.valueOf(lportTag);
262 ElanToPseudoPortDataKey key = new ElanToPseudoPortDataKey(portTag, scfTag);
263 InstanceIdentifier<ElanToPseudoPortData> path = InstanceIdentifier.builder(ElanInstances.class)
264 .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName))
265 .augmentation(ElanServiceChainState.class)
266 .child(ElanToPseudoPortData.class, key).build();
268 if (addOrRemove == NwConstants.ADD_FLOW) {
269 ElanToPseudoPortData newValue =
270 new ElanToPseudoPortDataBuilder().withKey(key).setElanLportTag(portTag)
271 .setScfTag(scfTag).build();
272 MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, path, newValue);
274 MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, path);
279 * Read from ElanToLportTagMap the PsuedoLogicalPort related with a given elan.
281 * @param broker dataBroker service reference
282 * @param elanInstanceName the name of the Elan
283 * @return the ElanToPseudoPortData object or Optional.empty() if it
286 public static Optional<ElanServiceChainState> getElanServiceChainState(final DataBroker broker,
287 final String elanInstanceName) {
288 InstanceIdentifier<ElanServiceChainState> path = InstanceIdentifier.builder(ElanInstances.class)
289 .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName))
290 .augmentation(ElanServiceChainState.class).build();
291 Optional<ElanServiceChainState> elanServiceChainStateOpc =
292 MDSALUtil.read(broker,LogicalDatastoreType.CONFIGURATION, path);
294 return elanServiceChainStateOpc;