Merge "Bug 7466 - NPE thrown for interface without lport tag"
[genius.git] / interfacemanager / interfacemanager-impl / src / main / java / org / opendaylight / genius / interfacemanager / servicebindings / flowbased / config / helpers / FlowBasedIngressServicesConfigUnbindHelper.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.config.helpers;
9
10 import com.google.common.util.concurrent.ListenableFuture;
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.concurrent.ConcurrentHashMap;
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
18 import org.opendaylight.genius.interfacemanager.IfmUtil;
19 import org.opendaylight.genius.interfacemanager.InterfacemgrProvider;
20 import org.opendaylight.genius.interfacemanager.commons.InterfaceManagerCommonUtils;
21 import org.opendaylight.genius.interfacemanager.servicebindings.flowbased.config.factory.FlowBasedServicesConfigRemovable;
22 import org.opendaylight.genius.interfacemanager.servicebindings.flowbased.utilities.FlowBasedServicesUtils;
23 import org.opendaylight.genius.mdsalutil.MatchInfo;
24 import org.opendaylight.genius.mdsalutil.NwConstants;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.Tunnel;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeBase;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
33 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 public class FlowBasedIngressServicesConfigUnbindHelper implements FlowBasedServicesConfigRemovable {
38     private static final Logger LOG = LoggerFactory.getLogger(FlowBasedIngressServicesConfigUnbindHelper.class);
39
40     private final InterfacemgrProvider interfaceMgrProvider;
41     private static volatile FlowBasedServicesConfigRemovable flowBasedIngressServicesRemovable;
42
43     private FlowBasedIngressServicesConfigUnbindHelper(InterfacemgrProvider interfaceMgrProvider) {
44         this.interfaceMgrProvider = interfaceMgrProvider;
45     }
46
47     public static void intitializeFlowBasedIngressServicesConfigRemoveHelper(
48             InterfacemgrProvider interfaceMgrProvider) {
49         if (flowBasedIngressServicesRemovable == null) {
50             synchronized (FlowBasedIngressServicesConfigUnbindHelper.class) {
51                 if (flowBasedIngressServicesRemovable == null) {
52                     flowBasedIngressServicesRemovable = new FlowBasedIngressServicesConfigUnbindHelper(
53                             interfaceMgrProvider);
54                 }
55             }
56         }
57     }
58
59     public static FlowBasedServicesConfigRemovable getFlowBasedIngressServicesRemoveHelper() {
60         if (flowBasedIngressServicesRemovable == null) {
61             LOG.error("FlowBasedIngressBindHelper is not initialized");
62         }
63         return flowBasedIngressServicesRemovable;
64     }
65
66     public static void clearFlowBasedIngressServicesConfigUnbindHelper() {
67         flowBasedIngressServicesRemovable = null;
68     }
69
70     @Override
71     public List<ListenableFuture<Void>> unbindService(InstanceIdentifier<BoundServices> instanceIdentifier,
72                                                              BoundServices boundServiceOld) {
73         List<ListenableFuture<Void>> futures = new ArrayList<>();
74         DataBroker dataBroker = interfaceMgrProvider.getDataBroker();
75         String interfaceName =
76                 InstanceIdentifier.keyOf(instanceIdentifier.firstIdentifierOf(ServicesInfo.class)).getInterfaceName();
77         Class<? extends ServiceModeBase> serviceMode = InstanceIdentifier
78                 .keyOf(instanceIdentifier.firstIdentifierOf(ServicesInfo.class)).getServiceMode();
79
80         // Get the Parent ServiceInfo
81         ServicesInfo servicesInfo = FlowBasedServicesUtils.getServicesInfoForInterface(interfaceName, serviceMode,
82                 dataBroker);
83         if (servicesInfo == null) {
84             LOG.error("Reached Impossible part in the code for bound service: {}", boundServiceOld);
85             return futures;
86         }
87
88         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface
89             ifState = InterfaceManagerCommonUtils.getInterfaceStateFromOperDS(interfaceName, dataBroker);
90         if (ifState == null || ifState.getType() == null || ifState.getOperStatus() == OperStatus.Down) {
91             LOG.info("Not unbinding Service since operstatus is DOWN for Interface: {}", interfaceName);
92             return futures;
93         }
94         List<BoundServices> boundServices = servicesInfo.getBoundServices();
95
96         // Split based on type of interface....
97         if (ifState.getType().isAssignableFrom(L2vlan.class)) {
98             return unbindServiceOnVlan(boundServiceOld, boundServices, ifState, dataBroker);
99         } else if (ifState.getType().isAssignableFrom(Tunnel.class)) {
100             return unbindServiceOnTunnel(boundServiceOld, boundServices, ifState, dataBroker);
101         }
102         return futures;
103     }
104
105     private static List<ListenableFuture<Void>> unbindServiceOnVlan(BoundServices boundServiceOld,
106             List<BoundServices> boundServices,
107             org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
108             .Interface ifaceState, DataBroker dataBroker) {
109
110         List<ListenableFuture<Void>> futures = new ArrayList<>();
111         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
112         BigInteger dpId = FlowBasedServicesUtils.getDpnIdFromInterface(ifaceState);
113         if (boundServices.isEmpty()) {
114             // Remove default entry from Lport Dispatcher Table.
115             FlowBasedServicesUtils.removeLPortDispatcherFlow(dpId, ifaceState.getName(), boundServiceOld, tx,
116                     NwConstants.DEFAULT_SERVICE_INDEX);
117             if (tx != null) {
118                 futures.add(tx.submit());
119             }
120             return futures;
121         }
122         BoundServices[] highLow = FlowBasedServicesUtils.getHighAndLowPriorityService(boundServices, boundServiceOld);
123         BoundServices low = highLow[0];
124         BoundServices high = highLow[1];
125         // This means the one removed was the highest priority service
126         if (high == null) {
127             LOG.trace("Deleting table entry for service {}, match service index {}", boundServiceOld,
128                     NwConstants.DEFAULT_SERVICE_INDEX);
129             FlowBasedServicesUtils.removeLPortDispatcherFlow(dpId, ifaceState.getName(), boundServiceOld, tx,
130                     NwConstants.DEFAULT_SERVICE_INDEX);
131             if (low != null) {
132                 //delete the lower services flow entry.
133                 LOG.trace("Deleting table entry for lower service {}, match service index {}", low,
134                         low.getServicePriority());
135                 FlowBasedServicesUtils.removeLPortDispatcherFlow(dpId, ifaceState.getName(), low, tx,
136                         low.getServicePriority());
137                 BoundServices lower = FlowBasedServicesUtils.getHighAndLowPriorityService(boundServices, low)[0];
138                 short lowerServiceIndex = (short) (lower != null ? lower.getServicePriority()
139                         : low.getServicePriority() + 1);
140                 LOG.trace("Installing new entry for lower service {}, match service index {}, update service index {}",
141                         low, NwConstants.DEFAULT_SERVICE_INDEX, lowerServiceIndex);
142                 FlowBasedServicesUtils.installLPortDispatcherFlow(dpId, low, ifaceState.getName(), tx,
143                         ifaceState.getIfIndex(), NwConstants.DEFAULT_SERVICE_INDEX, lowerServiceIndex);
144             }
145         } else {
146             LOG.trace("Deleting table entry for service {}, match service index {}", boundServiceOld,
147                     boundServiceOld.getServicePriority());
148             FlowBasedServicesUtils.removeLPortDispatcherFlow(dpId, ifaceState.getName(), boundServiceOld, tx,
149                     boundServiceOld.getServicePriority());
150             short lowerServiceIndex = (short) (low != null ? low.getServicePriority()
151                     : boundServiceOld.getServicePriority() + 1);
152             BoundServices highest = FlowBasedServicesUtils.getHighestPriorityService(boundServices);
153             if (high.equals(highest)) {
154                 LOG.trace("Update the existing higher service {}, match service index {}, update service index {}",
155                         high, NwConstants.DEFAULT_SERVICE_INDEX, lowerServiceIndex);
156                 FlowBasedServicesUtils.installLPortDispatcherFlow(dpId, high, ifaceState.getName(), tx,
157                         ifaceState.getIfIndex(), NwConstants.DEFAULT_SERVICE_INDEX, lowerServiceIndex);
158             } else {
159                 LOG.trace("Update the existing higher service {}, match service index {}, update service index {}",
160                         high, high.getServicePriority(), lowerServiceIndex);
161                 FlowBasedServicesUtils.installLPortDispatcherFlow(dpId, high, ifaceState.getName(), tx,
162                         ifaceState.getIfIndex(), high.getServicePriority(), lowerServiceIndex);
163             }
164         }
165         futures.add(tx.submit());
166         return futures;
167     }
168
169     private static List<ListenableFuture<Void>> unbindServiceOnTunnel(BoundServices boundServiceOld,
170             List<BoundServices> boundServices,
171             org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
172             .Interface ifState, DataBroker dataBroker) {
173         List<ListenableFuture<Void>> futures = new ArrayList<>();
174
175         Interface iface = InterfaceManagerCommonUtils.getInterfaceFromConfigDS(ifState.getName(), dataBroker);
176         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
177         NodeConnectorId nodeConnectorId = FlowBasedServicesUtils.getNodeConnectorIdFromInterface(ifState);
178         long portNo = IfmUtil.getPortNumberFromNodeConnectorId(nodeConnectorId);
179         BigInteger dpId = IfmUtil.getDpnFromNodeConnectorId(nodeConnectorId);
180
181         if (boundServices.isEmpty()) {
182             // Remove entry from Ingress Table.
183             FlowBasedServicesUtils.removeIngressFlow(ifState.getName(), boundServiceOld, dpId, tx);
184             if (tx != null) {
185                 futures.add(tx.submit());
186             }
187             return futures;
188         }
189
190         Map<Short, BoundServices> tmpServicesMap = new ConcurrentHashMap<>();
191         short highestPriority = 0xFF;
192         for (BoundServices boundService : boundServices) {
193             tmpServicesMap.put(boundService.getServicePriority(), boundService);
194             if (boundService.getServicePriority() < highestPriority) {
195                 highestPriority = boundService.getServicePriority();
196             }
197         }
198
199         if (highestPriority < boundServiceOld.getServicePriority()) {
200             FlowBasedServicesUtils.removeLPortDispatcherFlow(dpId, ifState.getName(), boundServiceOld, tx,
201                     boundServiceOld.getServicePriority());
202             if (tx != null) {
203                 futures.add(tx.submit());
204             }
205             return futures;
206         }
207
208         List<MatchInfo> matches = null;
209         matches = FlowBasedServicesUtils.getMatchInfoForTunnelPortAtIngressTable(dpId, portNo);
210
211         BoundServices toBeMoved = tmpServicesMap.get(highestPriority);
212         FlowBasedServicesUtils.removeIngressFlow(iface.getName(), boundServiceOld, dpId, tx);
213         FlowBasedServicesUtils.installInterfaceIngressFlow(dpId, iface, toBeMoved, tx,
214                 matches, ifState.getIfIndex(), NwConstants.VLAN_INTERFACE_INGRESS_TABLE);
215         FlowBasedServicesUtils.removeLPortDispatcherFlow(dpId, iface.getName(), toBeMoved, tx,
216                 toBeMoved.getServicePriority());
217
218         if (tx != null) {
219             futures.add(tx.submit());
220         }
221         return futures;
222     }
223
224 }