Optional clean-up
[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 com.google.common.base.Optional;
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.stream.Collectors;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
20 import org.opendaylight.genius.mdsalutil.ActionInfo;
21 import org.opendaylight.genius.mdsalutil.MDSALUtil;
22 import org.opendaylight.genius.mdsalutil.MatchFieldType;
23 import org.opendaylight.genius.mdsalutil.MatchInfo;
24 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
25 import org.opendaylight.genius.mdsalutil.NwConstants;
26 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
27 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
28 import org.opendaylight.genius.utils.ServiceIndex;
29 import org.opendaylight.netvirt.cloudservicechain.CloudServiceChainConstants;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev170511.ElanServiceChainState;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev170511.elan.to.pseudo.port.data.list.ElanToPseudoPortData;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev170511.elan.to.pseudo.port.data.list.ElanToPseudoPortDataBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev170511.elan.to.pseudo.port.data.list.ElanToPseudoPortDataKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanDpnInterfaces;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesList;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.ElanDpnInterfacesListKey;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
46 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 public class ElanServiceChainUtils {
51
52     private static final Logger LOG = LoggerFactory.getLogger(ElanServiceChainUtils.class);
53
54
55     public static InstanceIdentifier<ElanInstance> getElanInstanceConfigDataPath(String elanInstanceName) {
56         return InstanceIdentifier.builder(ElanInstances.class).child(ElanInstance.class,
57                                                                      new ElanInstanceKey(elanInstanceName)).build();
58     }
59
60     public static Optional<ElanInstance> getElanInstanceByName(DataBroker broker, String elanName) {
61         return MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, getElanInstanceConfigDataPath(elanName));
62     }
63
64     public static Collection<BigInteger> getElanDpnsByName(DataBroker broker, String elanInstanceName) {
65         InstanceIdentifier<ElanDpnInterfacesList> elanDpnIfacesIid =
66                 InstanceIdentifier.builder(ElanDpnInterfaces.class)
67                                   .child(ElanDpnInterfacesList.class,new ElanDpnInterfacesListKey(elanInstanceName))
68                                   .build();
69         Optional<ElanDpnInterfacesList> elanDpnIfacesOpc =
70                 MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, elanDpnIfacesIid);
71         if (!elanDpnIfacesOpc.isPresent()) {
72             LOG.warn("Could not find and DpnInterface for elan {}", elanInstanceName);
73             return Collections.emptySet();
74         }
75
76         return elanDpnIfacesOpc.get().getDpnInterfaces().stream().map(DpnInterfaces::getDpId).collect(
77                 Collectors.toSet());
78     }
79
80     public static BigInteger getElanMetadataLabel(long elanTag) {
81         return BigInteger.valueOf(elanTag).shiftLeft(24);
82     }
83
84     /**
85      * This flow is in charge of handling packets coming from ExtTunnelTable
86      * that must be redirected to the SCF Pipeline.
87      *  + Matches on lportTag=ElanPseudoLportTag + SI=1
88      *  + Sets scfTag and sends to the DlSubsFilter table.
89      *
90      * @param dpnId Dpn Id where the flow must be installed
91      * @param elanLportTag the Elan Pseudo Lport Id in Dataplane
92      * @param elanTag the Elan Id in the Dataplane
93      * @param addOrRemove States if the flow must be added or removed
94      */
95     public static void programLPortDispatcherToScf(IMdsalApiManager mdsalManager, BigInteger dpnId, long elanTag,
96             int elanLportTag, short tableId, long scfTag, int addOrRemove) {
97         LOG.info("L2-ServiceChaining: programLPortDispatcherToScf dpId={} elanLportTag={} scfTag={} addOrRemove={} ",
98                  dpnId, elanLportTag, scfTag, addOrRemove);
99         String flowRef = buildLportDispToScfFlowRef(elanLportTag, scfTag);
100         if (addOrRemove == NwConstants.ADD_FLOW) {
101             int instructionKey = 0;
102             List<Instruction> instructions = new ArrayList<>();
103             List<ActionInfo> actionsInfos = new ArrayList<>();
104             actionsInfos.add( new ActionRegLoad(NxmNxReg2.class, 0, 31, scfTag));
105             instructions.add(MDSALUtil.buildApplyActionsInstruction(MDSALUtil
106                     .buildActions(actionsInfos),instructionKey++));
107             instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(tableId, instructionKey++));
108             List<MatchInfo> matches = Arrays.asList(
109                     new MatchInfo(MatchFieldType.metadata,
110                             new BigInteger[] { MetaDataUtil.getMetaDataForLPortDispatcher(elanLportTag,
111                                     ServiceIndex.getIndex(NwConstants.SCF_SERVICE_NAME, NwConstants.SCF_SERVICE_INDEX)),
112                                     MetaDataUtil.getMetaDataMaskForLPortDispatcher() }));
113
114             Flow flow = MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
115                     CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY, flowRef,
116                     0, 0, CloudServiceChainConstants.COOKIE_LPORT_DISPATCHER_BASE.add(BigInteger.valueOf(elanTag)),
117                     matches, instructions);
118             mdsalManager.installFlow(dpnId, flow);
119         } else {
120             Flow flow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
121                                          .setId(new FlowId(flowRef)).build();
122             mdsalManager.removeFlow(dpnId, flow);
123         }
124     }
125
126     /**
127      * This flow is in charge of handling packets coming from the SCF Pipeline
128      * when there is no matching ServiceChain.
129      * <ul>
130      *  <li> Matches on ElanPseudoPortTag and SI=3 (ELAN)</li>
131      *  <li> Sets elanTag and sends to DMAC table</li>
132      * </ul>
133      * @param dpnId Dpn Id where the flow must be installed
134      * @param elanLportTag the Elan Pseudo Lport Id to be used in the Dataplane
135      * @param elanTag the Elan Id to be used in the Dataplane
136      * @param addOrRemove States if the flow must be added or removed
137      */
138     public static void programLPortDispatcherFromScf(IMdsalApiManager mdsalManager, BigInteger dpnId,
139                                                      int elanLportTag, long elanTag, int addOrRemove) {
140         LOG.info("L2-ServiceChaining: programLPortDispatcherFromScf dpId={} elanLportTag={} elanTag={} addOrRemove={} ",
141                  dpnId, elanLportTag, elanTag, addOrRemove);
142         String flowRef = buildLportDispFromScfFlowRef(elanTag, elanLportTag );
143         if (addOrRemove == NwConstants.ADD_FLOW) {
144             List<MatchInfo> matches = Arrays.asList(
145                 new MatchInfo(MatchFieldType.metadata,
146                               new BigInteger[] {
147                                   MetaDataUtil.getMetaDataForLPortDispatcher(elanLportTag,
148                                                                 ServiceIndex.getIndex(NwConstants.ELAN_SERVICE_NAME,
149                                                                                       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_SCF_FLOW_PRIORITY,
164                             flowRef, 0, 0,
165                             CloudServiceChainConstants.COOKIE_LPORT_DISPATCHER_BASE.add(BigInteger.valueOf(elanTag)),
166                             matches, instructions);
167             mdsalManager.installFlow(dpnId, flow);
168         } else {
169             Flow flow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
170                     .setId(new FlowId(flowRef)).build();
171             mdsalManager.removeFlow(dpnId, flow);
172         }
173     }
174
175
176     /**
177      * This flow is in charge of receiving packets from the TOR and sending
178      * them to the SCF Pipeline by setting the LportTag of ElanPseudoPort.
179      * Note that ELAN already has a flow in this table that redirects packets
180      * to the ELAN Pipeline. However, the flow for the SCF Pipeline will have
181      * higher priority, and will only be present when there is a ServiceChain
182      * using this ElanPseudoPort.
183      * <ul>
184      *  <li> Matches on the VNI
185      *  <li> Sets SI=1 and ElanPseudoPort tag in the Metadata and sends to
186      *    LPortDispatcher via table 80.
187      * </ul>
188      * @param dpnId Dpn Id where the flow must be installed
189      * @param elanLportTag the Elan Pseudo Lport Id to be used in the Dataplane
190      * @param vni the VNI to which the Elan is related
191      * @param elanTag the Elan Id to be used in the Dataplane
192      * @param addOrRemove States if the flow must be added or removed
193      */
194     public static void programExternalTunnelTable(IMdsalApiManager mdsalManager, BigInteger dpnId, int elanLportTag,
195             long vni, int elanTag, int addOrRemove) {
196         LOG.info("L2-ServiceChaining: programExternalTunnelTable dpId={} vni={} elanLportTag={} addOrRemove={} ",
197                  dpnId, vni, elanLportTag, addOrRemove);
198         String flowRef = buildExtTunnelTblToLportDispFlowRef(vni, elanLportTag);
199         if (addOrRemove == NwConstants.ADD_FLOW) {
200             List<MatchInfo> matches = Arrays.asList(new MatchInfo(MatchFieldType.tunnel_id,
201                     new BigInteger[] { 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);
208         } else {
209             Flow flow =
210                 new FlowBuilder().setTableId(NwConstants.EXTERNAL_TUNNEL_TABLE).setId(new FlowId(flowRef)).build();
211             mdsalManager.removeFlow(dpnId, flow);
212         }
213     }
214
215     /**
216      * Builds a List of Instructions that set the ElanPseudoPort Tag in
217      * metadata and sends to LPortDispatcher table (via Table 80).
218      *
219      * @param lportTag Dataplane identifier of the ElanPseudoPort
220      *
221      * @return the List of Instructions
222      */
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(),
230                         ++instructionKey),
231                         MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.L3_INTERFACE_TABLE, ++instructionKey));
232         return result;
233     }
234
235     private static String buildExtTunnelTblToLportDispFlowRef(long vni, int elanLportTag) {
236         return CloudServiceChainConstants.L2_FLOWID_PREFIX + vni
237                 + NwConstants.FLOWID_SEPARATOR + elanLportTag;
238     }
239
240     private static String buildLportDispToScfFlowRef(int elanLportTag, long scfTag) {
241         return CloudServiceChainConstants.ELAN_TO_SCF_L2_FLOWID_PREFIX + elanLportTag
242                 + NwConstants.FLOWID_SEPARATOR + scfTag;
243     }
244
245     private static String buildLportDispFromScfFlowRef(long elanTag, int elanLportTag) {
246         return CloudServiceChainConstants.SCF_TO_ELAN_L2_FLOWID_PREFIX + elanTag
247                 + NwConstants.FLOWID_SEPARATOR + elanLportTag;
248     }
249
250     /**
251      * Stores the relation between ElanLport and scfTag.
252      *
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
258      */
259     public static void updateElanToLportTagMap(final DataBroker broker, final String elanInstanceName,
260                                                final int lportTag, final long scfTag, final int addOrRemove) {
261         ElanToPseudoPortDataKey key = new ElanToPseudoPortDataKey(new Long(lportTag), scfTag);
262         InstanceIdentifier<ElanToPseudoPortData> path = InstanceIdentifier.builder(ElanInstances.class)
263                 .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName))
264                 .augmentation(ElanServiceChainState.class)
265                 .child(ElanToPseudoPortData.class, new ElanToPseudoPortDataKey(key)).build();
266
267         if ( addOrRemove == NwConstants.ADD_FLOW ) {
268             ElanToPseudoPortData newValue =
269                     new ElanToPseudoPortDataBuilder().setKey(key).setElanLportTag(new Long(lportTag))
270                                                      .setScfTag(scfTag).build();
271             MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION, path, newValue);
272         } else {
273             MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, path);
274         }
275     }
276
277     /**
278      * Read from ElanToLportTagMap the PsuedoLogicalPort related with a given elan.
279      *
280      * @param broker dataBroker service reference
281      * @param elanInstanceName the name of the Elan
282      * @return the ElanToPseudoPortData object or Optional.absent() if it
283      *     cannot be found
284      */
285     public static Optional<ElanServiceChainState> getElanServiceChainState(final DataBroker broker,
286                                                                            final String elanInstanceName) {
287         InstanceIdentifier<ElanServiceChainState> path = InstanceIdentifier.builder(ElanInstances.class)
288                 .child(ElanInstance.class, new ElanInstanceKey(elanInstanceName))
289                 .augmentation(ElanServiceChainState.class).build();
290         Optional<ElanServiceChainState> elanServiceChainStateOpc =
291             MDSALUtil.read(broker,LogicalDatastoreType.CONFIGURATION, path);
292
293         return elanServiceChainStateOpc;
294
295     }
296 }
297