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.Collection;
12 import java.util.HashSet;
13 import java.util.List;
15 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
16 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
17 import org.opendaylight.genius.mdsalutil.MatchFieldType;
18 import org.opendaylight.genius.mdsalutil.MatchInfo;
19 import org.opendaylight.genius.mdsalutil.MDSALUtil;
20 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
21 import org.opendaylight.genius.mdsalutil.NwConstants;
22 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
23 import org.opendaylight.genius.itm.globals.ITMConstants;
24 import org.opendaylight.genius.utils.ServiceIndex;
25 import org.opendaylight.netvirt.cloudservicechain.CloudServiceChainConstants;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev170511.ElanServiceChainState;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev170511.elan.to.pseudo.port.data.list.ElanToPseudoPortData;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev170511.elan.to.pseudo.port.data.list.ElanToPseudoPortDataBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev170511.elan.to.pseudo.port.data.list.ElanToPseudoPortDataKey;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanDpnInterfaces;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesList;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesListKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceKey;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
42 import java.util.Arrays;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
46 import com.google.common.base.Optional;
48 public class ElanServiceChainUtils {
50 private static final Logger logger = LoggerFactory.getLogger(ElanServiceChainUtils.class);
53 public static InstanceIdentifier<ElanInstance> getElanInstanceConfigDataPath(String elanInstanceName) {
54 return InstanceIdentifier.builder(ElanInstances.class).child(ElanInstance.class,
55 new ElanInstanceKey(elanInstanceName)).build();
58 public static Optional<ElanInstance> getElanInstanceByName(DataBroker broker, String elanName) {
59 return MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, getElanInstanceConfigDataPath(elanName));
62 public static Optional<Collection<BigInteger>> getElanDpnsByName(DataBroker broker, String elanInstanceName) {
63 InstanceIdentifier<ElanDpnInterfacesList> elanDpnIfacesIid =
64 InstanceIdentifier.builder(ElanDpnInterfaces.class)
65 .child(ElanDpnInterfacesList.class,new ElanDpnInterfacesListKey(elanInstanceName))
67 Optional<ElanDpnInterfacesList> elanDpnIfacesOpc =
68 MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, elanDpnIfacesIid);
69 if (!elanDpnIfacesOpc.isPresent()) {
70 logger.warn("Could not find and DpnInterface for elan {}", elanInstanceName);
71 return Optional.<Collection<BigInteger>>absent();
74 Collection<BigInteger> dpns = new HashSet<>();
75 List<DpnInterfaces> elanDpnIfaces = elanDpnIfacesOpc.get().getDpnInterfaces();
76 for ( DpnInterfaces dpnIf : elanDpnIfaces) {
77 dpns.add(dpnIf.getDpId());
80 return Optional.of(dpns);
83 public static BigInteger getElanMetadataLabel(long elanTag) {
84 return BigInteger.valueOf(elanTag).shiftLeft(24);
88 * This flow is in charge of handling packets coming from ExtTunnelTable
89 * that must be redirected to the SCF Pipeline.
90 * + Matches on lportTag=ElanPseudoLportTag + SI=1
91 * + Sets scfTag and sends to the DlSubsFilter table.
93 * @param dpnId Dpn Id where the flow must be installed
94 * @param elanLportTag the Elan Pseudo Lport Id in Dataplane
95 * @param elanTag the Elan Id in the Dataplane
96 * @param addOrRemove States if the flow must be added or removed
98 public static void programLPortDispatcherToScf(IMdsalApiManager mdsalManager, BigInteger dpnId, long elanTag,
99 int elanLportTag, short tableId, long scfTag, int addOrRemove) {
100 logger.info("L2-ServiceChaining: programLPortDispatcherToScf dpId={} elanLportTag={} scfTag={} addOrRemove={} ",
101 dpnId, elanLportTag, scfTag, addOrRemove);
102 String flowRef = buildLportDispToScfFlowRef(elanLportTag, scfTag);
103 if (addOrRemove == NwConstants.ADD_FLOW) {
104 List<MatchInfo> matches = Arrays.asList(
105 new MatchInfo(MatchFieldType.metadata,
106 new BigInteger[] { MetaDataUtil.getMetaDataForLPortDispatcher(elanLportTag,
107 ServiceIndex.getIndex(NwConstants.SCF_SERVICE_NAME, NwConstants.SCF_SERVICE_INDEX)),
108 MetaDataUtil.getMetaDataMaskForLPortDispatcher() }));
109 int instructionKey = 0;
110 List<Instruction> instructions = Arrays.asList(
111 MDSALUtil.buildAndGetWriteMetadaInstruction(VpnServiceChainUtils.getMetadataSCF(scfTag),
112 CloudServiceChainConstants.METADATA_MASK_SCF_WRITE,
114 MDSALUtil.buildAndGetGotoTableInstruction(tableId, instructionKey++) );
116 Flow flow = MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
117 CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY, flowRef,
118 0, 0, ITMConstants.COOKIE_ITM_EXTERNAL.add(BigInteger.valueOf(elanTag)),
119 matches, instructions);
120 mdsalManager.installFlow(dpnId, flow);
122 Flow flow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
123 .setId(new FlowId(flowRef)).build();
124 mdsalManager.removeFlow(dpnId, flow);
129 * This flow is in charge of handling packets coming from the SCF Pipeline
130 * when there is no matching ServiceChain.
132 * + Matches on ElanPseudoPortTag and SI=3 (ELAN)
133 * + Sets elanTag and sends to DMAC table
135 * @param dpnId Dpn Id where the flow must be installed
136 * @param elanLportTag the Elan Pseudo Lport Id to be used in the Dataplane
137 * @param elanTag the Elan Id to be used in the Dataplane
138 * @param addOrRemove States if the flow must be added or removed
140 public static void programLPortDispatcherFromScf(IMdsalApiManager mdsalManager, BigInteger dpnId,
141 int elanLportTag, long elanTag, int addOrRemove) {
142 logger.info("L2-ServiceChaining: programLPortDispatcherFromScf dpId={} elanLportTag={} elanTag={} addOrRemove={} ",
143 dpnId, elanLportTag, elanTag, addOrRemove);
144 String flowRef = buildLportDispFromScfFlowRef(elanTag, elanLportTag );
145 if (addOrRemove == NwConstants.ADD_FLOW) {
146 List<MatchInfo> matches = Arrays.asList(
147 new MatchInfo(MatchFieldType.metadata,
148 new BigInteger[] { MetaDataUtil.getMetaDataForLPortDispatcher(elanLportTag,
149 ServiceIndex.getIndex(NwConstants.ELAN_SERVICE_NAME, NwConstants.ELAN_SERVICE_INDEX)),
150 MetaDataUtil.getMetaDataMaskForLPortDispatcher() }));
151 int instructionKey = 0;
152 List<Instruction> instructions = Arrays.asList(
153 // BigInter.ONE is for setting also the Split-Horizon flag since it could have been cleared
154 // while going through the SCF Pipeline
155 MDSALUtil.buildAndGetWriteMetadaInstruction(getElanMetadataLabel(elanTag).or(BigInteger.ONE),
156 MetaDataUtil.METADATA_MASK_SERVICE.or(BigInteger.ONE),
158 MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.ELAN_SMAC_TABLE,
162 MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
163 CloudServiceChainConstants.DEFAULT_LPORT_DISPATCHER_FLOW_PRIORITY,
164 flowRef, 0, 0, ITMConstants.COOKIE_ITM_EXTERNAL.add(BigInteger.valueOf(elanTag)),
165 matches, instructions);
166 mdsalManager.installFlow(dpnId, flow);
168 Flow flow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
169 .setId(new FlowId(flowRef)).build();
170 mdsalManager.removeFlow(dpnId, flow);
176 * This flow is in charge of receiving packets from the TOR and sending
177 * them to the SCF Pipeline by setting the LportTag of ElanPseudoPort.
178 * Note that ELAN already has a flow in this table that redirects packets
179 * to the ELAN Pipeline. However, the flow for the SCF Pipeline will have
180 * higher priority, and will only be present when there is a ServiceChain
181 * using this ElanPseudoPort.
183 * + Matches on the VNI
184 * + Sets SI=1 and ElanPseudoPort tag in the Metadata and sends to
185 * LPortDispatcher via table 80.
187 * @param dpnId Dpn Id where the flow must be installed
188 * @param elanLportTag the Elan Pseudo Lport Id to be used in the Dataplane
189 * @param vni the VNI to which the Elan is related
190 * @param elanTag the Elan Id to be used in the Dataplane
191 * @param addOrRemove States if the flow must be added or removed
193 public static void programExternalTunnelTable(IMdsalApiManager mdsalManager, BigInteger dpnId, int elanLportTag,
194 long vni, int elanTag, int addOrRemove) {
195 logger.info("L2-ServiceChaining: programExternalTunnelTable dpId={} vni={} elanLportTag={} addOrRemove={} ",
196 dpnId, vni, elanLportTag, addOrRemove);
197 String flowRef = buildExtTunnelTblToLportDispFlowRef(vni, elanLportTag);
198 if (addOrRemove == NwConstants.ADD_FLOW) {
199 List<MatchInfo> matches = Arrays.asList(new MatchInfo(MatchFieldType.tunnel_id,
200 new BigInteger[] { BigInteger.valueOf(vni) } ) );
201 List<Instruction> instructions = buildSetLportTagAndGotoLportDispInstructions(elanLportTag);
202 Flow flow = MDSALUtil.buildFlowNew(NwConstants.EXTERNAL_TUNNEL_TABLE, flowRef,
203 CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY, flowRef,
204 0, 0, ITMConstants.COOKIE_ITM_EXTERNAL.add(BigInteger.valueOf(elanTag)),
205 matches, instructions);
206 mdsalManager.installFlow(dpnId, flow);
208 Flow flow = new FlowBuilder().setTableId(NwConstants.EXTERNAL_TUNNEL_TABLE).setId(new FlowId(flowRef)).build();
209 mdsalManager.removeFlow(dpnId, flow);
214 * Builds a List of Instructions that set the ElanPseudoPort Tag in
215 * metadata and sends to LPortDispatcher table (via Table 80)
217 * @param lportTag Dataplane identifier of the ElanPseudoPort
219 * @return the List of Instructions
221 public static List<Instruction> buildSetLportTagAndGotoLportDispInstructions(int lportTag) {
222 int instructionKey = 0;
223 BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(lportTag,
224 ServiceIndex.getIndex(NwConstants.SCF_SERVICE_NAME, NwConstants.SCF_SERVICE_INDEX));
225 List<Instruction> result =
226 Arrays.asList(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata,
227 MetaDataUtil.getMetaDataMaskForLPortDispatcher(),
229 MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.L3_INTERFACE_TABLE, ++instructionKey));
233 private static String buildExtTunnelTblToLportDispFlowRef(long vni, int elanLportTag) {
234 return CloudServiceChainConstants.L2_FLOWID_PREFIX + vni
235 + NwConstants.FLOWID_SEPARATOR + elanLportTag;
238 private static String buildLportDispToScfFlowRef(int elanLportTag, long scfTag) {
239 return CloudServiceChainConstants.ELAN_TO_SCF_L2_FLOWID_PREFIX + elanLportTag
240 + NwConstants.FLOWID_SEPARATOR + scfTag;
243 private static String buildLportDispFromScfFlowRef(long elanTag, int elanLportTag) {
244 return CloudServiceChainConstants.SCF_TO_ELAN_L2_FLOWID_PREFIX + elanTag
245 + NwConstants.FLOWID_SEPARATOR + elanLportTag;
249 * Stores the relation between ElanLport and scfTag.
251 * @param broker dataBroker service reference
252 * @param elanInstanceName Name of the ELAN. Typically its UUID
253 * @param lportTag Dataplane identifier of the ElanPseudoPort
254 * @param scfTag Dataplane identifier of the SCF
255 * @param addOrRemove States if flows must be added or removed
257 public static void updateElanToLportTagMap(final DataBroker broker, final String elanInstanceName,
258 final int lportTag, final long scfTag, final int addOrRemove) {
259 ElanToPseudoPortDataKey key = new ElanToPseudoPortDataKey(new Long(lportTag), scfTag);
260 InstanceIdentifier<ElanToPseudoPortData> path = InstanceIdentifier.builder(ElanInstances.class)
261 .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName))
262 .augmentation(ElanServiceChainState.class)
263 .child(ElanToPseudoPortData.class, new ElanToPseudoPortDataKey(key)).build();
265 if ( addOrRemove == NwConstants.ADD_FLOW ) {
266 ElanToPseudoPortData newValue =
267 new ElanToPseudoPortDataBuilder().setKey(key).setElanLportTag(new Long(lportTag))
268 .setScfTag(scfTag).build();
269 MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, path, newValue);
271 MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, path);
276 * Read from ElanToLportTagMap the PsuedoLogicalPort related with a given elan.
278 * @param broker dataBroker service reference
279 * @param elanInstanceName
280 * @return the ElanToPseudoPortData object or Optional.absent() if it
283 public static Optional<ElanServiceChainState> getElanServiceChainState(final DataBroker broker, final String elanInstanceName) {
284 InstanceIdentifier<ElanServiceChainState> path = InstanceIdentifier.builder(ElanInstances.class)
285 .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName))
286 .augmentation(ElanServiceChainState.class).build();
287 Optional<ElanServiceChainState> elanServiceChainStateOpc =
288 MDSALUtil.read(broker,LogicalDatastoreType.OPERATIONAL,path);
290 return elanServiceChainStateOpc;