Supporting DHCP as a service
[genius.git] / interfacemanager / interfacemanager-impl / src / main / java / org / opendaylight / genius / interfacemanager / servicebindings / flowbased / utilities / FlowBasedServicesUtils.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.genius.interfacemanager.servicebindings.flowbased.utilities;
9
10 import com.google.common.base.Optional;
11 import com.google.common.collect.ImmutableBiMap;
12 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
13 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
14 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
15 import org.opendaylight.genius.interfacemanager.IfmConstants;
16 import org.opendaylight.genius.interfacemanager.IfmUtil;
17 import org.opendaylight.genius.interfacemanager.commons.InterfaceManagerCommonUtils;
18 import org.opendaylight.genius.mdsalutil.*;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.WriteMetadataCase;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.*;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfoKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.InterfaceTypeBase;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.InterfaceTypeGre;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.InterfaceTypeVxlan;
45 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 import java.math.BigInteger;
50 import java.util.ArrayList;
51 import java.util.Collections;
52 import java.util.Comparator;
53 import java.util.List;
54
55 public class FlowBasedServicesUtils {
56     private static final Logger LOG = LoggerFactory.getLogger(FlowBasedServicesUtils.class);
57
58     public enum ServiceMode  {
59         INGRESS,
60         EGRESS;
61     }
62
63     public static final ImmutableBiMap SERVICE_MODE_MAP =
64             new ImmutableBiMap.Builder<ServiceMode, Class<? extends ServiceModeBase>>()
65                     .put(ServiceMode.EGRESS, ServiceModeEgress.class)
66                     .put(ServiceMode.INGRESS, ServiceModeIngress.class)
67                     .build();
68
69     public static ServicesInfo getServicesInfoForInterface(String interfaceName, Class<? extends ServiceModeBase> serviceMode,
70                                                            DataBroker dataBroker) {
71         ServicesInfoKey servicesInfoKey = new ServicesInfoKey(interfaceName,serviceMode);
72         InstanceIdentifier.InstanceIdentifierBuilder<ServicesInfo> servicesInfoIdentifierBuilder =
73                 InstanceIdentifier.builder(ServiceBindings.class).child(ServicesInfo.class, servicesInfoKey);
74         Optional<ServicesInfo> servicesInfoOptional = IfmUtil.read(LogicalDatastoreType.CONFIGURATION,
75                 servicesInfoIdentifierBuilder.build(), dataBroker);
76
77         if (servicesInfoOptional.isPresent()) {
78             return servicesInfoOptional.get();
79         }
80
81         return null;
82     }
83
84     public static NodeConnectorId getNodeConnectorIdFromInterface(String interfaceName, DataBroker dataBroker) {
85         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface ifState =
86                 InterfaceManagerCommonUtils.getInterfaceStateFromOperDS(interfaceName, dataBroker);
87         if(ifState != null) {
88             List<String> ofportIds = ifState.getLowerLayerIf();
89             return new NodeConnectorId(ofportIds.get(0));
90         }
91         return null;
92     }
93
94     public static NodeConnectorId getNodeConnectorIdFromInterface(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface ifState) {
95         if(ifState != null) {
96             List<String> ofportIds = ifState.getLowerLayerIf();
97             return new NodeConnectorId(ofportIds.get(0));
98         }
99         return null;
100     }
101
102     public static BigInteger getDpnIdFromInterface(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface ifState) {
103         NodeConnectorId nodeConnectorId = null;
104         if(ifState != null) {
105             List<String> ofportIds = ifState.getLowerLayerIf();
106             nodeConnectorId = new NodeConnectorId(ofportIds.get(0));
107         }
108         return new BigInteger(IfmUtil.getDpnFromNodeConnectorId(nodeConnectorId));
109     }
110
111     public static List<MatchInfo> getMatchInfoForVlanPortAtIngressTable(BigInteger dpId, long portNo, Interface iface) {
112         List<MatchInfo> matches = new ArrayList<>();
113         matches.add(new MatchInfo(MatchFieldType.in_port, new BigInteger[] {dpId, BigInteger.valueOf(portNo)}));
114         int vlanId = 0;
115         IfL2vlan l2vlan = iface.getAugmentation(IfL2vlan.class);
116         if(l2vlan != null && l2vlan.getL2vlanMode() != IfL2vlan.L2vlanMode.Transparent){
117             vlanId = l2vlan.getVlanId() == null ? 0 : l2vlan.getVlanId().getValue();
118         }
119         if (vlanId > 0) {
120             matches.add(new MatchInfo(MatchFieldType.vlan_vid, new long[]{vlanId}));
121         }
122         return matches;
123     }
124
125     public static List<MatchInfo> getMatchInfoForTunnelPortAtIngressTable(BigInteger dpId, long portNo) {
126         List<MatchInfo> matches = new ArrayList<MatchInfo>();
127         matches.add(new MatchInfo(MatchFieldType.in_port, new BigInteger[]{dpId, BigInteger.valueOf(portNo)}));
128         return matches;
129     }
130
131     public static List<MatchInfo> getMatchInfoForDispatcherTable(BigInteger dpId,
132                                                                  int interfaceTag, short servicePriority) {
133         List<MatchInfo> matches = new ArrayList<MatchInfo>();
134         matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
135                 MetaDataUtil.getMetaDataForLPortDispatcher(interfaceTag, servicePriority),
136                 MetaDataUtil.getMetaDataMaskForLPortDispatcher() }));
137         return matches;
138     }
139
140     public static void installInterfaceIngressFlow(BigInteger dpId, Interface iface,
141                                                    BoundServices boundServiceNew,
142                                                    WriteTransaction t,
143                                                    List<MatchInfo> matches, int lportTag, short tableId) {
144         List<Instruction> instructions = boundServiceNew.getAugmentation(StypeOpenflow.class).getInstruction();
145
146         int serviceInstructionsSize = instructions.size();
147         List<Instruction> instructionSet = new ArrayList<Instruction>();
148         int vlanId = 0;
149         IfL2vlan l2vlan = iface.getAugmentation(IfL2vlan.class);
150         if(l2vlan != null && l2vlan.getVlanId() != null){
151             vlanId = l2vlan.getVlanId().getValue();
152         }
153         if (vlanId != 0) {
154             // incrementing instructionSize and using it as actionKey. Because it won't clash with any other instructions
155             int actionKey = ++serviceInstructionsSize;
156             instructionSet.add(MDSALUtil.buildAndGetPopVlanActionInstruction(actionKey, ++serviceInstructionsSize));
157         }
158
159         if (lportTag != 0L) {
160             BigInteger[] metadataValues = IfmUtil.mergeOpenflowMetadataWriteInstructions(instructions);
161             short sIndex = boundServiceNew.getServicePriority();
162             BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(lportTag,
163                     ++sIndex, metadataValues[0]);
164             BigInteger metadataMask = MetaDataUtil.getMetaDataMaskForLPortDispatcher(
165                     MetaDataUtil.METADATA_MASK_SERVICE_INDEX,
166                     MetaDataUtil.METADATA_MASK_LPORT_TAG, metadataValues[1]);
167             instructionSet.add(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask,
168                     ++serviceInstructionsSize));
169         }
170
171         if (instructions != null && !instructions.isEmpty()) {
172             for (Instruction info : instructions) {
173                 // Skip meta data write as that is handled already
174                 if (info.getInstruction() instanceof WriteMetadataCase) {
175                     continue;
176                 }
177                 instructionSet.add(info);
178             }
179         }
180
181         String serviceRef = boundServiceNew.getServiceName();
182         String flowRef = getFlowRef(dpId, iface.getName(), boundServiceNew, boundServiceNew.getServicePriority());
183         StypeOpenflow stypeOpenflow = boundServiceNew.getAugmentation(StypeOpenflow.class);
184         Flow ingressFlow = MDSALUtil.buildFlowNew(tableId, flowRef,
185                 stypeOpenflow.getFlowPriority(), serviceRef, 0, 0,
186                 stypeOpenflow.getFlowCookie(), matches, instructionSet);
187         installFlow(dpId, ingressFlow, t);
188     }
189
190     public static void installFlow(BigInteger dpId, Flow flow, WriteTransaction t) {
191         FlowKey flowKey = new FlowKey(new FlowId(flow.getId()));
192         Node nodeDpn = buildInventoryDpnNode(dpId);
193         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
194                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
195                 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class,flowKey).build();
196
197         t.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId, flow, true);
198     }
199
200     public static void removeFlow(String flowRef, BigInteger dpId, WriteTransaction t) {
201         LOG.debug("Removing Ingress Flows");
202         FlowKey flowKey = new FlowKey(new FlowId(flowRef));
203         Node nodeDpn = buildInventoryDpnNode(dpId);
204         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
205                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
206                 .child(Table.class, new TableKey(NwConstants.VLAN_INTERFACE_INGRESS_TABLE)).child(Flow.class, flowKey).build();
207
208         t.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
209     }
210
211     private static Node buildInventoryDpnNode(BigInteger dpnId) {
212         NodeId nodeId = new NodeId("openflow:" + dpnId);
213         Node nodeDpn = new NodeBuilder().setId(nodeId).setKey(new NodeKey(nodeId)).build();
214
215         return nodeDpn;
216     }
217
218     public static void installLPortDispatcherFlow(BigInteger dpId, BoundServices boundService, String interfaceName,
219                                                   WriteTransaction t, int interfaceTag, short currentServiceIndex, short nextServiceIndex) {
220         LOG.debug("Installing LPort Dispatcher Flows {}, {}", dpId, interfaceName);
221         String serviceRef = boundService.getServiceName();
222         List<MatchInfo> matches = FlowBasedServicesUtils.getMatchInfoForDispatcherTable(dpId,
223                 interfaceTag, currentServiceIndex);
224
225         // Get the metadata and mask from the service's write metadata instruction
226         StypeOpenflow stypeOpenFlow = boundService.getAugmentation(StypeOpenflow.class);
227         List<Instruction> serviceInstructions = stypeOpenFlow.getInstruction();
228         int instructionSize = serviceInstructions.size();
229         BigInteger[] metadataValues = IfmUtil.mergeOpenflowMetadataWriteInstructions(serviceInstructions);
230         BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(interfaceTag, nextServiceIndex, metadataValues[0]);
231         BigInteger metadataMask = MetaDataUtil.getWriteMetaDataMaskForDispatcherTable();
232
233         // build the final instruction for LPort Dispatcher table flow entry
234         List<Instruction> instructions = new ArrayList<Instruction>();
235         instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask, ++instructionSize));
236         if (serviceInstructions != null && !serviceInstructions.isEmpty()) {
237             for (Instruction info : serviceInstructions) {
238                 // Skip meta data write as that is handled already
239                 if (info.getInstruction() instanceof WriteMetadataCase) {
240                     continue;
241                 }
242                 instructions.add(info);
243             }
244         }
245
246         // build the flow and install it
247         String flowRef = getFlowRef(dpId, interfaceName, boundService, currentServiceIndex);
248         Flow ingressFlow = MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
249                 boundService.getServicePriority(), serviceRef, 0, 0, stypeOpenFlow.getFlowCookie(), matches, instructions);
250         installFlow(dpId, ingressFlow, t);
251     }
252
253     public static void removeIngressFlow(String name, BoundServices serviceOld, BigInteger dpId, WriteTransaction t) {
254         LOG.debug("Removing Ingress Flows");
255         String flowKeyStr = getFlowRef(dpId, name, serviceOld, serviceOld.getServicePriority());
256         FlowKey flowKey = new FlowKey(new FlowId(flowKeyStr));
257         Node nodeDpn = buildInventoryDpnNode(dpId);
258         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
259                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
260                 .child(Table.class, new TableKey(NwConstants.VLAN_INTERFACE_INGRESS_TABLE)).child(Flow.class, flowKey).build();
261
262         t.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
263     }
264
265     public static void removeLPortDispatcherFlow(BigInteger dpId, String iface, BoundServices boundServicesOld, WriteTransaction t, short currentServiceIndex) {
266         LOG.debug("Removing LPort Dispatcher Flows {}, {}", dpId, iface);
267
268         StypeOpenflow stypeOpenFlow = boundServicesOld.getAugmentation(StypeOpenflow.class);
269         // build the flow and install it
270         String flowRef = getFlowRef(dpId, iface, boundServicesOld, currentServiceIndex);
271         FlowKey flowKey = new FlowKey(new FlowId(flowRef));
272         Node nodeDpn = buildInventoryDpnNode(dpId);
273         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
274                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
275                 .child(Table.class, new TableKey(NwConstants.LPORT_DISPATCHER_TABLE)).child(Flow.class, flowKey).build();
276
277         t.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
278     }
279
280     private static String getFlowRef(BigInteger dpnId, String iface, BoundServices service, short currentServiceIndex) {
281         return new StringBuffer().append(dpnId).append(NwConstants.VLAN_INTERFACE_INGRESS_TABLE).append(NwConstants.FLOWID_SEPARATOR)
282                 .append(iface).append(NwConstants.FLOWID_SEPARATOR).append(currentServiceIndex).toString();
283     }
284
285     /**
286      * This util method returns an array of ServiceInfo in which index 0 will
287      * have the immediate lower priority service and index 1 will have the
288      * immediate higher priority service among the list of existing serviceInfos
289      *
290      * @param serviceInfos
291      * @param currentServiceInfo
292      * @return
293      */
294     public static BoundServices[] getHighAndLowPriorityService(
295             List<BoundServices> serviceInfos, BoundServices currentServiceInfo) {
296         BoundServices higher = null; // this will be used to hold the immediate higher service priority with respect to the currentServiceInfo
297         BoundServices lower = null; // this will be used to hold the immediate lower service priority with respect to the currentServiceInfo
298         if (serviceInfos == null || serviceInfos.isEmpty()) {
299             return new BoundServices[]{lower, higher};
300         }
301         List <BoundServices> availableServiceInfos = new ArrayList<BoundServices>(serviceInfos);
302         Collections.sort(availableServiceInfos, new Comparator<BoundServices>() {
303             @Override
304             public int compare(BoundServices serviceInfo1, BoundServices serviceInfo2) {
305                 return serviceInfo1.getServicePriority().compareTo(serviceInfo2.getServicePriority());
306             }
307         });
308         for (BoundServices availableServiceInfo: availableServiceInfos) {
309             if (currentServiceInfo.getServicePriority() < availableServiceInfo.getServicePriority()) {
310                 lower = availableServiceInfo;
311                 break;
312             } else {
313                 higher = availableServiceInfo;
314             }
315         }
316         return new BoundServices[]{lower,higher};
317     }
318
319     public static BoundServices getHighestPriorityService(List<BoundServices> serviceInfos) {
320         List <BoundServices> availableServiceInfos = new ArrayList<BoundServices>(serviceInfos);
321         if (availableServiceInfos.isEmpty()) {
322             return null;
323         }
324         BoundServices highPriorityService = availableServiceInfos.get(0);
325         availableServiceInfos.remove(0);
326         for (BoundServices availableServiceInfo: availableServiceInfos) {
327             if (availableServiceInfo.getServicePriority() < highPriorityService.getServicePriority()) {
328                 highPriorityService = availableServiceInfo;
329             }
330         }
331         return highPriorityService;
332     }
333
334     public static void installVlanFlow(BigInteger dpId, long portNo, Interface iface,
335                                        WriteTransaction t, List<MatchInfo> matches, int lportTag) {
336         int vlanId = 0;
337         boolean isVlanTransparent = false;
338         IfL2vlan l2vlan = iface.getAugmentation(IfL2vlan.class);
339         if(l2vlan != null){
340             vlanId = l2vlan.getVlanId() == null ? 0 : l2vlan.getVlanId().getValue();
341             isVlanTransparent = l2vlan.getL2vlanMode() == IfL2vlan.L2vlanMode.Transparent;
342         }
343         int instructionKey = 0;
344         BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(lportTag, (short) 0);
345         BigInteger metadataMask = MetaDataUtil.getMetaDataMaskForLPortDispatcher();
346         List<Instruction> instructions = new ArrayList<Instruction>();
347         if (vlanId != 0 && !isVlanTransparent) {
348             instructions.add(MDSALUtil.buildAndGetPopVlanActionInstruction(lportTag, instructionKey++));
349         }
350         instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(metadata, metadataMask, instructionKey++));
351         instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.LPORT_DISPATCHER_TABLE, instructionKey++));
352         int priority =  isVlanTransparent ? 1 : vlanId == 0 ? IfmConstants.FLOW_PRIORITY_FOR_UNTAGGED_VLAN : IfmConstants.FLOW_HIGH_PRIORITY;
353         String flowRef = getFlowRef(IfmConstants.VLAN_INTERFACE_INGRESS_TABLE, dpId, iface.getName());
354         Flow ingressFlow = MDSALUtil.buildFlowNew(IfmConstants.VLAN_INTERFACE_INGRESS_TABLE, flowRef, priority, flowRef, 0, 0,
355                 IfmConstants.VLAN_TABLE_COOKIE, matches, instructions);
356         installFlow(dpId, ingressFlow, t);
357     }
358
359     public static String getFlowRef(short tableId, BigInteger dpnId, String infName) {
360         return String.format("%d:%s:%s", tableId, dpnId, infName);
361     }
362
363     public static void removeIngressFlow(String interfaceName, BigInteger dpId, WriteTransaction t) {
364         if(dpId == null){
365             return;
366         }
367         LOG.debug("Removing Ingress Flows for {}", interfaceName);
368         String flowKeyStr = getFlowRef(IfmConstants.VLAN_INTERFACE_INGRESS_TABLE, dpId, interfaceName);
369         FlowKey flowKey = new FlowKey(new FlowId(flowKeyStr));
370         Node nodeDpn = buildInventoryDpnNode(dpId);
371         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
372                 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
373                 .child(Table.class, new TableKey(NwConstants.VLAN_INTERFACE_INGRESS_TABLE)).child(Flow.class, flowKey).build();
374
375         t.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
376     }
377 }