Fixed the use of index NwConstants
[netvirt.git] / vpnservice / cloud-servicechain / cloud-servicechain-impl / src / main / java / org / opendaylight / netvirt / cloudservicechain / utils / ElanServiceChainUtils.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netvirt.cloudservicechain.utils;
9
10 import java.math.BigInteger;
11 import java.util.Collection;
12 import java.util.HashSet;
13 import java.util.List;
14
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.utils.ServiceIndex;
40 import org.opendaylight.genius.mdsalutil.MatchFieldType;
41 import org.opendaylight.genius.mdsalutil.MatchInfo;
42 import java.util.Arrays;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 import com.google.common.base.Optional;
47
48 public class ElanServiceChainUtils {
49
50     private static final Logger logger = LoggerFactory.getLogger(ElanServiceChainUtils.class);
51
52
53     public static InstanceIdentifier<ElanInstance> getElanInstanceConfigurationDataPath(String elanInstanceName) {
54         return InstanceIdentifier.builder(ElanInstances.class).child(ElanInstance.class, new ElanInstanceKey(elanInstanceName)).build();
55     }
56
57     public static Optional<ElanInstance> getElanInstanceByName(DataBroker broker, String elanName) {
58         return MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION,
59                 getElanInstanceConfigurationDataPath(elanName));
60     }
61
62     public static Optional<Collection<BigInteger>> getElanDnsByName(DataBroker broker, String elanInstanceName) {
63         InstanceIdentifier<ElanDpnInterfacesList> elanDpnIfacesIid =
64                 InstanceIdentifier.builder(ElanDpnInterfaces.class)
65                                   .child(ElanDpnInterfacesList.class,new ElanDpnInterfacesListKey(elanInstanceName))
66                                   .build();
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();
72         }
73
74         Collection<BigInteger> dpns = new HashSet<BigInteger>();
75         List<DpnInterfaces> elanDpnIfaces = elanDpnIfacesOpc.get().getDpnInterfaces();
76         for ( DpnInterfaces dpnIf : elanDpnIfaces) {
77             dpns.add(dpnIf.getDpId());
78         }
79
80         return Optional.of(dpns);
81     }
82
83     public static BigInteger getElanMetadataLabel(long elanTag) {
84         return (BigInteger.valueOf(elanTag)).shiftLeft(24);
85     }
86
87     /**
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.
92      *
93      * @param dpnId  Dpn Id where the LPortDispatcher table must be modified
94      * @param elanLportTag Dataplane identifier of the ElanPseudoPort
95      * @param elanTag Dataplane identifier of the ELAN
96      * @param addOrRemove  States if flows must be added or removed
97      */
98     public static void programLPortDispatcherToScf(IMdsalApiManager mdsalManager, BigInteger dpnId, int elanTag,
99             int elanLportTag, short tableId, int 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,
113                             instructionKey++),
114                     MDSALUtil.buildAndGetGotoTableInstruction(tableId, instructionKey++) );
115
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);
121         } else {
122             Flow flow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
123                     .setId(new FlowId(flowRef)).build();
124             mdsalManager.removeFlow(dpnId, flow);
125         }
126     }
127
128     /**
129      * This flow is in charge of handling packets coming from the SCF Pipeline
130      * when there is no matching ServiceChain.
131      *
132      *  + Matches on ElanPseudoPortTag and SI=3 (ELAN)
133      *  + Sets elanTag and sends to DMAC table
134      *
135      * @param dpnId Dpn Id where the LPortDispatcher table must be modified
136      * @param elanLportTag Dataplane identifier of the ElanPseudoPort
137      * @param elanTag Dataplane identifier of the ELAN
138      * @param addOrRemove States if flows must be added or removed
139      */
140     public static void programLPortDispatcherFromScf(IMdsalApiManager mdsalManager, BigInteger dpnId,
141                                                      int elanLportTag, int 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),
157                             instructionKey++),
158                     MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.ELAN_SMAC_TABLE,
159                             instructionKey++) );
160
161             Flow flow =
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);
167         } else {
168             Flow flow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
169                     .setId(new FlowId(flowRef)).build();
170             mdsalManager.removeFlow(dpnId, flow);
171         }
172     }
173
174
175     /**
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.
182      *
183      *  + Matches on the VNI
184      *  + Sets SI=1 and ElanPseudoPort tag in the Metadata and sends to
185      *    LPortDispatcher via table 80.
186      *
187      * @param dpnId Dpn Id where the ExtTunnel table must be modified
188      * @param elanLportTag Dataplane identifier of the ElanPseudoPort
189      * @param vni Virtual Network Identifier
190      * @param elanTag Dataplane identifier of the ELAN
191      * @param addOrRemove States if flows must be added or removed
192      */
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);
207         } else {
208             Flow flow = new FlowBuilder().setTableId(NwConstants.EXTERNAL_TUNNEL_TABLE).setId(new FlowId(flowRef)).build();
209             mdsalManager.removeFlow(dpnId, flow);
210         }
211     }
212
213     /**
214      * Builds a List of Instructions that set the ElanPseudoPort Tag in
215      * metadata and sends to LPortDispatcher table (via Table 80)
216      *
217      * @param lportTag Dataplane identifier of the ElanPseudoPort
218      *
219      * @return the List of Instructions
220      */
221     public static List<Instruction> buildSetLportTagAndGotoLportDispInstructions(long lportTag) {
222         int instructionKey = 0;
223         BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher((int) 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(),
228                         ++instructionKey),
229                         MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.L3_INTERFACE_TABLE, ++instructionKey));
230         return result;
231     }
232
233     private static String buildExtTunnelTblToLportDispFlowRef(Long vni, int elanLportTag) {
234         return CloudServiceChainConstants.L2_FLOWID_PREFIX + vni
235                 + NwConstants.FLOWID_SEPARATOR + elanLportTag;
236     }
237
238     private static String buildLportDispToScfFlowRef(int elanLportTag, int scfTag) {
239         return CloudServiceChainConstants.ELAN_TO_SCF_L2_FLOWID_PREFIX + elanLportTag
240                 + NwConstants.FLOWID_SEPARATOR + scfTag;
241     }
242
243     private static String buildLportDispFromScfFlowRef(int elanTag, int elanLportTag) {
244         return CloudServiceChainConstants.SCF_TO_ELAN_L2_FLOWID_PREFIX + elanTag
245                 + NwConstants.FLOWID_SEPARATOR + elanLportTag;
246     }
247
248     /**
249      * Stores the relation between elanInstanceName and ElanLport and scfTag.
250      *
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
256      */
257     public static void updateElanToLportTagMap(final DataBroker broker, final String elanInstanceName,
258                                                 final int lportTag, final int scfTag, final int addOrRemove) {
259         ElanToPseudoPortDataKey key = new ElanToPseudoPortDataKey(elanInstanceName);
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(elanInstanceName)).build();
264
265         if ( addOrRemove == NwConstants.ADD_FLOW ) {
266             ElanToPseudoPortData newValue =
267                     new ElanToPseudoPortDataBuilder().setKey(key).setElanInstanceName(elanInstanceName)
268                                                      .setElanLportTag((long) lportTag).setScfTag(scfTag).build();
269             MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, path, newValue);
270         } else {
271             MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, path);
272         }
273     }
274
275     /**
276      * Read from ElanToLportTagMap the PsuedoLogicalPort related with a given elan.
277      *
278      * @param broker dataBroker service reference
279      * @param elanInstanceName
280      * @return the ElanToPseudoPortData object or Optional.absent() if it
281      *     cannot be found
282      */
283     public static Optional<ElanToPseudoPortData> getElanToLportTagList(final DataBroker broker, final String elanInstanceName) {
284         ElanToPseudoPortDataKey key = new ElanToPseudoPortDataKey(elanInstanceName);
285         InstanceIdentifier<ElanToPseudoPortData> path = InstanceIdentifier.builder(ElanInstances.class)
286                 .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName))
287                 .augmentation(ElanServiceChainState.class)
288                 .child(ElanToPseudoPortData.class, key).build();
289
290         Optional<ElanToPseudoPortData> elanLPortListOpc =
291                 MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, path);
292         if (!elanLPortListOpc.isPresent()) {
293             logger.warn("Could not find and LPort for elan {}", elanInstanceName);
294             return Optional.absent();
295         }
296
297         return elanLPortListOpc;
298
299     }
300 }
301