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.MDSALUtil;
18 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
19 import org.opendaylight.genius.mdsalutil.NwConstants;
20 import org.opendaylight.genius.itm.globals.ITMConstants;
21 import org.opendaylight.netvirt.cloudservicechain.CloudServiceChainConstants;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev170511.ElanServiceChainState;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev170511.elan.to.pseudo.port.data.list.ElanToPseudoPortData;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev170511.elan.to.pseudo.port.data.list.ElanToPseudoPortDataBuilder;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev170511.elan.to.pseudo.port.data.list.ElanToPseudoPortDataKey;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanDpnInterfaces;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesList;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesListKey;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceKey;
33 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
38 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
39 import org.opendaylight.genius.mdsalutil.MatchFieldType;
40 import org.opendaylight.genius.mdsalutil.MatchInfo;
41 import java.util.Arrays;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
45 import com.google.common.base.Optional;
47 public class ElanServiceChainUtils {
49 private static final Logger logger = LoggerFactory.getLogger(ElanServiceChainUtils.class);
52 public static InstanceIdentifier<ElanInstance> getElanInstanceConfigurationDataPath(String elanInstanceName) {
53 return InstanceIdentifier.builder(ElanInstances.class).child(ElanInstance.class, new ElanInstanceKey(elanInstanceName)).build();
56 public static Optional<ElanInstance> getElanInstanceByName(DataBroker broker, String elanName) {
57 return MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION,
58 getElanInstanceConfigurationDataPath(elanName));
61 public static Optional<Collection<BigInteger>> getElanDnsByName(DataBroker broker, String elanInstanceName) {
62 InstanceIdentifier<ElanDpnInterfacesList> elanDpnIfacesIid =
63 InstanceIdentifier.builder(ElanDpnInterfaces.class)
64 .child(ElanDpnInterfacesList.class,new ElanDpnInterfacesListKey(elanInstanceName))
66 Optional<ElanDpnInterfacesList> elanDpnIfacesOpc =
67 MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, elanDpnIfacesIid);
68 if (!elanDpnIfacesOpc.isPresent()) {
69 logger.warn("Could not find and DpnInterface for elan {}", elanInstanceName);
70 return Optional.<Collection<BigInteger>>absent();
73 Collection<BigInteger> dpns = new HashSet<BigInteger>();
74 List<DpnInterfaces> elanDpnIfaces = elanDpnIfacesOpc.get().getDpnInterfaces();
75 for ( DpnInterfaces dpnIf : elanDpnIfaces) {
76 dpns.add(dpnIf.getDpId());
79 return Optional.of(dpns);
82 public static BigInteger getElanMetadataLabel(long elanTag) {
83 return (BigInteger.valueOf(elanTag)).shiftLeft(24);
87 * This flow is in charge of handling packets coming from ExtTunnelTable
88 * that must be redirected to the SCF Pipeline.
89 * + Matches on lportTag=ElanPseudoLportTag + SI=1
90 * + Sets scfTag and sends to the DlSubsFilter table.
92 * @param dpnId Dpn Id where the LPortDispatcher table must be modified
93 * @param elanLportTag Dataplane identifier of the ElanPseudoPort
94 * @param elanTag Dataplane identifier of the ELAN
95 * @param addOrRemove States if flows must be added or removed
97 public static void programLPortDispatcherToScf(IMdsalApiManager mdsalManager, BigInteger dpnId, int elanTag,
98 int elanLportTag, short tableId, int scfTag, int addOrRemove) {
99 logger.info("L2-ServiceChaining: programLPortDispatcherToScf dpId={} elanLportTag={} scfTag={} addOrRemove={} ",
100 dpnId, elanLportTag, scfTag, addOrRemove);
101 String flowRef = buildLportDispToScfFlowRef(elanLportTag, scfTag);
102 if (addOrRemove == NwConstants.ADD_FLOW) {
103 List<MatchInfo> matches = Arrays.asList(
104 new MatchInfo(MatchFieldType.metadata,
105 new BigInteger[] { MetaDataUtil.getMetaDataForLPortDispatcher(elanLportTag,
106 NwConstants.SCF_SERVICE_INDEX),
107 MetaDataUtil.getMetaDataMaskForLPortDispatcher() }));
108 int instructionKey = 0;
109 List<Instruction> instructions = Arrays.asList(
110 MDSALUtil.buildAndGetWriteMetadaInstruction(VpnServiceChainUtils.getMetadataSCF(scfTag),
111 CloudServiceChainConstants.METADATA_MASK_SCF_WRITE,
113 MDSALUtil.buildAndGetGotoTableInstruction(tableId, instructionKey++) );
115 Flow flow = MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
116 CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY, flowRef,
117 0, 0, ITMConstants.COOKIE_ITM_EXTERNAL.add(BigInteger.valueOf(elanTag)),
118 matches, instructions);
119 mdsalManager.installFlow(dpnId, flow);
121 Flow flow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
122 .setId(new FlowId(flowRef)).build();
123 mdsalManager.removeFlow(dpnId, flow);
128 * This flow is in charge of handling packets coming from the SCF Pipeline
129 * when there is no matching ServiceChain.
131 * + Matches on ElanPseudoPortTag and SI=3 (ELAN)
132 * + Sets elanTag and sends to DMAC table
134 * @param dpnId Dpn Id where the LPortDispatcher table must be modified
135 * @param elanLportTag Dataplane identifier of the ElanPseudoPort
136 * @param elanTag Dataplane identifier of the ELAN
137 * @param addOrRemove States if flows must be added or removed
139 public static void programLPortDispatcherFromScf(IMdsalApiManager mdsalManager, BigInteger dpnId,
140 int elanLportTag, int elanTag, int addOrRemove) {
141 logger.info("L2-ServiceChaining: programLPortDispatcherFromScf dpId={} elanLportTag={} elanTag={} addOrRemove={} ",
142 dpnId, elanLportTag, elanTag, addOrRemove);
143 String flowRef = buildLportDispFromScfFlowRef(elanTag, elanLportTag );
144 if (addOrRemove == NwConstants.ADD_FLOW) {
145 List<MatchInfo> matches = Arrays.asList(
146 new MatchInfo(MatchFieldType.metadata,
147 new BigInteger[] { MetaDataUtil.getMetaDataForLPortDispatcher(elanLportTag,
148 NwConstants.ELAN_SERVICE_INDEX),
149 MetaDataUtil.getMetaDataMaskForLPortDispatcher() }));
150 int instructionKey = 0;
151 List<Instruction> instructions = Arrays.asList(
152 // BigInter.ONE is for setting also the Split-Horizon flag since it could have been cleared
153 // while going through the SCF Pipeline
154 MDSALUtil.buildAndGetWriteMetadaInstruction(getElanMetadataLabel(elanTag).or(BigInteger.ONE),
155 MetaDataUtil.METADATA_MASK_SERVICE.or(BigInteger.ONE),
157 MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.ELAN_SMAC_TABLE,
161 MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
162 CloudServiceChainConstants.DEFAULT_LPORT_DISPATCHER_FLOW_PRIORITY,
163 flowRef, 0, 0, ITMConstants.COOKIE_ITM_EXTERNAL.add(BigInteger.valueOf(elanTag)),
164 matches, instructions);
165 mdsalManager.installFlow(dpnId, flow);
167 Flow flow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
168 .setId(new FlowId(flowRef)).build();
169 mdsalManager.removeFlow(dpnId, flow);
175 * This flow is in charge of receiving packets from the TOR and sending
176 * them to the SCF Pipeline by setting the LportTag of ElanPseudoPort.
177 * Note that ELAN already has a flow in this table that redirects packets
178 * to the ELAN Pipeline. However, the flow for the SCF Pipeline will have
179 * higher priority, and will only be present when there is a ServiceChain
180 * using this ElanPseudoPort.
182 * + Matches on the VNI
183 * + Sets SI=1 and ElanPseudoPort tag in the Metadata and sends to
184 * LPortDispatcher via table 80.
186 * @param dpnId Dpn Id where the ExtTunnel table must be modified
187 * @param elanLportTag Dataplane identifier of the ElanPseudoPort
188 * @param vni Virtual Network Identifier
189 * @param elanTag Dataplane identifier of the ELAN
190 * @param addOrRemove States if flows must be added or removed
192 public static void programExternalTunnelTable(IMdsalApiManager mdsalManager, BigInteger dpnId, int elanLportTag,
193 Long vni, int elanTag, int addOrRemove) {
194 logger.info("L2-ServiceChaining: programExternalTunnelTable dpId={} vni={} elanLportTag={} addOrRemove={} ",
195 dpnId, vni, elanLportTag, addOrRemove);
196 String flowRef = buildExtTunnelTblToLportDispFlowRef(vni, elanLportTag);
197 if (addOrRemove == NwConstants.ADD_FLOW) {
198 List<MatchInfo> matches = Arrays.asList(new MatchInfo(MatchFieldType.tunnel_id,
199 new BigInteger[] { BigInteger.valueOf(vni) } ) );
200 List<Instruction> instructions = buildSetLportTagAndGotoLportDispInstructions(elanLportTag);
201 Flow flow = MDSALUtil.buildFlowNew(NwConstants.EXTERNAL_TUNNEL_TABLE, flowRef,
202 CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY, flowRef,
203 0, 0, ITMConstants.COOKIE_ITM_EXTERNAL.add(BigInteger.valueOf(elanTag)),
204 matches, instructions);
205 mdsalManager.installFlow(dpnId, flow);
207 Flow flow = new FlowBuilder().setTableId(NwConstants.EXTERNAL_TUNNEL_TABLE).setId(new FlowId(flowRef)).build();
208 mdsalManager.removeFlow(dpnId, flow);
213 * Builds a List of Instructions that set the ElanPseudoPort Tag in
214 * metadata and sends to LPortDispatcher table (via Table 80)
216 * @param lportTag Dataplane identifier of the ElanPseudoPort
218 * @return the List of Instructions
220 public static List<Instruction> buildSetLportTagAndGotoLportDispInstructions(long lportTag) {
221 int instructionKey = 0;
222 BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher((int) lportTag,
223 NwConstants.SCF_SERVICE_INDEX);
224 List<Instruction> result =
225 Arrays.asList(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata,
226 MetaDataUtil.getMetaDataMaskForLPortDispatcher(),
228 MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.L3_INTERFACE_TABLE, ++instructionKey));
232 private static String buildExtTunnelTblToLportDispFlowRef(Long vni, int elanLportTag) {
233 return CloudServiceChainConstants.L2_FLOWID_PREFIX + vni
234 + NwConstants.FLOWID_SEPARATOR + elanLportTag;
237 private static String buildLportDispToScfFlowRef(int elanLportTag, int scfTag) {
238 return CloudServiceChainConstants.ELAN_TO_SCF_L2_FLOWID_PREFIX + elanLportTag
239 + NwConstants.FLOWID_SEPARATOR + scfTag;
242 private static String buildLportDispFromScfFlowRef(int elanTag, int elanLportTag) {
243 return CloudServiceChainConstants.SCF_TO_ELAN_L2_FLOWID_PREFIX + elanTag
244 + NwConstants.FLOWID_SEPARATOR + elanLportTag;
248 * Stores the relation between elanInstanceName and ElanLport and scfTag.
250 * @param broker dataBroker service reference
251 * @param elanInstanceName Name of the ELAN. Typically its UUID
252 * @param lportTag Dataplane identifier of the ElanPseudoPort
253 * @param scfTag Dataplane identifier of the SCF
254 * @param addOrRemove States if flows must be added or removed
256 public static void updateElanToLportTagMap(final DataBroker broker, final String elanInstanceName,
257 final int lportTag, final int scfTag, final int addOrRemove) {
258 ElanToPseudoPortDataKey key = new ElanToPseudoPortDataKey(elanInstanceName);
259 InstanceIdentifier<ElanToPseudoPortData> path = InstanceIdentifier.builder(ElanInstances.class)
260 .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName))
261 .augmentation(ElanServiceChainState.class)
262 .child(ElanToPseudoPortData.class, new ElanToPseudoPortDataKey(elanInstanceName)).build();
264 if ( addOrRemove == NwConstants.ADD_FLOW ) {
265 ElanToPseudoPortData newValue =
266 new ElanToPseudoPortDataBuilder().setKey(key).setElanInstanceName(elanInstanceName)
267 .setElanLportTag((long) lportTag).setScfTag(scfTag).build();
268 MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, path, newValue);
270 MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, path);
275 * Read from ElanToLportTagMap the PsuedoLogicalPort related with a given elan.
277 * @param broker dataBroker service reference
278 * @param elanInstanceName
279 * @return the ElanToPseudoPortData object or Optional.absent() if it
282 public static Optional<ElanToPseudoPortData> getElanToLportTagList(final DataBroker broker, final String elanInstanceName) {
283 ElanToPseudoPortDataKey key = new ElanToPseudoPortDataKey(elanInstanceName);
284 InstanceIdentifier<ElanToPseudoPortData> path = InstanceIdentifier.builder(ElanInstances.class)
285 .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName))
286 .augmentation(ElanServiceChainState.class)
287 .child(ElanToPseudoPortData.class, key).build();
289 Optional<ElanToPseudoPortData> elanLPortListOpc =
290 MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, path);
291 if (!elanLPortListOpc.isPresent()) {
292 logger.warn("Could not find and LPort for elan {}", elanInstanceName);
293 return Optional.absent();
296 return elanLPortListOpc;